# Order Flow Example (Merchant-Configurable Go Runtime, Stripe-Only Launch)

This example matches the current Go API contract used by the live GCP deploy:

- merchant bootstrap from canonical `/v1/merchants/{merchantHandle}/*` endpoints
- request-bound merchant context via `X-Merchant-Handle`
- Stripe card checkout in the merchant storefront currency
- card confirm returns a Stripe Checkout Session payload before the order exists
- x402 remains future/experimental and is not part of launch checkout

> **Agent Warning:** Launch payment strategy: Stripe-only. Do not prioritize Coinbase x402, USDC, Base, wallet setup, or crypto checkout for launch unless the founder explicitly reopens that decision. x402 remains a long-term architectural direction, not a launch blocker.

## Step 0: Bootstrap Merchant Context

```bash
curl http://localhost:3000/v1/merchants/au-comptoir-a-patons/info
```

Response (example):

```json
{
  "merchantHandle": "au-comptoir-a-patons",
  "requestContext": {
    "transport": "header",
    "header": "X-Merchant-Handle",
    "value": "au-comptoir-a-patons"
  },
  "paymentRails": {
    "defaultCheckoutMethod": "card",
    "available": ["card"]
  },
  "walletSetupUrl": "http://localhost:3000/v1/wallet-setup"
}
```

Use the returned `requestContext` on the shared ordering APIs shown below.

## Step 1: Get Menu

```bash
curl http://localhost:3000/v1/menu \
  -H "X-Merchant-Handle: au-comptoir-a-patons"
```

## Step 2: Create Quote

```bash
curl -X POST http://localhost:3000/v1/orders/quote \
  -H "Content-Type: application/json" \
  -H "X-Merchant-Handle: au-comptoir-a-patons" \
  -d '{
    "items": [
      {"sku": "PZ-MAR", "qty": 1},
      {"sku": "DR-COL", "qty": 2}
    ],
    "fulfillment": {"type": "pickup"},
    "contact": {"name": "John Doe", "phone": "+33612345678", "email": "john@example.com"},
    "paymentMethod": "card"
  }'
```

Response (example):

```json
{
  "quoteId": "550e8400-e29b-41d4-a716-446655440000",
  "currency": "EUR",
  "availablePaymentMethods": ["card"],
  "walletSetupUrl": "http://localhost:3000/v1/wallet-setup",
  "paymentInstructions": "Card payment required: 14.50 EUR. Confirm your order to complete checkout securely by card.",
  "quoteHash": "sha256:...",
  "significance": "low",
  "riskHints": {
    "recommendedConfirmation": "auto"
  }
}
```

Notes:

- `availablePaymentMethods` is merchant-bound
- Axel currently returns `["card"]`
- `quoteHash`, `significance`, and `riskHints` stay available for agent safety and approval policy
- `walletSetupUrl` can stay in the payload even when the storefront hides crypto

## Step 3: First Confirm (Card branch)

```bash
curl -i -X POST http://localhost:3000/v1/orders/confirm \
  -H "Content-Type: application/json" \
  -H "X-Merchant-Handle: au-comptoir-a-patons" \
  -d '{
    "quoteId": "550e8400-e29b-41d4-a716-446655440000",
    "paymentMethod": "card",
    "assertions": {
      "expectedQuoteHash": "sha256:...",
      "expectedPaymentMethod": "card"
    }
  }'
```

The response is `200` and includes:

- `checkoutSessionId`
- `checkoutUrl`
- `status = open`
- `paymentStatus = unpaid`
- `assertions` can still be reused to pin `quoteHash`, `paymentMethod`, and merchant identity on confirm

## Step 4: Complete Payment And Poll Checkout Status

Complete the payment in hosted Stripe Checkout, then poll checkout status until the API returns the order:

```bash
curl http://localhost:3000/v1/orders/checkout-sessions/cs_test_123 \
  -H "X-Merchant-Handle: au-comptoir-a-patons"
```

Successful response:

```json
{
  "checkoutSessionId": "cs_test_123",
  "ready": true,
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "orderStatus": "paid",
  "pickupCode": "440000",
  "status": "paid",
  "currency": "EUR",
  "paymentMethod": "card"
}
```

## Step 5: Future/Experimental Crypto Variant

For `merchant-template`, the crypto flow still uses:

- `paymentMethod = "crypto"`
- first confirm `402`
- `PAYMENT-REQUIRED`
- second confirm with a real `PAYMENT-SIGNATURE`

Use this branch only for explicit future/experimental x402 compatibility work. Launch storefronts must hide it.

## Step 6: Future/Experimental Crypto Recovery Examples

If the quote snapshot changed before confirm, the API fails fast with a deterministic assertion error:

```json
{
  "error": "Quote assertion failed: total 1450c exceeds max 1400c.",
  "errorCode": "ASSERTION_FAILED_TOTAL",
  "retryable": false
}
```

Recovery rule:

- rebuild the quote and reuse the new `quoteHash` / total / merchant assertions
- do not keep replaying confirm against the stale quote snapshot

If the onchain proof is malformed or otherwise invalid, the API keeps the failure actionable without echoing raw proof bytes:

```json
{
  "error": "invalid payment proof",
  "errorCode": "PAYMENT_INVALID",
  "retryable": false,
  "details": {
    "reason": "invalid_signature",
    "requiredAtomic": "14500000",
    "network": "eip155:8453"
  }
}
```

Recovery rule:

- `invalid_signature`: rebuild the proof payload and retry once
- `expired`: create a fresh quote before retrying
- `wrong_network`: switch back to Base before retrying
- `insufficient_funds`: top up the wallet before retrying
