API and data model

Every gateway endpoint with its method, path and permission; the payment, payout, refund and webhook status values; the seven database tables; the twelve permission keys; and the three scheduled jobs.

2 min readUpdated 3 August 2026reference, api, permissions, schema, cron

Everything the addon exposes, in one place. Paths are as registered by the router: a file named index serves its folder, a bracketed folder becomes a path parameter, and any other filename becomes a path segment — which is why creating a payment is POST /v1/payment/create and not POST /v1/payment.

Merchant API — API-key authenticated

Base: /api/gateway/v1. Authentication is X-API-Key. No session, no cookie.

Confirms a key and reports merchant, mode, key type and permissions
Creates a payment session and returns a checkout URL
Reads a payment by its pi_ identifier
Cancels an unpaid session
Creates a full or partial refund
Reads a refund by its re_ identifier

Key permissions are the six values a merchant may put on a key: payment.create, payment.read, payment.cancel, refund.create, refund.read and the wildcard *. Anything else submitted is silently dropped, and an empty result becomes ["*"].

Merchant dashboard — session authenticated

Base: /api/gateway. These are what the merchant's own screens call. They require a logged-in user who owns the merchant record; there is no permission key involved.

Dashboard: merchant record, balances, 30-day stats and recent payments. Accepts ?mode=LIVE|TEST
Registers the current user as a merchant, returning four keys and the webhook secret
Updates the merchant profile. Identity fields freeze once VERIFIED
Balances per currency and wallet type, with totals and payout settings
Lists the merchant's payments
Reads one of the merchant's payments
Refunds one of the merchant's payments from the dashboard
Lists the merchant's payouts
Reads one payout
Lists API keys, masked to their last four characters
Creates a public/secret key pair. Requires verificationStatus VERIFIED
Updates a key's name, permissions, IP allowlist and URLs
Issues a new value for a key
Deletes a key
Downloads an integration plugin as a zip. Only pluginId=woocommerce exists

Checkout and public endpoints

Public gateway settings a checkout or landing page needs
Landing page content
Checkout session detail for the payment page
The buyer's eligible wallets with live rates and coverage
Confirms the payment, debiting the buyer's chosen wallets
Cancels the session and redirects the buyer back

The confirm endpoint takes a bare JSON array as its body — one object per wallet the buyer is paying from, each with walletId, walletType, currency, amount and equivalentInPaymentCurrency. The server recomputes the rate and settles on its own figure; the buyer's equivalentInPaymentCurrency is only checked for staleness against a 2% tolerance.

Admin API

Base: /api/admin/gateway. Session authenticated plus a permission key.

Dashboard statistics: merchants, payments, volume by currency, refunds and the aged payout queue
Paginated merchant list with the owning user joined
Full merchant record
Updates business details, fees, limits and payout settings
Sets PENDING, ACTIVE, SUSPENDED or REJECTED
Sets UNVERIFIED, PENDING or VERIFIED
Soft-deletes a merchant
Paginated payment list across all merchants
Payment detail
Refunds a payment. Accepts either the pi_ id or the UUID
Paginated payout list
Payout detail
Approves a payout and credits the merchant's wallet
Rejects a payout. A reason is mandatory
Lists refunds across all merchants. No screen is bound to it
Reads the gateway settings block
Writes gateway settings. The admin UI does not use this — it writes through the core settings endpoint
Wallet types and their available currencies, filtered by what the platform has enabled

Identifier formats

All generated from a cryptographically secure RNG over a 62-character alphabet.

Prefix Length after prefix Used for
pi_ 24 Payment intent, public — it is in the checkout URL
re_ 24 Refund
po_ 24 Payout
pk_live_ · pk_test_ 48 Public API key
sk_live_ · sk_test_ 48 Secret API key
evt_ + payment or refund id Webhook event id. Stable across retries

API keys are stored as SHA-256 hashes with only the last four characters kept in clear. They cannot be recovered — only rotated.

Status values

PaymentPENDING · PROCESSING · COMPLETED · FAILED · CANCELLED · EXPIRED · REFUNDED · PARTIALLY_REFUNDED

PayoutPENDING · PROCESSING · COMPLETED · FAILED · CANCELLED. A rejected payout is recorded as CANCELLED.

RefundPENDING · COMPLETED · FAILED · CANCELLED. In practice a refund is created already COMPLETED or the transaction rolls back.

Refund reasonREQUESTED_BY_CUSTOMER · DUPLICATE · FRAUDULENT · OTHER

Webhook deliveryPENDING · SENT · RETRYING · FAILED

Merchant statusPENDING · ACTIVE · SUSPENDED · REJECTED

Merchant verificationUNVERIFIED · PENDING · VERIFIED

Fee typePERCENTAGE · FIXED · BOTH

Payout scheduleINSTANT · DAILY · WEEKLY · MONTHLY

Wallet typeFIAT · SPOT · ECO

Webhook events

Emitted: payment.created, payment.completed, payment.cancelled, payment.expired, payment.failed, refund.completed.

Defined but never emitted: refund.created, refund.failed.

Headers: X-Gateway-Signature (sha256= + hex), X-Gateway-Timestamp (Unix seconds), X-Gateway-Event, User-Agent: PaymentGateway-Webhook/1.0. The signed string is <timestamp>.<raw body>, keyed with the merchant's webhookSecret. Full recipe in Webhooks.

Database tables

Seven tables, all prefixed gateway_. They are auto-synced with the rest of the schema; there is no separate migration step.

Table Holds
gateway_merchant One row per merchant. Soft-deleted (paranoid). Unique on slug, apiKey and secretKey
gateway_api_key Key hashes, mode, type, permissions, IP allowlist, last-used stamp
gateway_payment Sessions and their outcome, including allocations (which wallets paid) and lineItems
gateway_refund One row per refund, linked to a payment
gateway_payout One row per proposed payout, with period, gross, fee, net and counts
gateway_merchant_balance One row per merchant × currency × wallet type. pending is the source of truth
gateway_webhook One row per delivery, with payload, signature sent, response status, body, timing and attempt count

Money columns are DECIMAL(30,8). The driver returns decimals as strings — anything reading these tables directly must parse before it does arithmetic.

Permission keys

Key Grants
access.gateway.merchant Open the admin dashboard and merchant screen
view.gateway.merchant Read merchants
edit.gateway.merchant Edit, approve, suspend, verify
delete.gateway.merchant Soft-delete a merchant
create.gateway.merchant Seeded; no route uses it
access.gateway.payment Open the payment screen
view.gateway.payment Read payments
manage.gateway.payment Issue an admin refund
create.gateway.payment · delete.gateway.payment · edit.gateway.payment Seeded; no route uses them
view.gateway.payout Read payouts
edit.gateway.payout Approve and reject payouts
create.gateway.payout · delete.gateway.payout Seeded; no route uses them
view.gateway.refund The refund list endpoint
access.gateway.settings Open the settings screen
view.gateway.settings Read settings and the currency picker
edit.gateway.settings The parallel settings endpoint the UI does not call

Do not confuse these with access.deposit.gateway, view.deposit.gateway and edit.deposit.gateway, which belong to the core fiat deposit providers.

Scheduled jobs

All three run in the cron worker, under the gateway category.

Job Interval Behaviour
processGatewayPayouts 60 min Proposes PENDING payouts for unclaimed balance. Skips the whole run when gatewayEnabled is off. Concurrency 3
processGatewayWebhookRetries 60 s Redelivers RETRYING rows past nextRetryAt, plus PENDING rows older than 2 minutes. Batch of 100
processGatewayPaymentExpiry 5 min Expires PENDING sessions past expiresAt and emits payment.expired. Batch of 500, with a 60-second wall-clock budget per run

Environment

Variable Effect
APP_PUBLIC_URL Origin used to build every checkout URL. Empty in .env.example; falls back to http://localhost:3000
APP_DEFAULT_LOCALE Locale segment in the checkout URL. Not in .env.example; defaults to en

Where the fee goes

The platform fee is credited to the first Super Admin user's wallet, in the same currency and wallet type the buyer paid in, as a PLATFORM_FEE operation with reference <paymentId>_fee. An adminProfit row is written with type GATEWAY_PAYMENT. If no Super Admin role or user exists, the fee collection logs a warning and is skipped — the payment still completes.