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

# Develop with Atum SDK

> Register Atum's MPP payment method with mppx and accept MPP payments directly in your server's code.

Register `atum-escrow` as a payment method on your `mppx` server to accept it alongside any other MPP methods you support — no example repo required.

```bash theme={null}
npm install @atumlabs/mppx-atum-escrow @atumlabs/payment-gateway-client mppx zod
```

`registerServer` takes a single thing: a **submitter** — an adapter that hands the signed `PaymentRequest` to Atum's Payment Gateway. The **corridor** (what you receive and the terms you offer payers) is built separately and passed to `buildChargeChallenge` per charge:

```ts theme={null}
import * as http from "node:http";
import { Mppx } from "mppx/server";
import {
  registerServer,
  buildChargeChallenge,
  corridorFromDefaults,
  type PaymentSubmitter,
} from "@atumlabs/mppx-atum-escrow/server";
import { PaymentGatewayClient } from "@atumlabs/payment-gateway-client";

// Example testnet gateway URL — see Endpoints for current hosts.
const gateway = new PaymentGatewayClient({ BASE: "https://payment-gw.production-testnet.atum.xyz" });

// A submitter forwards the verified PaymentRequest to the Payment Gateway to settle.
// Submit and return what the gateway said — do NOT poll for completion here.
// The gateway holds the connection up to ~30s. If settlement finishes in that window
// you get the confirmation; if not, you get pending + payment_id, and that is the
// honest answer. Polling would hold the payer's HTTP request open (proxy timeouts)
// and hide the pending state that makes the payer's re-attempt safe.
const submitter: PaymentSubmitter = {
  async submit(request) {
    const res = await gateway.payments.submitPayment({ requestBody: request });
    return {
      payment_id: res.payment_id,
      status: res.status,
      fulfillment_confirmation: res.fulfillment_confirmation,
    };
  },
};

// What you receive and which sources you accept. `corridorFromDefaults` fetches the
// settlement vault, role, proxy, and verifier addresses from the gateway, so you never
// hardcode protocol contracts — only your own business config.
const corridor = await corridorFromDefaults(gateway, {
  destination: { network: "eip155:11142220", asset: "0x…USDT", account: "0x…merchant" },
  sources: [{ network: "eip155:421614", assets: ["0x…srcUSDT"] }],
  markupBps: 300, // source cap = fulfillment amount + 3%
  quoteDeadlineSeconds: 60,
  fulfillmentDeadlineSeconds: 600,
});

// `registerServer` takes only a submitter: it trusts the HMAC-bound challenge, so one
// registration serves every corridor you advertise.
const mppx = Mppx.create({
  realm: "api.example.com",
  secretKey: process.env.MPP_SECRET_KEY,
  methods: [registerServer({ submitter })],
});

// `buildChargeChallenge` turns the corridor + one source option into the atum-escrow
// challenge and stamps intentId — the purchase being paid for. Build it fresh **per
// request**: a challenge built once at module scope would stamp every payer with the
// same intentId, so a returning customer's second purchase would resolve onto their
// first (paid once, delivered twice).
const source = corridor.sources[0];

http.createServer(async (req, res) => {
  const purchaseId = req.url?.match(/^\/paid\/([^?]+)/)?.[1]; // one id per purchase, e.g. from the URL
  if (!purchaseId) {
    res.statusCode = 404;
    res.end();
    return;
  }

  const { request, meta } = buildChargeChallenge(
    corridor,
    { network: source.network, asset: source.assets[0] },
    "100000",
    { intentId: purchaseId },
  );
  const route = Mppx.toNodeListener((input) =>
    mppx.compose(["atum-escrow/charge", { ...request, meta }])(input),
  );

  const result = await route(req, res);
  if (result.status === 402) return; // mppx already wrote the 402 (challenge or still-settling)
  res.end(JSON.stringify({ message: "Here is your resource." }));
}).listen(4030);
```

### What's happening

1. `registerServer` builds an `atum-escrow` server method for `mppx` — the same `Method.toServer()` idiom every MPP payment method implements. It takes only a `submitter`; the corridor rides in the HMAC-bound challenge, so one registration serves every corridor you advertise.
2. `buildChargeChallenge` turns your corridor and one source option into the `atum-escrow` challenge, including an `intentId` for the purchase — built fresh per request from the URL, so two different purchases never share an id; `mppx.compose` serves that challenge with `402` when the request carries no credential.
3. When a credential for `atum-escrow` arrives (`Authorization: Payment`), `mppx` calls this method's `verify()`: a cheap local check (signature recovery, terms match), then `submitter.submit(paymentRequest)`.
4. Your submitter forwards the request to the Payment Gateway, the **authoritative validator** — it re-checks everything and drives settlement (selecting a settler's price quote, and orchestrating the source-chain deposit and destination-chain fulfillment).
5. Your submitter returns what the gateway said. Within \~30s you may get a confirmation; past that window you get pending, `verify()` raises `SettlementPendingError`, and the payer [re-attempts the same purchase](/payment-protocols/idempotency). On success, `mppx` serves your resource with `Payment-Receipt` attached — key fulfillment on that receipt's `payment_id`.

<Warning>
  **Do not poll inside `verify()`.** A missing confirmation means still settling, not failed. Pass `status` through so `verify()` can raise `SettlementPendingError` (re-attempt) versus `SettlementFailedError` (new purchase id). Size proxy timeouts for the \~30s synchronous window only — past that, answer pending. Full model: [Idempotency](/payment-protocols/idempotency).
</Warning>

<Note>
  Chain IDs, tokens, and addresses above are **illustrative placeholders**. Use [Supported networks](/get-started/reference/supported-networks) and [Supported assets](/get-started/reference/supported-assets) for the live catalog.
</Note>

## Next steps

| Topic                      | Link                                                                                         |
| -------------------------- | -------------------------------------------------------------------------------------------- |
| Idempotency                | [Idempotency](/payment-protocols/idempotency)                                                |
| Payer-side guide           | [Make payments](/payment-protocols/mpp/make-payments/overview)                               |
| Run the example first      | [Agentic receives](/payment-protocols/mpp/accept-payments/agentic-payment-acceptance)        |
| MPP server/SDK reference   | [mpp.dev](https://mpp.dev/)                                                                  |
| Accepting via x402 instead | [Develop with Atum SDK (x402)](/payment-protocols/x402/accept-payments/develop-with-the-sdk) |
