Payment methods and your account
Which payment_method values your account accepts, the amount rule for three-decimal currencies, and how checkout, fees, refunds and disputes work on each kind of account.
Every Teyra Pay account uses the same integration: the same endpoints, objects, webhooks and checkout_url. When your account is approved, Teyra Pay connects it to the card-processing setup that fits your country and currencies. A few things depend on that setup, and this page lists all of them, so your code works on any account.
In short
Leave payment_method out unless you really need one method, handle 422 validation_error on POST /v1/payments, keep three-decimal amounts ending in 0, and confirm payments from the webhook or GET /v1/payments/:id, never from the redirect alone.
Payment methods by account
| Method | payment_method | Available on |
|---|---|---|
| Visa, Mastercard and other cards | card | Every account |
| Apple Pay | apple_pay | Every account, on devices and browsers that support it |
| Google Pay | google_pay | Every account, on devices and browsers that support it |
| KNET | knet | Depends on your account: accounts that support it, for KWD |
| mada | mada | Depends on your account: accounts that support it, for SAR |
| Benefit | benefit | Depends on your account: accounts that support it, for BHD |
| STC Pay | stc_pay | Depends on your account: accounts that support it |
UAE accounts accept card, apple_pay and google_pay. Local methods such as KNET and mada are only available on accounts that support them. If you're not sure what your account supports, ask support@teyra.co, or try it in test mode: a method your account doesn't support is refused straight away (see below).
Asking for a method your account doesn't support
payment_method is optional. Without it, checkout shows every method your account supports for the currency and the customer's device, which is what most integrations want. If you do pass it and your account can't take that method, POST /v1/payments answers 422 and creates nothing:
{
"error": {
"code": "validation_error",
"message": "This payment method is not available for your account.",
"details": { "fields": { "payment_method": "unsupported" } }
}
}Fix it by leaving payment_method out, or by offering only the methods in the table above that your account supports. Don't retry the same request: the answer won't change.
What payment_method reports
Once a payment succeeds, payment_method says how the customer paid. On UAE accounts it is always card, apple_pay or google_pay. For Apple Pay and Google Pay, card holds the brand and last four digits of the card in the customer's wallet:
{ "payment_method": "apple_pay", "card": { "brand": "visa", "last4": "4242" } }Build your code to accept any value from the full list in Payments, so nothing breaks if your account gains a method later.
Three-decimal amounts must end in 0 (some accounts)
On some accounts, including UAE accounts, amounts in the three-decimal currencies (KWD, BHD, OMR, JOD) must end in 0: 1250 (1.250 KWD) is accepted, 1255 (1.255 KWD) is not. Two-decimal currencies such as AED and SAR are not affected.
On those accounts, POST /v1/payments and POST /v1/payment-links (and payment links created in the dashboard) refuse such an amount before anything is created:
{
"error": {
"code": "validation_error",
"message": "Amounts in this currency must end in 0 (for example 1.250, not 1.255).",
"details": { "fields": { "amount": "unsupported" } }
}
}The simplest way to stay safe on any account is to round three-decimal prices to the nearest 10 fils (or baisa) before you send them, and to keep partial refunds in those currencies ending in 0 too:
const THREE_DECIMALS = new Set(['KWD', 'BHD', 'OMR', 'JOD']);
// 1255 KWD -> 1260; 1250 KWD -> 1250; AED amounts are unchanged
function payableAmount(minor, currency) {
return THREE_DECIMALS.has(currency) ? Math.round(minor / 10) * 10 : minor;
}See Money and currencies for minor units.
Checkout
The flow is the same on every account: create the payment, redirect to checkout_url, the customer comes back to your return_url with payment_id and status, and you confirm on your server. See Hosted checkout.
What the customer sees on UAE accounts:
- Secure card fields in the Teyra Pay checkout page. The card number, expiry and CVV are typed into fields served by the card processor; they never reach your servers or Teyra Pay's.
- Apple Pay and Google Pay as options in the same form when the customer's device and browser support them (Apple Pay in Safari with a card in Apple Wallet, Google Pay in a browser with a saved Google Pay card). Passing
payment_methodlimits the page to that method. - 3-D Secure opens in a window on top of the checkout page when the customer's bank asks for it. The customer never leaves the page for the bank, and is sent to your
return_urlas soon as the payment is confirmed. - Declines are shown on the page with the bank's reason and a Try again button. The payment becomes
failed(you getpayment.failed), but it stays payable at the samecheckout_urluntilexpires_at, so apayment.succeededcan still follow. - Cancel works as on every account: the payment becomes
canceledand the customer goes to yourreturn_urlwithstatus=failed.
On accounts with local methods, KNET, Benefit and similar methods send the customer to their bank's page and back, and 3-D Secure for cards may also take place on the bank's page. Your code doesn't need to know which: both end at your return_url.
A payment can be processing for a short time when the customer comes back. Wait for the payment.succeeded or payment.failed webhook, or check again with GET /v1/payments/:id.
Fees and net amount
You pay one price on every account, whatever the payment method: the Teyra Pay fee, a percentage of the amount plus a fixed amount per payment. Your rate is shown under Business profile and Deposits in the dashboard. Processing costs are included in it; nothing else is deducted.
commission and net are set when the payment succeeds, in the payment.succeeded webhook and in GET /v1/payments/:id:
commissionis the Teyra Pay fee for this payment: the percentage ofamount, rounded to the nearest minor unit, plus the fixed amount. It's never more thanamount.netisamount − commission: what you receive for the payment.
For example, at 2.5% + 0.000 KWD, a 12.500 KWD payment (12500) has commission 313 and net 12187. Your balance is the sum of net less refunded amounts.
Refunds
Refunds work the same on every account: POST /v1/payments/:id/refunds, full or partial, with the same statuses, errors and refund.succeeded / refund.failed webhooks. See Refunds.
- The refunded amount comes out of your balance. Teyra Pay's commission on the original payment isn't returned.
- All four
reasonvalues are accepted on every account. - A refund starts as
pendingand usually becomessucceededwithin seconds on UAE accounts. Card and wallet refunds take a few business days to appear on the customer's statement, depending on their bank.
Disputes
A dispute (chargeback) happens when a cardholder asks their bank to reverse a card, Apple Pay or Google Pay payment. On UAE accounts, every owner gets an email when a payment is disputed, and Refunds → Disputes in the dashboard shows each dispute's status and respond-by date. On other accounts, support contacts you. See Disputes for the statuses and what to do.
Testing
Test mode behaves like live mode for all of the above, so you can check your integration before you go live:
| Check | How | Expected |
|---|---|---|
| Methods your account accepts | Create test payments with each payment_method you plan to use | 201, or 422 with details.fields.payment_method = "unsupported" for a method your account doesn't support |
| Three-decimal amounts | Create a KWD test payment for 1255, then for 1250 | On accounts with the rule: 422 with details.fields.amount = "unsupported", then 201 |
| Card payment and decline | Pay with a declined test card, click Try again, then pay with a successful one | The decline reason on the page and payment.failed, then payment.succeeded for the same payment |
| 3-D Secure | Pay with a test card that asks for authentication, then complete or fail the challenge in the window | Complete: succeeded. Fail: an error on the page with Try again, and failed |
| Apple Pay / Google Pay | Open checkout_url on a supported device | The wallet button appears. In test mode no real money moves, even with a real card in the wallet |
| Wallet reporting | Pay with Apple Pay or Google Pay | payment_method is apple_pay or google_pay, with the wallet card's brand and last4 |
| Fee and net amount | Pay a test payment and read it with GET /v1/payments/:id | commission is your rate applied to amount, and net is amount − commission |
Test card numbers depend on your account: see Testing.