testkit
Drive orvacon flows in a test — no gateway, no database, no network.
@orvacon/testkit lets you (and orvacon itself) drive the full payment lifecycle in a unit test with
no gateway account, no Postgres, and no network. It's a published dev dependency:
bun add -D @orvacon/testkitThe mock connector and database productize the core's own test doubles, so your test exercises the real orchestrator — not a stand-in that might drift from the contract.
A complete flow, in memory
import { idempotencyKey, money, orvacon } from "@orvacon/paykit";
import { mockConnector, mockDatabase, mockCallback, testCards, testSigningKey } from "@orvacon/testkit";
const db = mockDatabase();
const orva = orvacon({
database: db,
connectors: [mockConnector()],
webhookSigningKey: testSigningKey,
});
const outcome = await orva.authorize({
idempotencyKey: idempotencyKey(crypto.randomUUID()),
amount: money(2500, "TRY"),
source: { type: "card", card: testCards.success },
threeDSecure: true,
});
// 3-D Secure: finalize with the callback the gateway would have POSTed.
if (outcome.ok && outcome.status === "requires_action") {
await orva.handleWebhook(
"mock",
mockCallback({ paymentId: outcome.paymentId, amount: money(2500, "TRY") }),
);
// db.payments.get(outcome.paymentId)?.status === "captured"
}What it provides
| Export | |
|---|---|
mockConnector(opts?) | A deterministic in-memory gateway — see below. |
mockDatabase() | An in-memory DatabaseAdapter; .payments, .ledger, .idempotency are exposed for assertions. |
mockCallback(input) | The RawWebhook a 3-D Secure callback delivers — pass it to handleWebhook. |
mockReconcile(input) | A resolved ReconcileOutcome for the reconcile option, to drive reconcile(). |
testCards / testToken | Magic fixtures: success, declined, gatewayError. |
testSigningKey / testPublicKey | A throwaway Ed25519 pair, so no orvacon keys is needed. |
The mock connector
Outcomes are deterministic — the card decides them, the way a gateway sandbox publishes fixed numbers:
testCards.… | Result |
|---|---|
success | authorizes; with threeDSecure: true, returns requires_action |
declined | { ok: false, error: { code: "declined" } } — final |
gatewayError | { ok: false, error: { code: "gateway_error" } } — the one auto-retry class |
Tune it with options:
mockConnector({ capabilities: { autoCapture: true } }); // captures at authorize, no separate captureReconcile
mockConnector implements retrievePayment, so orva.reconcile() works. By default a pending payment
stays pending (a no-op). To drive the settled-but-unreflected path, return mockReconcile:
mockConnector({
reconcile: (input) =>
mockReconcile({ paymentId: input.paymentId, type: "payment.captured", amount: money(2500, "TRY") }),
});
// orva.reconcile(paymentId) now advances a requires_action payment to captured.Test fixtures only
testSigningKey is a real Ed25519 secret published in this package — it exists purely so a test can
construct an orvacon instance. Never use it in production.