API-Driven Card Checkout

This page shows how to accept card payments with your own UI by calling the checkout and card endpoints directly. Use this when you want to render the card form on your own site or mobile app instead of redirecting the customer to a hosted page.

What This Flow Does

You build and host the payment UI. Transfaar provides the payment session, card encryption key, and card processing endpoints:

  1. Initialize a payment session on your backend.
  2. Fetch the public key used to encrypt the card data.
  3. Select the payment method for the session.
  4. Encrypt and submit the card details.
  5. Handle 3-D Secure if the acquirer requires it.
  6. Track the result via status endpoints or webhooks.

Supported Currencies

  • USDCARD
  • NGNCARD

Flow Overview

Your backend                         Transfaar API    | POST /client/checkout/initialize  (HMAC) |    |------------------------------------------>|    | <---------- access_code, checkout metadata|    |                                          |Your app / browser                     Transfaar API    | GET /card/encryption-key          (public) |    |------------------------------------------>|    | <---------- public key (RSA PEM)          |    |                                          |    | POST /checkout/:access_code/select-method (public) |    |------------------------------------------>|    | <---------- payment session updated       |    |                                          |    | POST /card/card-charge           (public) |    |------------------------------------------>|    | <---------- 3DS URL OR completed result   |    | (optional) complete 3-D Secure challenge  |    |                                          |    | GET /checkout/:access_code/payment-status (public) |    |------------------------------------------>|    | <---------- payment status                |

Step 1 — Initialize the payment session

POST /client/checkout/initialize (protected — HMAC API key)

Create the payment session from your backend. This is the only step that requires your signed API key — keep it off the browser or mobile client.

Request Body

json
{  "email": "customer@example.com",  "first_name": "John",  "last_name": "Doe",  "amount": "1000.00",  "currency": "USD",  "reference": "ORDER-12345",  "redirectUrl": "https://your-website.com/payment-result",  "description": "Payment for order #12345",  "metadata": {    "custom_field": "value"  }}

Success Response

json
{  "success": true,  "message": "Transaction Initialized Successfully",  "data": {    "access_code": "AbCdEfGhIjKlMnOpQrStUv",    "checkout_link": "https://checkout.transfaar.com/checkout/AbCdEfGh...",    "transactionRef": "TXN-123456789",    "reference": "ORDER-12345",    "redirectUrl": "https://your-website.com/payment-result",    "valid_until": "2026-05-02T12:30:00Z"  }}

Keep the access_code — it authenticates every subsequent step in this flow. The checkout_link is not needed when you build your own UI.

Step 2 — Fetch the card encryption key

GET /card/encryption-key (public)

The card data you submit in Step 4 is encrypted before it leaves the client. This endpoint returns the public key used for that encryption.

Response

json
{  "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----",  "algorithm": "RSA-OAEP-SHA256",  "format": "PEM"}

Step 3 — Select the payment method

POST /checkout/:access_code/select-method (public — access_code auth)

Tell the session which payment method the customer is using. For card payments, send CARD.

Request Body

json
{  "method": "CARD"}

Success Response

json
{  "quote_id": "3d8b1e2f-5a1c-4f2a-9c3e-6f7a8b9c0d1e",  "transaction_id": "6196e335-a293-4c9e-b292-1e1ab219ea20",  "transaction_reference": "TXN-123456789",  "payment_details": {    "quote_id": "3d8b1e2f-5a1c-4f2a-9c3e-6f7a8b9c0d1e",    "type": "card"  },  "total_payable": "1000.00",  "timeline": {    "created_at": "2026-05-02T12:00:00Z",    "valid_until": "2026-05-02T12:30:00Z"  },  "status": "AWAITING_PAYMENT"}

Step 4 — Encrypt and submit the card details

POST /card/card-charge (public — access_code auth)

Card data is submitted in a hybrid-encrypted envelope:

  • Generate a random 32-byte AES key and a 12-byte IV/nonce.
  • Encrypt the card payload with AES-256-GCM using that key.
  • Encrypt the AES key with RSA-OAEP-SHA256 using the public key from Step 2.
  • Send the envelope with the access_code kept in plaintext so the backend can resolve the session.

Envelope Request Body

json
{  "access_code": "AbCdEfGhIjKlMnOpQrStUv",  "encrypted_key": "<base64 RSA-OAEP-encrypted AES key>",  "iv": "<base64 AES-GCM nonce/IV (12 bytes)>",  "encrypted_payload": "<base64 AES-GCM ciphertext + tag>"}

Decrypted Inner Payload

The plaintext inside encrypted_payload (encrypt exactly this JSON):

json
{  "card_number": "5399838383838381",  "cvv": "123",  "expiry_month": "12",  "expiry_year": "25",  "amount": "1000.00",  "currency": "USD",  "customer_email": "customer@example.com",  "name_on_card": "John Doe",  "customer_phone_number": "+18005550199",  "transaction_reference": "TXN-123456789",  "description": "Payment for order #12345",  "webhook_url": "https://your-website.com/card-webhook",  "business_code": "your-business-code"}

Success Response (payment completed)

json
{  "success": true,  "message": "Card charge initiated successfully",  "paymentCompleted": true,  "data": {    "transaction_reference": "TXN-123456789",    "status": "completed",    "amount": "1000.00",    "currency": "USD",    "authorization_code": "AUTH123456",    "payment_method": "card",    "paymentCompleted": true  }}

Step 5 — Handle 3-D Secure

If the acquirer requires 3-D Secure, the charge response is not final. Redirect the customer to the returned threeDsUrl to complete the challenge, then continue to Step 6.

Response (3-D Secure required)

json
{  "success": true,  "message": "3DS authentication required",  "do3dsAuth": true,  "threeDsUrl": "https://pay.3ds-provider.com/challenge/abc123",  "paymentCompleted": false,  "data": {    "transaction_reference": "TXN-123456789",    "status": "pending",    "amount": "1000.00",    "currency": "USD",    "paymentCompleted": false  }}

After the customer completes the challenge, poll the status endpoint below until it reaches a terminal state.

Step 6 — Check payment status

GET /checkout/:access_code/payment-status (public — access_code auth)

Poll this endpoint to track the session to completion, or rely on webhooks as your source of truth.

Response

json
{  "status_id": 5,  "status_name": "COMPLETED",  "deposit_confirmed_at": "2026-05-02T12:25:00Z",  "rail_name": "CARD",  "transaction_status_id": 3,  "transaction_status_name": "COMPLETED",  "payment_completed": true}

For a server-side check from your backend, you can also call GET /client/payment/verify?reference={transaction_reference} (HMAC) with the transactionRef from Step 1.

Webhooks

Configure a webhook endpoint on your API key to receive the final result automatically. See Webhooks for the payload shape and delivery behavior.

Security Best Practices

  1. Never sign requests on the client. HMAC-signing requires your secret key — keep it on your backend.
  2. Encrypt card data before it leaves the client. Use the key from Step 2 and the AES-256-GCM + RSA-OAEP-SHA256 envelope described above.
  3. Do not store card data. Your servers never need to see a plaintext card number.
  4. Use webhooks as the source of truth. Do not rely on redirects or client-side callbacks alone.
  5. Use unique references for every initialization to prevent duplicate charges.

Related Pages