Payments
Kontorion routes payments through pluggable gateways. An organization wires up one or more PaymentGatewayConfig rows; customers store payment methods that reference a provider's tokenized instrument; finalized invoices get charged through the resolved gateway. The same surface backs Stripe-only SaaS, SEPA via GoCardless, Mollie/Adyen for European card processing, and operator-recorded wires from EXTERNAL processors - without rewriting the surrounding billing logic.
Payment gateways
A gateway is an integration with one external processor. Each PaymentGatewayConfig row holds the credentials, capability advertisements, and enable/default flags for one provider in one mode.
| Field | Notes |
|---|---|
provider | One of stripe, gocardless, mollie, adyen |
display_name | Free-form label shown in operator UI |
mode | test or live - keeps sandbox credentials separate from production |
config | Encrypted JSON blob - provider-specific (api_key, webhook_secret, etc.). Never returned in plaintext on read |
supported_currencies | Currencies this gateway will accept charges in |
supported_methods | Method types this gateway can process (e.g. CREDIT_CARD, SEPA_DIRECT_DEBIT) |
is_enabled | Resolver only considers enabled rows |
is_default | Resolver fallback when no per-customer preference matches |
Code
PUT /v1/payment-gateways/{id} updates display_name, mode, capability arrays, is_enabled, is_default, or the encrypted config. DELETE removes the gateway - only safe when no payment methods reference it.
Payment methods
A PaymentMethod is a customer-owned reference to a tokenized instrument held by the provider. Kontorion stores the provider's token (provider_id), never the underlying card / IBAN data.
| Field | Notes |
|---|---|
customer_id | Owning customer |
type | CREDIT_CARD / BANK_ACCOUNT / SEPA_DIRECT_DEBIT / PAYPAL / EXTERNAL |
provider | The gateway provider that holds the token |
provider_id | The provider's token / customer reference (e.g. Stripe pm_…, GoCardless mandate id) |
is_default | The pick when an invoice charge doesn't specify a method |
status | active, expired, or removed |
EXTERNAL is the catch-all for instruments tracked outside any integrated gateway - e.g. a bank account a customer pays from manually. Charges against an EXTERNAL method don't auto-collect; they wait for record-payment to settle the invoice.
Code
The charge endpoint returns 202 Accepted and runs the attempt asynchronously - subscribe to invoice.payment_recorded and subscription.payment_failed webhooks for the outcome.
Manual payment recording
For payments collected outside Kontorion (wire transfer, check, cash):
Code
The full surface: GET /v1/payment-methods?customer_id=…, GET /v1/payment-methods/{id}, PUT /v1/payment-methods/{id} (update is_default, status, provider_id), DELETE /v1/payment-methods/{id} (soft-deletes - sets status: removed).
Provider resolver
In sandbox workspaces, pin a payment-method to a deterministic outcome so charges against it return that outcome instead of routing to the real gateway. The endpoint returns 403 FORBIDDEN outside sandbox.
Code
payment_method_id and outcome are required. Allowed outcomes: success, decline, insufficient_funds, expired_card, fraud, processing_error, network_error. See Authentication - Payment simulations for the full surface.
Refunds
Code
Two paths depending on how the invoice was paid:
- Gateway path (Stripe charge in flight): the refund is dispatched to the gateway. Response is
202 Acceptedwith{"status":"refund_pending","refund_id":"re_…"}. The credit note materializes when the gateway's settlement webhook arrives. - Inline path (manual payment, wallet, or already-settled): a credit note is created and applied immediately. Response is
200 OKwith the credit note inline.
amount is in minor units (cents) - pass it for partial refunds, omit for a full refund of amount_paid.
Recording manual payments
For payments that landed outside any gateway - wires, checks, write-offs - record them directly against the invoice. See Invoices → Record a manual payment for the full body shape and the source enum (BANK_TRANSFER, CASH, CHECK, WIRE, WRITE_OFF, EXTERNAL_PROCESSOR).
Test-mode simulations
For sandbox orgs, TestPaymentSimulation rows pin a deterministic outcome onto a PaymentMethod so charge attempts produce a predictable result without hitting the live gateway.
Code
outcome is one of success, decline, insufficient_funds, expired_card, fraud, processing_error, network_error. Each simulated outcome maps to the corresponding error_type so dunning, retry, and reporting code paths are exercised end-to-end.
GET /v1/test-payment-simulations?payment_method_id=… lists configured simulations; DELETE /v1/test-payment-simulations/{id} removes one.
Stripe webhook ingress
Stripe-side events (charge succeeded, refund settled, dispute opened) arrive at:
Code
The endpoint is unauthenticated for the public - it verifies the Stripe-Signature header against the webhook_secret stored on the matching PaymentGatewayConfig.config. Verified events update PaymentIntent / PaymentAttempt rows, settle pending refunds into credit notes, and emit the corresponding domain webhooks (invoice.paid, credit_note.applied).
GoCardless / Mollie / Adyen ingress endpoints follow the same pattern under their respective provider names as those integrations land.
Endpoints
Payment Gateways, Payment Methods, Invoices → charge / refund / record-payment, Test Payment Simulations.
Related
- Invoices - what
charge,refund, andrecord-paymentoperate on - Credit notes - the audit document refunds materialize as
- Dunning - what happens when a charge fails
- Wallets - wallet drains can substitute for or supplement a gateway charge
- Customers - owns the payment methods and the per-customer provider preference