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
- Deploy the template (
docker build -f apps/seller-template/Dockerfile .orpnpm -C apps/seller-template dev). - Set
SUIPAY_GATEWAY_ISSUERandSUIPAY_GATEWAY_JWKS_URLto the exact issuer origin (the URL the gateway puts on delivery-proof JWTs).localhostand127.0.0.1are different. That binds JWKS and OAuth AS metadata. This step does not list the service. - In the Sui Agent Payments console, paste this seller origin. The console fetches
GET /.well-known/suipay-seller(aliasGET /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. - Review terms and click Publish. That is
POST /gateway/api/services/:id/status { "status": "live" }. - The service appears on
/directoryand/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-party503 upstream_not_readydoes 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.jsonOptional 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
| Piece | Where |
|---|---|
| Shared shell | @suipay/seller-runtime (posture, HTTP catalog, Postgres jti, env JWKS bind, manifesto) |
| Product handler | src/echo.ts — swap for your upstream |
| Manifesto | GET /.well-known/suipay-seller (mint: false) |
| Boot | src/server.ts |
Default listen port is 8089. Set SUIPAY_WRAP_ORIGIN to wrap an existing API
instead of the in-process stub.