@agent-score/commerce 1.8.0 → 2.0.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.
Files changed (90) hide show
  1. package/README.md +73 -9
  2. package/dist/{_response-BMt2y4Or.d.mts → _response-BFYN3b6i.d.mts} +19 -22
  3. package/dist/{_response-DyJ3mWI3.d.ts → _response-_iPD5AIj.d.ts} +19 -22
  4. package/dist/challenge/index.d.mts +106 -198
  5. package/dist/challenge/index.d.ts +106 -198
  6. package/dist/challenge/index.js +238 -111
  7. package/dist/challenge/index.js.map +1 -1
  8. package/dist/challenge/index.mjs +238 -111
  9. package/dist/challenge/index.mjs.map +1 -1
  10. package/dist/checkout-BoFwnVsj.d.ts +931 -0
  11. package/dist/checkout-DRbQ0Fsh.d.mts +931 -0
  12. package/dist/core.d.mts +2 -2
  13. package/dist/core.d.ts +2 -2
  14. package/dist/core.js +1 -1
  15. package/dist/core.js.map +1 -1
  16. package/dist/core.mjs +1 -1
  17. package/dist/core.mjs.map +1 -1
  18. package/dist/discovery/index.d.mts +453 -51
  19. package/dist/discovery/index.d.ts +453 -51
  20. package/dist/discovery/index.js +1092 -58
  21. package/dist/discovery/index.js.map +1 -1
  22. package/dist/discovery/index.mjs +1060 -57
  23. package/dist/discovery/index.mjs.map +1 -1
  24. package/dist/identity/express.d.mts +3 -3
  25. package/dist/identity/express.d.ts +3 -3
  26. package/dist/identity/express.js +30 -19
  27. package/dist/identity/express.js.map +1 -1
  28. package/dist/identity/express.mjs +30 -19
  29. package/dist/identity/express.mjs.map +1 -1
  30. package/dist/identity/fastify.d.mts +4 -4
  31. package/dist/identity/fastify.d.ts +4 -4
  32. package/dist/identity/fastify.js +30 -19
  33. package/dist/identity/fastify.js.map +1 -1
  34. package/dist/identity/fastify.mjs +30 -19
  35. package/dist/identity/fastify.mjs.map +1 -1
  36. package/dist/identity/hono.d.mts +3 -3
  37. package/dist/identity/hono.d.ts +3 -3
  38. package/dist/identity/hono.js +30 -19
  39. package/dist/identity/hono.js.map +1 -1
  40. package/dist/identity/hono.mjs +30 -19
  41. package/dist/identity/hono.mjs.map +1 -1
  42. package/dist/identity/nextjs.d.mts +6 -7
  43. package/dist/identity/nextjs.d.ts +6 -7
  44. package/dist/identity/nextjs.js +30 -19
  45. package/dist/identity/nextjs.js.map +1 -1
  46. package/dist/identity/nextjs.mjs +30 -19
  47. package/dist/identity/nextjs.mjs.map +1 -1
  48. package/dist/identity/policy.d.mts +41 -4
  49. package/dist/identity/policy.d.ts +41 -4
  50. package/dist/identity/policy.js +3662 -18
  51. package/dist/identity/policy.js.map +1 -1
  52. package/dist/identity/policy.mjs +3648 -3
  53. package/dist/identity/policy.mjs.map +1 -1
  54. package/dist/identity/web.d.mts +3 -3
  55. package/dist/identity/web.d.ts +3 -3
  56. package/dist/identity/web.js +30 -19
  57. package/dist/identity/web.js.map +1 -1
  58. package/dist/identity/web.mjs +30 -19
  59. package/dist/identity/web.mjs.map +1 -1
  60. package/dist/index.d.mts +72 -329
  61. package/dist/index.d.ts +72 -329
  62. package/dist/index.js +3651 -373
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +3628 -361
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/payment/index.d.mts +257 -266
  67. package/dist/payment/index.d.ts +257 -266
  68. package/dist/payment/index.js +586 -149
  69. package/dist/payment/index.js.map +1 -1
  70. package/dist/payment/index.mjs +573 -148
  71. package/dist/payment/index.mjs.map +1 -1
  72. package/dist/{agent_instructions-DiMSGkdm.d.mts → pricing-CQ9DIFaw.d.ts} +109 -56
  73. package/dist/{agent_instructions-DiMSGkdm.d.ts → pricing-CxzwyiO6.d.mts} +109 -56
  74. package/dist/rail_spec-XP0wKgJV.d.mts +132 -0
  75. package/dist/rail_spec-XP0wKgJV.d.ts +132 -0
  76. package/dist/{signer-CFVQsWjL.d.mts → signer-3FAit11j.d.mts} +27 -1
  77. package/dist/{signer-CFVQsWjL.d.ts → signer-3FAit11j.d.ts} +27 -1
  78. package/dist/solana-Cds87OTu.d.mts +67 -0
  79. package/dist/solana-Cds87OTu.d.ts +67 -0
  80. package/dist/stripe-multichain/index.d.mts +56 -67
  81. package/dist/stripe-multichain/index.d.ts +56 -67
  82. package/dist/stripe-multichain/index.js +68 -42
  83. package/dist/stripe-multichain/index.js.map +1 -1
  84. package/dist/stripe-multichain/index.mjs +68 -41
  85. package/dist/stripe-multichain/index.mjs.map +1 -1
  86. package/dist/{wwwauthenticate-CU1eNvMQ.d.mts → wwwauthenticate-D_FMnPgU.d.mts} +9 -10
  87. package/dist/{wwwauthenticate-CU1eNvMQ.d.ts → wwwauthenticate-D_FMnPgU.d.ts} +9 -10
  88. package/dist/x402_server-hgQzWQwB.d.mts +81 -0
  89. package/dist/x402_server-hgQzWQwB.d.ts +81 -0
  90. package/package.json +13 -9
@@ -35,30 +35,36 @@ __export(stripe_multichain_exports, {
35
35
  createMppxStripe: () => createMppxStripe,
36
36
  createMultichainPaymentIntent: () => createMultichainPaymentIntent,
37
37
  createPiCache: () => createPiCache,
38
- getDepositAddress: () => getDepositAddress,
39
38
  simulateCryptoDeposit: () => simulateCryptoDeposit,
40
39
  simulateDepositIfTestMode: () => simulateDepositIfTestMode
41
40
  });
42
41
  module.exports = __toCommonJS(stripe_multichain_exports);
43
42
 
44
43
  // src/stripe-multichain/payment_intent.ts
45
- async function createMultichainPaymentIntent(input) {
46
- const pi = await input.stripe.paymentIntents.create(
44
+ async function createMultichainPaymentIntent({
45
+ stripe,
46
+ amount,
47
+ currency = "usd",
48
+ networks,
49
+ metadata,
50
+ idempotencyKey
51
+ }) {
52
+ const pi = await stripe.paymentIntents.create(
47
53
  {
48
- amount: input.amount,
49
- currency: input.currency ?? "usd",
54
+ amount,
55
+ currency,
50
56
  payment_method_types: ["crypto"],
51
57
  payment_method_data: { type: "crypto" },
52
58
  payment_method_options: {
53
59
  crypto: {
54
60
  mode: "deposit",
55
- deposit_options: { networks: input.networks ?? ["tempo", "base", "solana"] }
61
+ deposit_options: { networks: networks ?? ["tempo", "base", "solana"] }
56
62
  }
57
63
  },
58
64
  confirm: true,
59
- ...input.metadata ? { metadata: input.metadata } : {}
65
+ ...metadata ? { metadata } : {}
60
66
  },
61
- input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : void 0
67
+ idempotencyKey ? { idempotencyKey } : void 0
62
68
  );
63
69
  const depositAddresses = {};
64
70
  const addrs = pi.next_action?.crypto_display_details?.deposit_addresses ?? {};
@@ -70,9 +76,6 @@ async function createMultichainPaymentIntent(input) {
70
76
  }
71
77
  return { paymentIntentId: pi.id, depositAddresses };
72
78
  }
73
- function getDepositAddress(result, network) {
74
- return result.depositAddresses[network];
75
- }
76
79
 
77
80
  // src/stripe-multichain/simulate_deposit.ts
78
81
  var STRIPE_TEST_TX_HASH_SUCCESS = "0x00000000000000000000000000000000000000000000000000000testsuccess";
@@ -82,57 +85,79 @@ var DEFAULT_BUYER_WALLET = {
82
85
  tempo: "0x0000000000000000000000000000000000000001",
83
86
  solana: "11111111111111111111111111111111"
84
87
  };
85
- async function simulateCryptoDeposit(input) {
86
- const url = `${input.stripeApiBase ?? "https://api.stripe.com"}/v1/test_helpers/payment_intents/${input.paymentIntentId}/simulate_crypto_deposit`;
88
+ async function simulateCryptoDeposit({
89
+ paymentIntentId,
90
+ network,
91
+ buyerWallet,
92
+ tokenCurrency,
93
+ transactionHash,
94
+ stripeSecretKey,
95
+ stripeVersion,
96
+ stripeApiBase,
97
+ extra
98
+ }) {
99
+ const url = `${stripeApiBase ?? "https://api.stripe.com"}/v1/test_helpers/payment_intents/${paymentIntentId}/simulate_crypto_deposit`;
87
100
  const params = new URLSearchParams({
88
- network: input.network,
89
- buyer_wallet: input.buyerWallet ?? DEFAULT_BUYER_WALLET[input.network] ?? ""
101
+ network,
102
+ buyer_wallet: buyerWallet ?? DEFAULT_BUYER_WALLET[network] ?? ""
90
103
  });
91
- if (input.tokenCurrency) params.set("token_currency", input.tokenCurrency);
92
- if (input.transactionHash) params.set("transaction_hash", input.transactionHash);
93
- for (const [k, v] of Object.entries(input.extra ?? {})) {
104
+ if (tokenCurrency) params.set("token_currency", tokenCurrency);
105
+ if (transactionHash) params.set("transaction_hash", transactionHash);
106
+ for (const [k, v] of Object.entries(extra ?? {})) {
94
107
  params.set(k, v);
95
108
  }
96
109
  const headers = {
97
- Authorization: `Bearer ${input.stripeSecretKey}`,
110
+ Authorization: `Bearer ${stripeSecretKey}`,
98
111
  "Content-Type": "application/x-www-form-urlencoded"
99
112
  };
100
- if (input.stripeVersion) headers["Stripe-Version"] = input.stripeVersion;
113
+ if (stripeVersion) headers["Stripe-Version"] = stripeVersion;
101
114
  const res = await fetch(url, { method: "POST", headers, body: params.toString() });
102
115
  if (!res.ok) {
103
116
  throw new Error(`Stripe simulate_crypto_deposit failed: ${res.status} ${await res.text()}`);
104
117
  }
105
118
  }
106
- async function simulateDepositIfTestMode(input) {
107
- if (!input.stripeSecretKey.startsWith("sk_test_")) return;
108
- const piId = input.getPaymentIntentId(input.depositAddress);
119
+ async function simulateDepositIfTestMode({
120
+ getPaymentIntentId,
121
+ depositAddress,
122
+ network,
123
+ buyerWallet,
124
+ tokenCurrency,
125
+ stripeSecretKey,
126
+ stripeVersion
127
+ }) {
128
+ if (!stripeSecretKey.startsWith("sk_test_")) return;
129
+ const piId = getPaymentIntentId(depositAddress);
109
130
  if (!piId) {
110
131
  console.warn(
111
- `[stripe] Skipping deposit simulation \u2014 no PI cached for deposit address ${input.depositAddress.slice(0, 10)}\u2026 (network=${input.network}). The PI cache TTL may have expired between 402 emission and settlement.`
132
+ `[stripe] Skipping deposit simulation \u2014 no PI cached for deposit address ${depositAddress.slice(0, 10)}\u2026 (network=${network}). The PI cache TTL may have expired between 402 emission and settlement.`
112
133
  );
113
134
  return;
114
135
  }
115
136
  try {
116
137
  await simulateCryptoDeposit({
117
138
  paymentIntentId: piId,
118
- network: input.network,
119
- ...input.buyerWallet !== void 0 && { buyerWallet: input.buyerWallet },
120
- tokenCurrency: input.tokenCurrency ?? "usdc",
139
+ network,
140
+ ...buyerWallet !== void 0 && { buyerWallet },
141
+ tokenCurrency: tokenCurrency ?? "usdc",
121
142
  transactionHash: STRIPE_TEST_TX_HASH_SUCCESS,
122
- stripeSecretKey: input.stripeSecretKey,
123
- ...input.stripeVersion !== void 0 && { stripeVersion: input.stripeVersion }
143
+ stripeSecretKey,
144
+ ...stripeVersion !== void 0 && { stripeVersion }
124
145
  });
125
- console.warn(`[stripe] \u2713 Simulated ${input.network} deposit for PI ${piId}`);
146
+ console.warn(`[stripe] \u2713 Simulated ${network} deposit for PI ${piId}`);
126
147
  } catch (err) {
127
148
  console.error(
128
- `[stripe] \u2717 Failed to simulate ${input.network} deposit for PI ${piId}:`,
149
+ `[stripe] \u2717 Failed to simulate ${network} deposit for PI ${piId}:`,
129
150
  err instanceof Error ? err.message : err
130
151
  );
131
152
  }
132
153
  }
133
154
 
134
155
  // src/stripe-multichain/mppx_stripe.ts
135
- async function createMppxStripe(input) {
156
+ async function createMppxStripe({
157
+ profileId,
158
+ secretKey,
159
+ paymentMethodTypes
160
+ }) {
136
161
  const moduleName = "mppx/server";
137
162
  const mppx = await import(moduleName).catch(() => null);
138
163
  if (!mppx?.stripe?.charge) {
@@ -141,16 +166,18 @@ async function createMppxStripe(input) {
141
166
  );
142
167
  }
143
168
  return mppx.stripe.charge({
144
- networkId: input.profileId,
145
- paymentMethodTypes: input.paymentMethodTypes ?? ["card", "link"],
146
- secretKey: input.secretKey
169
+ networkId: profileId,
170
+ paymentMethodTypes: paymentMethodTypes ?? ["card", "link"],
171
+ secretKey
147
172
  });
148
173
  }
149
174
 
150
175
  // src/stripe-multichain/pi-cache.ts
151
- function createPiCache(opts = {}) {
152
- const ttlSeconds = opts.ttlSeconds ?? 300;
153
- const keyPrefix = opts.keyPrefix ?? "payto:";
176
+ function createPiCache({
177
+ redisUrl,
178
+ ttlSeconds = 300,
179
+ keyPrefix = "payto:"
180
+ } = {}) {
154
181
  let redis = null;
155
182
  const addrMemCache = /* @__PURE__ */ new Map();
156
183
  const piCache = /* @__PURE__ */ new Map();
@@ -169,17 +196,17 @@ function createPiCache(opts = {}) {
169
196
  }, 6e4);
170
197
  if (typeof evict.unref === "function") evict.unref();
171
198
  async function getRedis() {
172
- if (!opts.redisUrl) return null;
199
+ if (!redisUrl) return null;
173
200
  if (redis) return redis;
174
201
  const mod = await import("ioredis").catch(() => null);
175
202
  if (!mod) {
176
203
  console.error("[pi-cache] redisUrl set but `ioredis` is not installed. Run `npm install ioredis` or unset redisUrl.");
177
204
  return null;
178
205
  }
179
- redis = new mod.default(opts.redisUrl, {
206
+ redis = new mod.default(redisUrl, {
180
207
  connectTimeout: 5e3,
181
208
  maxRetriesPerRequest: 1,
182
- tls: opts.redisUrl.startsWith("rediss://") ? {} : void 0
209
+ tls: redisUrl.startsWith("rediss://") ? {} : void 0
183
210
  });
184
211
  redis.on("error", (err) => console.error("[pi-cache] Redis error:", err.message));
185
212
  return redis;
@@ -236,7 +263,6 @@ function createPiCache(opts = {}) {
236
263
  createMppxStripe,
237
264
  createMultichainPaymentIntent,
238
265
  createPiCache,
239
- getDepositAddress,
240
266
  simulateCryptoDeposit,
241
267
  simulateDepositIfTestMode
242
268
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/stripe-multichain/index.ts","../../src/stripe-multichain/payment_intent.ts","../../src/stripe-multichain/simulate_deposit.ts","../../src/stripe-multichain/mppx_stripe.ts","../../src/stripe-multichain/pi-cache.ts"],"sourcesContent":["export * from './payment_intent';\nexport * from './simulate_deposit';\nexport * from './mppx_stripe';\nexport * from './pi-cache';\n","/**\n * Minimal Stripe client surface — only the methods we use. Vendors pass their actual\n * `Stripe` instance (peer dep on the `stripe` package); this interface keeps the SDK\n * decoupled from any specific Stripe version.\n */\nexport interface StripeClientLike {\n paymentIntents: {\n create(\n params: Record<string, unknown>,\n opts?: { idempotencyKey?: string },\n ): Promise<StripePaymentIntent>;\n };\n}\n\nexport interface StripePaymentIntent {\n id: string;\n next_action?: {\n crypto_display_details?: {\n deposit_addresses?: Record<string, { address?: string } | undefined>;\n };\n } | null;\n [key: string]: unknown;\n}\n\nexport interface CreateMultichainPaymentIntentInput {\n /** A configured Stripe SDK instance. */\n stripe: StripeClientLike;\n /** Amount in cents (Stripe convention — $1.00 = 100). */\n amount: number;\n /** Currency code. Default 'usd'. */\n currency?: string;\n /** Networks to advertise to Stripe deposit_options. Default ['tempo', 'base', 'solana']. */\n networks?: string[];\n /** Metadata to attach to the PI (visible in Stripe dashboard). */\n metadata?: Record<string, string>;\n /** Idempotency key — agent retries of the same purchase won't create duplicate PIs. */\n idempotencyKey?: string;\n}\n\nexport interface MultichainPaymentIntentResult {\n /** Stripe PaymentIntent ID. */\n paymentIntentId: string;\n /** Map of network name → on-chain deposit address. e.g., { tempo: '0x...', base: '0x...', solana: '...' }. */\n depositAddresses: Record<string, string>;\n}\n\n/**\n * Create a Stripe PaymentIntent with `deposit_options.networks` set to multiple chains,\n * returning the PI id + deposit addresses per network. The agent sends funds to the\n * address on whichever chain they prefer (via x402 or MPP), and Stripe auto-captures\n * the PI when funds land.\n *\n * This is the path used by martin-estate for the multi-chain x402 + Tempo flow.\n * Distinct from the Stripe SPT (Shared Payment Token) flow, which is handled via\n * `createMppxStripe` + the agent's own Stripe account or `link-cli`.\n */\nexport async function createMultichainPaymentIntent(\n input: CreateMultichainPaymentIntentInput,\n): Promise<MultichainPaymentIntentResult> {\n const pi = await input.stripe.paymentIntents.create(\n {\n amount: input.amount,\n currency: input.currency ?? 'usd',\n payment_method_types: ['crypto'],\n payment_method_data: { type: 'crypto' },\n payment_method_options: {\n crypto: {\n mode: 'deposit',\n deposit_options: { networks: input.networks ?? ['tempo', 'base', 'solana'] },\n },\n },\n confirm: true,\n ...(input.metadata ? { metadata: input.metadata } : {}),\n },\n input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : undefined,\n );\n\n const depositAddresses: Record<string, string> = {};\n const addrs = pi.next_action?.crypto_display_details?.deposit_addresses ?? {};\n for (const [network, info] of Object.entries(addrs)) {\n if (info?.address) depositAddresses[network] = info.address;\n }\n\n if (Object.keys(depositAddresses).length === 0) {\n throw new Error('No deposit addresses returned from Stripe PaymentIntent');\n }\n\n return { paymentIntentId: pi.id, depositAddresses };\n}\n\n/**\n * Convenience accessor: get the deposit address for a specific network from a\n * createMultichainPaymentIntent result. Returns undefined if Stripe didn't issue\n * an address for that network.\n */\nexport function getDepositAddress(\n result: MultichainPaymentIntentResult,\n network: string,\n): string | undefined {\n return result.depositAddresses[network];\n}\n","/**\n * Stripe's documented magic test_helpers transaction hash that resolves the\n * PaymentIntent to `succeeded` within 15 seconds. Same value across all networks —\n * Stripe normalizes the format internally. Anything else (including network-shaped\n * placeholder bytes) is rejected with \"not a valid testmode transaction hash\".\n *\n * See: https://docs.stripe.com/payments/deposit-mode-stablecoin-payments\n */\nexport const STRIPE_TEST_TX_HASH_SUCCESS =\n '0x00000000000000000000000000000000000000000000000000000testsuccess';\n\n/**\n * Stripe's documented magic test_helpers transaction hash that fails the charge\n * (PaymentIntent returns to `requires_payment_method` within 15 seconds).\n */\nexport const STRIPE_TEST_TX_HASH_FAILED =\n '0x000000000000000000000000000000000000000000000000000000testfailed';\n\nexport interface SimulateCryptoDepositInput {\n /** Stripe PaymentIntent id to simulate a deposit on. */\n paymentIntentId: string;\n /** Network the simulated deposit lands on. */\n network: 'tempo' | 'base' | 'solana';\n /** Optional simulated buyer wallet address. Defaults to a sensible placeholder per network. */\n buyerWallet?: string;\n /** Token currency (e.g., 'usdc'). Optional — passed as a form param if set. */\n tokenCurrency?: string;\n /** Simulated transaction hash. Optional — passed as a form param if set. */\n transactionHash?: string;\n /** Stripe secret key (for the test-helpers Authorization header). Must be a `sk_test_...` key. */\n stripeSecretKey: string;\n /** Stripe API version to request via the `Stripe-Version` header. Useful for preview APIs. */\n stripeVersion?: string;\n /** Override the Stripe API base URL. Default 'https://api.stripe.com'. */\n stripeApiBase?: string;\n /** Arbitrary additional form params to merge into the request body. */\n extra?: Record<string, string>;\n}\n\nconst DEFAULT_BUYER_WALLET: Record<string, string> = {\n base: '0x0000000000000000000000000000000000000001',\n tempo: '0x0000000000000000000000000000000000000001',\n solana: '11111111111111111111111111111111',\n};\n\n/**\n * Call Stripe's `test_helpers/payment_intents/{id}/simulate_crypto_deposit` endpoint. Used\n * in testnet/dev to simulate a deposit landing on a PaymentIntent so the integration\n * end-to-end can be exercised without on-chain transfers.\n *\n * Throws on non-2xx responses (returns Stripe's error body in the message).\n */\nexport async function simulateCryptoDeposit(input: SimulateCryptoDepositInput): Promise<void> {\n const url = `${input.stripeApiBase ?? 'https://api.stripe.com'}/v1/test_helpers/payment_intents/${input.paymentIntentId}/simulate_crypto_deposit`;\n const params = new URLSearchParams({\n network: input.network,\n buyer_wallet: input.buyerWallet ?? DEFAULT_BUYER_WALLET[input.network] ?? '',\n });\n if (input.tokenCurrency) params.set('token_currency', input.tokenCurrency);\n if (input.transactionHash) params.set('transaction_hash', input.transactionHash);\n for (const [k, v] of Object.entries(input.extra ?? {})) {\n params.set(k, v);\n }\n const headers: Record<string, string> = {\n Authorization: `Bearer ${input.stripeSecretKey}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (input.stripeVersion) headers['Stripe-Version'] = input.stripeVersion;\n const res = await fetch(url, { method: 'POST', headers, body: params.toString() });\n if (!res.ok) {\n throw new Error(`Stripe simulate_crypto_deposit failed: ${res.status} ${await res.text()}`);\n }\n}\n\nexport interface SimulateDepositIfTestModeInput {\n /** Stripe PaymentIntent id resolver — given a deposit address, return the PI id (or undefined\n * if the cache TTL expired between 402 emit and settlement). Typically `cache.getPaymentIntentId`. */\n getPaymentIntentId: (depositAddress: string) => string | undefined;\n /** The deposit address that was paid to (recipient). */\n depositAddress: string;\n /** Network the simulated deposit lands on. */\n network: 'tempo' | 'base' | 'solana';\n /** Optional simulated buyer wallet (defaults per network in `simulateCryptoDeposit`). */\n buyerWallet?: string;\n /** Token currency to pass through to Stripe (typically `'usdc'`). */\n tokenCurrency?: string;\n /** Stripe secret key. The wrapper checks this starts with `sk_test_` and skips otherwise. */\n stripeSecretKey: string;\n /** Stripe API version (e.g. `'2026-03-04.preview'` for the deposit-mode preview). */\n stripeVersion?: string;\n}\n\n/**\n * Higher-level wrapper around {@link simulateCryptoDeposit} for the testnet/dev path.\n * Bundles the three steps every Stripe-multichain merchant repeats:\n *\n * 1. Gate on `sk_test_` key prefix — production keys reject the test_helpers endpoint\n * with 400; live deposits reach Stripe's real crypto-deposit watcher instead.\n * 2. Resolve the PaymentIntent id from the deposit address (cache lookup).\n * 3. Call `simulate_crypto_deposit` with Stripe's documented success magic hash.\n *\n * Logs `[stripe] ✓ Simulated <network> deposit for PI <id>` on success and\n * `[stripe] ✗ Failed to simulate <network> deposit for PI <id>: <err>` on failure.\n * Errors are caught + logged (never thrown) so a sim hiccup doesn't fail the order.\n *\n * Use case is exclusively dev/testnet end-to-end — production servers (sk_live_) no-op.\n */\nexport async function simulateDepositIfTestMode(input: SimulateDepositIfTestModeInput): Promise<void> {\n if (!input.stripeSecretKey.startsWith('sk_test_')) return;\n const piId = input.getPaymentIntentId(input.depositAddress);\n if (!piId) {\n console.warn(\n `[stripe] Skipping deposit simulation — no PI cached for deposit address ${input.depositAddress.slice(0, 10)}… (network=${input.network}). The PI cache TTL may have expired between 402 emission and settlement.`,\n );\n return;\n }\n try {\n await simulateCryptoDeposit({\n paymentIntentId: piId,\n network: input.network,\n ...(input.buyerWallet !== undefined && { buyerWallet: input.buyerWallet }),\n tokenCurrency: input.tokenCurrency ?? 'usdc',\n transactionHash: STRIPE_TEST_TX_HASH_SUCCESS,\n stripeSecretKey: input.stripeSecretKey,\n ...(input.stripeVersion !== undefined && { stripeVersion: input.stripeVersion }),\n });\n console.warn(`[stripe] ✓ Simulated ${input.network} deposit for PI ${piId}`);\n } catch (err) {\n console.error(\n `[stripe] ✗ Failed to simulate ${input.network} deposit for PI ${piId}:`,\n err instanceof Error ? err.message : err,\n );\n }\n}\n","export interface CreateMppxStripeInput {\n /** Stripe profile_id / network_id (the value advertised in your `stripe/charge` accepted_methods entry). */\n profileId: string;\n /** Stripe secret key — mppx uses it to validate inbound SharedPaymentTokens. */\n secretKey: string;\n /** Payment method types this stripe rail accepts. Default ['card', 'link']. */\n paymentMethodTypes?: string[];\n}\n\n/**\n * Wraps the `mppStripe.charge(...)` boilerplate from `mppx/server`. Returns the value\n * vendors pass into `Mppx.create({ methods: [...] })`. mppx is an OPTIONAL peer dependency —\n * vendors who don't use Stripe SPT don't need to install it.\n *\n * Example:\n *\n * import { Mppx, tempo } from 'mppx/server';\n * import { createMppxStripe } from '@agent-score/commerce/stripe-multichain';\n *\n * const stripeMethod = await createMppxStripe({\n * profileId: process.env.STRIPE_PROFILE_ID!,\n * secretKey: process.env.STRIPE_SECRET_KEY!,\n * });\n *\n * const mppx = Mppx.create({\n * methods: [tempo.charge({...}), stripeMethod],\n * secretKey: process.env.MPP_SECRET_KEY!,\n * });\n *\n * Throws if mppx is not installed.\n */\nexport async function createMppxStripe(input: CreateMppxStripeInput): Promise<unknown> {\n const moduleName = 'mppx/server';\n const mppx = (await import(moduleName).catch(() => null)) as {\n stripe?: {\n charge: (config: {\n networkId: string;\n paymentMethodTypes?: string[];\n secretKey: string;\n }) => unknown;\n };\n } | null;\n /* v8 ignore start -- peer-dep-absence guard; mppx is installed in the test env so this branch can't be exercised without mocking the dynamic import */\n if (!mppx?.stripe?.charge) {\n throw new Error(\n 'mppx not installed — install with `npm install mppx` to use createMppxStripe.',\n );\n }\n /* v8 ignore stop */\n return mppx.stripe.charge({\n networkId: input.profileId,\n paymentMethodTypes: input.paymentMethodTypes ?? ['card', 'link'],\n secretKey: input.secretKey,\n });\n}\n","/**\n * Stripe PaymentIntent + deposit-address cache.\n *\n * Stripe-multichain merchants need three lookups during a request lifecycle:\n *\n * 1. **Is this on-chain `pay_to` address one we minted?** — when an MPP credential\n * arrives with a `recipient`, verify it matches a recently-minted Stripe deposit\n * address. Validates the credential's deposit address against the addresses the\n * merchant has actually minted.\n *\n * 2. **Which PaymentIntent owns this deposit address?** — when settling, the\n * `simulate_crypto_deposit` test_helpers call needs the PaymentIntent id for the\n * deposit address that was paid to.\n *\n * 3. **Which sibling deposit addresses belong to the same PaymentIntent?** — when\n * enriching a 402 with x402 entries, the merchant needs the Base + Solana addresses\n * Stripe minted alongside the original Tempo address (one PI carries up to three).\n *\n * All three are TTL-bounded (default 300s — long enough for an agent to retry, short\n * enough to bound memory). Backed by Redis when `redisUrl` is set, falls back to\n * in-process Map otherwise. Single-instance servers can use the in-memory cache;\n * multi-instance deployments need a shared cache (Redis) so a deposit lands on\n * whichever instance settles it.\n */\n\n// ioredis is an optional peer dep — typed structurally to avoid pulling its types into\n// the build for merchants that run in-process without Redis. The structural type covers\n// only the methods we call (set with EX/get/on); merchants using Redis install ioredis\n// themselves.\ninterface RedisLike {\n set: (key: string, value: string, mode: 'EX', ttl: number) => Promise<unknown>;\n get: (key: string) => Promise<string | null>;\n on: (event: 'error', cb: (err: Error) => void) => unknown;\n}\n\nexport interface PiCacheOptions {\n /** Redis connection URL (e.g. `rediss://…cache.amazonaws.com:6379`). When omitted,\n * the cache falls back to in-process Maps with the same API. */\n redisUrl?: string;\n /** TTL for cached entries in seconds. Default 300. */\n ttlSeconds?: number;\n /** Prefix for Redis keys. Default `'payto:'`. */\n keyPrefix?: string;\n}\n\nexport interface PiCache {\n /** Mark an on-chain address as one this merchant minted. Idempotent + TTL-bounded. */\n cacheAddress(address: string): Promise<void>;\n /** Return true when the address was minted by this merchant within TTL. */\n hasAddress(address: string): Promise<boolean>;\n /** Associate an on-chain deposit address with the Stripe PaymentIntent that minted it. */\n cachePaymentIntent(depositAddress: string, paymentIntentId: string): void;\n /** Get the Stripe PaymentIntent id for a previously-minted deposit address, or undefined. */\n getPaymentIntentId(depositAddress: string): string | undefined;\n /** Associate a PaymentIntent id with the full set of sibling deposit addresses (one per network). */\n cacheNetworkAddresses(paymentIntentId: string, addresses: Record<string, string>): void;\n /** Look up the deposit address Stripe minted on a specific network for a given PaymentIntent. */\n getNetworkDepositAddress(paymentIntentId: string, network: string): string | undefined;\n /** Stop the background TTL-eviction loop. Call from server shutdown handlers. */\n stop(): void;\n}\n\ninterface Entry<T> { value: T; expiresAt: number }\n\nexport function createPiCache(opts: PiCacheOptions = {}): PiCache {\n const ttlSeconds = opts.ttlSeconds ?? 300;\n const keyPrefix = opts.keyPrefix ?? 'payto:';\n\n let redis: RedisLike | null = null;\n const addrMemCache = new Map<string, number>();\n const piCache = new Map<string, Entry<string>>();\n const networkAddressCache = new Map<string, Entry<Record<string, string>>>();\n\n const evict = setInterval(() => {\n const now = Date.now();\n for (const [k, v] of piCache) { if (v.expiresAt < now) piCache.delete(k); }\n for (const [k, v] of networkAddressCache) { if (v.expiresAt < now) networkAddressCache.delete(k); }\n for (const [k, expires] of addrMemCache) { if (expires < now) addrMemCache.delete(k); }\n }, 60_000);\n // Don't keep the event loop alive on test shutdown / one-shot scripts.\n if (typeof evict.unref === 'function') evict.unref();\n\n async function getRedis(): Promise<RedisLike | null> {\n if (!opts.redisUrl) return null;\n if (redis) return redis;\n // Dynamic import keeps ioredis as an optional peer dep — merchants without\n // Redis don't pay the install cost.\n const mod = await import('ioredis' as string).catch(() => null) as\n | { default: new (url: string, opts: unknown) => RedisLike }\n | null;\n if (!mod) {\n console.error('[pi-cache] redisUrl set but `ioredis` is not installed. Run `npm install ioredis` or unset redisUrl.');\n return null;\n }\n redis = new mod.default(opts.redisUrl, {\n connectTimeout: 5000,\n maxRetriesPerRequest: 1,\n tls: opts.redisUrl.startsWith('rediss://') ? {} : undefined,\n });\n redis.on('error', (err: Error) => console.error('[pi-cache] Redis error:', err.message));\n return redis;\n }\n\n return {\n async cacheAddress(address) {\n const r = await getRedis();\n if (r) await r.set(`${keyPrefix}${address}`, '1', 'EX', ttlSeconds).catch(() => {});\n addrMemCache.set(address, Date.now() + ttlSeconds * 1000);\n },\n async hasAddress(address) {\n const r = await getRedis();\n if (r) {\n const val = await r.get(`${keyPrefix}${address}`).catch(() => null);\n if (val) return true;\n }\n const expiry = addrMemCache.get(address);\n return !!expiry && expiry > Date.now();\n },\n cachePaymentIntent(depositAddress, paymentIntentId) {\n piCache.set(depositAddress, { value: paymentIntentId, expiresAt: Date.now() + ttlSeconds * 1000 });\n },\n getPaymentIntentId(depositAddress) {\n const entry = piCache.get(depositAddress);\n if (!entry) return undefined;\n if (entry.expiresAt < Date.now()) { piCache.delete(depositAddress); return undefined; }\n return entry.value;\n },\n cacheNetworkAddresses(paymentIntentId, addresses) {\n networkAddressCache.set(paymentIntentId, { value: addresses, expiresAt: Date.now() + ttlSeconds * 1000 });\n },\n getNetworkDepositAddress(paymentIntentId, network) {\n const entry = networkAddressCache.get(paymentIntentId);\n if (!entry) return undefined;\n if (entry.expiresAt < Date.now()) { networkAddressCache.delete(paymentIntentId); return undefined; }\n return entry.value[network];\n },\n stop() {\n clearInterval(evict);\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/stripe-multichain/index.ts","../../src/stripe-multichain/payment_intent.ts","../../src/stripe-multichain/simulate_deposit.ts","../../src/stripe-multichain/mppx_stripe.ts","../../src/stripe-multichain/pi-cache.ts"],"sourcesContent":["export * from './payment_intent';\nexport * from './simulate_deposit';\nexport * from './mppx_stripe';\nexport * from './pi-cache';\n","/**\n * Minimal Stripe client surface — only the methods we use. Vendors pass their actual\n * `Stripe` instance (peer dep on the `stripe` package); this interface keeps the SDK\n * decoupled from any specific Stripe version.\n */\nexport interface StripeClientLike {\n paymentIntents: {\n create(\n params: Record<string, unknown>,\n opts?: { idempotencyKey?: string },\n ): Promise<StripePaymentIntent>;\n };\n}\n\nexport interface StripePaymentIntent {\n id: string;\n next_action?: {\n crypto_display_details?: {\n deposit_addresses?: Record<string, { address?: string } | undefined>;\n };\n } | null;\n [key: string]: unknown;\n}\n\nexport interface MultichainPaymentIntentResult {\n /** Stripe PaymentIntent ID. */\n paymentIntentId: string;\n /** Map of network name → on-chain deposit address. e.g., { tempo: '0x...', base: '0x...', solana: '...' }. */\n depositAddresses: Record<string, string>;\n}\n\n/**\n * Create a Stripe PaymentIntent with `deposit_options.networks` set to multiple chains,\n * returning the PI id + deposit addresses per network. The agent sends funds to the\n * address on whichever chain they prefer (via x402 or MPP), and Stripe auto-captures\n * the PI when funds land.\n *\n * This is the canonical path for the multi-chain x402 + Tempo flow.\n * Distinct from the Stripe SPT (Shared Payment Token) flow, which is handled via\n * `createMppxStripe` + the agent's own Stripe account or `link-cli`.\n */\nexport async function createMultichainPaymentIntent({\n stripe,\n amount,\n currency = 'usd',\n networks,\n metadata,\n idempotencyKey,\n}: {\n /** A configured Stripe SDK instance. */\n stripe: StripeClientLike;\n /** Amount in cents (Stripe convention — $1.00 = 100). */\n amount: number;\n /** Currency code. Default 'usd'. */\n currency?: string;\n /** Networks to advertise to Stripe deposit_options. Default ['tempo', 'base', 'solana']. */\n networks?: string[];\n /** Metadata to attach to the PI (visible in Stripe dashboard). */\n metadata?: Record<string, string>;\n /** Idempotency key — agent retries of the same purchase won't create duplicate PIs. */\n idempotencyKey?: string;\n}): Promise<MultichainPaymentIntentResult> {\n const pi = await stripe.paymentIntents.create(\n {\n amount,\n currency,\n payment_method_types: ['crypto'],\n payment_method_data: { type: 'crypto' },\n payment_method_options: {\n crypto: {\n mode: 'deposit',\n deposit_options: { networks: networks ?? ['tempo', 'base', 'solana'] },\n },\n },\n confirm: true,\n ...(metadata ? { metadata } : {}),\n },\n idempotencyKey ? { idempotencyKey } : undefined,\n );\n\n const depositAddresses: Record<string, string> = {};\n const addrs = pi.next_action?.crypto_display_details?.deposit_addresses ?? {};\n for (const [network, info] of Object.entries(addrs)) {\n if (info?.address) depositAddresses[network] = info.address;\n }\n\n if (Object.keys(depositAddresses).length === 0) {\n throw new Error('No deposit addresses returned from Stripe PaymentIntent');\n }\n\n return { paymentIntentId: pi.id, depositAddresses };\n}\n","/**\n * Stripe's documented magic test_helpers transaction hash that resolves the\n * PaymentIntent to `succeeded` within 15 seconds. Same value across all networks —\n * Stripe normalizes the format internally. Anything else (including network-shaped\n * placeholder bytes) is rejected with \"not a valid testmode transaction hash\".\n *\n * See: https://docs.stripe.com/payments/deposit-mode-stablecoin-payments\n */\nexport const STRIPE_TEST_TX_HASH_SUCCESS =\n '0x00000000000000000000000000000000000000000000000000000testsuccess';\n\n/**\n * Stripe's documented magic test_helpers transaction hash that fails the charge\n * (PaymentIntent returns to `requires_payment_method` within 15 seconds).\n */\nexport const STRIPE_TEST_TX_HASH_FAILED =\n '0x000000000000000000000000000000000000000000000000000000testfailed';\n\nconst DEFAULT_BUYER_WALLET: Record<string, string> = {\n base: '0x0000000000000000000000000000000000000001',\n tempo: '0x0000000000000000000000000000000000000001',\n solana: '11111111111111111111111111111111',\n};\n\n/**\n * Call Stripe's `test_helpers/payment_intents/{id}/simulate_crypto_deposit` endpoint. Used\n * in testnet/dev to simulate a deposit landing on a PaymentIntent so the integration\n * end-to-end can be exercised without on-chain transfers.\n *\n * Throws on non-2xx responses (returns Stripe's error body in the message).\n */\nexport async function simulateCryptoDeposit({\n paymentIntentId,\n network,\n buyerWallet,\n tokenCurrency,\n transactionHash,\n stripeSecretKey,\n stripeVersion,\n stripeApiBase,\n extra,\n}: {\n /** Stripe PaymentIntent id to simulate a deposit on. */\n paymentIntentId: string;\n /** Network the simulated deposit lands on. */\n network: 'tempo' | 'base' | 'solana';\n /** Optional simulated buyer wallet address. Defaults to a sensible placeholder per network. */\n buyerWallet?: string;\n /** Token currency (e.g., 'usdc'). Optional — passed as a form param if set. */\n tokenCurrency?: string;\n /** Simulated transaction hash. Optional — passed as a form param if set. */\n transactionHash?: string;\n /** Stripe secret key (for the test-helpers Authorization header). Must be a `sk_test_...` key. */\n stripeSecretKey: string;\n /** Stripe API version to request via the `Stripe-Version` header. Useful for preview APIs. */\n stripeVersion?: string;\n /** Override the Stripe API base URL. Default 'https://api.stripe.com'. */\n stripeApiBase?: string;\n /** Arbitrary additional form params to merge into the request body. */\n extra?: Record<string, string>;\n}): Promise<void> {\n const url = `${stripeApiBase ?? 'https://api.stripe.com'}/v1/test_helpers/payment_intents/${paymentIntentId}/simulate_crypto_deposit`;\n const params = new URLSearchParams({\n network,\n buyer_wallet: buyerWallet ?? DEFAULT_BUYER_WALLET[network] ?? '',\n });\n if (tokenCurrency) params.set('token_currency', tokenCurrency);\n if (transactionHash) params.set('transaction_hash', transactionHash);\n for (const [k, v] of Object.entries(extra ?? {})) {\n params.set(k, v);\n }\n const headers: Record<string, string> = {\n Authorization: `Bearer ${stripeSecretKey}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (stripeVersion) headers['Stripe-Version'] = stripeVersion;\n const res = await fetch(url, { method: 'POST', headers, body: params.toString() });\n if (!res.ok) {\n throw new Error(`Stripe simulate_crypto_deposit failed: ${res.status} ${await res.text()}`);\n }\n}\n\n/**\n * Higher-level wrapper around {@link simulateCryptoDeposit} for the testnet/dev path.\n * Bundles the three steps every Stripe-multichain merchant repeats:\n *\n * 1. Gate on `sk_test_` key prefix — production keys reject the test_helpers endpoint\n * with 400; live deposits reach Stripe's real crypto-deposit watcher instead.\n * 2. Resolve the PaymentIntent id from the deposit address (cache lookup).\n * 3. Call `simulate_crypto_deposit` with Stripe's documented success magic hash.\n *\n * Logs `[stripe] ✓ Simulated <network> deposit for PI <id>` on success and\n * `[stripe] ✗ Failed to simulate <network> deposit for PI <id>: <err>` on failure.\n * Errors are caught + logged (never thrown) so a sim hiccup doesn't fail the order.\n *\n * Use case is exclusively dev/testnet end-to-end — production servers (sk_live_) no-op.\n */\nexport async function simulateDepositIfTestMode({\n getPaymentIntentId,\n depositAddress,\n network,\n buyerWallet,\n tokenCurrency,\n stripeSecretKey,\n stripeVersion,\n}: {\n /** Stripe PaymentIntent id resolver — given a deposit address, return the PI id (or undefined\n * if the cache TTL expired between 402 emit and settlement). Typically `cache.getPaymentIntentId`. */\n getPaymentIntentId: (depositAddress: string) => string | undefined;\n /** The deposit address that was paid to (recipient). */\n depositAddress: string;\n /** Network the simulated deposit lands on. */\n network: 'tempo' | 'base' | 'solana';\n /** Optional simulated buyer wallet (defaults per network in `simulateCryptoDeposit`). */\n buyerWallet?: string;\n /** Token currency to pass through to Stripe (typically `'usdc'`). */\n tokenCurrency?: string;\n /** Stripe secret key. The wrapper checks this starts with `sk_test_` and skips otherwise. */\n stripeSecretKey: string;\n /** Stripe API version (e.g. `'2026-03-04.preview'` for the deposit-mode preview). */\n stripeVersion?: string;\n}): Promise<void> {\n if (!stripeSecretKey.startsWith('sk_test_')) return;\n const piId = getPaymentIntentId(depositAddress);\n if (!piId) {\n console.warn(\n `[stripe] Skipping deposit simulation — no PI cached for deposit address ${depositAddress.slice(0, 10)}… (network=${network}). The PI cache TTL may have expired between 402 emission and settlement.`,\n );\n return;\n }\n try {\n await simulateCryptoDeposit({\n paymentIntentId: piId,\n network,\n ...(buyerWallet !== undefined && { buyerWallet }),\n tokenCurrency: tokenCurrency ?? 'usdc',\n transactionHash: STRIPE_TEST_TX_HASH_SUCCESS,\n stripeSecretKey,\n ...(stripeVersion !== undefined && { stripeVersion }),\n });\n console.warn(`[stripe] ✓ Simulated ${network} deposit for PI ${piId}`);\n } catch (err) {\n console.error(\n `[stripe] ✗ Failed to simulate ${network} deposit for PI ${piId}:`,\n err instanceof Error ? err.message : err,\n );\n }\n}\n","/**\n * Wraps the `mppStripe.charge(...)` boilerplate from `mppx/server`. Returns the value\n * vendors pass into `Mppx.create({ methods: [...] })`. mppx is an OPTIONAL peer dependency —\n * vendors who don't use Stripe SPT don't need to install it.\n *\n * Example:\n *\n * import { Mppx, tempo } from 'mppx/server';\n * import { createMppxStripe } from '@agent-score/commerce/stripe-multichain';\n *\n * const stripeMethod = await createMppxStripe({\n * profileId: process.env.STRIPE_PROFILE_ID!,\n * secretKey: process.env.STRIPE_SECRET_KEY!,\n * });\n *\n * const mppx = Mppx.create({\n * methods: [tempo.charge({...}), stripeMethod],\n * secretKey: process.env.MPP_SECRET_KEY!,\n * });\n *\n * Throws if mppx is not installed.\n */\nexport async function createMppxStripe({\n profileId,\n secretKey,\n paymentMethodTypes,\n}: {\n /** Stripe profile_id / network_id (the value advertised in your `stripe/charge` accepted_methods entry). */\n profileId: string;\n /** Stripe secret key — mppx uses it to validate inbound SharedPaymentTokens. */\n secretKey: string;\n /** Payment method types this stripe rail accepts. Default ['card', 'link']. */\n paymentMethodTypes?: string[];\n}): Promise<unknown> {\n const moduleName = 'mppx/server';\n const mppx = (await import(moduleName).catch(() => null)) as {\n stripe?: {\n charge: (config: {\n networkId: string;\n paymentMethodTypes?: string[];\n secretKey: string;\n }) => unknown;\n };\n } | null;\n /* v8 ignore start -- peer-dep-absence guard; mppx is installed in the test env so this branch can't be exercised without mocking the dynamic import */\n if (!mppx?.stripe?.charge) {\n throw new Error(\n 'mppx not installed — install with `npm install mppx` to use createMppxStripe.',\n );\n }\n /* v8 ignore stop */\n return mppx.stripe.charge({\n networkId: profileId,\n paymentMethodTypes: paymentMethodTypes ?? ['card', 'link'],\n secretKey,\n });\n}\n","/**\n * Stripe PaymentIntent + deposit-address cache.\n *\n * Stripe-multichain merchants need three lookups during a request lifecycle:\n *\n * 1. **Is this on-chain `pay_to` address one we minted?** — when an MPP credential\n * arrives with a `recipient`, verify it matches a recently-minted Stripe deposit\n * address. Validates the credential's deposit address against the addresses the\n * merchant has actually minted.\n *\n * 2. **Which PaymentIntent owns this deposit address?** — when settling, the\n * `simulate_crypto_deposit` test_helpers call needs the PaymentIntent id for the\n * deposit address that was paid to.\n *\n * 3. **Which sibling deposit addresses belong to the same PaymentIntent?** — when\n * enriching a 402 with x402 entries, the merchant needs the Base + Solana addresses\n * Stripe minted alongside the original Tempo address (one PI carries up to three).\n *\n * All three are TTL-bounded (default 300s — long enough for an agent to retry, short\n * enough to bound memory). Backed by Redis when `redisUrl` is set, falls back to\n * in-process Map otherwise. Single-instance servers can use the in-memory cache;\n * multi-instance deployments need a shared cache (Redis) so a deposit lands on\n * whichever instance settles it.\n */\n\n// ioredis is an optional peer dep — typed structurally to avoid pulling its types into\n// the build for merchants that run in-process without Redis. The structural type covers\n// only the methods we call (set with EX/get/on); merchants using Redis install ioredis\n// themselves.\ninterface RedisLike {\n set: (key: string, value: string, mode: 'EX', ttl: number) => Promise<unknown>;\n get: (key: string) => Promise<string | null>;\n on: (event: 'error', cb: (err: Error) => void) => unknown;\n}\n\n\nexport interface PiCache {\n /** Mark an on-chain address as one this merchant minted. Idempotent + TTL-bounded. */\n cacheAddress(address: string): Promise<void>;\n /** Return true when the address was minted by this merchant within TTL. */\n hasAddress(address: string): Promise<boolean>;\n /** Associate an on-chain deposit address with the Stripe PaymentIntent that minted it. */\n cachePaymentIntent(depositAddress: string, paymentIntentId: string): void;\n /** Get the Stripe PaymentIntent id for a previously-minted deposit address, or undefined. */\n getPaymentIntentId(depositAddress: string): string | undefined;\n /** Associate a PaymentIntent id with the full set of sibling deposit addresses (one per network). */\n cacheNetworkAddresses(paymentIntentId: string, addresses: Record<string, string>): void;\n /** Look up the deposit address Stripe minted on a specific network for a given PaymentIntent. */\n getNetworkDepositAddress(paymentIntentId: string, network: string): string | undefined;\n /** Stop the background TTL-eviction loop. Call from server shutdown handlers. */\n stop(): void;\n}\n\ninterface Entry<T> { value: T; expiresAt: number }\n\nexport function createPiCache({\n redisUrl,\n ttlSeconds = 300,\n keyPrefix = 'payto:',\n}: {\n /** Redis connection URL (e.g. `rediss://…cache.amazonaws.com:6379`). When omitted,\n * the cache falls back to in-process Maps with the same API. */\n redisUrl?: string;\n /** TTL for cached entries in seconds. Default 300. */\n ttlSeconds?: number;\n /** Prefix for Redis keys. Default `'payto:'`. */\n keyPrefix?: string;\n} = {}): PiCache {\n\n let redis: RedisLike | null = null;\n const addrMemCache = new Map<string, number>();\n const piCache = new Map<string, Entry<string>>();\n const networkAddressCache = new Map<string, Entry<Record<string, string>>>();\n\n const evict = setInterval(() => {\n const now = Date.now();\n for (const [k, v] of piCache) { if (v.expiresAt < now) piCache.delete(k); }\n for (const [k, v] of networkAddressCache) { if (v.expiresAt < now) networkAddressCache.delete(k); }\n for (const [k, expires] of addrMemCache) { if (expires < now) addrMemCache.delete(k); }\n }, 60_000);\n // Don't keep the event loop alive on test shutdown / one-shot scripts.\n if (typeof evict.unref === 'function') evict.unref();\n\n async function getRedis(): Promise<RedisLike | null> {\n if (!redisUrl) return null;\n if (redis) return redis;\n // Dynamic import keeps ioredis as an optional peer dep — merchants without\n // Redis don't pay the install cost.\n const mod = await import('ioredis' as string).catch(() => null) as\n | { default: new (url: string, opts: unknown) => RedisLike }\n | null;\n if (!mod) {\n console.error('[pi-cache] redisUrl set but `ioredis` is not installed. Run `npm install ioredis` or unset redisUrl.');\n return null;\n }\n redis = new mod.default(redisUrl, {\n connectTimeout: 5000,\n maxRetriesPerRequest: 1,\n tls: redisUrl.startsWith('rediss://') ? {} : undefined,\n });\n redis.on('error', (err: Error) => console.error('[pi-cache] Redis error:', err.message));\n return redis;\n }\n\n return {\n async cacheAddress(address) {\n const r = await getRedis();\n if (r) await r.set(`${keyPrefix}${address}`, '1', 'EX', ttlSeconds).catch(() => {});\n addrMemCache.set(address, Date.now() + ttlSeconds * 1000);\n },\n async hasAddress(address) {\n const r = await getRedis();\n if (r) {\n const val = await r.get(`${keyPrefix}${address}`).catch(() => null);\n if (val) return true;\n }\n const expiry = addrMemCache.get(address);\n return !!expiry && expiry > Date.now();\n },\n cachePaymentIntent(depositAddress, paymentIntentId) {\n piCache.set(depositAddress, { value: paymentIntentId, expiresAt: Date.now() + ttlSeconds * 1000 });\n },\n getPaymentIntentId(depositAddress) {\n const entry = piCache.get(depositAddress);\n if (!entry) return undefined;\n if (entry.expiresAt < Date.now()) { piCache.delete(depositAddress); return undefined; }\n return entry.value;\n },\n cacheNetworkAddresses(paymentIntentId, addresses) {\n networkAddressCache.set(paymentIntentId, { value: addresses, expiresAt: Date.now() + ttlSeconds * 1000 });\n },\n getNetworkDepositAddress(paymentIntentId, network) {\n const entry = networkAddressCache.get(paymentIntentId);\n if (!entry) return undefined;\n if (entry.expiresAt < Date.now()) { networkAddressCache.delete(paymentIntentId); return undefined; }\n return entry.value[network];\n },\n stop() {\n clearInterval(evict);\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,21 +1,28 @@
1
1
  // src/stripe-multichain/payment_intent.ts
2
- async function createMultichainPaymentIntent(input) {
3
- const pi = await input.stripe.paymentIntents.create(
2
+ async function createMultichainPaymentIntent({
3
+ stripe,
4
+ amount,
5
+ currency = "usd",
6
+ networks,
7
+ metadata,
8
+ idempotencyKey
9
+ }) {
10
+ const pi = await stripe.paymentIntents.create(
4
11
  {
5
- amount: input.amount,
6
- currency: input.currency ?? "usd",
12
+ amount,
13
+ currency,
7
14
  payment_method_types: ["crypto"],
8
15
  payment_method_data: { type: "crypto" },
9
16
  payment_method_options: {
10
17
  crypto: {
11
18
  mode: "deposit",
12
- deposit_options: { networks: input.networks ?? ["tempo", "base", "solana"] }
19
+ deposit_options: { networks: networks ?? ["tempo", "base", "solana"] }
13
20
  }
14
21
  },
15
22
  confirm: true,
16
- ...input.metadata ? { metadata: input.metadata } : {}
23
+ ...metadata ? { metadata } : {}
17
24
  },
18
- input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : void 0
25
+ idempotencyKey ? { idempotencyKey } : void 0
19
26
  );
20
27
  const depositAddresses = {};
21
28
  const addrs = pi.next_action?.crypto_display_details?.deposit_addresses ?? {};
@@ -27,9 +34,6 @@ async function createMultichainPaymentIntent(input) {
27
34
  }
28
35
  return { paymentIntentId: pi.id, depositAddresses };
29
36
  }
30
- function getDepositAddress(result, network) {
31
- return result.depositAddresses[network];
32
- }
33
37
 
34
38
  // src/stripe-multichain/simulate_deposit.ts
35
39
  var STRIPE_TEST_TX_HASH_SUCCESS = "0x00000000000000000000000000000000000000000000000000000testsuccess";
@@ -39,57 +43,79 @@ var DEFAULT_BUYER_WALLET = {
39
43
  tempo: "0x0000000000000000000000000000000000000001",
40
44
  solana: "11111111111111111111111111111111"
41
45
  };
42
- async function simulateCryptoDeposit(input) {
43
- const url = `${input.stripeApiBase ?? "https://api.stripe.com"}/v1/test_helpers/payment_intents/${input.paymentIntentId}/simulate_crypto_deposit`;
46
+ async function simulateCryptoDeposit({
47
+ paymentIntentId,
48
+ network,
49
+ buyerWallet,
50
+ tokenCurrency,
51
+ transactionHash,
52
+ stripeSecretKey,
53
+ stripeVersion,
54
+ stripeApiBase,
55
+ extra
56
+ }) {
57
+ const url = `${stripeApiBase ?? "https://api.stripe.com"}/v1/test_helpers/payment_intents/${paymentIntentId}/simulate_crypto_deposit`;
44
58
  const params = new URLSearchParams({
45
- network: input.network,
46
- buyer_wallet: input.buyerWallet ?? DEFAULT_BUYER_WALLET[input.network] ?? ""
59
+ network,
60
+ buyer_wallet: buyerWallet ?? DEFAULT_BUYER_WALLET[network] ?? ""
47
61
  });
48
- if (input.tokenCurrency) params.set("token_currency", input.tokenCurrency);
49
- if (input.transactionHash) params.set("transaction_hash", input.transactionHash);
50
- for (const [k, v] of Object.entries(input.extra ?? {})) {
62
+ if (tokenCurrency) params.set("token_currency", tokenCurrency);
63
+ if (transactionHash) params.set("transaction_hash", transactionHash);
64
+ for (const [k, v] of Object.entries(extra ?? {})) {
51
65
  params.set(k, v);
52
66
  }
53
67
  const headers = {
54
- Authorization: `Bearer ${input.stripeSecretKey}`,
68
+ Authorization: `Bearer ${stripeSecretKey}`,
55
69
  "Content-Type": "application/x-www-form-urlencoded"
56
70
  };
57
- if (input.stripeVersion) headers["Stripe-Version"] = input.stripeVersion;
71
+ if (stripeVersion) headers["Stripe-Version"] = stripeVersion;
58
72
  const res = await fetch(url, { method: "POST", headers, body: params.toString() });
59
73
  if (!res.ok) {
60
74
  throw new Error(`Stripe simulate_crypto_deposit failed: ${res.status} ${await res.text()}`);
61
75
  }
62
76
  }
63
- async function simulateDepositIfTestMode(input) {
64
- if (!input.stripeSecretKey.startsWith("sk_test_")) return;
65
- const piId = input.getPaymentIntentId(input.depositAddress);
77
+ async function simulateDepositIfTestMode({
78
+ getPaymentIntentId,
79
+ depositAddress,
80
+ network,
81
+ buyerWallet,
82
+ tokenCurrency,
83
+ stripeSecretKey,
84
+ stripeVersion
85
+ }) {
86
+ if (!stripeSecretKey.startsWith("sk_test_")) return;
87
+ const piId = getPaymentIntentId(depositAddress);
66
88
  if (!piId) {
67
89
  console.warn(
68
- `[stripe] Skipping deposit simulation \u2014 no PI cached for deposit address ${input.depositAddress.slice(0, 10)}\u2026 (network=${input.network}). The PI cache TTL may have expired between 402 emission and settlement.`
90
+ `[stripe] Skipping deposit simulation \u2014 no PI cached for deposit address ${depositAddress.slice(0, 10)}\u2026 (network=${network}). The PI cache TTL may have expired between 402 emission and settlement.`
69
91
  );
70
92
  return;
71
93
  }
72
94
  try {
73
95
  await simulateCryptoDeposit({
74
96
  paymentIntentId: piId,
75
- network: input.network,
76
- ...input.buyerWallet !== void 0 && { buyerWallet: input.buyerWallet },
77
- tokenCurrency: input.tokenCurrency ?? "usdc",
97
+ network,
98
+ ...buyerWallet !== void 0 && { buyerWallet },
99
+ tokenCurrency: tokenCurrency ?? "usdc",
78
100
  transactionHash: STRIPE_TEST_TX_HASH_SUCCESS,
79
- stripeSecretKey: input.stripeSecretKey,
80
- ...input.stripeVersion !== void 0 && { stripeVersion: input.stripeVersion }
101
+ stripeSecretKey,
102
+ ...stripeVersion !== void 0 && { stripeVersion }
81
103
  });
82
- console.warn(`[stripe] \u2713 Simulated ${input.network} deposit for PI ${piId}`);
104
+ console.warn(`[stripe] \u2713 Simulated ${network} deposit for PI ${piId}`);
83
105
  } catch (err) {
84
106
  console.error(
85
- `[stripe] \u2717 Failed to simulate ${input.network} deposit for PI ${piId}:`,
107
+ `[stripe] \u2717 Failed to simulate ${network} deposit for PI ${piId}:`,
86
108
  err instanceof Error ? err.message : err
87
109
  );
88
110
  }
89
111
  }
90
112
 
91
113
  // src/stripe-multichain/mppx_stripe.ts
92
- async function createMppxStripe(input) {
114
+ async function createMppxStripe({
115
+ profileId,
116
+ secretKey,
117
+ paymentMethodTypes
118
+ }) {
93
119
  const moduleName = "mppx/server";
94
120
  const mppx = await import(moduleName).catch(() => null);
95
121
  if (!mppx?.stripe?.charge) {
@@ -98,16 +124,18 @@ async function createMppxStripe(input) {
98
124
  );
99
125
  }
100
126
  return mppx.stripe.charge({
101
- networkId: input.profileId,
102
- paymentMethodTypes: input.paymentMethodTypes ?? ["card", "link"],
103
- secretKey: input.secretKey
127
+ networkId: profileId,
128
+ paymentMethodTypes: paymentMethodTypes ?? ["card", "link"],
129
+ secretKey
104
130
  });
105
131
  }
106
132
 
107
133
  // src/stripe-multichain/pi-cache.ts
108
- function createPiCache(opts = {}) {
109
- const ttlSeconds = opts.ttlSeconds ?? 300;
110
- const keyPrefix = opts.keyPrefix ?? "payto:";
134
+ function createPiCache({
135
+ redisUrl,
136
+ ttlSeconds = 300,
137
+ keyPrefix = "payto:"
138
+ } = {}) {
111
139
  let redis = null;
112
140
  const addrMemCache = /* @__PURE__ */ new Map();
113
141
  const piCache = /* @__PURE__ */ new Map();
@@ -126,17 +154,17 @@ function createPiCache(opts = {}) {
126
154
  }, 6e4);
127
155
  if (typeof evict.unref === "function") evict.unref();
128
156
  async function getRedis() {
129
- if (!opts.redisUrl) return null;
157
+ if (!redisUrl) return null;
130
158
  if (redis) return redis;
131
159
  const mod = await import("ioredis").catch(() => null);
132
160
  if (!mod) {
133
161
  console.error("[pi-cache] redisUrl set but `ioredis` is not installed. Run `npm install ioredis` or unset redisUrl.");
134
162
  return null;
135
163
  }
136
- redis = new mod.default(opts.redisUrl, {
164
+ redis = new mod.default(redisUrl, {
137
165
  connectTimeout: 5e3,
138
166
  maxRetriesPerRequest: 1,
139
- tls: opts.redisUrl.startsWith("rediss://") ? {} : void 0
167
+ tls: redisUrl.startsWith("rediss://") ? {} : void 0
140
168
  });
141
169
  redis.on("error", (err) => console.error("[pi-cache] Redis error:", err.message));
142
170
  return redis;
@@ -192,7 +220,6 @@ export {
192
220
  createMppxStripe,
193
221
  createMultichainPaymentIntent,
194
222
  createPiCache,
195
- getDepositAddress,
196
223
  simulateCryptoDeposit,
197
224
  simulateDepositIfTestMode
198
225
  };