@cmdoss/suipay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @cmdoss/suipay
2
+
3
+ Embeddable SuiPay client for app developers. Quote a 402 resource and pay it
4
+ from a spend-account grant. This package does **not** bake hosted gateway URLs
5
+ — pass `gatewayUrl` yourself.
6
+
7
+ ## 30-second quickstart
8
+
9
+ ```ts
10
+ import { createClient } from '@cmdoss/suipay';
11
+ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
12
+
13
+ const keypair = Ed25519Keypair.fromSecretKey(/* 32-byte seed */);
14
+
15
+ const client = await createClient({
16
+ gatewayUrl: process.env.SUIPAY_GATEWAY_URL!,
17
+ keypair,
18
+ });
19
+
20
+ const url = 'https://gateway.example/v1/image/generate';
21
+ const result = await client.pay(url, {
22
+ json: { prompt: 'a small red circle' },
23
+ });
24
+ ```
25
+
26
+ `pay` quotes that POST, then pays the quoted price. You do not pass atomic
27
+ units. `result.quote` is the price it agreed to, and `result.paid` says whether
28
+ settlement finished.
29
+
30
+ `createClient` throws if `gatewayUrl` or `keypair` is missing. The key must
31
+ already have a live spend-account grant on that gateway. A body makes the
32
+ request POST; a GET resource is `pay(url, { method: 'GET' })`. To show the
33
+ price before spending, call `quote` first and pass it back:
34
+
35
+ ```ts
36
+ const quote = await client.quote(url, { body: JSON.stringify({ prompt: '...' }) });
37
+ const result = await client.pay(url, { quote });
38
+ ```
39
+
40
+ Passing `quote` pins `payTo`, so a later offer cannot swap the recipient under
41
+ the same cap. `pay` still refuses to sign more than that quote.
42
+
43
+ ## Which package?
44
+
45
+ | Install | Use it when |
46
+ | --- | --- |
47
+ | `@cmdoss/suipay` | An app needs `quote` + `pay` against a 402. This barrel. |
48
+ | `@cmdoss/suipay-buyer` | You need the typed `/v1` control plane, connection manifests, or `createPayer` directly. |
49
+ | `@cmdoss/suipay-mcp` | An MCP host (Claude, Codex, …) should log in, discover, and pay. |
50
+
51
+ Sellers copy `apps/seller-template` (and `@suipay/seller-runtime` inside the
52
+ repo). There is **no** published seller SDK.
53
+
54
+ Do not publish or depend on `@cmdoss/suipay-core` or `@cmdoss/suipay-sdk`.
55
+ Those stay private; this tarball bundles what it needs.
56
+
57
+ ## Build (this repo)
58
+
59
+ ```bash
60
+ pnpm --filter @cmdoss/suipay test
61
+ pnpm --filter @cmdoss/suipay build
62
+ ```
63
+
64
+ Publishing is the `release-npm` workflow: merge to `dev` publishes
65
+ `@cmdoss/suipay@<version>-dev.N` with dist-tag `dev` (OIDC, pack gate,
66
+ provenance). `latest` is a gated dispatch from `main` (`dry_run` default
67
+ true). Do not `npm publish` from a laptop. See
68
+ [`docs/guide/npm-release.md`](../../docs/guide/npm-release.md).
@@ -0,0 +1,96 @@
1
+ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
2
+
3
+ /** Price a 402 resource advertised without settling it. */
4
+ interface Quote {
5
+ amount: string;
6
+ asset: string;
7
+ resource: string;
8
+ network: string;
9
+ description: string;
10
+ expiresAt: number;
11
+ /** Recipient the offer pays. Pin this on `pay` so caps cannot stop substitution. */
12
+ payTo: string;
13
+ dialect: 'mpp' | 'x402';
14
+ decimals: number;
15
+ /** Method the probe used. `pay` replays it when the caller does not set one. */
16
+ method: 'GET' | 'POST';
17
+ /** Exact POST bytes the probe sent. Absent for GET. */
18
+ body?: string;
19
+ }
20
+ interface QuoteRequest {
21
+ /** HTTP method the resource is sold at. Defaults to POST, same as the shared pay-flow. */
22
+ method?: 'GET' | 'POST';
23
+ /** POST probe body. Ignored for GET. Defaults to `{}`. */
24
+ body?: string;
25
+ }
26
+
27
+ interface ClientOptions {
28
+ /** Gateway origin. Required; this package does not bake hosted URLs. */
29
+ gatewayUrl: string;
30
+ /** Delegate keypair that will sign spend-account payments. */
31
+ keypair: Ed25519Keypair;
32
+ /** Injectable fetch (tests / custom transports). Defaults to global `fetch`. */
33
+ fetchImpl?: typeof fetch;
34
+ }
35
+ interface PayRequest {
36
+ /**
37
+ * JSON object sent as `POST` `application/json`. This is the usual call.
38
+ * Do not also pass `body`.
39
+ */
40
+ json?: Record<string, unknown>;
41
+ body?: string | Uint8Array;
42
+ /**
43
+ * Only `Content-Type` is forwarded (the inner payer has no other header
44
+ * slot). Any other header throws rather than being dropped.
45
+ */
46
+ headers?: Record<string, string>;
47
+ maxAmount?: string;
48
+ asset?: string;
49
+ /**
50
+ * Defaults to POST when `json` or `body` is set, otherwise GET. A quote
51
+ * remembers the method it probed, and pay replays that when this is omitted.
52
+ */
53
+ method?: 'GET' | 'POST';
54
+ dialect?: Quote['dialect'];
55
+ /**
56
+ * Quote taken for this same URL. Supplies amount/asset/payTo/dialect when
57
+ * the caller omitted them, and pins `intent.recipient` to the quoted payTo.
58
+ * When omitted, `pay` quotes the same request first and uses that price as
59
+ * the ceiling, so a caller does not have to know atomic units.
60
+ */
61
+ quote?: Quote;
62
+ }
63
+ /** App-facing pay outcome. Defined here so published `.d.ts` does not name the private SDK. */
64
+ interface PayResult {
65
+ status: 'ok' | 'failed' | 'ambiguous';
66
+ paid: boolean | 'unknown';
67
+ body?: unknown;
68
+ code?: string;
69
+ httpStatus?: number;
70
+ detail?: string;
71
+ txDigest?: string;
72
+ paymentId?: string;
73
+ /** The price this pay agreed to. Present when pay quoted, or when the caller passed one. */
74
+ quote?: Quote;
75
+ settlement?: {
76
+ status: 'success';
77
+ txDigest: string;
78
+ amount: string;
79
+ recipient: string;
80
+ coinType: string;
81
+ asset: string;
82
+ };
83
+ }
84
+ interface Client {
85
+ quote(url: string, request?: QuoteRequest): Promise<Quote>;
86
+ pay(url: string, input?: PayRequest): Promise<PayResult>;
87
+ }
88
+ /**
89
+ * Build a tiny quote/pay client from a gateway origin and a delegate key.
90
+ *
91
+ * Fail closed: missing `gatewayUrl` or `keypair` throws. The inner payer is
92
+ * `createPayer` over a spend-account grant; this barrel does not expose `/v1`.
93
+ */
94
+ declare function createClient(options: ClientOptions): Promise<Client>;
95
+
96
+ export { type Client, type ClientOptions, type PayRequest, type PayResult, type Quote, type QuoteRequest, createClient };