Agent Wranglers

Payment events and reconciliation Required

Record every money movement in an append-only ledger

Write each charge, refund, fee, dispute and payout as a new ledger row with the provider's object ID, and never update or delete a row.

How to do it

Each row holds the time, type, signed amount in minor units, currency, order ID and provider object ID. A unique constraint on the provider ID stops double entries. Corrections are new rows that reverse old ones. Balances are sums of rows. Give the app's database role only INSERT and SELECT on the ledger.

Example

CREATE TABLE ledger_entries (
  id           bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  occurred_at  timestamptz NOT NULL,
  entry_type   text NOT NULL CHECK (entry_type IN ('charge', 'refund', 'fee', 'dispute', 'dispute_reversal', 'payout', 'adjustment')),
  amount_minor bigint NOT NULL,
  currency     char(3) NOT NULL,
  order_id     text,
  provider_id  text NOT NULL UNIQUE
);
GRANT SELECT, INSERT ON ledger_entries TO app_role;

How to check

As the app role, run an UPDATE and a DELETE on the ledger. Both fail with permission denied. For every order, the sum of its charge and refund rows equals the paid amount minus refunds on the order.

For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers payments playbook: https://agentwranglers.se/playbook/payments/payment-events-and-reconciliation/payment-ledger.