Payments

A connector framework with two production methods + reserved slots for hosted gateways.

Connector contract

Every payment method implements three async ops:

class PaymentConnector(ABC):
    name: str          # 'cod', 'bank_transfer', 'card_stripe', ...
    display_name: str
    requires_redirect: bool
    supports_refund: bool

    async def start_payment(...) -> StartResult: ...
    async def confirm_payment(...) -> ConfirmResult: ...
    async def refund(...) -> RefundResult: ...   # optional

Connectors register at module import. The checkout service calls get_connector(method_name) to dispatch.

Production methods (Phase 1)

COD (cod) — Dobírka

The customer pays the courier on delivery. From the system:

  • start_payment → synthetic intent ID, requires_inline_confirm=true, no external call
  • confirm_payment → always succeeds, status=awaiting_delivery
  • Order created with payment_status='unpaid'
  • Order's payment_status flips to paid when the courier confirms cash receipt (separate flow via fulfillment update or manual mark-paid)

Bank transfer (bank_transfer) — Bankovní převod s VS

Customer wires money manually using a Variabilní Symbol (typically the order number). From the system:

  • start_payment → instant success, returns IBAN + VS for the customer to use
  • confirm_payment → instant success, status=awaiting_transfer
  • Order's payment_status flips to paid when:
    • Staff manually marks paid in admin
    • Bank statement import (apps/api banks module) reconciles
    • Open Banking webhook reports the deposit (future)

carrier_config for bank_transfer:

{
  "iban": "CZ...",
  "bic": "...",
  "beneficiary_name": "Acme s.r.o.",
  "qr_payment_url": "https://qr.example.com/pay/..."
}

Reserved slots (future iterations)

  • Stripe (card_stripe) — generic card payments via Stripe Checkout / PaymentIntents
  • Comgate (card_comgate) — CZ-native card gateway
  • GoPay (card_gopay) — alternative CZ gateway

The connector contract is designed to slot in any of these — requires_redirect=true, start_payment returns a redirect_url, the customer pays on the gateway's hosted page, the gateway calls back to your storefront's return_url, and your storefront calls POST /confirm.json with the callback payload.

Payment attempts ledger

checkout_payment_attempts records every attempt with:

FieldNotes
methodConnector name
providerGateway adapter (same as method for now)
provider_intent_idGateway-side reference
status`started → redirected
amount_cents, currencyAmount being charged
provider_request / provider_responseFull audit
error_code, error_messageOn failure

A checkout can have multiple attempts — failed retries each get a new row.

Refunds

The refund flow is part of the Orders module (via payments table on the order). The connector's refund() op is called when staff issues a refund in admin.

Phase 1: refund support depends on the gateway. COD doesn't support refund() (refunds happen out of band). Bank transfer same. Stripe / Comgate support real refunds when those connectors land.

Next steps

Orders — what complete produces.