ZintlePayments

Documentation

Take your first payment.

Everything below is the real contract: the routes, the statuses, the failure codes, and the rules a correct integration follows.

How it works

A hosted checkout is a payment link made for one order. Your server creates it and redirects the customer — or embeds it in your own page; the checkout does the rest.

  1. Your server calls POST /v1/payment_links with the amount and your order reference, using a secret key or a restricted key with billing.links.manage.
  2. It redirects the customer to the url in the response.
  3. The customer picks a method and pays. The page handles the phone prompt, the provider’s page and the wait.
  4. You receive payment_intent.succeeded with your reference, and fulfil the order.

Create a checkout

currency and title are required, with either amount or lineItems. reference is your order id and comes back on every payment made through the checkout. A checkout takes one payment and closes, unless you send singleUse: false. Limit the methods offered with paymentMethods, set an expiry with expiresAt, and send the payer back to your site with successUrl.

curl https://api.zintle.io/v1/payment_links \
  -H "Authorization: Bearer zk_sk_test_4f2a9c1e8b7d6053" \
  -H "Idempotency-Key: order-4471-checkout" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "500000",
    "currency": "XAF",
    "title": "Order #4471",
    "reference": "order-4471",
    "successUrl": "https://shop.example.com/orders/4471/thanks",
    "metadata": { "cartId": "c_9" }
  }'

Show what is being bought

Send lineItems instead of amount and the page lists each one with its total; the checkout’s amount is their sum. Add imageUrl for a product picture and description for a line under the title. Send lineItems or amount, never both.

# Line items instead of an amount: the total is their sum, and the page
# lists them. Send one or the other, never both.
curl https://api.zintle.io/v1/payment_links \
  -H "Authorization: Bearer zk_sk_test_4f2a9c1e8b7d6053" \
  -H "Idempotency-Key: order-4472-checkout" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "XAF",
    "title": "Order #4472",
    "description": "Delivered in Douala within 48 hours",
    "imageUrl": "https://shop.example.com/images/headphones.jpg",
    "lineItems": [
      { "name": "Wireless headphones", "quantity": 2, "unitAmount": "15000" },
      { "name": "USB-C cable", "quantity": 1, "unitAmount": "2500" }
    ],
    "reference": "order-4472"
  }'

Send the customer to it

Redirect to url, or put it behind a button or in a message. The page shows your business name, logo and colour, then the methods that can take this amount right now.

Embed it in your site

The same checkout can sit inside your own page in an iframe, so the customer never leaves your site. It carries your appearance settings, resizes itself to its content and tells your page when the payment succeeds.

  1. Create the checkout with embedOrigins: the exact origins of the pages that will show it, such as https://shop.example.com. The response then includes an embedUrl.
  2. Load checkout.js on that page and mount the checkout into an element with the embedUrl.
  3. React to onSuccess in the browser for the customer’s benefit, and fulfil the order from the webhook as usual.
# embedOrigins: the sites allowed to show this checkout in an iframe.
# The response then carries an embedUrl.
curl https://api.zintle.io/v1/payment_links \
  -H "Authorization: Bearer zk_sk_test_4f2a9c1e8b7d6053" \
  -H "Idempotency-Key: order-4473-checkout" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "32500",
    "currency": "XAF",
    "title": "Order #4473",
    "reference": "order-4473",
    "embedOrigins": ["https://shop.example.com", "http://localhost:5173"],
    "appearance": {
      "variables": { "colorPrimary": "#0a7cff", "fontFamily": "Poppins", "borderRadius": 12 },
      "layout": { "showMerchant": false }
    }
  }'

# → { ..., "url": "https://dashboard.zintle.io/pay/order-4473-p2k9",
#          "embedUrl": "https://dashboard.zintle.io/embed/pay/order-4473-p2k9", ... }
EventWhat it means
readyThe checkout has loaded. onReady.
statusThe payment changed status. onStatus fires for every change, onSuccess on SUCCEEDED and onFailure on FAILED or CANCELED. Without an onSuccess handler, the script sends the page to your successUrl.
redirectA card or PayPal payment continues on the provider’s page. The checkout opens it in a new tab when the customer clicks, and keeps following the payment. onRedirect is for your information only.
resizeThe checkout’s height changed. The script resizes the frame; you do not need to handle it.

Match your brand

Send appearance when you create the checkout to set its colours, font, corner radius, layout and the pay button’s label. Anything you leave out keeps the checkout’s own design, so a brand colour alone is enough for a finished page. Colours are hex values; nothing is raw CSS.

"appearance": {
  "theme": "light",                 // "auto" (default) | "light" | "dark"
  "variables": {
    "colorPrimary": "#0a7cff",      // buttons, the selected method, focus
    "colorPrimaryText": "#ffffff",  // text on colorPrimary
    "colorBackground": "#ffffff",   // behind the form
    "colorSurface": "#f7f7f8",      // the form card and inputs
    "colorText": "#111827",
    "colorMutedText": "#6b7280",    // labels and hints
    "colorBorder": "#e5e7eb",
    "colorDanger": "#dc2626",       // errors
    "fontFamily": "Poppins",        // "system" or a listed family
    "fontSizeBase": 16,             // px, 12–20; everything scales from it
    "borderRadius": 8               // px, 0–24
  },
  "layout": {
    "methodLayout": "list",         // "grid" (default) | "list"
    "showMerchant": false,          // your logo and name above the form
    "showDescription": true,
    "showLineItems": true
  },
  "labels": {
    "payButton": "Complete order"   // replaces "Pay now", in every language
  }
}

Fonts you can use: system, Inter, Roboto, Open Sans, Lato, Montserrat, Poppins, Nunito, Source Sans 3, Work Sans, DM Sans, Manrope, IBM Plex Sans, Merriweather, Playfair Display

Local prices and methods

The checkout works out where the customer is and adapts to it, without changing what they are charged.

  • The country comes from the customer’s IP address. When it cannot be told — a private network, an unknown address — the checkout treats them as abroad and shows US dollars.
  • Under the price, the checkout shows an estimate in the customer’s own currency (euros in France, pounds in the UK, dollars when unknown). None is shown when they already use the checkout’s currency. Rates are refreshed several times a day.
  • Methods the customer can use where they are come first. Mobile money moves to the end of the list for someone outside the countries it reaches — but stays available, for a customer abroad who still has their number.
# See the page as a payer in another country would: the price estimate
# and the method order change; what is charged does not.
https://dashboard.zintle.io/pay/order-4473-p2k9?country=FR
https://dashboard.zintle.io/pay/order-4473-p2k9?country=GB

Know when it is paid

Each attempt is an ordinary payment, so the outcome arrives as payment_intent.succeeded or payment_intent.failed, with your reference and the checkout’s metadata on it.

// The webhook, not the redirect, is what says the order is paid.
// A payer can close the tab before the redirect; the webhook still arrives.
if (event.type === 'payment_intent.succeeded') {
  const payment = event.data.object;
  await orders.markPaid(payment.reference, {   // the reference you sent
    paymentId: payment.id,
    amount: payment.amount,
  });
}

Check or close a checkout

Retrieve a checkout to see its status and the payments made through it. Close it if the order is cancelled before it is paid; a payment already under way still completes.

curl https://api.zintle.io/v1/payment_links/plink_01JAY9W3K8TQ2M5ZB7XF4RC6VD \
  -H "Authorization: Bearer zk_sk_test_4f2a9c1e8b7d6053"