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

Configuration

Three ways to run it#

ConfigWhat happensMoney
nothingThe built-in test gateway, in memory.None moves
gateways and storecallCash 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#

TypeScriptlib/cash.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#

OptionTypeWhat it does
gateways.yoco.secretKeystringYour Yoco secret key. sk_test_ runs in test mode, anything else in live mode.
gateways.yoco.webhookSecretstringThe whsec_ secret from registering your Yoco webhook. Needed to accept Yoco events.
gateways.netcash.serviceKeystringYour Pay Now service key.
gateways.netcash.testbooleantrue while "Make test mode active" is ticked in your Netcash dashboard. Marks payments as test.
gateways.netcash.vendorKeystringYour software vendor key if you are a Netcash ISV. Netcash's default is used otherwise.
routesobjectWhich gateways to try, in order, per way of paying. See Payments.
storeStoreWhere payments are kept. Required with real gateways.
returnUrlstringWhere customers land after paying. Yoco needs it.
keystringA cc_test_ or cc_live_ key for the hosted API. Planned.
baseUrlstringThe hosted API's address. Planned.
fetchfunctionReplace fetch, for tests.
nowfunctionReplace 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:

TypeScript
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:

Terminal
YOCO_SECRET_KEY=sk_test_...
YOCO_WEBHOOK_SECRET=whsec_...
NETCASH_PAYNOW_KEY=...
NETCASH_TEST=1