# Payment Flow — Happy Path

This page describes the **standard end-to-end payment flow** in Fluxis: from creating a payment request through on-chain detection, asynchronous processing, and webhook delivery. For non-standard outcomes (underpayment, expiration, failed transactions), see [Payment Flows — Edge Cases](/docs/payment-flows/edge-cases).

## Architecture overview

Fluxis payment processing spans four cooperating services:

```mermaid
graph LR
    API[Core API] --> DB[(PostgreSQL)]
    API --> Redis[(Redis)]
    Cron[Cron] --> Redis
    Cron --> SQS[(SQS transactions queue)]
    Worker[Transaction Worker] --> SQS
    Worker --> DB
    Worker --> API
    Cron --> Chain[Polygon / Base / Avalanche / Arbitrum]
```

| Service | Role |
| ------- | ---- |
| **Core API** | Creates payment requests, assigns vault addresses, generates [NASPIP tokens](/docs/key-concepts/naspip/introduction), exposes REST endpoints |
| **Cron** | Polls blockchains every **3 seconds** for ERC-20 `Transfer` events to active vault addresses |
| **Transaction Worker** | Consumes the `transactions` SQS queue and runs the **3-hop** payment lifecycle |
| **Crypto Service** | External service for vault management, consolidation, and settlement (post-payment) |

Merchants interact only with the Core API and [account-level webhooks](/docs/developers/webhooks). Blockchain monitoring and transaction confirmation happen asynchronously.

## Prerequisites

Before a payment can succeed, the merchant hierarchy must be configured:

```mermaid
graph TD
    Org[Organization] --> Acct[Account]
    Acct --> POS[Point of Sale]
    Acct --> WH[Account Webhook]
    POS --> PR[Payment Request]
```

1. **Organization and Account** — See [Merchant Sign Up](/docs/merchant-integration/sign-up).
2. **Point of Sale** — An `online_fixed` PoS (e-commerce) or `cashier_fixed` PoS (physical register). See [Point of Sale](/docs/key-concepts/point-of-sale).
3. **Authentication** — Bearer token + `x-fluxis-api-key` on protected routes. See [Authentication & Configuration](/docs/merchant-integration/auth-and-conf).
4. **Webhook (recommended)** — Register at account scope: `POST /v1/account/{accountID}/webhook`. See [Webhooks](/docs/developers/webhooks).

## Step 1 — Create a payment request

Create a payment request through the **Point of Sale** endpoint — not a standalone `/v1/payment-requests` route:

```
POST /v1/pos/{posID}/payment-request
Authorization: Bearer <token>
x-fluxis-api-key: <api_key_id>
```

```json
{
  "reference_id": "order-12345",
  "amount": "49.99",
  "unique_asset_id": "npolygon_t0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
  "order": {
    "total": "49.99",
    "coin_code": "USD",
    "description": "Premium Subscription"
  }
}
```

::::tip
**Hosted checkout alternative**

When the payer should choose their asset from the PoS `payment_options`, use `POST /v1/pos/{posID}/payment-request-checkout`. The payment request starts in `pending` status and transitions to `created` once the payer selects a UAI. See [Creating Payment Requests](/docs/merchant-integration/create-payreq).
::::

### What Fluxis does internally

```mermaid
sequenceDiagram
    participant M as Merchant App
    participant API as Core API
    participant Pool as Vault Pool
    participant Redis as Redis Cache

    M->>API: POST /v1/pos/:posID/payment-request
    API->>Pool: Atomically assign vault (FOR UPDATE SKIP LOCKED)
    Pool-->>API: Vault address
    API->>API: Create payment request (status: created)
    API->>Redis: Cache vault → payment request mapping
    API->>API: Generate NASPIP token (PASETO v4)
    API-->>M: id, status, token, expiration
```

On creation, Fluxis:

1. Validates the PoS and resolves the [UAI](/docs/key-concepts/uai)
2. Assigns a unique **vault address** from the pre-deployed pool
3. Sets status to **`created`** and an **8-minute expiration** timestamp
4. Caches the vault address in Redis for fast cron lookups
5. Returns a [NASPIP token](/docs/key-concepts/naspip/introduction) for QR code / payment link display

Example response:

```json
{
  "status": "success",
  "data": {
    "id": "180ede28-2968-4ac7-8e81-ca446f201949",
    "reference_id": "order-12345",
    "status": "created",
    "token": "naspip;fluxis.us;fluxis.qr.dyn.1;v4.public.IhYyMDI...",
    "expiration": 1759397652
  }
}
```

## Step 2 — Customer pays on-chain

The customer scans the NASPIP token (QR code or link) and sends the exact amount to the vault address on the configured network. Fluxis does not hold custody during this step — the payment goes directly to the vault smart contract.

Your application should:

- Display the NASPIP token and a countdown based on `expiration`
- Poll `GET /v1/pos/{posID}/payment-request/{paymentRequestID}` or wait for webhooks
- **Not** fulfill the order until the payment reaches a terminal status

## Step 3 — Cron detects the transfer

The cron service runs a blockchain reading job **every 3 seconds** on supported networks (Polygon, Base, Avalanche, Arbitrum):

```mermaid
flowchart TD
    A[Acquire Redis lock] --> B[Read last processed block]
    B --> C[eth_getLogs Transfer events]
    C --> D[Match tx.to against active vaults in Redis]
    D --> E{Match found?}
    E -->|Yes| F[Enqueue transaction_incoming_payment to SQS]
    E -->|No| G[Update last processed block]
    F --> G
```

Key details:

- Uses `eth_getLogs` with **adaptive chunking** to query ERC-20 `Transfer` events efficiently
- Filters transactions where the recipient (`to`) matches an active vault address cached in Redis
- Enqueues matched transactions to the **`transactions`** SQS queue with event type `transaction_incoming_payment`

## Step 4 — Three-hop SQS worker

The transaction worker processes each payment through **three sequential SQS hops**:

```mermaid
sequenceDiagram
    participant SQS as SQS Queue
    participant W as Transaction Worker
    participant Chain as Blockchain RPC
    participant WH as Merchant Webhook

    Note over SQS,W: Hop 1 — transaction_incoming_payment
    SQS->>W: Incoming payment detected
    W->>W: created → processing
    W->>WH: Webhook (processing)
    W->>SQS: Enqueue check (finality delay)

    Note over SQS,W: Hop 2 — transaction_check_update_payment_request
    SQS->>W: After finality delay (~256s default)
    W->>Chain: GetTransaction (verify status)
    alt Confirmed
        W->>W: Compare amounts → completed / underpaid / overpaid
        W->>WH: Webhook (final status)
        W->>SQS: Enqueue confirm (immediate)
        W->>W: Release vault, clear Redis cache
    else Still pending
        W->>SQS: Re-enqueue with finality delay
    end

    Note over SQS,W: Hop 3 — transaction_confirm_payment_request
    SQS->>W: Confirm payment request
    W->>W: Create internal Transaction record
    W->>W: completed/overpaid → confirmed
```

### Hop 1 — `transaction_incoming_payment`

- Validates payment request is in `created` or `processing`
- Updates status to **`processing`**
- Sends a **webhook** with status `processing`
- Enqueues hop 2 with a **finality delay** (default **256 seconds** on Polygon, configurable per network)

### Hop 2 — `transaction_check_update_payment_request`

After the finality delay:

- Calls `GetTransaction` to verify on-chain status (pending / confirmed / failed)
- Uses the **amount from cron's `eth_getLogs` detection** (not from `GetTransaction`, which fails on proxy/router transactions)
- Compares received amount vs expected amount:
  - Equal → **`completed`**
  - Less → **`underpaid`**
  - Greater → **`overpaid`**
- Sends a **webhook** with the final status
- Enqueues hop 3 immediately and releases the vault

### Hop 3 — `transaction_confirm_payment_request`

- Runs only for **`completed`** or **`overpaid`** payments
- Creates an internal **Transaction** record (fees, addresses, ledger entry)
- Updates payment request status to **`confirmed`**
- Does **not** send a webhook (see webhook matrix in [Edge Cases](/docs/payment-flows/edge-cases))

::::info
**Underpaid payments**

Hop 3 does **not** run for `underpaid` payments — no internal transaction is created. See [Edge Cases — Underpaid](/docs/payment-flows/edge-cases#underpaid).
::::

## Step 5 — Webhook notifications

Fluxis delivers webhooks at account scope when payment request status changes during hops 1 and 2:

```json
{
  "data": {
    "notification_type": "payment_request_status_update",
    "payment_request_id": "aa5d6c80-0b0b-4a2a-bbc8-14a41e6245bf",
    "payment_request_type": "fixed",
    "external_reference_id": "order-12345",
    "status": "completed",
    "point_of_sale_id": "3cc1f7ca-c017-4e7e-b00d-aca0da6e464e",
    "account_id": "c87464d1-c54a-48c3-80c9-bee4fddf593d"
  }
}
```

| Webhook-delivered status | When |
| ------------------------ | ---- |
| `processing` | Cron detected an on-chain transfer to the vault |
| `completed` | Confirmed after finality; amount matches expected |
| `underpaid` | Confirmed after finality; received less than expected |
| `overpaid` | Confirmed after finality; received more than expected |

Verify every webhook with HMAC-SHA256 before processing. See [Webhook Signature Verification](/docs/developers/webhook-signature-verification) and [Webhooks](/docs/developers/webhooks).

## Payment request states

```mermaid
stateDiagram-v2
    [*] --> pending: Checkout created
    pending --> created: Payer selects UAI / vault assigned
    created --> expired: 8 min timeout
    created --> processing: On-chain payment detected
    processing --> completed: Amount matches
    processing --> underpaid: Amount too low
    processing --> overpaid: Amount too high
    completed --> confirmed: Internal transaction created
    overpaid --> confirmed: Internal transaction created

    expired --> [*]
    underpaid --> [*]
    confirmed --> [*]
    completed --> [*]
    overpaid --> [*]
```

| State | Description | Merchant action |
| ----- | ----------- | --------------- |
| **`pending`** | Created via checkout; payer has not selected a UAI yet | Show checkout / asset picker |
| **`created`** | Vault assigned; awaiting on-chain payment (**8-minute** window) | Display NASPIP token; poll or use webhooks |
| **`processing`** | Transfer detected; awaiting blockchain finality | Inform customer payment is being confirmed |
| **`completed`** | Full payment confirmed after finality | Fulfill order |
| **`confirmed`** | Internal ledger transaction created | Payment is settled in Fluxis accounting |
| **`underpaid`** | Confirmed but amount is less than expected | See [Edge Cases](/docs/payment-flows/edge-cases#underpaid) |
| **`overpaid`** | Confirmed but amount exceeds expected | See [Edge Cases](/docs/payment-flows/edge-cases#overpaid) |
| **`expired`** | No payment received before expiration | Create a new payment request |

::::warning
**Do not fulfill on `processing`**

Wait for `completed`, `underpaid`, or `overpaid` before fulfilling orders. The `processing` status means a transfer was detected but has not yet reached finality.
::::

## Timing and expiration

Two different timeouts apply depending on the flow:

| Flow | Timeout | Applies to |
| ---- | ------- | ---------- |
| **Payment request** | **8 minutes** | From vault assignment (`created` status) until expiration |
| **Payment intention** | **5 minutes** | Cashier PoS flow (`cashier_fixed` / `cashier_open`) before the merchant creates the payment request |

::::info
**Expiration cron**

A background job runs every **30 seconds** to mark payment requests with status `created` and `expiration < now` as **`expired`**. Transactions broadcast near the deadline may still be detected while the vault remains cached briefly after expiration.
::::

For the cashier flow, the merchant creates a payment intention via `POST /v1/pos/{posID}/payment-intention` with a **5-minute** window. Once confirmed, Fluxis creates the payment request with the standard 8-minute payment window.

## End-to-end sequence

```mermaid
sequenceDiagram
    participant M as Merchant
    participant F as Fluxis API
    participant C as Customer
    participant B as Blockchain
    participant Cron as Cron
    participant W as Worker

    M->>F: POST /v1/pos/:posID/payment-request
    F-->>M: status: created, NASPIP token
    M->>C: Display QR / payment link
    C->>B: Send USDC to vault address
    Cron->>B: eth_getLogs (every 3s)
    Cron->>W: SQS: transaction_incoming_payment
    W->>M: Webhook: processing
    Note over W: Finality delay (~256s)
    W->>B: Verify transaction confirmed
    W->>M: Webhook: completed
    W->>W: Create internal transaction
    Note over W: status → confirmed (no webhook)
    M->>C: Order fulfilled
```

## Monitoring options

| Method | Endpoint / setup | Best for |
| ------ | ---------------- | -------- |
| **Webhooks (recommended)** | `POST /v1/account/{accountID}/webhook` | Production — real-time status updates |
| **Polling** | `GET /v1/pos/{posID}/payment-request/{paymentRequestID}` | Development or webhook fallback |
| **Transactions list** | `GET /v1/transactions?page=1&page_size=50` | Reconciliation after `confirmed` |

## Related documentation

- [Creating Payment Requests](/docs/merchant-integration/create-payreq) — Integration guide with API examples
- [Payment Requests](/docs/key-concepts/payment-request) — States, fields, and checkout flow
- [Payment Flows — Edge Cases](/docs/payment-flows/edge-cases) — Underpaid, overpaid, expired, failed transactions, refunds
- [Webhooks](/docs/developers/webhooks) — Registration and payload format
- [Webhook Signature Verification](/docs/developers/webhook-signature-verification) — HMAC verification and test workflow
- [NASPIP](/docs/key-concepts/naspip/introduction) — Token format and wallet compatibility
- [API Reference — Point of Sale](/api/point-of-sale) — Interactive endpoint documentation
