# Configuration

## Three ways to run it

| Config | What happens | Money |
|---|---|---|
| nothing | The built-in test gateway, in memory. | None moves |
| `gateways` and `store` | callCash runs in your server on your own Yoco and Netcash accounts. | Customer pays your gateway, your gateway pays your bank |
| `key: "cc_test_…"` | A client for the hosted API. | **Planned.** The server does not exist yet |

Giving both `key` and `gateways` throws `config_invalid`.

## Your own gateways

```ts
// lib/cash.ts (server only)
import { CallCash } from "callcash";
import { firestoreStore } from "callcash/firestore";

export const cash = new CallCash({
  gateways: {
    yoco: { secretKey: process.env.YOCO_SECRET_KEY!, webhookSecret: process.env.YOCO_WEBHOOK_SECRET },
    netcash: { serviceKey: process.env.NETCASH_PAYNOW_KEY!, test: process.env.NETCASH_TEST === "1" },
  },
  routes: { card: ["yoco", "netcash"], default: ["netcash"] },
  store: firestoreStore(db),
  returnUrl: "https://example.co.za/paid",
});
```

## Options

| Option | Type | What it does |
|---|---|---|
| `gateways.yoco.secretKey` | string | Your Yoco secret key. `sk_test_` runs in test mode, anything else in live mode. |
| `gateways.yoco.webhookSecret` | string | The `whsec_` secret from registering your Yoco webhook. Needed to accept Yoco events. |
| `gateways.netcash.serviceKey` | string | Your Pay Now service key. |
| `gateways.netcash.test` | boolean | `true` while "Make test mode active" is ticked in your Netcash dashboard. Marks payments as test. |
| `gateways.netcash.vendorKey` | string | Your software vendor key if you are a Netcash ISV. Netcash's default is used otherwise. |
| `routes` | object | Which gateways to try, in order, per way of paying. See [Payments](https://callcash.riverrr.co.za/docs/payments#routing). |
| `store` | `Store` | Where payments are kept. **Required with real gateways.** |
| `returnUrl` | string | Where customers land after paying. Yoco needs it. |
| `key` | string | A `cc_test_` or `cc_live_` key for the hosted API. Planned. |
| `baseUrl` | string | The hosted API's address. Planned. |
| `fetch` | function | Replace `fetch`, for tests. |
| `now` | function | Replace the clock, for tests. |

A gateway named in `routes` but missing from `gateways` throws `config_invalid` when the client is created.

## The store

Real gateways need a store that outlives your process, or the constructor throws `config_invalid`. The store is what makes a payment count once when a gateway sends the same notification twice.

- **Firestore:** `firestoreStore(db, collection?)` from `callcash/firestore`. One document per payment, in `callcashPayments` unless you name another collection. It works with `firebase-admin`'s Firestore without depending on it, and needs no composite index.
- **Your own:** implement the `Store` interface (`create`, `get`, `find`, `update`, `list`). `update` must read, change and write as one atomic step, for example in a database transaction.
- **`MemoryStore`:** for tests only. A restart forgets everything.

## callCash() and configure()

`callCash(input)` is a shortcut for `client().payments.create(input)`. Set the client it uses once, where your server starts:

```ts
import { configure, callCash } from "callcash";

configure({ gateways: { yoco: { secretKey: process.env.YOCO_SECRET_KEY! } }, store, returnUrl });

const payment = await callCash({ amount: 250, reference: "order_1042" });
```

Without `configure()`, `callCash()` uses `CALLCASH_KEY` (and `CALLCASH_URL`) from the environment when set, which means the planned hosted API. Otherwise it uses the test gateway.

## Environment variables

Keep keys in environment variables, never in code or in a client bundle. The names are yours; these docs use:

```bash
YOCO_SECRET_KEY=sk_test_...
YOCO_WEBHOOK_SECRET=whsec_...
NETCASH_PAYNOW_KEY=...
NETCASH_TEST=1
```
