Skip to content
LogoLogo

Seller template

A third-party provider copies apps/seller-template and deploys it in front of an existing or stub API. The gateway mints the 402 and settles. The sidecar verifies a delivery proof, then calls upstream. There is no published seller SDK.

Do not copy apps/seller-openrouter, apps/seller-gcp, apps/seller-aws, or apps/seller-walrus. Those are first-party Railway products (seeded by the gateway, not listed via console Publish).

Follow apps/seller-template/README.md.

Loop

  1. Deploy the template (docker build -f apps/seller-template/Dockerfile . or pnpm -C apps/seller-template dev).
  2. Set SUIPAY_GATEWAY_ISSUER and SUIPAY_GATEWAY_JWKS_URL to the exact issuer origin (the URL the gateway puts on delivery-proof JWTs). localhost and 127.0.0.1 are different. That binds JWKS and OAuth AS metadata. This step does not list the service.
  3. In the Sui Agent Payments console, paste this seller origin. The console fetches GET /.well-known/suipay-seller (alias GET /setup.json) and fills every field. Several endpoints on one origin appear as a picker. Fetching a URL does not register or list the service. Sui Agent Payments-operated product ids are refused here.
  4. Review terms and click Publish. That is POST /gateway/api/services/:id/status { "status": "live" }.
  5. The service appears on /directory and /services/[slug].

POST /v1/probe is the other surface. A managed origin answers 402 missing_proof without an MPP challenge, so probe against this process fails.

Contract

  • Buyer SDK is 2-call. The agent talks to the gateway: unpaid request → 402 → local sign → POST /v1/pay. The seller process never mints a 402.
  • Managed mode. After settlement the gateway forwards the paid request with a delivery proof. The template verifies it before serving.
  • Spend-account rail. The manifesto defaults to delegate, the same settlement engine first-party products seed, so a buyer grant can pay it.
  • Do not self-host mint-and-settle on the same route.
  • Do not import @suipay/gateway.
  • Listing does not append the origin to SUIPAY_UPSTREAM_ALLOW. The operator adds origins by hand. A listed service that is not on the allowlist still settles; the gateway returns synthesized JSON, not Echo. First-party 503 upstream_not_ready does not apply (managed: false).

Connect the gateway

Set the issuer origin at deploy. GET /config is a JSON snapshot, not a form. localhost and 127.0.0.1 are different origins.

SUIPAY_GATEWAY_ISSUER=https://gateway.example
SUIPAY_GATEWAY_JWKS_URL=https://gateway.example/.well-known/jwks.json

Optional local rebind without a restart (ignored when those env vars are set):

curl -s -X POST http://localhost:8089/config \
  -H 'content-type: application/json' \
  -d '{"gatewayUrl":"https://gateway.example"}'

Loopback http://localhost:4340 is allowed. Plain HTTP on any other host is refused.

What you copy

PieceWhere
Shared shell@suipay/seller-runtime (posture, HTTP catalog, Postgres jti, env JWKS bind, manifesto)
Product handlersrc/echo.ts — swap for your upstream
ManifestoGET /.well-known/suipay-seller (mint: false)
Bootsrc/server.ts

Default listen port is 8089. Set SUIPAY_WRAP_ORIGIN to wrap an existing API instead of the in-process stub.