callCash()by riverrr
Get early access
Open as .md

Payments

Create#

TypeScript
const payment = await cash.payments.create({
  amount: 250,                       // rand, two decimals at most
  reference: "order_1042",           // your own order number, 1 to 100 characters
  email: "customer@example.co.za",   // optional
  description: "Order 1042",         // optional; 50 characters at most on Netcash
  metadata: { order: "1042" },       // optional; values become strings
  method: "card",                    // optional; steers routing
  gateway: "yoco",                   // optional; skips routing
  return_url: "https://example.co.za/paid", // optional; overrides config.returnUrl
});

Then send the customer to pay:

TypeScript
if (payment.checkout_url) {
  // Yoco: redirect to the link
} else if (payment.checkout_form) {
  // Netcash: send { action, fields } to the browser and post it as a form (see the Netcash page)
}

The payment is stored before any gateway hears of it, so a notification can never arrive for a payment callCash does not know.

Amounts#

amount is rand: 250 is R250.00 and 99.5 is R99.50. Anything that is not a positive number with at most two decimals throws amount_invalid before a gateway is called. If your app stores cents, divide by 100 going in.

References#

Your reference decides what happens when the same order is paid for twice:

The last payment with this reference is…What create does
paidThrows reference_used (409). Use a new reference.
pending, under 30 minutes old, same gateway, same amountReturns that same payment. A double click opens one checkout.
pending, under 30 minutes old, same gateway, different amountThrows reference_used. Use a new reference to charge a different amount.
pending and older, or on another gatewayCreates a new payment.
failedCreates a new payment. A failure frees the reference.

Routing#

routes in the config says which gateways to try, in order, for each way of paying:

TypeScript
routes: { card: ["yoco", "netcash"], default: ["netcash"] }

How a payment picks its gateway:

  1. gateway on the call wins, and skips routing.
  2. Otherwise routes[method] when you pass a method.
  3. Otherwise routes.default.
  4. Otherwise every gateway you configured, in the order you listed them.
  5. With no real gateways, the test gateway.

Failover#

Only a gateway that is down hands over to the next one in the route: a network error, a timeout (15 seconds), a 5xx or a 429. Those throw gateway_down, which is retryable.

A gateway that refuses the payment (a 4xx, gateway_rejected) is not tried elsewhere, because the next one would most likely refuse it too. The payment is left failed with the reason in failure, and the error is thrown.

Failover only helps if you are approved on more than one gateway. That is mostly agencies and bigger shops.

Get, list, update#

TypeScript
const p = await cash.payments.get("pay_…");                    // not_found if there is no such payment
const recent = await cash.payments.list({ status: "paid", gateway: "yoco", limit: 50 });
const owed = await cash.payments.list({ undelivered: true });   // payments with events your handler has not taken yet
await cash.payments.update("pay_…", { metadata: { shipped: "yes" } }); // merges into metadata

list returns newest first, 100 at most unless you set limit.

The payment object#

FieldMeaning
idpay_ and 16 letters and digits
statuspending, paid or failed
amountRand
currencyAlways ZAR
referenceYour order number
emailOr null
gatewayyoco, netcash or test
modetest or live
methodcard, instant_eft, bank_eft, cash, voucher, other, or null until paid
verifiedtrue once the gateway itself confirmed the payment, never on a notification's word
checkout_urlYoco's link, or null
checkout_formNetcash's { action, fields }, or null
metadataYour data, as strings
refundedRand refunded so far (succeeded refunds only)
refundsEach refund: id, amount, status, failure
gateway_refThe reference the gateway knows this payment by (CC…)
gateway_idYoco's checkout id, or Netcash's RequestTrace
failureWhy it failed, or null
created_at, paid_at, failed_atISO timestamps