@envarai/envarpay 0.2.0-alpha.2 → 0.2.0-alpha.20

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.
Files changed (85) hide show
  1. package/README.md +18 -56
  2. package/dist/commerce/agent-setup.d.ts +19 -0
  3. package/dist/commerce/agent-setup.js +210 -0
  4. package/dist/commerce/alerts.d.ts +27 -0
  5. package/dist/commerce/alerts.js +71 -0
  6. package/dist/commerce/card.js +3 -3
  7. package/dist/commerce/cli.js +91 -119
  8. package/dist/commerce/config.d.ts +3 -2
  9. package/dist/commerce/config.js +19 -77
  10. package/dist/commerce/configure.d.ts +2 -0
  11. package/dist/commerce/configure.js +78 -0
  12. package/dist/commerce/discovery.d.ts +20 -0
  13. package/dist/commerce/discovery.js +162 -0
  14. package/dist/commerce/envar-balance.d.ts +25 -0
  15. package/dist/commerce/envar-balance.js +65 -0
  16. package/dist/commerce/envar-runtime.d.ts +3 -1
  17. package/dist/commerce/envar-runtime.js +28 -1
  18. package/dist/commerce/envar.d.ts +13 -31
  19. package/dist/commerce/envar.js +35 -83
  20. package/dist/commerce/external-agent.d.ts +26 -0
  21. package/dist/commerce/external-agent.js +129 -0
  22. package/dist/commerce/external-events.d.ts +76 -0
  23. package/dist/commerce/external-events.js +273 -0
  24. package/dist/commerce/external-mcp.d.ts +10 -0
  25. package/dist/commerce/external-mcp.js +199 -0
  26. package/dist/commerce/external-oauth.d.ts +12 -0
  27. package/dist/commerce/external-oauth.js +230 -0
  28. package/dist/commerce/external-runtime.d.ts +25 -0
  29. package/dist/commerce/external-runtime.js +244 -0
  30. package/dist/commerce/index.d.ts +1 -0
  31. package/dist/commerce/index.js +1 -0
  32. package/dist/commerce/managed.d.ts +3 -0
  33. package/dist/commerce/managed.js +143 -0
  34. package/dist/commerce/native-inventory.d.ts +9 -0
  35. package/dist/commerce/native-inventory.js +75 -0
  36. package/dist/commerce/native-skills.d.ts +32 -0
  37. package/dist/commerce/native-skills.js +174 -0
  38. package/dist/commerce/openclaw-setup.d.ts +28 -0
  39. package/dist/commerce/openclaw-setup.js +43 -0
  40. package/dist/commerce/operations.d.ts +8 -3
  41. package/dist/commerce/operations.js +21 -18
  42. package/dist/commerce/pricing.d.ts +0 -1
  43. package/dist/commerce/pricing.js +3 -5
  44. package/dist/commerce/runtime.d.ts +4 -2
  45. package/dist/commerce/runtime.js +4 -2
  46. package/dist/commerce/server.d.ts +3 -2
  47. package/dist/commerce/server.js +26 -10
  48. package/dist/commerce/setup.d.ts +25 -0
  49. package/dist/commerce/setup.js +379 -0
  50. package/dist/commerce/skill-gate.d.ts +54 -0
  51. package/dist/commerce/skill-gate.js +153 -0
  52. package/dist/commerce/store.d.ts +11 -1
  53. package/dist/commerce/store.js +36 -2
  54. package/dist/commerce/types.d.ts +6 -57
  55. package/examples/envar-integration-policy.json +1 -9
  56. package/examples/hermes-setup-probe.py +109 -0
  57. package/examples/hermes-skill-inventory.py +108 -0
  58. package/examples/native-skill-runner.py +197 -0
  59. package/examples/seller.json +143 -26
  60. package/package.json +5 -16
  61. package/schemas/envarpay.config.schema.json +28 -87
  62. package/dist/commerce/buyer-store.d.ts +0 -101
  63. package/dist/commerce/buyer-store.js +0 -168
  64. package/dist/commerce/buyer.d.ts +0 -132
  65. package/dist/commerce/buyer.js +0 -755
  66. package/dist/commerce/chain.d.ts +0 -24
  67. package/dist/commerce/chain.js +0 -146
  68. package/dist/commerce/client-runtime.d.ts +0 -8
  69. package/dist/commerce/client-runtime.js +0 -7
  70. package/dist/commerce/management.d.ts +0 -14
  71. package/dist/commerce/management.js +0 -138
  72. package/dist/commerce/mpp-client.d.ts +0 -59
  73. package/dist/commerce/mpp-client.js +0 -24
  74. package/dist/commerce/mpp-runtime.d.ts +0 -3
  75. package/dist/commerce/mpp-runtime.js +0 -3
  76. package/dist/commerce/mpp.d.ts +0 -53
  77. package/dist/commerce/mpp.js +0 -163
  78. package/dist/commerce/peer-proxy.d.ts +0 -44
  79. package/dist/commerce/peer-proxy.js +0 -673
  80. package/dist/commerce/stripe-provider.d.ts +0 -9
  81. package/dist/commerce/stripe-provider.js +0 -97
  82. package/dist/commerce/x402.d.ts +0 -37
  83. package/dist/commerce/x402.js +0 -174
  84. package/examples/buyer-policy.json +0 -18
  85. package/schemas/buyer-policy.schema.json +0 -253
@@ -1,24 +0,0 @@
1
- import type { PaymentPayload, PaymentRequirements, SettleResponse } from '@x402/core/types';
2
- export interface ExpiredUnusedProof {
3
- expiredUnused: true;
4
- network: string;
5
- asset: string;
6
- payer: string;
7
- nonce: string;
8
- validAfter: string;
9
- validBefore: string;
10
- blockNumber: string;
11
- blockHash: string;
12
- blockTimestamp: string;
13
- finality: 'finalized';
14
- authorizationUsed: false;
15
- }
16
- /** Checks the canonical chain receipt and exact official-token Transfer. */
17
- export declare function evmReceiptVerifier(rpcUrls: Readonly<Record<string, string>>, confirmations?: number): (receipt: SettleResponse, payload: PaymentPayload, requirements: PaymentRequirements) => Promise<boolean>;
18
- /** Locate an original EIP-3009 settlement even when facilitator lost its tx response. */
19
- export declare function evmSettlementRecovery(rpcUrls: Readonly<Record<string, string>>): {
20
- expiredUnusedProof: (payload: PaymentPayload, requirements: PaymentRequirements) => Promise<ExpiredUnusedProof | undefined>;
21
- proveExpiredUnused: (payload: PaymentPayload, requirements: PaymentRequirements) => Promise<boolean>;
22
- checkpoint: (requirements: PaymentRequirements) => Promise<string>;
23
- findOriginalReceipt: (payload: PaymentPayload, requirements: PaymentRequirements, fromBlock: string) => Promise<SettleResponse | undefined>;
24
- };
@@ -1,146 +0,0 @@
1
- import { createPublicClient, http, decodeEventLog, parseAbi } from 'viem';
2
- import { CommerceError } from './types.js';
3
- import { validateUrl } from './config.js';
4
- const abi = parseAbi(['event Transfer(address indexed from, address indexed to, uint256 value)']);
5
- const authorizationAbi = parseAbi(['event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce)']);
6
- const authorizationStateAbi = parseAbi(['function authorizationState(address authorizer,bytes32 nonce) view returns (bool)']);
7
- const officialUsdc = {
8
- 'eip155:84532': '0x036cbd53842c5426634e7929541ec2318f3dcf7e',
9
- 'eip155:8453': '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
10
- };
11
- /** Checks the canonical chain receipt and exact official-token Transfer. */
12
- export function evmReceiptVerifier(rpcUrls, confirmations = 2) {
13
- if (!Number.isInteger(confirmations) || confirmations < 1)
14
- throw new CommerceError('confirmation_policy', 'Positive confirmation depth required');
15
- return async (receipt, payload, requirements) => {
16
- const rpc = rpcUrls[requirements.network];
17
- if (!rpc)
18
- throw new CommerceError('rpc_missing', 'Configure the settlement network RPC');
19
- validateUrl(rpc, true);
20
- const chainId = Number(requirements.network.split(':')[1]);
21
- if (!Number.isSafeInteger(chainId) || !/^0x[0-9a-fA-F]{64}$/.test(receipt.transaction) || receipt.network !== requirements.network)
22
- return false;
23
- const client = createPublicClient({ transport: http(rpc, { timeout: 15000, retryCount: 0 }) });
24
- if (await client.getChainId() !== chainId)
25
- return false;
26
- const mined = await client.getTransactionReceipt({ hash: receipt.transaction });
27
- if (mined.status !== 'success')
28
- return false;
29
- const block = await client.getBlock({ blockNumber: mined.blockNumber });
30
- if (block.hash !== mined.blockHash)
31
- return false;
32
- const latest = await client.getBlockNumber();
33
- if (latest - mined.blockNumber + 1n < BigInt(confirmations))
34
- return false;
35
- const auth = payload.payload.authorization;
36
- if (!auth?.from || !auth.nonce || auth.to?.toLowerCase() !== requirements.payTo.toLowerCase() || auth.value !== requirements.amount)
37
- return false;
38
- if (receipt.payer && receipt.payer.toLowerCase() !== auth.from.toLowerCase())
39
- return false;
40
- const matches = mined.logs.filter(log => {
41
- if (log.address.toLowerCase() !== requirements.asset.toLowerCase())
42
- return false;
43
- try {
44
- const event = decodeEventLog({ abi, data: log.data, topics: log.topics });
45
- return event.args.from.toLowerCase() === auth.from.toLowerCase() && event.args.to.toLowerCase() === requirements.payTo.toLowerCase() && event.args.value === BigInt(requirements.amount);
46
- }
47
- catch {
48
- return false;
49
- }
50
- });
51
- const authorizations = mined.logs.filter(log => {
52
- if (log.address.toLowerCase() !== requirements.asset.toLowerCase())
53
- return false;
54
- try {
55
- const event = decodeEventLog({ abi: authorizationAbi, data: log.data, topics: log.topics });
56
- return event.args.authorizer.toLowerCase() === auth.from.toLowerCase() && event.args.nonce.toLowerCase() === auth.nonce.toLowerCase();
57
- }
58
- catch {
59
- return false;
60
- }
61
- });
62
- return matches.length === 1 && authorizations.length === 1;
63
- };
64
- }
65
- /** Locate an original EIP-3009 settlement even when facilitator lost its tx response. */
66
- export function evmSettlementRecovery(rpcUrls) {
67
- const clientFor = async (requirements) => {
68
- const rpc = rpcUrls[requirements.network];
69
- if (!rpc)
70
- throw new CommerceError('rpc_missing', 'Configure the settlement network RPC');
71
- validateUrl(rpc, true);
72
- const client = createPublicClient({ transport: http(rpc, { timeout: 15000, retryCount: 0 }) });
73
- if (await client.getChainId() !== Number(requirements.network.split(':')[1]))
74
- throw new CommerceError('rpc_network', 'RPC network does not match frozen requirements');
75
- return client;
76
- };
77
- const expiredUnusedProof = async (payload, requirements) => {
78
- try {
79
- const asset = officialUsdc[requirements.network];
80
- if (!asset || typeof requirements.asset !== 'string' || requirements.asset.toLowerCase() !== asset)
81
- return undefined;
82
- const auth = payload.payload?.authorization;
83
- if (!auth || !/^0x[0-9a-fA-F]{40}$/.test(auth.from ?? '') || !/^0x[0-9a-fA-F]{64}$/.test(auth.nonce ?? '') ||
84
- auth.to?.toLowerCase() !== requirements.payTo.toLowerCase() || auth.value !== requirements.amount ||
85
- !/^(0|[1-9][0-9]{0,77})$/.test(auth.validAfter ?? '') || !/^(0|[1-9][0-9]{0,77})$/.test(auth.validBefore ?? ''))
86
- return undefined;
87
- const validAfter = BigInt(auth.validAfter), validBefore = BigInt(auth.validBefore);
88
- if (validAfter >= validBefore || validBefore >= 2n ** 256n)
89
- return undefined;
90
- const client = await clientFor(requirements);
91
- const finalized = await client.getBlock({ blockTag: 'finalized' });
92
- if (finalized.number === null || !/^0x[0-9a-fA-F]{64}$/.test(finalized.hash ?? '') ||
93
- finalized.timestamp < validBefore || finalized.timestamp < validAfter)
94
- return undefined;
95
- // A missing nonce log is not proof. Read the official token's state at
96
- // the exact finalized block, after this authorization can no longer execute.
97
- const used = await client.readContract({ address: asset, abi: authorizationStateAbi, functionName: 'authorizationState',
98
- args: [auth.from, auth.nonce], blockNumber: finalized.number });
99
- if (used !== false)
100
- return undefined;
101
- const canonical = await client.getBlock({ blockNumber: finalized.number });
102
- if (canonical.hash !== finalized.hash || canonical.number !== finalized.number || canonical.timestamp !== finalized.timestamp)
103
- return undefined;
104
- return { expiredUnused: true, network: requirements.network, asset, payer: auth.from.toLowerCase(), nonce: auth.nonce.toLowerCase(),
105
- validAfter: auth.validAfter, validBefore: auth.validBefore, blockNumber: finalized.number.toString(),
106
- blockHash: finalized.hash, blockTimestamp: finalized.timestamp.toString(), finality: 'finalized', authorizationUsed: false };
107
- }
108
- catch {
109
- // Unsupported finalized tags, archive state gaps, malformed return values
110
- // and network errors retain the original unknown operation and its budget.
111
- return undefined;
112
- }
113
- };
114
- return {
115
- expiredUnusedProof,
116
- proveExpiredUnused: async (payload, requirements) => !!await expiredUnusedProof(payload, requirements),
117
- checkpoint: async (requirements) => {
118
- const client = await clientFor(requirements);
119
- const block = await client.getBlockNumber();
120
- return (block > 12n ? block - 12n : 0n).toString();
121
- },
122
- findOriginalReceipt: async (payload, requirements, fromBlock) => {
123
- if (!/^(0|[1-9][0-9]*)$/.test(fromBlock))
124
- throw new CommerceError('recovery_checkpoint', 'Invalid original chain checkpoint');
125
- const auth = payload.payload.authorization;
126
- if (!/^0x[0-9a-fA-F]{40}$/.test(auth?.from ?? '') || !/^0x[0-9a-fA-F]{64}$/.test(auth?.nonce ?? ''))
127
- throw new CommerceError('invalid_payment', 'Missing original EIP-3009 identity');
128
- const client = await clientFor(requirements);
129
- const latest = await client.getBlockNumber();
130
- // Bounded per reconciliation. Very old operations need an operator/archive
131
- // RPC lookup; never reinterpret incomplete search as proof of non-payment.
132
- if (latest - BigInt(fromBlock) > 100000n)
133
- throw new CommerceError('archive_reconciliation_required', 'Original authorization requires an archive log lookup');
134
- for (let start = BigInt(fromBlock); start <= latest; start += 2000n) {
135
- const end = start + 1999n < latest ? start + 1999n : latest;
136
- const logs = await client.getLogs({ address: requirements.asset, event: authorizationAbi[0], args: { authorizer: auth.from, nonce: auth.nonce }, fromBlock: start, toBlock: end, strict: true });
137
- const settled = logs.filter(log => !log.removed && log.transactionHash);
138
- if (settled.length > 1)
139
- throw new CommerceError('ambiguous_receipt', 'Multiple authorization events require manual reconciliation');
140
- if (settled.length === 1)
141
- return { success: true, transaction: settled[0].transactionHash, network: requirements.network, payer: auth.from };
142
- }
143
- return undefined;
144
- },
145
- };
146
- }
@@ -1,8 +0,0 @@
1
- /** Node.js buyer runtime. Does not initialize seller storage or any signer. */
2
- export * from './buyer-store.js';
3
- export * from './buyer.js';
4
- export * from './management.js';
5
- export * from './vault.js';
6
- export * from './chain.js';
7
- export type { MppBuyerOptions, MppTokenOperation, MppVerificationContext, MppReceipt } from './mpp-client.js';
8
- export * from './peer-proxy.js';
@@ -1,7 +0,0 @@
1
- /** Node.js buyer runtime. Does not initialize seller storage or any signer. */
2
- export * from './buyer-store.js';
3
- export * from './buyer.js';
4
- export * from './management.js';
5
- export * from './vault.js';
6
- export * from './chain.js';
7
- export * from './peer-proxy.js';
@@ -1,14 +0,0 @@
1
- import { CommerceBuyer } from './buyer.js';
2
- export interface BuyerManagementOptions {
3
- buyer: CommerceBuyer;
4
- authenticate: (request: Request) => Promise<string | null>;
5
- origin: string;
6
- }
7
- /** Private API: authentication binds every purchase to its caller. No CORS or public wallet proxy. */
8
- export declare class BuyerManagement {
9
- private readonly options;
10
- readonly origin: string;
11
- constructor(options: BuyerManagementOptions);
12
- handle(request: Request): Promise<Response>;
13
- }
14
- export declare function listenBuyerManagement(management: BuyerManagement, origin?: string, host?: string, port?: number): import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
@@ -1,138 +0,0 @@
1
- import { createServer } from 'node:http';
2
- import { Readable } from 'node:stream';
3
- import { CommerceError } from './types.js';
4
- import { validateUrl } from './config.js';
5
- const loopback = (host) => ['localhost', '127.0.0.1', '[::1]', '::1'].includes(host);
6
- const headers = { 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' };
7
- async function jsonBody(request) {
8
- if (!request.headers.get('Content-Type')?.toLowerCase().startsWith('application/json'))
9
- throw new CommerceError('invalid_content_type', 'Send application/json');
10
- let size = 0;
11
- const chunks = [];
12
- if (request.body) {
13
- const reader = request.body.getReader();
14
- for (;;) {
15
- const { value, done } = await reader.read();
16
- if (done)
17
- break;
18
- size += value.byteLength;
19
- if (size > 1024 * 1024) {
20
- await reader.cancel();
21
- throw new CommerceError('request_too_large', 'Management request exceeds 1 MiB');
22
- }
23
- chunks.push(value);
24
- }
25
- }
26
- let value;
27
- try {
28
- value = JSON.parse(Buffer.concat(chunks).toString('utf8'));
29
- }
30
- catch {
31
- throw new CommerceError('invalid_json', 'Valid JSON object required');
32
- }
33
- if (!value || typeof value !== 'object' || Array.isArray(value))
34
- throw new CommerceError('invalid_json', 'Valid JSON object required');
35
- return value;
36
- }
37
- function exactKeys(value, expected) {
38
- if (Object.keys(value).some(key => !expected.includes(key)) || expected.some(key => !Object.hasOwn(value, key)))
39
- throw new CommerceError('invalid_request', 'Request fields do not match this management operation');
40
- }
41
- /** Private API: authentication binds every purchase to its caller. No CORS or public wallet proxy. */
42
- export class BuyerManagement {
43
- options;
44
- origin;
45
- constructor(options) {
46
- this.options = options;
47
- const url = validateUrl(options.origin, true);
48
- if (url.pathname !== '/' || url.search || (!loopback(url.hostname) && url.protocol !== 'https:'))
49
- throw new CommerceError('management_origin', 'Use a loopback origin or an explicitly configured HTTPS management origin');
50
- this.origin = url.origin;
51
- }
52
- async handle(request) {
53
- const url = new URL(request.url);
54
- if (url.origin !== this.origin || (request.headers.get('Host') && request.headers.get('Host') !== url.host))
55
- return Response.json({ error: 'invalid_host' }, { status: 421, headers });
56
- if (request.headers.get('Origin') && request.headers.get('Origin') !== this.origin)
57
- return Response.json({ error: 'origin_not_allowed' }, { status: 403, headers });
58
- if (request.method === 'OPTIONS')
59
- return Response.json({ error: 'method_not_allowed' }, { status: 405, headers });
60
- try {
61
- const caller = await this.options.authenticate(request);
62
- if (!caller)
63
- return Response.json({ error: 'authentication_required' }, { status: 401, headers: { ...headers, 'WWW-Authenticate': 'Bearer' } });
64
- if (url.search)
65
- throw new CommerceError('invalid_request', 'Management query parameters are not supported');
66
- if (request.method === 'GET' && url.pathname === '/management/v1/policy')
67
- return Response.json(this.options.buyer.policySnapshot(), { headers });
68
- if (request.method === 'POST' && url.pathname === '/management/v1/purchases/preview') {
69
- const body = await jsonBody(request);
70
- exactKeys(body, ['cardUrl', 'messageId', 'input', 'offerId']);
71
- return Response.json(await this.options.buyer.preview(caller, body), { headers });
72
- }
73
- if (request.method === 'POST' && url.pathname === '/management/v1/purchases/confirm') {
74
- const body = await jsonBody(request);
75
- exactKeys(body, ['previewId', 'quoteToken', 'messageId']);
76
- if (['previewId', 'quoteToken', 'messageId'].some(key => typeof body[key] !== 'string'))
77
- throw new CommerceError('invalid_request', 'Confirmation identifiers must be strings');
78
- return Response.json(await this.options.buyer.confirm(caller, body), { headers });
79
- }
80
- const match = /^\/management\/v1\/purchases\/([0-9a-f-]{36})(\/(?:recover|continue))?$/.exec(url.pathname);
81
- if (match && request.method === 'GET' && !match[2])
82
- return Response.json(this.options.buyer.get(caller, match[1]), { headers });
83
- if (match && request.method === 'POST' && match[2] === '/continue') {
84
- const body = await jsonBody(request);
85
- exactKeys(body, ['messageId', 'input']);
86
- if (typeof body.messageId !== 'string')
87
- throw new CommerceError('invalid_request', 'Continuation message ID must be a string');
88
- return Response.json(await this.options.buyer.continue(caller, match[1], body), { headers });
89
- }
90
- if (match && request.method === 'POST' && match[2] === '/recover') {
91
- if (request.body) {
92
- const body = await jsonBody(request);
93
- exactKeys(body, []);
94
- }
95
- return Response.json(await this.options.buyer.recover(caller, match[1]), { headers });
96
- }
97
- return Response.json({ error: 'not_found' }, { status: 404, headers });
98
- }
99
- catch (error) {
100
- const code = error instanceof CommerceError ? error.code : 'request_failed';
101
- const status = code === 'purchase_not_found' ? 404 : code === 'request_too_large' ? 413 : ['purchase_conflict', 'quote_expired', 'approval_mismatch', 'budget_exceeded', 'payments_disabled'].includes(code) ? 409 : 400;
102
- return Response.json({ error: code }, { status, headers });
103
- }
104
- }
105
- }
106
- export function listenBuyerManagement(management, origin = management.origin, host = '127.0.0.1', port = 4021) {
107
- if (new URL(origin).origin !== management.origin || (!loopback(host) && new URL(origin).protocol !== 'https:'))
108
- throw new CommerceError('management_bind', 'Remote management requires an explicit HTTPS reverse proxy origin');
109
- const server = createServer(async (req, res) => {
110
- try {
111
- if (req.headers.host !== new URL(origin).host) {
112
- res.writeHead(421, headers);
113
- res.end('{"error":"invalid_host"}');
114
- return;
115
- }
116
- const incoming = new Headers();
117
- for (const [key, value] of Object.entries(req.headers))
118
- if (value !== undefined)
119
- incoming.set(key, Array.isArray(value) ? value.join(',') : value);
120
- const request = new Request(new URL(req.url ?? '/', origin), { method: req.method, headers: incoming, ...(req.method !== 'GET' && req.method !== 'HEAD' ? { body: Readable.toWeb(req), duplex: 'half' } : {}) });
121
- const response = await management.handle(request);
122
- res.writeHead(response.status, Object.fromEntries(response.headers));
123
- if (response.body)
124
- Readable.fromWeb(response.body).pipe(res);
125
- else
126
- res.end();
127
- }
128
- catch {
129
- if (!res.headersSent)
130
- res.writeHead(500, headers);
131
- res.end('{"error":"request_failed"}');
132
- }
133
- });
134
- server.requestTimeout = 30000;
135
- server.headersTimeout = 15000;
136
- server.listen(port, host);
137
- return server;
138
- }
@@ -1,59 +0,0 @@
1
- import { Challenge, Receipt } from 'mppx';
2
- import { charge } from 'mppx/stripe/client';
3
- import type { BuyerQuote } from './buyer-store.js';
4
- export type MppReceipt = Receipt.Receipt & {
5
- amount: string;
6
- currency: 'usd';
7
- merchant: string;
8
- accountId?: string;
9
- livemode: boolean;
10
- };
11
- export interface MppTokenOperation {
12
- operationId: string;
13
- idempotencyKey: string;
14
- purchaseId: string;
15
- quote: BuyerQuote;
16
- mode: 'test' | 'live';
17
- amount: string;
18
- currency: string;
19
- networkId: string;
20
- expiresAt: number;
21
- paymentMethod: string;
22
- metadata?: Record<string, string>;
23
- challenge: Challenge.Challenge;
24
- }
25
- export interface MppVerificationContext {
26
- purchaseId: string;
27
- quote: BuyerQuote;
28
- mode: 'test' | 'live';
29
- operationId: string;
30
- }
31
- export interface MppBuyerOptions {
32
- /** Stable private buyer/PSP identity; never inferred from seller metadata. */
33
- payer: string;
34
- mode: 'test' | 'live';
35
- paymentMethod: string;
36
- createToken: (operation: MppTokenOperation) => Promise<string>;
37
- /** Read the original operation only; never create another SPT on absence. */
38
- recoverToken?: (operation: MppTokenOperation) => Promise<string | undefined>;
39
- /** Independently read provider state and match account/mode/merchant/amount/order. */
40
- verifyReceipt: (receipt: MppReceipt, context: MppVerificationContext) => Promise<boolean>;
41
- }
42
- export interface SavedMppAuthorization {
43
- protocol: 'mpp';
44
- credential: string;
45
- challenge: Challenge.Challenge;
46
- fingerprint: string;
47
- operationId: string;
48
- }
49
- export interface SavedMppToken {
50
- protocol: 'mpp-token';
51
- token: string;
52
- fingerprint: string;
53
- operationId: string;
54
- }
55
- /** Native SDK preparation only; no fetch polyfill or automatic payment retry. */
56
- export declare function prepareMppCredential(challenge: Challenge.Challenge, paymentMethod: string, createToken: (parameters: charge.OnChallengeParameters) => Promise<string>): Promise<string>;
57
- export declare function validateMppCredential(value: string, challenge: Challenge.Challenge): void;
58
- export declare function assertMppDigest(challenge: Challenge.Challenge, body: string): void;
59
- export declare function publicMppReceipt(value: MppReceipt): MppReceipt;
@@ -1,24 +0,0 @@
1
- import { Challenge, Credential } from 'mppx';
2
- import { Mppx } from 'mppx/client';
3
- import { charge } from 'mppx/stripe/client';
4
- import { createHash } from 'node:crypto';
5
- import { CommerceError } from './types.js';
6
- /** Native SDK preparation only; no fetch polyfill or automatic payment retry. */
7
- export async function prepareMppCredential(challenge, paymentMethod, createToken) {
8
- const client = Mppx.create({ polyfill: false, maxPaymentRetries: 0, methods: [charge({ paymentMethod, createToken })] });
9
- const payment = await client.preparePayment(new Response(null, { status: 402, headers: { 'WWW-Authenticate': Challenge.serialize(challenge) } }));
10
- return payment.createCredential();
11
- }
12
- export function validateMppCredential(value, challenge) {
13
- const credential = Credential.deserialize(value);
14
- const payload = credential.payload;
15
- if (Challenge.serialize(credential.challenge) !== Challenge.serialize(challenge) || payload.externalId !== challenge.request.externalId || typeof payload.spt !== 'string' || !/^spt_[A-Za-z0-9_]+$/.test(payload.spt))
16
- throw new CommerceError('mpp_credential_binding', 'Native MPP credential differs from the original challenge');
17
- }
18
- export function assertMppDigest(challenge, body) {
19
- if (challenge.digest && challenge.digest !== `sha-256=:${createHash('sha256').update(body).digest('base64')}:`)
20
- throw new CommerceError('mpp_request_digest', 'MPP challenge is bound to different request bytes');
21
- }
22
- export function publicMppReceipt(value) {
23
- return { method: value.method, status: value.status, reference: value.reference, timestamp: value.timestamp, ...(value.externalId ? { externalId: value.externalId } : {}), amount: value.amount, currency: value.currency, merchant: value.merchant, livemode: value.livemode, ...(value.accountId ? { accountId: value.accountId } : {}) };
24
- }
@@ -1,3 +0,0 @@
1
- /** Opt-in MPP server adapter. Account eligibility is checked at startup. */
2
- export * from './mpp.js';
3
- export * from './stripe-provider.js';
@@ -1,3 +0,0 @@
1
- /** Opt-in MPP server adapter. Account eligibility is checked at startup. */
2
- export * from './mpp.js';
3
- export * from './stripe-provider.js';
@@ -1,53 +0,0 @@
1
- import { CommerceStore, type OrderRecord } from './store.js';
2
- import { CredentialVault } from './vault.js';
3
- import type { GateResult } from './x402.js';
4
- export interface StripeIntent {
5
- id: string;
6
- status: string;
7
- amount: number;
8
- amount_received?: number;
9
- currency: string;
10
- livemode: boolean;
11
- metadata: Record<string, string>;
12
- lastResponse?: {
13
- headers?: Record<string, string>;
14
- };
15
- }
16
- export interface StripeProvider {
17
- accountId: string;
18
- merchantProfile: string;
19
- mode: 'test' | 'live';
20
- assertReady(): Promise<void>;
21
- create(params: Record<string, unknown>, options: {
22
- idempotencyKey: string;
23
- apiVersion: string;
24
- }): Promise<StripeIntent>;
25
- retrieve(id: string): Promise<StripeIntent>;
26
- findOriginal(orderId: string): Promise<StripeIntent | undefined>;
27
- }
28
- export interface MppGateOptions {
29
- store: CommerceStore;
30
- vault: CredentialVault;
31
- origin: string;
32
- hmacSecret: string;
33
- providers: Readonly<Record<string, StripeProvider>>;
34
- }
35
- /** Native MPP Stripe charge; only a verified PSP success may queue a task. */
36
- export declare class MppGate {
37
- private options;
38
- constructor(options: MppGateOptions);
39
- merchantRecipient(accountRef: string): string | undefined;
40
- check(): Promise<void>;
41
- private provider;
42
- private optionsFor;
43
- private runtime;
44
- private bound;
45
- private verified;
46
- private receipt;
47
- private headers;
48
- recover(orderId: string, caller: string): Promise<{
49
- state: string;
50
- taskId: string;
51
- }>;
52
- handle(request: Request, order: OrderRecord): Promise<GateResult>;
53
- }