> ## Documentation Index
> Fetch the complete documentation index at: https://docs.skinloop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout and payment state map

> Map hosted checkout, deposit, provider payment, and webhook states.

Skinloop exposes related lifecycle views for different consumers. Do not treat
their field names as interchangeable:

| Surface                                            | Field         | Published values                                                                                                                                                            |
| -------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hosted checkout create, retrieve, and status       | `status`      | `created`, `initiated`, `pending`, `active`, `hold`, `completed`, `canceled`, `declined`, `failed`, `reverted`, `expired`, `reconciliation_required`                        |
| Checkout-deposit creation and status               | `state`       | `initiated`, `pending`, `active`, `hold`, `completed`, `canceled`, `declined`, `failed`, `reverted`, `expired`, `reconciliation_required`                                   |
| Provider payment lifecycle represented by Skinloop | `status`      | `initiated`, `pending`, `active`, `hold`, `completed`, `canceled`, `declined`, `failed`, `reverted`                                                                         |
| Merchant webhook payload family                    | `type`        | `payment.initiated`, `payment.pending`, `payment.active`, `payment.hold`, `payment.completed`, `payment.canceled`, `payment.declined`, `payment.failed`, `payment.reverted` |
| Configurable merchant endpoint subscriptions       | `type`        | `payment.pending`, `payment.completed`, `payment.reverted`                                                                                                                  |
| Merchant webhook body                              | `data.status` | The event suffix without the `payment.` prefix                                                                                                                              |

`created` belongs only to a hosted session before a deposit starts. `expired`
and `reconciliation_required` are checkout/deposit recovery projections, not
provider payment statuses, so there is no `payment.expired` or
`payment.reconciliation_required` event.

The serializer and schema can represent every accepted provider transition.
The merchant dashboard currently offers endpoint subscriptions for
`payment.pending`, `payment.completed`, and `payment.reverted`. Use checkout
status polling for other intermediate or unsuccessful states instead of
assuming an unsubscribable event will be delivered.

## State mapping

| Meaning                                   | Hosted checkout `status`                                | Deposit `state`                                | Provider payment `status`        | Webhook `type`                               | Terminal?                                       |
| ----------------------------------------- | ------------------------------------------------------- | ---------------------------------------------- | -------------------------------- | -------------------------------------------- | ----------------------------------------------- |
| Session exists; no trade attached         | `created`                                               | Not created                                    | Not created                      | None                                         | No                                              |
| Deposit dispatch began                    | `pending` or `initiated` when provider state is visible | `initiated`                                    | Not yet available or `initiated` | `payment.initiated` when accepted            | No                                              |
| Provider is processing                    | `pending`                                               | `pending`                                      | `pending`                        | `payment.pending`                            | No                                              |
| Provider trade is active                  | `active`                                                | `active`                                       | `active`                         | `payment.active`                             | No                                              |
| CS2 provider hold                         | `hold`                                                  | `hold`                                         | `hold`                           | `payment.hold`                               | No                                              |
| Provider-verified completion              | `completed`                                             | `completed`                                    | `completed`                      | `payment.completed`                          | Yes; fulfillment allowed                        |
| Canceled                                  | `canceled`                                              | `canceled`                                     | `canceled`                       | `payment.canceled`                           | Yes                                             |
| Declined                                  | `declined`                                              | `declined`                                     | `declined`                       | `payment.declined`                           | Yes                                             |
| Failed                                    | `failed`                                                | `failed`                                       | `failed`                         | `payment.failed`                             | Yes                                             |
| Reversed after progress or completion     | `reverted`                                              | `reverted`                                     | `reverted`                       | `payment.reverted`                           | Yes; reconcile if fulfillment may have occurred |
| Offer deadline passed before attachment   | `expired`                                               | `expired` when no protected trade can continue | Not created                      | None                                         | Yes                                             |
| Dispatch or identity outcome is ambiguous | `reconciliation_required`                               | `reconciliation_required`                      | May be unknown                   | None until a provider transition is accepted | Automation stops                                |

The hosted checkout projection uses the provider payment status when a payment
exists. Before that, it uses the hosted-session state. The deposit projection
uses the provider trade state when available and maps uncertain dispatch to
`reconciliation_required`.

## Completion authority

Only provider-verified `completed` produces all of these signals:

* hosted checkout `status: "completed"`,
* deposit `state: "completed"`,
* provider payment `status: "completed"`,
* webhook `type: "payment.completed"` with
  `data.status: "completed"`, and
* `fulfillmentAllowed: true`.

The authoritative fulfillment decision requires `completed` and
`fulfillmentAllowed: true` from an authenticated server-side status response or
a verified webhook. Browser redirects, `initiated`, `pending`, `active`, and
`hold` never authorize fulfillment.

## Skipped, duplicate, and delayed observations

Intermediate states are observable but not guaranteed. A provider can move from
`initiated` directly to `active`, `hold`, `completed`, or a failure state.
Skinloop accepts same-state callbacks idempotently and ignores delayed
lower-state or invalid transitions. Merchant webhook delivery is at least once,
so duplicates are expected.

Do not require every row in the mapping table to appear. Deduplicate webhook
events, compare immutable identities, and fetch the current hosted checkout
status when observations conflict. A later `reverted` is authoritative even
after `completed`; if fulfillment may have occurred, stop and reconcile rather
than creating another payment attempt.

## Expiry is not trade cancellation

For an unattached `created` checkout, `expiresAt` is a real pre-trade deadline.
After a deposit is attached, the deposit status capability remains usable
beyond that deadline and the protected trade can still complete. Never map an
attached pending, active, or hold trade to `expired` using a local timer.
