> ## Documentation Index
> Fetch the complete documentation index at: https://ftp-tech.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# x402 Merchant Integration

> Gate your Express or Next.js API endpoints behind Canton Coin or CIP-56 token payments with x402 middleware.

You are a **merchant** if you have an HTTP API and want to charge Canton Coin (CC), or any CIP-56 registry token (e.g. USDCx), per request. Your callers (agents, wallets, or any x402-compatible client) pay before your handler runs.

**You do not need a token balance.** You are receiving, not sending. Settlement uses the `transfer-factory` method: the payer signs a `TransferFactory_Transfer` naming you as receiver, the facilitator relays it, and because you hold a standing `TransferPreapproval` it settles directly to your party in a single transaction. Canton Coin and registry tokens work identically — you pick the instrument via `extra.instrumentId` in your `PaymentRequirements`.

## Before You Start

You need:

* A Canton party ID (your receiver identity on Canton mainnet)
* A live `TransferPreapproval` for Canton Coin with you as receiver (see Step 1) — this is what lets an incoming transfer settle in one transaction
* A facilitator URL, party ID, and synchronizer ID (Step 2)

## Step 1: Provision a TransferPreapproval

Before any agent can pay you, your party MUST hold a live `TransferPreapproval` for the Canton Coin instrument (receiver = you). It is what lets an incoming `TransferFactory_Transfer` resolve **direct** — accepted automatically and settled in one transaction. Without it the transfer resolves to a two-step pending `TransferInstruction`, the facilitator cannot settle in one round-trip, and `/settle` is rejected with `invalid_exact_canton_preapproval_missing` (your callers get a 402 instead of the resource). A `TransferPreapproval` is time-bounded — renew it before expiry to keep the one-transaction path available.

Pick the path that matches how your party is hosted.

### External-party merchant (relay-managed wallet)

Your party is an Ed25519 self-custody wallet managed through the relay. Provision the preapproval with the CLI — it self-provisions, no operator token needed:

```bash theme={null}
npm i -g @ftptech/canton-agent-wallet@latest   # provides the `canton-agent-wallet` binary

# Canton Coin: --admin is the DSO party
canton-agent-wallet preapproval --admin DSO::1220... --days 30
# → TransferPreapproval created (updateId ...)

# CIP-56 registry token (e.g. USDCx): --admin is the token's registrar, --id its instrument
canton-agent-wallet preapproval --admin <registrar>::1220... --id USDCx
```

For Canton Coin, `--admin` is the DSO party id (get it from your Canton Scan API) and `--days` sets the validity before renewal. For a registry token, `--admin` is the registrar and `--id` the instrument id; the registrar must be trusted (`CANTON_AGENT_REGISTRY_TRUSTED_PARTIES`, with USDCx built in). One preapproval per instrument you want to receive.

### Hosted-node merchant

If your party lives on your OWN Canton node/wallet, create the `TransferPreapproval` through that wallet/validator directly (receiver = your party) — no relay or CLI needed. There is no custom DAR to install: `transfer-factory` runs through the standard CIP-56 `:TransferInstruction` interface.

## Step 2: Get Facilitator Parameters

Call `GET /supported` on your facilitator to get the values you need:

```bash theme={null}
curl https://facilitator.ftptech.xyz/supported
```

```json theme={null}
{
  "kinds": [{
    "x402Version": 2,
    "scheme": "exact",
    "network": "canton:mainnet",
    "extra": {
      "transferMethods": ["transfer-factory"],
      "synchronizerId": "global-domain::1220..."
    }
  }],
  "signers": {
    "canton:*": ["<facilitator-party>::1220..."]
  }
}
```

From this response: `signers["canton:*"][0]` is your `feePayer`, and `extra.synchronizerId` is your synchronizer. Get the DSO party id (for `instrumentId.admin`) from your Canton Scan API.

## Step 3: Install

```bash theme={null}
# Express
npm i @ftptech/x402-canton-core @ftptech/x402-canton-express

# Next.js App Router
npm i @ftptech/x402-canton-core @ftptech/x402-canton-next
```

## Step 4: Define PaymentRequirements

```typescript theme={null}
import type { PaymentRequirements } from "@ftptech/x402-canton-core";

const paymentReq: PaymentRequirements = {
  scheme: "exact",
  network: "canton:mainnet",
  amount: "10000000000",            // atomic units (1 CC = 10^10 units)
  asset: "CC",
  payTo: "<YOUR_PARTY>::1220...",   // your Canton party id; CC lands here
  maxTimeoutSeconds: 120,
  extra: {
    assetTransferMethod: "transfer-factory",
    feePayer: "<facilitator-party>::1220...",
    synchronizerId: "global-domain::1220...",
    instrumentId: { admin: "<DSO-party>::1220...", id: "Amulet" },
    executeBeforeSeconds: 120,
    // memo: "invoice-2024-001"      // optional reconciliation tag (not validated)
  },
};
```

**Amount format:** an integer string of atomic units (1 CC = 10^10 units). The facilitator compares by value (BigInt atomic units), so `"10000000000"` = 1 CC and `"100000000"` = 0.01 CC; a mismatch returns `invalid_exact_canton_amount_mismatch`.

**Field notes:**

* `feePayer`: the facilitator party — the relayer that submits the payer-signed transfer and pays its traffic fee. Clients MUST NOT alter it.
* `instrumentId`: the token to charge. Canton Coin is `{ admin: "<DSO-party>", id: "Amulet" }`; a CIP-56 registry token (e.g. USDCx) is `{ admin: "<registrar-party>", id: "<instrument-id>" }`. To charge a registry token, set this and the matching `asset` symbol, and hold that token's `TransferPreapproval` (Step 1).
* `executeBeforeSeconds`: relative deadline (seconds from request time) the client uses to compute the transfer's absolute `executeBefore`; after it, the signed transfer is no longer executable.
* `memo` (optional): a string the client stamps into the transfer's metadata; the facilitator does not validate it.

> These field names follow the x402-ENVELOPE convention. `synchronizerId` may be omitted; clients fall back to the value advertised by `GET /supported`.

## Step 5: Add Middleware

### Express

```typescript theme={null}
import express from "express";
import { cantonPaymentMiddleware } from "@ftptech/x402-canton-express";

const app = express();
app.use(express.json());

app.use(
  cantonPaymentMiddleware({
    facilitatorUrl: "https://facilitator.ftptech.xyz",
    routes: {
      "POST /api/data": {
        accepts:     [paymentReq],
        description: "Access to premium data",  // shown to clients in 402 body
        mimeType:    "application/json",
      },
      "GET /api/data": { accepts: [paymentReq] },
    },
  })
);

// Handler runs only after payment is verified and settled
app.post("/api/data", (req, res) => {
  res.json({ result: "..." });
});

app.listen(3000);
```

Route keys use `"METHOD /path"` format, matched exactly against `${req.method} ${req.path}`. Unregistered routes pass through ungated.

### Next.js App Router

```typescript theme={null}
// app/api/data/route.ts
import { withCantonPayment } from "@ftptech/x402-canton-next";

export const POST = withCantonPayment(
  async (req) => Response.json({ result: "..." }),
  {
    accepts: [paymentReq],
    facilitatorUrl: "https://facilitator.ftptech.xyz",
  }
);
```

## Step 6: Verify It Works

Call your gated endpoint without payment:

```bash theme={null}
curl -si https://your-api.com/api/data \
  -H "Content-Type: application/json" \
  -d '{}'
```

Expected:

```
HTTP/2 402
payment-required: <base64>
```

Decode to confirm your requirements are correct:

```bash theme={null}
echo "<base64-value>" | base64 -d | jq '.accepts[0]'
```

You should see your `amount`, `payTo`, and `feePayer` values.

## Security Notes

* **Your `accepts[]` is authoritative.** The middleware ignores any amount the client claims. A client cannot request a 1 CC resource for 0.01 CC by tampering with the payment payload.
* **The proven payer is on-ledger.** The facilitator binds the proven payer to the sender of the signed `TransferFactory_Transfer`, not to anything the client puts in the payload, so a spoofed `payer` field cannot impersonate.
* **Settlement happens before your handler.** If your handler throws after a successful `/settle`, the payer already paid. Handle errors gracefully; do not retry settlement.
* **Keep `payTo` correct.** CC goes to whatever `payTo` says. If your party ID changes, update `paymentReq` AND provision a `TransferPreapproval` (Step 1) for the new party.
