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

# Payment Lifecycle

> How a payment moves from session creation to confirmed payment and fund sweep

## Overview

```
create session → detect transfer → verify finality → mark paid → sweep → payout
   (address)       (webhook/poll)    (confirmations)   (status)   (drain)  (merchant wallet)
```

Each checkout session gets a unique deposit address. The customer sends funds to that address. My Crypto Server detects the transfer, waits for the required confirmations, marks the session paid, and sweeps the funds to your payout wallet.

The entire pipeline is **idempotent and crash-resumable** — webhook retries, manual checks, and Inngest replays can all fire more than once without double-crediting or double-sweeping.

***

## Stage 1: Session creation

When `POST /api/v1/checkout_sessions` is called:

1. A unique derivation index is allocated and a **deposit address** is derived from the merchant seed (BIP-44 for EVM).
2. A price quote (`ethPriceUsd`) is frozen and the required on-chain amount is computed using fixed-point math — no floats.
3. The session is inserted with `status: pending`.
4. A `session/created` Inngest event is emitted.

The `sessionLifecycle` Inngest function then:

* Subscribes the deposit address to Alchemy's Address Activity webhook so incoming transfers trigger an immediate notification.
* Sleeps durably until `expiresAt`. If the session is still `pending` at expiry, it transitions to `expired`.

***

## Stage 2: Detecting the transfer

Two independent paths feed the same ingestion pipeline:

**Primary — Alchemy webhook (push):**
Alchemy detects the transfer and POSTs to `/api/webhooks/alchemy`. The HMAC-SHA256 signature is verified, the transfer is ingested, and `payment/detected` is emitted to Inngest.

**Fallback — merchant check (pull):**
The dashboard's "Check payment" button hits the on-chain scanner directly, scanning the chain for transfers to the session's address.

**Customer fallback (pull):**
While the customer has the checkout page open, the browser polls after a delay (2 min for EVM/Solana, 4 min for Bitcoin). This closes the gap between webhook delivery and user experience.

All paths converge on the same `ingestTransfer` function, which is idempotent via a unique index on `(txHash, toAddress, vout)`.

### Amount classification

| Result      | What happened                                     |
| ----------- | ------------------------------------------------- |
| `exact`     | Transfer matches the required amount → `detected` |
| `underpaid` | Transfer is less than required → `underpaid`      |
| `overpaid`  | Transfer is more than required → `overpaid`       |

***

## Stage 3: Verifying finality

After detection, the `paymentVerify` Inngest workflow:

1. Waits 60 seconds.
2. Calls `verifyAndFinalize` — checks the transaction receipt and counts confirmations.
3. If not enough confirmations yet, waits 30 seconds and retries (up to 10 times, \~5 min total).
4. **Reorg protection** — if the receipt's block doesn't match what was recorded, the payment is reset and the session goes back to `pending`.

Once the transaction has `≥ REQUIRED_CONFIRMATIONS`:

* The payment is marked `finalized`.
* The session transitions to `paid` (or `paid_late` if past expiry but within the grace window).
* The deposit address is removed from Alchemy's watchlist.
* The sweep runs inline.

`REQUIRED_CONFIRMATIONS` is set via the env var. Default is 3; mainnet ETH should use 12+.

***

## Stage 4: Session status machine

```
                  ┌─── overpaid ──────┐
pending ─detect──►│                   ├─finalize──► paid
                  └─── detected ──────┘
                  └─── underpaid ─────── (requires merchant action)

pending ──expire──► expired
detected/overpaid/underpaid ──reorg──► pending
```

| Transition                              | Trigger                                                |
| --------------------------------------- | ------------------------------------------------------ |
| `pending → detected/underpaid/overpaid` | Transfer detected                                      |
| `detected/overpaid → paid`              | Finality confirmed, within expiry                      |
| `detected/overpaid → paid_late`         | Finality confirmed, after expiry + within grace window |
| `pending → expired`                     | Session TTL elapsed                                    |
| Any `detected` status → `pending`       | Chain reorg detected                                   |

***

## Stage 5: Webhook delivery

Once the session is marked `paid`, a `session.paid` event is enqueued. The `webhookDeliver` Inngest function POSTs it to each of your registered webhook endpoints. Failures are retried up to 6 times with exponential backoff (\~31h total).

***

## Recovery paths

| Problem                        | Recovery                                                               |
| ------------------------------ | ---------------------------------------------------------------------- |
| Alchemy webhook never arrived  | Click "Check payment" in dashboard                                     |
| Sweep failed                   | Dashboard → Payouts → Retry                                            |
| Address dropped from watchlist | Hourly `reconcileAlchemy` cron re-syncs                                |
| Inngest outage                 | `pnpm inngest:resync` re-emits lifecycle events for in-flight sessions |
