Developer guide
VEXPay siteAPI reference
Browse documentation

Recipe: accept payments in an online store

Sell to Venezuelan buyers with one hosted checkout: bolívares, stablecoins and pesos, confirmed by webhook and withdrawn to your bank.

What you will build

A store where each order opens a hosted checkout, the order is marked paid from a verified webhook, and the money is withdrawn to your own bank account. No payment screens to build or certify.

  1. Buyer clicks Pay

    Your server creates a checkout session for the order and redirects the buyer.

  2. Buyer pays on VEXPay

    The buyer picks Pago Móvil, C2P, card, USDT/USDC or pesos and completes it there.

  3. You fulfill

    payment.completed reaches your webhook; you mark the order paid.

  4. You get paid

    After the 24h hold, you withdraw your bolívares to your bank account.

Step 01

Create a session per order

Node SDKjavascript
app.post('/checkout/:orderId', async (req, res) => {
  const order = await orders.get(req.params.orderId);
  const session = await vexpay.checkout.sessions.create(
    {
      amountUsd: order.totalUsd,
      description: `Order #${order.number}`,
      reference: order.id,
      metadata: { orderNumber: String(order.number) },
      successUrl: `https://shop.example/orders/${order.id}/thanks`,
      cancelUrl: 'https://shop.example/cart',
    },
    { idempotencyKey: `checkout-${order.id}` },
  );
  await orders.update(order.id, { checkoutSessionId: session.id });
  res.redirect(303, session.url);
});
  • Price in USD. Bolívar methods convert at your Bolívar rate; pesos at VEX Pay’s USDT/COP rate.
  • Leave methods out to offer everything your account accepts, or pass e.g. ["pago_movil", "usdt"].
  • The idempotency key makes a double click open the same session, not two.

Step 02

Mark the order paid from the webhook

Node SDKjavascript
app.post('/webhooks/vexpay', express.raw({ type: 'application/json' }), async (req, res) => {
  const event = vexpay.webhooks.constructEvent(req.body, req.headers['vexpay-signature'], process.env.VEXPAY_WEBHOOK_SECRET);
  if (await seen(req.headers['vexpay-event-id'])) return res.sendStatus(200);

  const ref = event.data.checkoutSession?.reference;
  if (event.event === 'payment.completed' && ref) await orders.markPaid(ref, event.data.paymentId);
  if (event.event === 'payment.reversed' && ref) await orders.flagReversed(ref);
  res.sendStatus(200);
});

Step 03

Catch anything you missed

Webhooks are delivered once. Every few minutes, look up sessions still waiting on your side and settle them from their real status.

Node SDKjavascript
for (const order of await orders.awaitingPayment({ olderThanMinutes: 5 })) {
  const s = await vexpay.checkout.sessions.retrieve(order.checkoutSessionId);
  if (s.status === 'paid') await orders.markPaid(order.id, s.paymentId);
  if (s.status === 'expired' || s.status === 'canceled') await orders.release(order.id);
}

Step 04

Withdraw to your bank

  1. Verify your payout account once

    Save your bank, cédula/RIF and phone with PUT /v1/tenant/payout-account, then confirm the two micro-deposits (Portal → Payouts does both).

  2. Wait for the hold

    Each sale becomes available 24 hours after it completes. GET /v1/balance shows availableVes and nextReleaseAt.

  3. Withdraw

    POST /v1/payouts/instant with the amount, or press Withdraw in the portal. The bolívar percentage applies to the amount withdrawn.

Crypto sales stay in your USDT balance until you send them with POST /v1/crypto/payouts. Peso sales stay in your COP balance.