Storefront book-and-pay (customer-present)
const url = 'https://quikstik-core.fly.dev/storefront/purchases';const options = { method: 'POST', headers: { 'x-tenant-id': '2489E9AD-2EE2-8E00-8EC9-32D5F69181C0', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, body: '{"obligation_ref":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","settlement":"full","offering_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","offering_variant_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","demand_class":"example","effective_year":1,"effective_month":1,"effective_day":1,"provider":"stripe","instrument_token":"example","booking_intent_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","units":1,"rental_days":1}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://quikstik-core.fly.dev/storefront/purchases \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'x-tenant-id: 2489E9AD-2EE2-8E00-8EC9-32D5F69181C0' \ --data '{ "obligation_ref": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "settlement": "full", "offering_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "offering_variant_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "demand_class": "example", "effective_year": 1, "effective_month": 1, "effective_day": 1, "provider": "stripe", "instrument_token": "example", "booking_intent_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "units": 1, "rental_days": 1 }'Anonymous (publishable-key) one-shot book-and-pay. The caller submits the booking
coordinates plus a customer-present tokenized instrument; the token is vaulted and
charged through the same reserve-then-pay engine as recordCaptureViaRail — the seats are
committed against the capacity pool before the rail is touched (oversell rejected 409
with nothing charged), and net + tax + gross are resolved server-side from the governed
catalog (the client never supplies a price). Idempotent on the session idempotency key.
Fail-closed: an unconfigured processor (402) or a pool that cannot hold the seats (409)
charges nothing.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Tenant identifier (UUID).
Authn/authz semantics are intentionally TBD for this contract slice; this header exists to keep the contract explicitly multi-tenant from day 0.
Request Bodyrequired
Section titled “Request Bodyrequired”object
The booking obligation this charge settles.
Which settlement leg to charge. full charges the whole gross (default,
backward compatible). deposit charges the policy-derived deposit and leaves a
balance owed; the deposit policy is resolved server-side (an unconfigured
merchant is refused 422). The merchant-initiated balance leg is not
storefront-callable.
Demand lane the price resolves in. The net is derived server-side from the governed catalog; the client cannot supply a price.
Processor the instrument token belongs to.
Customer-present tokenized instrument (e.g. a Stripe PaymentMethod id). Never a PAN.
The hold this purchase settles. Its bound departure (resolved at intent creation) is the capacity leaf the seats commit against — the storefront supplies no capacity key, so it cannot mis-key a non-leaf parent. Unknown hold -> 404; a hold with no departure -> 422.
Seats to commit before any charge. Zero is rejected 400 before the rail is touched.
Rental-day count for a multi-day product (e.g. a multi-day equipment rental). The
charge scales by units × rental_days at the server-governed per-day rate; the
capacity commit stays at units (seats). A day COUNT, never an amount — the price
remains server-derived. Out-of-range values are rejected 400 (invalid_rental_days)
before the rail is touched. Omitted = 1 (single-day, byte-identical behavior).
Responses
Section titled “Responses”Charged — the seat was committed and the customer’s card settled
object
Transactional-outbox record id for the recorded capture.
The processor’s reference for the charge (Stripe / Finix).
Total charged in integer minor units (net + tax), server-derived.
Tax-exclusive net in integer minor units, server-derived from the governed catalog.
Tax in integer minor units, server-derived from the tax configuration.
Example generated
{ "outbox_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "processor_ref": "example", "gross_minor": 1, "net_minor": 1, "tax_minor": 1}Malformed request (unsupported provider, empty instrument token, or zero seats)
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}Missing or invalid session bearer token
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}The operator has not connected a processor for this tenant (fail-closed, nothing charged)
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}The capacity pool cannot hold the requested seats (oversell guard; nothing charged)
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}The processor rail refused the charge
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}The operator’s stored credential could not be opened into a usable processor
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}Capacity backend or funds-capture unavailable (nothing charged)
object
Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).
Example
{ "reason": "sold_out"}