# Payments

## Create

```ts
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:

```ts
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 |
|---|---|
| paid | Throws `reference_used` (409). Use a new reference. |
| pending, under 30 minutes old, same gateway, same amount | Returns that same payment. A double click opens one checkout. |
| pending, under 30 minutes old, same gateway, different amount | Throws `reference_used`. Use a new reference to charge a different amount. |
| pending and older, or on another gateway | Creates a new payment. |
| failed | Creates 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:

```ts
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

```ts
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

| Field | Meaning |
|---|---|
| `id` | `pay_` and 16 letters and digits |
| `status` | `pending`, `paid` or `failed` |
| `amount` | Rand |
| `currency` | Always `ZAR` |
| `reference` | Your order number |
| `email` | Or `null` |
| `gateway` | `yoco`, `netcash` or `test` |
| `mode` | `test` or `live` |
| `method` | `card`, `instant_eft`, `bank_eft`, `cash`, `voucher`, `other`, or `null` until paid |
| `verified` | `true` once the gateway itself confirmed the payment, never on a notification's word |
| `checkout_url` | Yoco's link, or `null` |
| `checkout_form` | Netcash's `{ action, fields }`, or `null` |
| `metadata` | Your data, as strings |
| `refunded` | Rand refunded so far (succeeded refunds only) |
| `refunds` | Each refund: `id`, `amount`, `status`, `failure` |
| `gateway_ref` | The reference the gateway knows this payment by (`CC…`) |
| `gateway_id` | Yoco's checkout id, or Netcash's RequestTrace |
| `failure` | Why it failed, or `null` |
| `created_at`, `paid_at`, `failed_at` | ISO timestamps |
