Virtual Terminal

Take a payment when the customer is in front of you but you have no physical reader. Create a session, then either show the customer a QR / hosted checkout link for card payments, or push a mobile money prompt to their phone.

Endpoints

All routes live under /v1/merchant/virtual-terminal and require an authenticated merchant with approved KYC.

MethodPathPurpose
GET/v1/merchant/virtual-terminal/sessionsList sessions (paginated, filterable)
POST/v1/merchant/virtual-terminal/sessionsCreate a collection session
GET/v1/merchant/virtual-terminal/sessions/:idRetrieve a session (with QR for pending)
POST/v1/merchant/virtual-terminal/sessions/:id/chargeCharge the session
DELETE/v1/merchant/virtual-terminal/sessions/:idCancel a pending session

Create a session

Supply the amount, currency, and a payment_method (card is the default). The response includes a hosted payment_url and a ready-to-display QR code.

http
POST /v1/merchant/virtual-terminal/sessions
Authorization: Bearer sk_test_...
Content-Type: application/json
Idempotency-Key: vt-2026-06-001

{
  "amount": "25.00",
  "currency": "USD",
  "description": "Counter sale",
  "payment_method": "card",
  "expires_in_minutes": 30
}
json
{
  "success": true,
  "data": {
    "session": {
      "id": "vts_01HQXYZ",
      "status": "pending",
      "amount": "25.0000",
      "currency": "USD",
      "description": "Counter sale",
      "payment_method": "card",
      "expires_at": "2026-06-03T11:00:00.000Z",
      "payment_url": "https://pay.watenga.africa/abc12xyz",
      "slug": "abc12xyz",
      "qr": { "svg": "<svg ...>" }
    }
  }
}

Card payments via QR / link

For card and zimswitch methods, present the payment_url or QR code to the customer — they complete payment on the hosted checkout. Calling charge without a saved token simply returns the link to display.

Charge a mobile money session

For mobile money methods, call charge with the customer's phone number to push a USSD prompt. Then poll the session (or listen for the payment webhook) until it is paid.

http
POST /v1/merchant/virtual-terminal/sessions/vts_01HQXYZ/charge
Authorization: Bearer sk_test_...
Content-Type: application/json

{
  "customer_phone": "+263771234567"
}
json
{
  "success": true,
  "data": {
    "session_id": "vts_01HQXYZ",
    "transaction_id": "txn_01HQ",
    "checkout_id": "chk_01HQ",
    "requires_polling": true,
    "message": "Payment request sent to customer phone"
  }
}
Sessions are single-use and expire automatically (default 30 minutes, configurable 5–120 via expires_in_minutes). Pass an Idempotency-Key on create and charge to safely retry.