Skip to content

Providers and flows

Every provider takes the same request and returns the same response, statuses, errors and webhooks. Send the provider as gateway, or leave it out and routing picks a connected one. Amounts are major units. Examples use fake values.

Three flows

  • Redirect: send the customer to authorizationUrl.
  • Phone approval: authorizationUrl is null; the customer approves on their phone and the result arrives by webhook.
  • Confirm on your page: Stripe wallets return clientSecret and publishableKey. See Payment initiate.

Redirect

POST https://api.tinkerpayments.com/v1/merchant/payment/initiate
Authorization: Bearer <app-token>
Content-Type: application/json
Idempotency-Key: <saved-unique-key>

{
  "amount": 2500,
  "currency": "NGN",
  "gateway": "paystack",
  "merchantReference": "ORDER-1",
  "returnUrl": "https://shop.example/return",
  "customerEmail": "[email protected]"
}
HTTP 200
{
  "success": true,
  "data": {
    "paymentReference": "PAY-abc123",
    "status": "pending",
    "authorizationUrl": "https://checkout.paystack.com/abc123"
  }
}

Phone approval

POST https://api.tinkerpayments.com/v1/merchant/payment/initiate
Authorization: Bearer <app-token>
Content-Type: application/json
Idempotency-Key: <saved-unique-key>

{
  "amount": 100,
  "currency": "KES",
  "gateway": "mpesa",
  "merchantReference": "ORDER-2",
  "returnUrl": "https://shop.example/return",
  "customerPhone": "+254712345678"
}
HTTP 200
{
  "success": true,
  "data": {
    "paymentReference": "ws_CO_07112026101530123456",
    "status": "pending",
    "authorizationUrl": null
  }
}

Wait for payment.completed or payment.failed. Query the payment if the webhook is late; a pending payment stays pending until the provider reports.

Each provider

Registered providers
gatewayMeaning
mpesaPhone approval. Needs customerPhone. KES.
mtnmomoPhone approval. Needs customerPhone. Currencies follow your MTN account.
paystackRedirect. Needs customerEmail. NGN, GHS, ZAR, KES, USD.
stripeRedirect to Checkout, or Klarna with bnpl. Apple Pay and Google Pay confirm on your page. Needs customerEmail, except for wallets. USD, EUR, KES.
pesapalRedirect. Needs customerEmail or customerPhone.
paypalRedirect. Needs customerEmail or customerPhone. 20 currencies, including USD, EUR, GBP.
adyenRedirect to a payment link, or Klarna with bnpl. Needs customerEmail or customerPhone; bnpl needs customerEmail and country.
dpoRedirect. Needs customerEmail or customerPhone. USD, ZAR, KES, UGX, TZS, GHS.
flutterwaveRedirect. Needs customerEmail. NGN, USD, EUR, GBP, GHS, KES, ZAR and 8 more.
interswitchRedirect. Needs customerEmail. NGN, Nigeria only.
plaid_transferRedirect to Plaid. US bank debit in USD. Needs customerName, and customerEmail or customerPhone.
plaid_paymentsRedirect to Plaid. UK and EU bank payment in GBP, EUR, PLN, SEK, DKK, NOK. Needs customerName, and customerEmail or customerPhone.

A missing customer field returns VALIDATION_ERROR (400) with the same wording on every provider. Your provider account decides which methods and currencies work. A provider is usable once Tinker Payments has enabled it and it is connected to your app; the capabilities call below shows which.

Country

country is the customer's ISO 3166-1 alpha-2 code, optional on every provider. Routing rules by country use it, and it comes back on the payment and its webhooks. Subscriptions and wallet top-ups accept it too. Adyen needs it for Klarna.

Klarna

Send paymentMethod bnpl on stripe or adyen. Both capture as soon as Klarna authorises, so ship within 24 hours. Adyen serves AT, DK, FI, DE, NO, SE, CH, NL, GB and US in that country's currency (EUR for AT, FI, DE and NL). A missing or unlisted country, or the wrong currency, returns VALIDATION_ERROR before any provider call. If Adyen reports a failed capture of the full amount, the payment becomes reversed and payment.reversed is sent. A failed partial capture is not reversed.

POST https://api.tinkerpayments.com/v1/merchant/payment/initiate
Authorization: Bearer <app-token>
Content-Type: application/json
Idempotency-Key: <saved-unique-key>

{
  "amount": 49.9,
  "currency": "EUR",
  "gateway": "adyen",
  "merchantReference": "ORDER-3",
  "returnUrl": "https://shop.example/return",
  "customerEmail": "[email protected]",
  "country": "DE",
  "paymentMethod": "bnpl"
}

Plaid

customerName (the payer's legal name) is required on plaid_transfer and plaid_payments; a missing one returns VALIDATION_ERROR. On plaid_payments, the Plaid status INITIATED counts as paid. It is not proof the money arrived, and Plaid reports no later failure, so reconcile the bank credit by your reference if you need proof.

POST https://api.tinkerpayments.com/v1/merchant/payment/initiate
Authorization: Bearer <app-token>
Content-Type: application/json
Idempotency-Key: <saved-unique-key>

{
  "amount": 40,
  "currency": "GBP",
  "gateway": "plaid_payments",
  "merchantReference": "ORDER-4",
  "returnUrl": "https://shop.example/return",
  "customerEmail": "[email protected]",
  "customerName": "Jane Doe",
  "country": "GB"
}

Ask before you charge

List the providers connected to your app, with a mode per feature: native, emulated, manual or unavailable. Both filters are optional.

GET https://api.tinkerpayments.com/v1/merchant/payment/capabilities?currency=KES&country=KE
{
  "success": true,
  "data": {
    "providers": [
      {
        "gateway": "mpesa",
        "name": "M-Pesa",
        "methods": ["mobile_money"],
        "currencies": ["KES"],
        "countries": [],
        "capabilities": {
          "pay_mobile_money": "native",
          "pay_apple_pay": "unavailable",
          "refund": "native",
          "partial_refund": "emulated"
        }
      }
    ]
  }
}

FEATURE_NOT_SUPPORTED

A request for an unavailable feature returns HTTP 422 before anything reaches the provider, so no money moves and no payment is stored. Retry on a gateway in details.alternatives; Tinker Payments never switches for you. details.gateway is absent when you named none.

HTTP 422
{
  "success": false,
  "error": {
    "code": "FEATURE_NOT_SUPPORTED",
    "message": "M-Pesa doesn't support Apple Pay.",
    "details": { "gateway": "mpesa", "feature": "apple_pay", "alternatives": ["stripe"] }
  }
}

Refunds and webhooks

Both work the same on every provider. A refund is a request Tinker Payments reviews; see Refunds. Results arrive as payment.* events; see Webhooks.

Credentials by plan

Pay-as-you-go apps always use Tinker's provider credentials; you connect nothing. Using your own provider credentials needs a paid plan. Saving your own on a pay-as-you-go app returns HTTP 403 PAID_PLAN_REQUIRED, and details.plan names the current plan (payg). A provider Tinker holds no credentials for shows default_available: false in the dashboard provider list; asking for Tinker's credentials for it returns HTTP 422 PLATFORM_CREDENTIALS_UNAVAILABLE with details.gateway. If an app moves back to pay-as-you-go, its own credentials are replaced by Tinker's where Tinker has them for that provider; otherwise the provider is disconnected.

HTTP 403
{
  "success": false,
  "error": {
    "code": "PAID_PLAN_REQUIRED",
    "message": "Your own adyen credentials need a paid plan. Pay as you go uses Tinker's credentials.",
    "details": { "plan": "payg" }
  }
}