Collect Payments

What you'll build

Accept a payment from your customer in their local currency. The customer pays via their preferred local method (M-Pesa, PIX, SEPA, GCash, etc.) and the funds settle to your TransFi account.

📘

Use this:

If you run a marketplace collecting from buyers, a remittance app collecting from senders, an e-commerce store, or a forex platform receiving from customers.

Before You Start

  • Sandbox API credentials and your MID from displai.transfi.com.
  • No pre-existing balance needed — collecting doesn't require prefunding.
  • Create the payer as a user first (Step 1). KYC is required based on transaction volume — Basic KYC is applied automatically at user creation.

The Flow

  1. Create the payer as a user → save the userId
  2. (Optional) Submit KYC if the payer needs identity verification
  3. Create a payin order → get a payUrl
  4. Redirect the payer to the payment page
  5. Listen for the status update → fulfil the order

Step 1 — Create the payer (sender)

Each payer (sender) is a user in TransFi. Create them once and reuse the userId for all future transactions.

curl --location 'https://sandbox-api.transfi.com/v3/users/individual' \
--header 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
--header 'mid: YOUR_MID' \
--header 'Content-Type: application/json' \
--data '{
    "firstName": "Asha",
    "lastName": "Kamau",
    "email": "[email protected]",
    "phone": "0712345678",
    "phoneCode": "+254",
    "country": "KE",
    "date": "15-06-1990",
    "address": {
        "street": "14 Moi Avenue",
        "city": "Nairobi",
        "state": "Nairobi",
        "postalCode": "00100"
    }
}'
// Response
{
    "status": "success",
    "data": {
        "userId": "UX-250930062608488"
    }
}

Store this userId — you'll use it on every order for this payer.

Step 2 — Create the payin order

Choose whether to use TransFi's hosted payment page (headlessMode not set or false) or display payment details in your own UI (headlessMode: true). The hosted page is faster to ship and handles all payment method UX.

Fiat payin — minimal payload (TransFi-hosted payment page):

curl --location 'https://sandbox-api.transfi.com/v3/orders' \
--header 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
--header 'mid: YOUR_MID' \
--header 'Content-Type: application/json' \
--data '{
    "userId": "UX-250930062608488",
    "orderType": "payin",
    "purposeCode": "company_expenses",
    "partnerId": "your-unique-order-ref-001",
    "source": {
        "currency": "KES",
        "amount": "5000",
        "paymentType": "mobile_money",
        "paymentCode": "MPESA"
    },
    "destination": {
        "currency": "USDT"
    }
}'
// Response
{
    "status": "success",
    "data": {
        "flow": "FIAT_PAYIN",
        "orderId": "OR-2509291454262384294",
        "payUrl": "https://sandbox-pay-widget.transfi.com/pay?paytoken=6c237e7e-..."
    }
}

purposeCode is required on every order.

Common values: company_expenses, personal, invoice_payment.

See the Purpose Code reference for the full list.

partnerId is your unique reference for this order and acts as the idempotency key.

Generate one per order, store it before the API call, and retry with the same value if a request times out.

Never reuse across different orders.

Don't know the right paymentCode?

Call List Payment Methods API to get all supported payment codes for a currency. Omitting paymentCode lets the payer choose from all enabled methods on the hosted page for a paymentType (bank_transfer, local_wallet, card)

Step 3 — Redirect the payer

Send the payer to the payUrl from the response. They complete payment on TransFi's hosted page and are returned to your successRedirectUrl or failureRedirectUrl when done.

Step 4 — Track the order status

Poll for status or listen to webhooks. Fiat payin orders move through these statuses:

StatusTypeWhat it meansWhat to do
initiatedIntermediateOrder created, waiting for the payer to complete paymentShow a "payment pending" state
fund_processingIntermediatePayer submitted details; waiting for partner to confirm receiptShow "processing"
fund_settledTerminal ✓Payment confirmed and received by TransFiFulfil the order — update your DB, release the goods/service
fund_failedTerminal ✗Payment not received or reversedShow failure message. Create a new order to retry.

To check order status at any time:

curl --location 'https://sandbox-api.transfi.com/v3/orders/OR-2509291454262384294' \
--header 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
--header 'mid: YOUR_MID'
🛑

If it fails

Never reuse the same orderId or partnerId. Create a brand-new order with a new partnerId

📘

Test in Sandbox

Create the order, open the payUrl, and complete the simulated payment flow. Or trigger status transitions directly using POST /v3/simulation/order

Note: The Simulation Flow only works in Sandbox

Collecting Crypto Payments

To accept a crypto payin (e.g. USDT from the payer's wallet), change the source currency to a crypto symbol. The response returns a walletAddress instead of a payUrl — share this address with the payer for them to send to.

// Crypto payin — source is crypto, destination is fiat
"source": {
    "currency": "USDTPOLYGON",
    "amount": "100"
},
"destination": {
    "currency": "EUR"
}
// Response — walletAddress to share with the payer
{
    "status": "success",
    "data": {
        "flow": "CRYPTO_PAYIN",
        "orderId": "OR-2509291506567158826",
        "walletAddress": "0x63d3CEAE8A65486a1caAb8B42f..."
    }
}

Crypto payin statuses: initiatedasset_settled (terminal ✓)