Payment SDK implementation
This skill scaffolds or extends a language port of Pay Kit while keeping it
wire-compatible with the public reference implementations in this repository.
Rust under rust/crates/kit/src/ is the broadest in-repo reference; TypeScript
under typescript/packages/ is also authoritative for the surfaces it ships.
Both MPP and x402 are implemented today, but coverage varies by language
and changes quickly.
Specs (authoritative)
- MPP / HTTP Payment Authentication scheme — https://paymentauth.org
- x402 — https://docs.x402.org/introduction
- Pay Kit interface —
docs/paykit-interface.md
- Rust reference —
rust/crates/kit/src/{mpp,x402}
- TypeScript reference —
typescript/packages/{mpp,pay-kit} and
typescript/external/x402
Compatibility matrix — pick the cells in scope
Both the client and server README matrices use the same seven rows. Confirm
with the user which cells you are implementing this pass. Each enabled cell
maps to exactly one reference file under references/intents/:
| Cell |
Reference file |
Status |
x402/exact |
intents/x402-exact.md |
public Rust and TypeScript references |
x402/upto |
intents/x402-upto.md |
public Rust and TypeScript references |
x402/batch-settlement |
intents/x402-batch-settlement.md |
public Rust and TypeScript references |
mpp/charge/pull |
intents/mpp-charge-pull.md |
public Rust and TypeScript references |
mpp/charge/push |
intents/mpp-charge-push.md |
public Rust and TypeScript references |
mpp/session |
intents/mpp-session.md |
public Rust and TypeScript references |
mpp/subscription |
intents/mpp-subscription.md |
public Rust and TypeScript references |
Do not infer a default protocol matrix. Read the target language's README,
source tree, and its entries in harness/src/implementations.ts, then implement
only the cells requested. A cell implemented in Rust is not automatically
available or harness-enabled in every other language.
Workflow
Work through these phases in order. Do not skip ahead; later phases assume
the directory skeleton and CI from earlier ones.
- Confirm scope. Ask the user (a) the target language, (b) which
matrix cells are in scope this pass, (c) the package name. Use
AskUserQuestion if any of these are unclear.
- Lay out the repo. Read
references/repo-layout.md and create the
directory tree, package manifest, and justfile recipes. Do this
before writing any protocol code — the layout drives the import paths
the intents docs assume.
- Pick conventions. Read
references/coding-conventions.md and lock
in the formatter, linter, type-checker, error type, and async runtime
for the language. This file also lists the per-language style guides
(PSR-12 for PHP, Standard Ruby, etc.) the SDK must follow.
- Wire CI before code. Read
references/ci-quality-coverage.md and
add a GitHub Actions job that mirrors test-rust/test-python in
.github/workflows/ci.yml, with a ≥90 % coverage gate, formatter and
linter steps, and the html-assets download. Push CI green on an
empty skeleton before adding intent code so regressions are caught
immediately.
- Implement intent code. For each matrix cell the user enabled,
read the matching file under
references/intents/. Each leaf is
self-contained: wire format, server obligations, client obligations,
subtle bugs to avoid, test cases to mirror, spec links. Reference the
current Rust and TypeScript paths cited in the leaf to disambiguate
anything that's under-specified.
- Consume Solana program clients via Codama. Read
references/codegen.md. Pay-kit vendors Codama IDLs at
idl/<program>.json and renders per-language clients with
@codama/renderers-* via the tooling under codegen/ (sibling of
this SKILL.md). Today the Rust path ships for the subscriptions
program; do not hand-write a Solana program client in a new
language — add a subscriptions-generate-<lang> recipe alongside
the existing subscriptions-generate-rs and consume the generated
tree the same way rust/crates/kit/src/mpp/program/ does in Rust.
- Add the harness adapter. Read
references/harness.md,
create harness/<lang>-client/ (and a bin/harness_server if
you're shipping a server), and register it in
harness/src/implementations.ts. Run the focused matrix with the
protocol-specific MPP_HARNESS_* or X402_HARNESS_* selectors documented
there before flipping enabled: true.
- Apply the operability caveats. Read
references/operability-caveats.md. These are the gaps the Ruby
gem's PR #142 follow-up closed (default localnet RPC, mainnet
mint fallback on localnet, preflight + Surfnet cheatcode
auto-bootstrap, MPP HMAC secret auto-resolution chain, embedded
recentBlockhash in the x402 challenge, framework-host quirks).
Every port has to land them; PRs that omit any of the numbered
items need an explicit "not applicable" note in the body.
- Write the README last. Read
references/readme-template.md and
fill in the title, badges, repo layout, basic snippet, install/usage,
client and server matrices (with the seven rows above), example
walkthrough, Solana dependency list, and links to spec. The matrix
must use the exact row order shown above so it's diffable across SDKs.
Hard rules
- No fabricated invariants. Every wire-format claim in the SDK you
ship must trace back to a public implementation under
rust/crates/kit/src/{mpp,x402}, the corresponding TypeScript package, or
the governing protocol spec. If a
reference file says "see X.rs", open it.
- Canonical JSON → base64url, no padding. Bodies that flow through
the
request field, the opaque field, or signing inputs use
RFC 8785 canonical JSON, then base64url-no-pad. Transactions
themselves use standard-alphabet base64 (with padding). This split
has bitten every implementation at least once — see
references/intents/mpp-charge-pull.md for the exact boundary.
- Cross-route replay protection is non-negotiable. Every server
must run the tier-2 pinned-field check before settlement — see the
pinned-field backstop in
rust/crates/kit/src/mpp/server/charge.rs. The new
SDK must expose a verify_credential_with_expected (or the
language-idiomatic equivalent) that pins amount, currency, and
recipient per route.
- Coverage gate, doc comments, no emoji. Public types and functions
carry a one-line doc comment so the language's LSP shows hover text.
Coverage gate is ≥ 90 % unless
references/ci-quality-coverage.md
says otherwise for the language. Do not put emoji in code, commits,
or documentation. Do not put model identifiers, "Generated by Claude"
signatures, or claude.ai/code URLs anywhere in committed artifacts.
- The harness is the truth. A unit-test green SDK that fails the
harness against Rust is a bug. Run the harness before declaring a
cell done.
When the user asks for something this skill does not cover
- A protocol or language cell without a public reference: report the exact
missing source or harness vector and ask for scope. Do not invent semantics
or depend on a private checkout.
- Anything cross-cutting in the on-chain payment-channels program: treat
that as out of scope. The Rust crate
re-exports the on-chain artifacts; the new SDK only needs to
serialize/deserialize them.
1---2name: payment-sdk-implementation3description: Port or extend a Pay Kit SDK for x402 and MPP on Solana. Use when the user asks to "port Pay Kit to <language>", "add <language> payment client/server support", "implement an MPP intent", or "add an x402 scheme". Inspect the target language's current matrix before choosing scope, then load only the relevant intent references.4---56# Payment SDK implementation78This skill scaffolds or extends a language port of Pay Kit while keeping it9wire-compatible with the public reference implementations in this repository.10Rust under `rust/crates/kit/src/` is the broadest in-repo reference; TypeScript11under `typescript/packages/` is also authoritative for the surfaces it ships.12Both **MPP** and **x402** are implemented today, but coverage varies by language13and changes quickly.1415## Specs (authoritative)1617- MPP / HTTP Payment Authentication scheme — <https://paymentauth.org>18 - Spec PRs — <https://github.com/tempoxyz/mpp-specs>19- x402 — <https://docs.x402.org/introduction>20- Pay Kit interface — `docs/paykit-interface.md`21- Rust reference — `rust/crates/kit/src/{mpp,x402}`22- TypeScript reference — `typescript/packages/{mpp,pay-kit}` and23 `typescript/external/x402`2425## Compatibility matrix — pick the cells in scope2627Both the client and server README matrices use the same seven rows. Confirm28with the user which cells you are implementing this pass. Each enabled cell29maps to exactly one reference file under `references/intents/`:3031| Cell | Reference file | Status |32|---|---|---|33| `x402/exact` | `intents/x402-exact.md` | public Rust and TypeScript references |34| `x402/upto` | `intents/x402-upto.md` | public Rust and TypeScript references |35| `x402/batch-settlement` | `intents/x402-batch-settlement.md` | public Rust and TypeScript references |36| `mpp/charge/pull` | `intents/mpp-charge-pull.md` | public Rust and TypeScript references |37| `mpp/charge/push` | `intents/mpp-charge-push.md` | public Rust and TypeScript references |38| `mpp/session` | `intents/mpp-session.md` | public Rust and TypeScript references |39| `mpp/subscription` | `intents/mpp-subscription.md` | public Rust and TypeScript references |4041Do not infer a default protocol matrix. Read the target language's README,42source tree, and its entries in `harness/src/implementations.ts`, then implement43only the cells requested. A cell implemented in Rust is not automatically44available or harness-enabled in every other language.4546## Workflow4748Work through these phases in order. Do not skip ahead; later phases assume49the directory skeleton and CI from earlier ones.50511. **Confirm scope.** Ask the user (a) the target language, (b) which52 matrix cells are in scope this pass, (c) the package name. Use53 `AskUserQuestion` if any of these are unclear.542. **Lay out the repo.** Read `references/repo-layout.md` and create the55 directory tree, package manifest, and `justfile` recipes. Do this56 before writing any protocol code — the layout drives the import paths57 the intents docs assume.583. **Pick conventions.** Read `references/coding-conventions.md` and lock59 in the formatter, linter, type-checker, error type, and async runtime60 for the language. This file also lists the per-language style guides61 (PSR-12 for PHP, Standard Ruby, etc.) the SDK must follow.624. **Wire CI before code.** Read `references/ci-quality-coverage.md` and63 add a GitHub Actions job that mirrors `test-rust`/`test-python` in64 `.github/workflows/ci.yml`, with a ≥90 % coverage gate, formatter and65 linter steps, and the `html-assets` download. Push CI green on an66 empty skeleton before adding intent code so regressions are caught67 immediately.685. **Implement intent code.** For **each** matrix cell the user enabled,69 read the matching file under `references/intents/`. Each leaf is70 self-contained: wire format, server obligations, client obligations,71 subtle bugs to avoid, test cases to mirror, spec links. Reference the72 current Rust and TypeScript paths cited in the leaf to disambiguate73 anything that's under-specified.746. **Consume Solana program clients via Codama.** Read75 `references/codegen.md`. Pay-kit vendors Codama IDLs at76 `idl/<program>.json` and renders per-language clients with77 `@codama/renderers-*` via the tooling under `codegen/` (sibling of78 this `SKILL.md`). Today the Rust path ships for the subscriptions79 program; do **not** hand-write a Solana program client in a new80 language — add a `subscriptions-generate-<lang>` recipe alongside81 the existing `subscriptions-generate-rs` and consume the generated82 tree the same way `rust/crates/kit/src/mpp/program/` does in Rust.837. **Add the harness adapter.** Read `references/harness.md`,84 create `harness/<lang>-client/` (and a `bin/harness_server` if85 you're shipping a server), and register it in86 `harness/src/implementations.ts`. Run the focused matrix with the87 protocol-specific `MPP_HARNESS_*` or `X402_HARNESS_*` selectors documented88 there before flipping `enabled: true`.898. **Apply the operability caveats.** Read90 `references/operability-caveats.md`. These are the gaps the Ruby91 gem's PR #142 follow-up closed (default `localnet` RPC, mainnet92 mint fallback on `localnet`, preflight + Surfnet cheatcode93 auto-bootstrap, MPP HMAC secret auto-resolution chain, embedded94 `recentBlockhash` in the x402 challenge, framework-host quirks).95 Every port has to land them; PRs that omit any of the numbered96 items need an explicit "not applicable" note in the body.979. **Write the README last.** Read `references/readme-template.md` and98 fill in the title, badges, repo layout, basic snippet, install/usage,99 client and server matrices (with the seven rows above), example100 walkthrough, Solana dependency list, and links to spec. The matrix101 must use the exact row order shown above so it's diffable across SDKs.102103## Hard rules104105- **No fabricated invariants.** Every wire-format claim in the SDK you106 ship must trace back to a public implementation under107 `rust/crates/kit/src/{mpp,x402}`, the corresponding TypeScript package, or108 the governing protocol spec. If a109 reference file says "see X.rs", open it.110- **Canonical JSON → base64url, no padding.** Bodies that flow through111 the `request` field, the `opaque` field, or signing inputs use112 RFC 8785 canonical JSON, then base64url-no-pad. Transactions113 themselves use standard-alphabet base64 (with padding). This split114 has bitten every implementation at least once — see115 `references/intents/mpp-charge-pull.md` for the exact boundary.116- **Cross-route replay protection is non-negotiable.** Every server117 must run the tier-2 pinned-field check before settlement — see the118 pinned-field backstop in `rust/crates/kit/src/mpp/server/charge.rs`. The new119 SDK must expose a `verify_credential_with_expected` (or the120 language-idiomatic equivalent) that pins `amount`, `currency`, and121 `recipient` per route.122- **Coverage gate, doc comments, no emoji.** Public types and functions123 carry a one-line doc comment so the language's LSP shows hover text.124 Coverage gate is ≥ 90 % unless `references/ci-quality-coverage.md`125 says otherwise for the language. Do not put emoji in code, commits,126 or documentation. Do not put model identifiers, "Generated by Claude"127 signatures, or `claude.ai/code` URLs anywhere in committed artifacts.128- **The harness is the truth.** A unit-test green SDK that fails the129 harness against Rust is a bug. Run the harness before declaring a130 cell done.131132## When the user asks for something this skill does not cover133134- A protocol or language cell without a public reference: report the exact135 missing source or harness vector and ask for scope. Do not invent semantics136 or depend on a private checkout.137- Anything cross-cutting in the on-chain payment-channels program: treat138 that as out of scope. The Rust crate139 re-exports the on-chain artifacts; the new SDK only needs to140 serialize/deserialize them.