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

# Finality and refunds

> When value may be delivered, and why there is no on-chain refund mechanism on either rail. Locked 2026-06-18.

## value-on-finality

The governing rule: **never deliver value or signal `completed` while the payment is still payer-reversible.**

| Rail    | Captured when                                                   |
| ------- | --------------------------------------------------------------- |
| Connect | The `deposit()` transaction reaches required confirmations      |
| Deposit | The inbound transfer confirms **and** the keeper sweep confirms |

Never on first-seen. Zero-confirmation crediting is how iGaming and exchange operators lose money, and reorgs are real.

Confirmation depth is **value-scaled**, with per-chain defaults and per-merchant overrides. Soft and hard finality are distinguished — Ethereum's finalized checkpoint, TRON's roughly 19 confirmations, and the soft-versus-hard distinction on L2s are not the same guarantee.

<Warning>
  A known gap: the checkout embed's `payment_confirmed` event currently waits for the keeper sweep even on the **connect** rail, where capture is defined as the deposit confirmation. The UX is stricter than the policy requires, which delays the merchant's completion signal.
</Warning>

## No refund mechanism on either rail

Locked 2026-06-18. This was a deliberate removal, not an omission.

* **Connect rail** — irreversible once the deposit reaches required confirmations. It never had a refund window.
* **Deposit rail** — the forwarder is sweep-only. No `refund()`, no `refundTo`, no `refundDeadline`, no `Refunded` event, no window errors.

## Why the window was removed

The constraint is the refund **target**, not a timer.

A large share of deposit-rail payments originate from exchange withdrawals. Those funds leave a shared omnibus hot wallet, not the customer's own address. There is no safe on-chain address to refund to.

<Danger>
  **Never refund to the inbound sender.** A CEX-originated deposit returned to its sending address is unattributable and almost always a permanent loss for the customer.
</Danger>

Given that, an on-chain refund window added attack surface and audit scope without protecting the users actually at risk. It also created a free-option exploit: a customer could pay, watch the market, and reverse within the window. Removing it kills that by construction and settles funds faster.

## How refunds actually work

Merchant-initiated, off-chain, and manual. The merchant collects a **customer-supplied self-custody address at refund time** and sends the funds themselves.

One path covers every case: overpayment, underpayment, wrong token, and cancellation. The platform never fronts funds and never claws anything back.

<Warning>
  Refunds require `distribute()` and `claim()` to have run first, because the money is not in the merchant's hands until then — and by that point the platform fee has already been taken. Whether a refunded order gets its fee returned is an **open commercial decision**, along with how that is disclosed.
</Warning>

## Non-happy paths

The deposit rail has explicit statuses rather than silent failure: `underpaid`, `overpaid`, `wrong_token`, `held_sanctioned`, `expired`, `expired_paid_late`. Each carries `confirmed_amount` against `expected_amount`.

An underpaid invoice is the sharpest open gap. With `refund()` removed there is no exit path — the customer's partial payment sits in a forwarder with no way to complete or return it without manual intervention. The designed answer is a customer top-up prompt plus a merchant sweep-and-refund action.

## Reconciliation guarantees

* Per-checkout discriminator: `deposit(paymentId)` on the connect rail, a unique address on the deposit rail.
* Atomic one-to-one claim via `UNIQUE(discriminator)` and `UNIQUE(chain_id, tx_hash, log_index)`.
* Reorg-safe: N confirmations plus a finalized cross-check.
* The destination is verified as the **on-chain-committed pool**, read from the chain rather than trusted from the database.

<Info>
  Matching by transaction hash, payer, amount, and time window is **recovery only** — operator-reviewed, never auto-shipping value.
</Info>
