Idempotent on `external_order_id`: a repeat POST for the same
`(benefit_provider_id, external_order_id)` returns the existing order with
`200` instead of creating a second one or charging again. If that order's
payment never settled (`Initiated`/`Pending`), the replay reconciles it
against PBA first — idempotently on the order id — so the caller receives the
resolved state. Already-terminal orders are returned as they stand.
Benefit Provider Orders
Create Benefit Provider Order
POST
Idempotent on `external_order_id`: a repeat POST for the same
`(benefit_provider_id, external_order_id)` returns the existing order with
`200` instead of creating a second one or charging again. If that order's
payment never settled (`Initiated`/`Pending`), the replay reconciles it
against PBA first — idempotently on the order id — so the caller receives the
resolved state. Already-terminal orders are returned as they stand.
Body
application/json
Response
Order already existed for this external_order_id — the existing order is returned unchanged (idempotent replay)
A validated 12-digit numeric user identifier.
Every external entry point validates:
- JSON
Deserializeruns [UserId::parse] (outer whitespace trimmed, length and digit-only enforced) via#[serde(try_from = "String")]. - DB
FromSqlruns [UserId::parse] on every row read.
Display / Deref<Target = str> / AsRef<str> / Into<String> are
derived via derive_more — the inner String is pass-through for
every read-only borrow, display, and move-out.
Example:
"012345678901"
Lifecycle status of the wallet-side transaction attached to an order.
Wallet-agnostic — today PBA, tomorrow any other wallet integration. The lifecycle has five observable states:
Initiated— order row inserted; no wallet call has been made yet. Set at row create-time. Single-phase (interactive add-money) advances straight toSuccess; two-phase (mandate-autopay) advances toPending.Pending(deposit_id)— two-phase Phase 1 succeeded: the wallet provider accepted the deposit and returned its id, but it is still parked on the provider side waiting for an external settlement signal. Carried between the Juspay/txnsack and the/orders/{id}=Chargedreconciliation.Success(deposit_id)— terminal: the deposit has been finalised at the wallet provider (Phase 2 confirm, or single-phase commit). The provider's id is the single dedup signal for any further status poll.Voided(deposit_id)— terminal: a parkedPendingdeposit was explicitly released back to the source because the originating charge reached a terminal failure. The provider's id is preserved for audit.Failed— terminal: the originating charge failed before any wallet-side deposit was held (Phase 1 never ran or itself failed).
Serialized to text as:
initiated→Initiatedpending:<deposit_id>→Pending(deposit_id)success:<deposit_id>→Success(deposit_id)voided:<deposit_id>→Voided(deposit_id)failed→Failed
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5