-- 065: Crypto payment orchestrator jobs + Relay deposit source tagging -- -- `crypto_payment_jobs` is the worker's mutable state machine (one row -- per crypto-paid order). `crypto_payment_ledger` is immutable audit; -- `crypto_payment_jobs` is where the worker records state transitions. -- -- Also adds `source_kind` to `relay_deposits` so the matcher can tell -- "this $X deposit was our Coinbase Prime offramp" vs "this was a -- Stripe ACH batch" vs "something else — operator review". CREATE TABLE IF NOT EXISTS crypto_payment_jobs ( order_id TEXT PRIMARY KEY, order_type TEXT NOT NULL, state TEXT NOT NULL DEFAULT 'received' CHECK (state IN ( 'received', -- SHKeeper webhook in, job enqueued 'sizing', -- computing vendor_obligations 'offramping', -- Coinbase Prime sell + wire initiated 'funds_at_relay', -- USD landed in RelayFi account 'ready', -- Playwright filing flow can now charge the card 'settled', -- reservation spent, order complete 'failed', -- attempt_count exhausted 'manual' -- admin intervention required )), coin TEXT NOT NULL, amount_coin NUMERIC(36,18) NOT NULL, amount_usd_cents BIGINT NOT NULL, -- SHKeeper balance_fiat at receipt needed_usd_cents BIGINT, -- sizer output: filing + 10% + commission offramp_provider TEXT DEFAULT 'coinbase_prime', offramp_ref TEXT, -- Coinbase Prime order id / transfer id relay_deposit_id INT REFERENCES relay_deposits(id), target_card_id TEXT DEFAULT 'RELAY_FILING_CARD_ID', last_error TEXT, attempt_count INT NOT NULL DEFAULT 0, next_retry_at TIMESTAMPTZ, idempotency_key TEXT UNIQUE, -- "shkeeper-settle::" received_at TIMESTAMPTZ, sized_at TIMESTAMPTZ, offramping_at TIMESTAMPTZ, funds_at_relay_at TIMESTAMPTZ, settled_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_cpj_active ON crypto_payment_jobs (state) WHERE state NOT IN ('settled','failed'); CREATE INDEX IF NOT EXISTS idx_cpj_retry ON crypto_payment_jobs (next_retry_at) WHERE state NOT IN ('settled','failed','manual') AND next_retry_at IS NOT NULL; -- ── Extend relay_deposits for source tagging ────────────────────────── -- Parsed from the Relay deposit notification email body/sender. Values: -- 'stripe_ach' — Stripe customer payments batched to Relay -- 'offramp_coinbase_prime' — our own Coinbase Prime → Relay wire/RTP -- 'internal_transfer' — between our own accounts -- 'unknown' — operator review required ALTER TABLE relay_deposits ADD COLUMN IF NOT EXISTS source_kind TEXT; ALTER TABLE relay_deposits ADD COLUMN IF NOT EXISTS memo TEXT; -- memo captures the beneficiary memo field (e.g., 'PW-ORDER-FO-2026-0042') -- so the matcher can find the corresponding crypto_payment_jobs row. CREATE INDEX IF NOT EXISTS idx_relay_deposits_unmatched_offramp ON relay_deposits (detected_at DESC) WHERE source_kind = 'offramp_coinbase_prime' AND processed = FALSE;