Skip to content

Payments #

A payment order belongs to an existing customer Product Instance. The reusable ledger stores priced allocations, an authorisation, captured/refunded balances and durable provider operations. Laundry machines and paid Wi-Fi sessions can use the same service; neither needs a separate merchant or payment platform.

Amounts are integer currency minor units: AUD 5.00 is 500. An immutable business key identifies the order; a stable operation key identifies each authorisation, capture, cancellation or refund. Reusing a key with changed parameters fails. Customer-owned Stripe Connect accounts come from the existing organisation connection, never application configuration or the equipment gateway.

Authorisation and service delivery #

The backend service in product_logic.services.payments supports:

  1. create_order: create immutable allocations for an existing instance.
  2. perform_operation(kind="authorize"): reserve an operation durably before requesting a manual-capture PaymentIntent.
  3. bind_fulfilment_command: attach an existing Command Invocation in that same instance to an allocation, once authorisation is confirmed.
  4. reconcile_fulfilment: inspect the existing command result, or record an identified administrator's delivery evidence.
  5. settle_order: capture the sum of confirmed fulfilled allocations, or cancel if every allocation definitely received no service.

A command accepted by equipment is not proof of delivery. Success must include an explicit service_delivered: true signal. A definite rejection proves non-delivery; a timeout after dispatch does not. Unknown allocations block both capture and cancellation until resolved. The ledger never issues or repeats machine commands: the application continues to use Product Processes, Operations and the existing command lifecycle.

A three-allocation order can therefore capture two delivered services while releasing the uncaptured third amount. Refunds are separate identified operations with a reason, bounded by the captured balance. Unknown refunds block further financial effects.

For workflows that capture before fulfilment, a definite non-delivery creates a durable allocation refund instruction. The existing Payments worker drains it without depending on the original process remaining alive. Successful allocations keep their charge. Manual refunds and automatic refunds share allocation balances, so they cannot refund the same captured amount twice. Unknown service or refund results require reconciliation and never cause an automatic repeat.

Provider accounts and recovery #

New ledger orders default to Stripe direct charges, with every provider request scoped to the customer's connected account. Existing Stripe integration bindings retain destination-charge behavior. A ledger order can explicitly select destination; its refunds reverse the corresponding transfer. This initial ledger adapter does not add an application fee.

Requests use the existing Stripe Connect client and runtime secret store. No provider secrets, client secrets or provider exception bodies appear in ledger responses. Direct charges require charges_enabled. Recipient-only onboarding used by existing destination-charge applications does not establish that direct merchant charges are ready; merchant onboarding must be completed with Stripe.

Provider outages, invalid responses and acknowledgement loss leave an unresolved operation. Capture and cancellation can be reconciled by retrieving the current PaymentIntent. Webhooks record verified evidence scoped by account and test/live mode; delayed events do not overwrite current balances.

An organisation administrator can explicitly retry an unresolved provider operation with its original idempotency key and immutable request, within 23 hours of reservation. This never retries physical service. Recovery waits at least five minutes for a still-pending request. After the window expires, use Stripe review and the original operation reference; the backend refuses a new financial effect. Unknown authorisation and refund outcomes require this original-key recovery or provider review, rather than a new order.

Customer payment APIs #

These routes sit beneath the existing portal API prefix:

RouteBehavior
GET products/{instance_id}/payments/Orders, allocation delivery states and reconciliation flags; limit up to 100 and before pagination
GET products/{instance_id}/payments/{order_id}/Order and non-secret operation history
POST products/{instance_id}/payments/{order_id}/Administrator refund, settlement, provider recovery or delivery evidence
GET products/{instance_id}/payments/export/CSV of the latest 10,000 orders in that instance

Payment review and financial actions require organisation administrator access, including administrators granted that role through a portal group. Device operator or arbitrary network write access does not grant finance permission. All order, allocation and provider operation lookups remain within the customer's active instance. Audit events use the existing Product Event envelope.

Action bodies use action with one of:

  • refund: operation_key, amount_minor, reason.
  • settle: operation_key.
  • reconcile: retrieve current provider payment evidence.
  • retry_provider_operation: operation_id.
  • resolve_fulfilment: allocation_id, boolean delivered, evidence_reference.

Order creation and authorisation are backend service entry points. The portal review APIs do not accept card data or create payment orders.

Australian terminal testing #

Initial hardware testing is planned for Australia/AUD using an attended countertop reader with an 8-inch screen; the exact model is not yet confirmed. The reader's Android application is deferred. This backend slice authorises ordinary provider PaymentMethods using card payments; it does not implement Terminal card-present collection or claim that reader commissioning is complete.

Before using the reader, confirm its exact model, supported Android SDK and app installation process, Australian connected-account provisioning, test-mode availability and location ownership. Terminal collection will need a dedicated card-present PaymentIntent flow, an account-scoped connection-token endpoint and reader/app recovery. The same order, allocation and settlement ledger can support that flow once implemented.

For the scheduled Stripe call, confirm:

  • Attended Australian test setup and the path to custom Android app deployment.
  • Direct connected-account Terminal charging, manual capture and partial capture.
  • Reader/location/connection-token ownership for connected accounts.
  • Later unattended approval, reader selection, UK/Spain availability and any programme or UX700 access requirements.

Backend contract tests use a fake provider and mocked Stripe client, including non-laundry allocations, partial capture, refunds, outages, duplicate keys, unknown delivery, recovery expiry and tenant permissions. No Stripe sandbox credentials are configured in this workspace; live sandbox and physical-reader qualification remain separate verification steps.

Authoring and owner review #

Product Process authors can add a cloud Payment step with order creation, authorisation, command binding, fulfilment resolution, settlement or refund. Configure failure and timeout paths explicitly. Edge command execution continues to use the existing command mechanism; payment credentials stay in the cloud. Test execution stubs provider effects.

The customer portal Payments page presents each service outcome beside the order's captured and refunded amounts. Administrators can export the order ledger, record concrete delivery evidence, settle resolved service and issue bounded refunds. If a refund response is lost, retrying retains the same operation identity rather than creating another refund. An authored business navigation mode gives the reference application its customer-facing navigation.

Credits, discounts, receipts, settlement/payout/dispute reporting, Product Query projections and terminal lifecycle remain additional implementation tracks. This foundation does not claim their completion.