SwitchLock Docs
Payments / Payment Links

Payment Links

A payment link is a one-time checkout URL that ties a seller, a set of line items, and an amount together. Send it to the buyer — they verify identity and pay without any further involvement from your server.

When to use payment links

Marketplace checkout

Buyer selects a product, your server generates the link, buyer pays via WhatsApp OTP.

Invoice payment

Send a client a link for a service invoice. Link expires in 24 hours if unpaid.

Peer-to-peer trade

One registered user pays another via a single link — no card details required.

Recurring billing

Each billing cycle needs a new link — payment links are single-use only.

Generate a payment link

Call POST /v1/payments/generate-escrow-payment-link from your server. The response includes a paymentUrl — send this to the buyer.

Always include an x-idempotency-key header. Duplicate requests with the same key return the original response instead of creating a second link — safe to retry on network errors.

Amounts are in cents. $250.00 USD = 25000. ZiG 100.00 = 10000. Never pass decimals.

curl -X POST "https://api.switchlock.co.zw/v1/payments/generate-escrow-payment-link" \
  -H "x-api-key: sl_live_a1b2c3d4e5f6..." \
  -H "x-company-id: cmp_01HXYZ..." \
  -H "x-idempotency-key: order-001-2026-06-23" \
  -H "Content-Type: application/json" \
  -d '{
    "sellerId": "usr_01HSELLER...",
    "buyerId": "usr_01HBUYER...",
    "amount": 35000,
    "currency": "USD",
    "externalReference": "ORDER-001",
    "userReference": "INV-2026-001",
    "items": [
      {
        "name": "Laptop Stand",
        "description": "Aluminium adjustable stand",
        "quantity": 1,
        "pricePerUnit": 25000
      },
      {
        "name": "USB-C Hub",
        "description": "7-in-1 hub",
        "quantity": 2,
        "pricePerUnit": 5000
      }
    ],
    "splitPlan": {
      "system_fee_cents": 700,
      "service_fee_cents": 300,
      "seller_payout_cents": 34000
    }
  }'
FieldTypeRequiredNotes
buyerIdUUIDYesThe buyer's user ID within your company
amountinteger (cents)YesTotal amount. Min 1. Must equal sum of items × quantity.
currencyUSD | ZIGYesCurrency for this transaction
itemsInvoiceItemDto[]YesAt least one item required. See sub-table below.
sellerIdUUIDDeveloper model onlyMust belong to the same companyId. Omit for Merchant model.
externalReferencestringNoYour internal order/invoice ID — stored on the transaction
userReferencestringNoUsed for ledger lookup fallback if buyerId lookup fails
splitPlanobjectNoOverride default fee split. See sub-table below.
InvoiceItemDto fields
FieldRequired
nameYes
descriptionNo
quantityYes — min 1
pricePerUnitYes — min 1 (cents)
EscrowSplitPlanDto fields
FieldNotes
system_fee_centsPlatform fee (IMTT / GL)
service_fee_centsYour service commission
seller_payout_centsWhat the seller receives
Response — 201 Created
{
  "id": "lnk_01HPAY...",
  "secureUrlHash": "aB3xK9mN2p",
  "paymentUrl": "https://switchlock.co.zw/checkout/aB3xK9mN2p",
  "status": "PENDING",
  "provider": "MOCK_BANK",
  "totalAmount": "35000",
  "currency": "USD",
  "expiresAt": "2026-06-24T09:00:00.000Z"
}

The secureUrlHash (10-char random slug) is what you pass as paymentSlug in all downstream endpoints — accept-payment, reject-payment, eft-pay/initiate, etc.

Merchant vs. Developer model

Your company's business_model (set at registration) controls how the seller is resolved.

Merchant modelisMerchant: true

You are the seller. Every payment link routes funds to your company's merchant avatar user. The sellerId field in the request is ignored — the system resolves the seller automatically.

Best for: single-seller stores, SaaS billing
Developer modelisMerchant: false

Buyers and sellers are separate registered users in your company. You must pass a valid sellerId on every request — the API returns 400 if it is missing or belongs to a different company.

Best for: marketplaces, peer-to-peer platforms

Link status reference

StatusMeaningTerminal?
PENDINGLink created. Awaiting buyer action. accept-payment and reject-payment are both valid.No
PAIDBuyer verified OTP and funds moved into escrow successfully.Yes
EXPIRED24 hours elapsed without payment. Link can no longer be accepted.Yes
CANCELLEDRejected by the buyer via reject-payment, or cancelled by the system.Yes

Who pays the IMTT?

Zimbabwe's Intermediated Money Transfer Tax (IMTT) applies to every electronic transfer. SwitchLock deducts it automatically at the point of escrow release. The feePayer defaults to SELLER — the seller receives the amount minus IMTT and any service fees.

Default: SELLER absorbs fees

The buyer pays the face amount. IMTT, the service fee, and any system fee are deducted from the seller's payout at release. To override this for a specific transaction, provide a custom splitPlan in the request body.

If you provide a splitPlan, the values must sum to the total amount in the request. The API does not validate this automatically — a mismatch will result in an accounting discrepancy.

Buyer checkout flow

After you generate the link, the buyer completes these steps entirely on their device — no API key required at any point.

1. Request Checkout OTP

The buyer enters their WhatsApp number to receive a 6-digit OTP.

curl -X POST "https://api.switchlock.co.zw/v1/payments/request-checkout-otp" \
  -H "Content-Type: application/json" \
  -d '{
    "whatsappNumber": "+263771234567"
  }'
FieldTypeRequired
whatsappNumberstringYes

2. Register Checkout User (If New)

If the buyer is not already registered on SwitchLock, they must be registered before the payment can be processed.

curl -X POST "https://api.switchlock.co.zw/v1/payments/register-checkout-user" \
  -H "Content-Type: application/json" \
  -d '{
    "mobileNumber": "+263771234567",
    "idNumber": "63-123456-A-00",
    "firstName": "Tendai",
    "lastName": "Moyo"
  }'
FieldTypeRequired
mobileNumberstringYes
idNumberstringYes
firstNamestringNo
lastNamestringNo

3. Accept Payment

The buyer submits the OTP to finalize the transaction. Funds move to escrow and the link status updates to PAID.

curl -X POST "https://api.switchlock.co.zw/v1/payments/accept-payment" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentSlug": "aB3xK9mN2p",
    "whatsappNumber": "+263771234567",
    "otp": "847201"
  }'
FieldTypeRequiredNotes
paymentSlugstringYesThe secureUrlHash from the link generation
whatsappNumberstringNoUsed for OTP verification
phoneNumberstringNoFallback if whatsappNumber isn't used
otpstringNoThe 6-digit code sent to the buyer

Reject Payment

A buyer can explicitly reject a payment link. This permanently transitions the link to CANCELLED.

curl -X POST "https://api.switchlock.co.zw/v1/payments/reject-payment" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentSlug": "aB3xK9mN2p"
  }'
FieldTypeRequired
paymentSlugstringYes

Next steps