Contract v1 · generated reference
RuntimeHandler.paymentsCaptureOnRequest
OP-RUNTIME-PAYMENTS-CAPTURE-ON-REQUEST is a firestore trigger operation owned by Traveler.
Back to the complete API catalogue
Authority and transport
- Actor
- Firestore document event
- Authentication
- platform invocation
- Authorization
- platform-only; authority re-verified from server-written state, never from the delivered payload
- Resource boundary
- server-derived from verified payload and stored state
- Transport
FIRESTORE_TRIGGER- Deployed export
paymentsCaptureOnRequest- Trigger
onDocumentCreated; Firestore document paymentCaptureRequests/{sagaId}; retry false- App Check
- NOT_APPLICABLE
- Canonical source
functions/src/properties/traveler/payments/capture.ts
Consumed document
A Firestore trigger has no client request. The fields below are read from the created document the platform delivers; the handler re-verifies authority from server-written state and never trusts the delivered payload.
| Field | Required | Schema |
|---|---|---|
paymentCaptureRequests/{sagaId} | Yes | document id of the created capture request; the payment saga id |
state | Yes | "REQUESTED"; any other state is SKIPPED_NOT_REQUESTED |
intentId | Yes | provider payment intent id (pi_ prefix); a malformed id freezes the row |
attempts | No | prior attempt count; five attempts stall the row |
Response schema
Promise<void>; outcome CAPTURED | ADOPTED_ALREADY_CAPTURED | SKIPPED_NOT_REQUESTED | SKIPPED_NOT_AUTHORIZED | FROZEN | PROVIDER_UNAVAILABLE | LEASE_BUSY | STALLED recorded on the request row
States, consistency and recovery
- State path
- DOCUMENT_CREATED → REQUEST_AND_SAGA_PRECONDITIONS → PROVIDER_PREFLIGHT_RETRIEVE → SAGA_LEASE_CLAIM → CAPTURE_STEP → CAPTURED_OR_ADOPTED → REQUESTED_FOR_REDRIVE_OR_STALLED → FROZEN_WITH_OPERATOR_SIGNAL
- Reads
- the paymentCaptureRequests row, the payment saga status and lease, and a provider payment-intent pre-flight read before any lease is taken
- Writes
- request row state, attempts and last error; the saga lease and CAPTURE step through the existing capture step; outbox capture-frozen or capture-stalled signals. The webhook remains the only writer of CONFIRMED
- Transaction
- saga lease with a fencing token around the existing capture step; the pre-flight read costs a retry, never a lease
- Idempotency
- provider capture idempotency key {sagaId}:CAPTURE; an intent the provider already captured is adopted, not charged again
- Concurrency
- saga lease with fencing token; a live lease returns LEASE_BUSY without capturing
- Retry/replay
- trigger retry is disabled; REQUESTED rows older than two minutes are re-driven every five minutes up to five attempts
- Rate limits
- Platform trigger only; there is no public caller quota.
- Audit
- request row attempts, privacy-safe last error code and outcome; payments.capture_request_frozen / payments.capture_request_stalled outbox signals; no card or customer data
Error contract
No public error envelope; provider unavailability leaves the row REQUESTED for the re-drive, a fifth failed attempt stalls it, and a shaped provider refusal freezes it with an outbox signal. Never treat an unknown error as success.
No public error code is exposed by this managed trigger.
Producer test references
Evidence tier: SEMANTIC_CORE_REFERENCE.
Reviewer-mapped semantic/core references
functions/test/recovery-decline-recovery.test.js- D3 the good-card retry on the SAME intent: amount_capturable_updated → AUTHORIZED → capture once → CONFIRMED exactly once, fee row once, seat held→booked once
functions/test/recovery-settlement.test.js- S1 amount_capturable_updated → AUTHORIZED, hold AUTHORIZED, a capture request is queued once
- S2 the capture producer: provider outage leaves the row REQUESTED; a reachable provider captures once
- S3 UAT ordering: the producer captured first, so payment_intent.succeeded is a DUPLICATE for the saga; the webhook's projection moves the seat (5 → 4)
- S8 producer resilience (adversarial review 2026-09-04): live lease → LEASE_BUSY; lapsed RUNNING is re-driven; an escaping throw is counted, not SKIPPED; a cancelled intent is SKIPPED without a capture
Each cited test name is checked against the referenced file by the documentation suite, so a renamed or removed producer test breaks this page instead of leaving a stale claim.
Protocol or inventory references
functions/test/estate-service-inventory.js
These references prove export, wrapper or protocol behavior only. They are not counted as complete operation semantics.
Example availability
NOT_PUBLIC_TRANSPORT: This handler is provider- or platform-invoked and has no public client invocation.
Common integration contract
Sandbox and Production are isolated
Run examples only in a governed sandbox or local emulator. Sandbox identities, App Check tokens, payment fixtures and data do not grant Production access. The typed ports do not ratify a public numeric quota; enforcement may be stricter. Honor RATE_LIMITED and the server's retry-after value.
Authentication and authorization
Firebase Auth proves identity. App Check proves the approved app where a callable requires it. Roles, organization membership, requester identity and resource ownership are derived and rechecked by the server.
Authentication guideErrors and recovery
Stable error codes distinguish validation, denial, conflict, expiry, authority pending, rate limiting and temporary unavailability. Never infer success from an HTTP redirect or a browser state.
Error catalogueIdempotency, concurrency and retry
Reuse an idempotency key only for an identical retry. A new intent needs a new key. Preserve version preconditions and reload after a conflict.
Retry guidanceChanges and escalation
Check compatibility before adopting a new contract version. Review the changelog, service status and support guidance before retrying a persistent failure.
| Family | Client decision |
|---|---|
| Authentication / authorization | Refresh or step up only when instructed; do not retry a denial unchanged. |
| Validation / conflict / expiry | Correct or reload state, then obtain confirmation for a new intent. |
| Rate limited / unavailable | Honor retry-after and use bounded backoff; inspect service status. |
| Unknown / server | Fail closed and escalate with a privacy-safe correlation id. |
Executable network examples are published only when the exact wire schema is producer-proven. Check the selected operation's example-availability status; an unresolved schema is a release blocker, not permission to guess.
Verified examples and local-only recovery snippets