@agent-score/commerce 2.0.1 → 2.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.
Files changed (116) hide show
  1. package/README.md +26 -11
  2. package/dist/_core-kI7FRAiZ.d.mts +10 -0
  3. package/dist/_core-kI7FRAiZ.d.ts +10 -0
  4. package/dist/challenge/index.d.mts +3 -3
  5. package/dist/challenge/index.d.ts +3 -3
  6. package/dist/challenge/index.js +21 -14
  7. package/dist/challenge/index.js.map +1 -1
  8. package/dist/challenge/index.mjs +21 -14
  9. package/dist/challenge/index.mjs.map +1 -1
  10. package/dist/{checkout-B1JuEcbx.d.ts → checkout-BH-I_Ns8.d.ts} +19 -12
  11. package/dist/{checkout-BN5i1Fi7.d.mts → checkout-Bd_4aQ6c.d.mts} +19 -12
  12. package/dist/core.js +1 -1
  13. package/dist/core.js.map +1 -1
  14. package/dist/core.mjs +1 -1
  15. package/dist/core.mjs.map +1 -1
  16. package/dist/default_rails-BWAquZeu.d.mts +188 -0
  17. package/dist/default_rails-BxBzcCA1.d.ts +188 -0
  18. package/dist/discovery/index.d.mts +5 -5
  19. package/dist/discovery/index.d.ts +5 -5
  20. package/dist/discovery/index.js +14 -1
  21. package/dist/discovery/index.js.map +1 -1
  22. package/dist/discovery/index.mjs +14 -1
  23. package/dist/discovery/index.mjs.map +1 -1
  24. package/dist/identity/express.d.mts +7 -3
  25. package/dist/identity/express.d.ts +7 -3
  26. package/dist/identity/express.js +39 -96
  27. package/dist/identity/express.js.map +1 -1
  28. package/dist/identity/express.mjs +37 -87
  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 +60 -96
  33. package/dist/identity/fastify.js.map +1 -1
  34. package/dist/identity/fastify.mjs +58 -87
  35. package/dist/identity/fastify.mjs.map +1 -1
  36. package/dist/identity/hono.d.mts +11 -3
  37. package/dist/identity/hono.d.ts +11 -3
  38. package/dist/identity/hono.js +39 -93
  39. package/dist/identity/hono.js.map +1 -1
  40. package/dist/identity/hono.mjs +37 -84
  41. package/dist/identity/hono.mjs.map +1 -1
  42. package/dist/identity/nextjs.d.mts +10 -3
  43. package/dist/identity/nextjs.d.ts +10 -3
  44. package/dist/identity/nextjs.js +49 -93
  45. package/dist/identity/nextjs.js.map +1 -1
  46. package/dist/identity/nextjs.mjs +46 -84
  47. package/dist/identity/nextjs.mjs.map +1 -1
  48. package/dist/identity/policy.js +220 -129
  49. package/dist/identity/policy.js.map +1 -1
  50. package/dist/identity/policy.mjs +222 -131
  51. package/dist/identity/policy.mjs.map +1 -1
  52. package/dist/identity/web.d.mts +9 -3
  53. package/dist/identity/web.d.ts +9 -3
  54. package/dist/identity/web.js +45 -93
  55. package/dist/identity/web.js.map +1 -1
  56. package/dist/identity/web.mjs +42 -84
  57. package/dist/identity/web.mjs.map +1 -1
  58. package/dist/index.d.mts +554 -90
  59. package/dist/index.d.ts +554 -90
  60. package/dist/index.js +951 -152
  61. package/dist/index.js.map +1 -1
  62. package/dist/index.mjs +939 -152
  63. package/dist/index.mjs.map +1 -1
  64. package/dist/middleware/express.d.mts +10 -0
  65. package/dist/middleware/express.d.ts +10 -0
  66. package/dist/middleware/express.js +128 -0
  67. package/dist/middleware/express.js.map +1 -0
  68. package/dist/middleware/express.mjs +91 -0
  69. package/dist/middleware/express.mjs.map +1 -0
  70. package/dist/middleware/fastify.d.mts +10 -0
  71. package/dist/middleware/fastify.d.ts +10 -0
  72. package/dist/middleware/fastify.js +127 -0
  73. package/dist/middleware/fastify.js.map +1 -0
  74. package/dist/middleware/fastify.mjs +90 -0
  75. package/dist/middleware/fastify.mjs.map +1 -0
  76. package/dist/middleware/hono.d.mts +10 -0
  77. package/dist/middleware/hono.d.ts +10 -0
  78. package/dist/middleware/hono.js +122 -0
  79. package/dist/middleware/hono.js.map +1 -0
  80. package/dist/middleware/hono.mjs +85 -0
  81. package/dist/middleware/hono.mjs.map +1 -0
  82. package/dist/middleware/nextjs.d.mts +22 -0
  83. package/dist/middleware/nextjs.d.ts +22 -0
  84. package/dist/middleware/nextjs.js +143 -0
  85. package/dist/middleware/nextjs.js.map +1 -0
  86. package/dist/middleware/nextjs.mjs +105 -0
  87. package/dist/middleware/nextjs.mjs.map +1 -0
  88. package/dist/middleware/web.d.mts +25 -0
  89. package/dist/middleware/web.d.ts +25 -0
  90. package/dist/middleware/web.js +128 -0
  91. package/dist/middleware/web.js.map +1 -0
  92. package/dist/middleware/web.mjs +91 -0
  93. package/dist/middleware/web.mjs.map +1 -0
  94. package/dist/payment/index.d.mts +21 -6
  95. package/dist/payment/index.d.ts +21 -6
  96. package/dist/payment/index.js +136 -9
  97. package/dist/payment/index.js.map +1 -1
  98. package/dist/payment/index.mjs +127 -9
  99. package/dist/payment/index.mjs.map +1 -1
  100. package/dist/{pricing-CxzwyiO6.d.mts → pricing-4n5Ota0D.d.mts} +14 -4
  101. package/dist/{pricing-CQ9DIFaw.d.ts → pricing-DHfH3ogG.d.ts} +14 -4
  102. package/dist/{rail_spec-XP0wKgJV.d.mts → rail_spec-D6qzh3J0.d.mts} +1 -1
  103. package/dist/{rail_spec-XP0wKgJV.d.ts → rail_spec-D6qzh3J0.d.ts} +1 -1
  104. package/dist/stripe-multichain/index.d.mts +150 -47
  105. package/dist/stripe-multichain/index.d.ts +150 -47
  106. package/dist/stripe-multichain/index.js +19749 -42
  107. package/dist/stripe-multichain/index.js.map +1 -1
  108. package/dist/stripe-multichain/index.mjs +19758 -27
  109. package/dist/stripe-multichain/index.mjs.map +1 -1
  110. package/dist/{x402_server-hgQzWQwB.d.mts → x402_server-Ciz2mls2.d.mts} +1 -1
  111. package/dist/{x402_server-hgQzWQwB.d.ts → x402_server-Ciz2mls2.d.ts} +1 -1
  112. package/package.json +43 -5
  113. package/dist/_response-BFYN3b6i.d.mts +0 -142
  114. package/dist/_response-_iPD5AIj.d.ts +0 -142
  115. package/dist/solana-Cds87OTu.d.mts +0 -67
  116. package/dist/solana-Cds87OTu.d.ts +0 -67
@@ -1,4 +1,4 @@
1
- import { T as TempoRailSpec, X as X402BaseRailSpec, S as SolanaMppRailSpec, b as StripeRailSpec } from './rail_spec-XP0wKgJV.mjs';
1
+ import { T as TempoRailSpec, X as X402BaseRailSpec, S as SolanaMppRailSpec, b as StripeRailSpec } from './rail_spec-D6qzh3J0.mjs';
2
2
 
3
3
  interface HowToPayRailEntry {
4
4
  setup?: string[];
@@ -34,7 +34,7 @@ interface HowToPayRails {
34
34
  *
35
35
  * Tool recommendations (tempo CLI vs agentscore-pay vs link-cli) are configurable per rail.
36
36
  */
37
- declare function buildHowToPay({ url, retryBodyJson, totalUsd, rails, opTokenPlaceholder, maxSpend, }: {
37
+ declare function buildHowToPay({ url, retryBodyJson, totalUsd, rails, opTokenPlaceholder, maxSpend, decimals, }: {
38
38
  /** The merchant's full URL (e.g., 'https://agents.merchant.example/api/buy'). */
39
39
  url: string;
40
40
  /** JSON string of the body the agent should retry with — typically the original request body. */
@@ -45,8 +45,13 @@ declare function buildHowToPay({ url, retryBodyJson, totalUsd, rails, opTokenPla
45
45
  rails: HowToPayRails;
46
46
  /** Placeholder text for the operator token in commands. Defaults to '<your_opc_token>'. */
47
47
  opTokenPlaceholder?: string;
48
- /** Override max-spend value used in commands. Default: ceil(totalUsd) + 1. */
48
+ /** Override max-spend value used in commands. Default: `ceil(totalUsd) + 1`
49
+ * (for prices ≥ $1) or `totalUsd.toFixed(decimals)` (for sub-dollar prices,
50
+ * so the command flags reflect the real amount instead of `1.00`). */
49
51
  maxSpend?: string | number;
52
+ /** Fractional digits when formatting the auto-derived `maxSpend` for
53
+ * sub-dollar / sub-cent prices. Default `2`. */
54
+ decimals?: number;
50
55
  }): HowToPayBlock;
51
56
 
52
57
  /** Map of rail key (e.g. 'x402_base', 'tempo_mpp', 'stripe') → list of client identifiers
@@ -172,7 +177,7 @@ interface PricingBlock {
172
177
  * omit entirely if you don't want shipping in the response shape at all. Total floors at 0
173
178
  * when discount exceeds subtotal + tax + shipping.
174
179
  */
175
- declare function buildPricingBlock({ subtotalCents, taxCents, shippingCents, discountCents, totalCents, taxRate, taxState, currency, }: {
180
+ declare function buildPricingBlock({ subtotalCents, taxCents, shippingCents, discountCents, totalCents, taxRate, taxState, currency, decimals, }: {
176
181
  subtotalCents: number;
177
182
  taxCents?: number;
178
183
  shippingCents?: number;
@@ -181,6 +186,11 @@ declare function buildPricingBlock({ subtotalCents, taxCents, shippingCents, dis
181
186
  taxRate?: number;
182
187
  taxState?: string;
183
188
  currency?: string;
189
+ /** Dollar-precision for every emitted money field (default `2`). Raise for
190
+ * sub-cent unit pricing so `subtotal` / `total` show the real amount
191
+ * instead of rounding to two decimals. Subtotal/tax/total inputs become
192
+ * fractional cents under this mode. */
193
+ decimals?: number;
184
194
  }): PricingBlock;
185
195
 
186
196
  export { type AgentInstructions as A, type CompatibleClients as C, type HowToPayBlock as H, type PricingBlock as P, type RailKey as R, type HowToPayRailEntry as a, type HowToPayRails as b, type HowToPayStripeEntry as c, buildAgentInstructions as d, buildHowToPay as e, buildPricingBlock as f, compatibleClientsByRails as g };
@@ -1,4 +1,4 @@
1
- import { T as TempoRailSpec, X as X402BaseRailSpec, S as SolanaMppRailSpec, b as StripeRailSpec } from './rail_spec-XP0wKgJV.js';
1
+ import { T as TempoRailSpec, X as X402BaseRailSpec, S as SolanaMppRailSpec, b as StripeRailSpec } from './rail_spec-D6qzh3J0.js';
2
2
 
3
3
  interface HowToPayRailEntry {
4
4
  setup?: string[];
@@ -34,7 +34,7 @@ interface HowToPayRails {
34
34
  *
35
35
  * Tool recommendations (tempo CLI vs agentscore-pay vs link-cli) are configurable per rail.
36
36
  */
37
- declare function buildHowToPay({ url, retryBodyJson, totalUsd, rails, opTokenPlaceholder, maxSpend, }: {
37
+ declare function buildHowToPay({ url, retryBodyJson, totalUsd, rails, opTokenPlaceholder, maxSpend, decimals, }: {
38
38
  /** The merchant's full URL (e.g., 'https://agents.merchant.example/api/buy'). */
39
39
  url: string;
40
40
  /** JSON string of the body the agent should retry with — typically the original request body. */
@@ -45,8 +45,13 @@ declare function buildHowToPay({ url, retryBodyJson, totalUsd, rails, opTokenPla
45
45
  rails: HowToPayRails;
46
46
  /** Placeholder text for the operator token in commands. Defaults to '<your_opc_token>'. */
47
47
  opTokenPlaceholder?: string;
48
- /** Override max-spend value used in commands. Default: ceil(totalUsd) + 1. */
48
+ /** Override max-spend value used in commands. Default: `ceil(totalUsd) + 1`
49
+ * (for prices ≥ $1) or `totalUsd.toFixed(decimals)` (for sub-dollar prices,
50
+ * so the command flags reflect the real amount instead of `1.00`). */
49
51
  maxSpend?: string | number;
52
+ /** Fractional digits when formatting the auto-derived `maxSpend` for
53
+ * sub-dollar / sub-cent prices. Default `2`. */
54
+ decimals?: number;
50
55
  }): HowToPayBlock;
51
56
 
52
57
  /** Map of rail key (e.g. 'x402_base', 'tempo_mpp', 'stripe') → list of client identifiers
@@ -172,7 +177,7 @@ interface PricingBlock {
172
177
  * omit entirely if you don't want shipping in the response shape at all. Total floors at 0
173
178
  * when discount exceeds subtotal + tax + shipping.
174
179
  */
175
- declare function buildPricingBlock({ subtotalCents, taxCents, shippingCents, discountCents, totalCents, taxRate, taxState, currency, }: {
180
+ declare function buildPricingBlock({ subtotalCents, taxCents, shippingCents, discountCents, totalCents, taxRate, taxState, currency, decimals, }: {
176
181
  subtotalCents: number;
177
182
  taxCents?: number;
178
183
  shippingCents?: number;
@@ -181,6 +186,11 @@ declare function buildPricingBlock({ subtotalCents, taxCents, shippingCents, dis
181
186
  taxRate?: number;
182
187
  taxState?: string;
183
188
  currency?: string;
189
+ /** Dollar-precision for every emitted money field (default `2`). Raise for
190
+ * sub-cent unit pricing so `subtotal` / `total` show the real amount
191
+ * instead of rounding to two decimals. Subtotal/tax/total inputs become
192
+ * fractional cents under this mode. */
193
+ decimals?: number;
184
194
  }): PricingBlock;
185
195
 
186
196
  export { type AgentInstructions as A, type CompatibleClients as C, type HowToPayBlock as H, type PricingBlock as P, type RailKey as R, type HowToPayRailEntry as a, type HowToPayRails as b, type HowToPayStripeEntry as c, buildAgentInstructions as d, buildHowToPay as e, buildPricingBlock as f, compatibleClientsByRails as g };
@@ -129,4 +129,4 @@ declare const RAIL_SPEC_DEFAULTS: {
129
129
  };
130
130
  };
131
131
 
132
- export { RAIL_SPEC_DEFAULTS as R, type SolanaMppRailSpec as S, type TempoRailSpec as T, type X402BaseRailSpec as X, type TempoSessionRailSpec as a, type StripeRailSpec as b, type RecipientLike as c, resolveRecipient as r };
132
+ export { RAIL_SPEC_DEFAULTS as R, type SolanaMppRailSpec as S, type TempoRailSpec as T, type X402BaseRailSpec as X, type RecipientLike as a, type StripeRailSpec as b, type TempoSessionRailSpec as c, resolveRecipient as r };
@@ -129,4 +129,4 @@ declare const RAIL_SPEC_DEFAULTS: {
129
129
  };
130
130
  };
131
131
 
132
- export { RAIL_SPEC_DEFAULTS as R, type SolanaMppRailSpec as S, type TempoRailSpec as T, type X402BaseRailSpec as X, type TempoSessionRailSpec as a, type StripeRailSpec as b, type RecipientLike as c, resolveRecipient as r };
132
+ export { RAIL_SPEC_DEFAULTS as R, type SolanaMppRailSpec as S, type TempoRailSpec as T, type X402BaseRailSpec as X, type RecipientLike as a, type StripeRailSpec as b, type TempoSessionRailSpec as c, resolveRecipient as r };
@@ -52,6 +52,106 @@ declare function createMultichainPaymentIntent({ stripe, amount, currency, netwo
52
52
  idempotencyKey?: string;
53
53
  }): Promise<MultichainPaymentIntentResult>;
54
54
 
55
+ /**
56
+ * Stripe PaymentIntent + deposit-address cache.
57
+ *
58
+ * Stripe-multichain merchants need three lookups during a request lifecycle:
59
+ *
60
+ * 1. **Is this on-chain `pay_to` address one we minted?** — when an MPP credential
61
+ * arrives with a `recipient`, verify it matches a recently-minted Stripe deposit
62
+ * address. Validates the credential's deposit address against the addresses the
63
+ * merchant has actually minted.
64
+ *
65
+ * 2. **Which PaymentIntent owns this deposit address?** — when settling, the
66
+ * `simulate_crypto_deposit` test_helpers call needs the PaymentIntent id for the
67
+ * deposit address that was paid to.
68
+ *
69
+ * 3. **Which sibling deposit addresses belong to the same PaymentIntent?** — when
70
+ * enriching a 402 with x402 entries, the merchant needs the Base + Solana addresses
71
+ * Stripe minted alongside the original Tempo address (one PI carries up to three).
72
+ *
73
+ * All three are TTL-bounded (default 300s — long enough for an agent to retry, short
74
+ * enough to bound memory). Backed by Redis when `redisUrl` is set, falls back to
75
+ * in-process Map otherwise. Single-instance servers can use the in-memory cache;
76
+ * multi-instance deployments need a shared cache (Redis) so a deposit lands on
77
+ * whichever instance settles it.
78
+ */
79
+ interface PiCache {
80
+ /** Mark an on-chain address as one this merchant minted. Idempotent + TTL-bounded. */
81
+ cacheAddress(address: string): Promise<void>;
82
+ /** Return true when the address was minted by this merchant within TTL. */
83
+ hasAddress(address: string): Promise<boolean>;
84
+ /** Associate an on-chain deposit address with the Stripe PaymentIntent that minted it. */
85
+ cachePaymentIntent(depositAddress: string, paymentIntentId: string): void;
86
+ /** Get the Stripe PaymentIntent id for a previously-minted deposit address, or undefined. */
87
+ getPaymentIntentId(depositAddress: string): string | undefined;
88
+ /** Associate a PaymentIntent id with the full set of sibling deposit addresses (one per network). */
89
+ cacheNetworkAddresses(paymentIntentId: string, addresses: Record<string, string>): void;
90
+ /** Look up the deposit address Stripe minted on a specific network for a given PaymentIntent. */
91
+ getNetworkDepositAddress(paymentIntentId: string, network: string): string | undefined;
92
+ /** Stop the background TTL-eviction loop. Call from server shutdown handlers. */
93
+ stop(): void;
94
+ }
95
+ declare function createPiCache({ redisUrl, ttlSeconds, keyPrefix, }?: {
96
+ /** Redis connection URL (e.g. `rediss://…cache.amazonaws.com:6379`). When omitted,
97
+ * the cache falls back to in-process Maps with the same API. */
98
+ redisUrl?: string;
99
+ /** TTL for cached entries in seconds. Default 300. */
100
+ ttlSeconds?: number;
101
+ /** Prefix for Redis keys. Default `'payto:'`. */
102
+ keyPrefix?: string;
103
+ }): PiCache;
104
+
105
+ /**
106
+ * Per-order Stripe-multichain `pay_to` resolver.
107
+ *
108
+ * Stripe-multichain merchants need ONE function for their `mintRecipients`
109
+ * (or per-request payTo) hook that does the right thing on both legs:
110
+ *
111
+ * - **Discovery leg** (no payment header): mint a fresh PaymentIntent so
112
+ * the 402 advertises a stable per-order deposit address.
113
+ * - **Settle leg** (MPP credential attached): reuse the buyer's
114
+ * signed-against payTo from the credential (after verifying it's in the
115
+ * local cache) — otherwise the verify leg would compare against a
116
+ * freshly-rotated address and reject the credential.
117
+ *
118
+ * Stripe SPT and card methods don't carry an on-chain recipient, so the
119
+ * settle leg still mints a fresh PaymentIntent for them.
120
+ *
121
+ * Mirrors the hand-rolled `createPayToAddress` block consumers wrote in
122
+ * `lib/payment.ts`. Lifts the structural branching; merchants keep
123
+ * env-driven config (network list, default network, metadata) at the call site.
124
+ */
125
+
126
+ interface CreatePayToAddressFromStripePIOptions {
127
+ /** Inbound HTTP request — header is read for an Authorization Payment credential. */
128
+ request: Request;
129
+ /** Order amount in cents. */
130
+ amountCents: number;
131
+ /** Configured Stripe client (peer dep). */
132
+ stripe: StripeClientLike;
133
+ /** Cache backing the merchant's minted addresses + PI lookups. */
134
+ piCache: PiCache;
135
+ /** Networks to advertise to Stripe `deposit_options`. Default ['tempo', 'base', 'solana']. */
136
+ networks?: string[];
137
+ /** Optional Stripe metadata. */
138
+ metadata?: Record<string, string>;
139
+ /** Pending-order id; used as the Stripe idempotency key seed so retries
140
+ * of the same order don't mint duplicate PaymentIntents. */
141
+ orderId?: string;
142
+ /** Preferred network for the returned address when minting fresh. Default
143
+ * picks `tempo`, falls back to `base`. */
144
+ preferredNetwork?: string;
145
+ }
146
+ /** Returns the on-chain `pay_to` address the agent should be told to pay
147
+ * (in the 402 challenge or the bound MPP credential).
148
+ *
149
+ * On the settle leg, when the inbound `Authorization: Payment` credential
150
+ * binds a `tempo` or `solana` recipient, the helper returns THAT address
151
+ * (after verifying it's still in `piCache`). Otherwise it mints a fresh
152
+ * `createMultichainPaymentIntent` and caches the addresses + PI mapping. */
153
+ declare function createPayToAddressFromStripePI(opts: CreatePayToAddressFromStripePIOptions): Promise<string>;
154
+
55
155
  /**
56
156
  * Stripe's documented magic test_helpers transaction hash that resolves the
57
157
  * PaymentIntent to `succeeded` within 15 seconds. Same value across all networks —
@@ -157,54 +257,57 @@ declare function createMppxStripe({ profileId, secretKey, paymentMethodTypes, }:
157
257
  paymentMethodTypes?: string[];
158
258
  }): Promise<unknown>;
159
259
 
160
- /**
161
- * Stripe PaymentIntent + deposit-address cache.
162
- *
163
- * Stripe-multichain merchants need three lookups during a request lifecycle:
260
+ /** Settle-outcome → simulator dispatch. Replaces the 3-branch rail/railKey
261
+ * switch + thin `simulateDepositIfTestnet(addr, network)` wrapper that
262
+ * consumer codebases (sayer, martin, sandbox) hand-rolled in their own
263
+ * `lib/payment.ts` files.
164
264
  *
165
- * 1. **Is this on-chain `pay_to` address one we minted?** — when an MPP credential
166
- * arrives with a `recipient`, verify it matches a recently-minted Stripe deposit
167
- * address. Validates the credential's deposit address against the addresses the
168
- * merchant has actually minted.
169
- *
170
- * 2. **Which PaymentIntent owns this deposit address?** — when settling, the
171
- * `simulate_crypto_deposit` test_helpers call needs the PaymentIntent id for the
172
- * deposit address that was paid to.
173
- *
174
- * 3. **Which sibling deposit addresses belong to the same PaymentIntent?** — when
175
- * enriching a 402 with x402 entries, the merchant needs the Base + Solana addresses
176
- * Stripe minted alongside the original Tempo address (one PI carries up to three).
177
- *
178
- * All three are TTL-bounded (default 300s — long enough for an agent to retry, short
179
- * enough to bound memory). Backed by Redis when `redisUrl` is set, falls back to
180
- * in-process Map otherwise. Single-instance servers can use the in-memory cache;
181
- * multi-instance deployments need a shared cache (Redis) so a deposit lands on
182
- * whichever instance settles it.
265
+ * Folding the dispatch into the SDK removes the consumer-wrap anti-pattern
266
+ * (`feedback_no_consumer_sdk_rewrapping`): merchants call this directly from
267
+ * `onSettled`, no per-merchant payment.ts wrapper needed.
183
268
  */
184
- interface PiCache {
185
- /** Mark an on-chain address as one this merchant minted. Idempotent + TTL-bounded. */
186
- cacheAddress(address: string): Promise<void>;
187
- /** Return true when the address was minted by this merchant within TTL. */
188
- hasAddress(address: string): Promise<boolean>;
189
- /** Associate an on-chain deposit address with the Stripe PaymentIntent that minted it. */
190
- cachePaymentIntent(depositAddress: string, paymentIntentId: string): void;
191
- /** Get the Stripe PaymentIntent id for a previously-minted deposit address, or undefined. */
192
- getPaymentIntentId(depositAddress: string): string | undefined;
193
- /** Associate a PaymentIntent id with the full set of sibling deposit addresses (one per network). */
194
- cacheNetworkAddresses(paymentIntentId: string, addresses: Record<string, string>): void;
195
- /** Look up the deposit address Stripe minted on a specific network for a given PaymentIntent. */
196
- getNetworkDepositAddress(paymentIntentId: string, network: string): string | undefined;
197
- /** Stop the background TTL-eviction loop. Call from server shutdown handlers. */
198
- stop(): void;
269
+ type SimulateNetwork = 'tempo' | 'base' | 'solana';
270
+ /** Map a settle outcome (from `Checkout.onSettled` or
271
+ * `computeFirstCheckout.onSettled`) to the `network` arg consumed by
272
+ * `simulateDepositIfTestMode`. Returns null for:
273
+ * - Stripe SPT (no on-chain deposit)
274
+ * - rails we don't recognize
275
+ *
276
+ * Accepts both Checkout-shaped outcomes (`rail` + `railKey`) and
277
+ * computeFirstCheckout-shaped outcomes (`rail` + `mppMethod`). The settle
278
+ * outcomes diverged historically; this helper canonicalizes them. */
279
+ declare function networkForOutcome(outcome: {
280
+ rail?: string;
281
+ railKey?: string;
282
+ mppMethod?: string;
283
+ }): SimulateNetwork | null;
284
+ interface SimulateDepositForOutcomeOptions {
285
+ /** The settle outcome handed to `onSettled`. Reads `rail` / `railKey` /
286
+ * `mppMethod` to pick the network arg. */
287
+ outcome: {
288
+ rail?: string;
289
+ railKey?: string;
290
+ mppMethod?: string;
291
+ };
292
+ /** On-chain deposit address that was paid to. For Stripe-multichain
293
+ * merchants this is the per-order minted address; for static-recipient
294
+ * merchants it's the merchant treasury. */
295
+ depositAddress: string;
296
+ /** PI-id resolver. Typically `piCache.getPaymentIntentId` from
297
+ * `createPiCache(...)`. */
298
+ getPaymentIntentId: (depositAddress: string) => string | undefined;
299
+ /** Stripe secret key. The wrapper checks `sk_test_` and no-ops on live. */
300
+ stripeSecretKey: string;
301
+ /** Stripe API version (defaults to the deposit-mode preview). */
302
+ stripeVersion?: string;
303
+ /** Optional simulated buyer wallet. */
304
+ buyerWallet?: string;
199
305
  }
200
- declare function createPiCache({ redisUrl, ttlSeconds, keyPrefix, }?: {
201
- /** Redis connection URL (e.g. `rediss://…cache.amazonaws.com:6379`). When omitted,
202
- * the cache falls back to in-process Maps with the same API. */
203
- redisUrl?: string;
204
- /** TTL for cached entries in seconds. Default 300. */
205
- ttlSeconds?: number;
206
- /** Prefix for Redis keys. Default `'payto:'`. */
207
- keyPrefix?: string;
208
- }): PiCache;
306
+ /** Dispatch `simulateDepositIfTestMode` based on the outcome's rail. Calls
307
+ * through to the SDK simulator; no-op for Stripe SPT or unknown rails.
308
+ *
309
+ * Use this in `onSettled` to replace the hand-rolled rail switch +
310
+ * `simulateDepositIfTestnet` wrapper pattern. */
311
+ declare function simulateDepositForOutcome(opts: SimulateDepositForOutcomeOptions): Promise<void>;
209
312
 
210
- export { type MultichainPaymentIntentResult, type PiCache, STRIPE_TEST_TX_HASH_FAILED, STRIPE_TEST_TX_HASH_SUCCESS, type StripeClientLike, type StripePaymentIntent, createMppxStripe, createMultichainPaymentIntent, createPiCache, simulateCryptoDeposit, simulateDepositIfTestMode };
313
+ export { type CreatePayToAddressFromStripePIOptions, type MultichainPaymentIntentResult, type PiCache, STRIPE_TEST_TX_HASH_FAILED, STRIPE_TEST_TX_HASH_SUCCESS, type SimulateDepositForOutcomeOptions, type SimulateNetwork, type StripeClientLike, type StripePaymentIntent, createMppxStripe, createMultichainPaymentIntent, createPayToAddressFromStripePI, createPiCache, networkForOutcome, simulateCryptoDeposit, simulateDepositForOutcome, simulateDepositIfTestMode };
@@ -52,6 +52,106 @@ declare function createMultichainPaymentIntent({ stripe, amount, currency, netwo
52
52
  idempotencyKey?: string;
53
53
  }): Promise<MultichainPaymentIntentResult>;
54
54
 
55
+ /**
56
+ * Stripe PaymentIntent + deposit-address cache.
57
+ *
58
+ * Stripe-multichain merchants need three lookups during a request lifecycle:
59
+ *
60
+ * 1. **Is this on-chain `pay_to` address one we minted?** — when an MPP credential
61
+ * arrives with a `recipient`, verify it matches a recently-minted Stripe deposit
62
+ * address. Validates the credential's deposit address against the addresses the
63
+ * merchant has actually minted.
64
+ *
65
+ * 2. **Which PaymentIntent owns this deposit address?** — when settling, the
66
+ * `simulate_crypto_deposit` test_helpers call needs the PaymentIntent id for the
67
+ * deposit address that was paid to.
68
+ *
69
+ * 3. **Which sibling deposit addresses belong to the same PaymentIntent?** — when
70
+ * enriching a 402 with x402 entries, the merchant needs the Base + Solana addresses
71
+ * Stripe minted alongside the original Tempo address (one PI carries up to three).
72
+ *
73
+ * All three are TTL-bounded (default 300s — long enough for an agent to retry, short
74
+ * enough to bound memory). Backed by Redis when `redisUrl` is set, falls back to
75
+ * in-process Map otherwise. Single-instance servers can use the in-memory cache;
76
+ * multi-instance deployments need a shared cache (Redis) so a deposit lands on
77
+ * whichever instance settles it.
78
+ */
79
+ interface PiCache {
80
+ /** Mark an on-chain address as one this merchant minted. Idempotent + TTL-bounded. */
81
+ cacheAddress(address: string): Promise<void>;
82
+ /** Return true when the address was minted by this merchant within TTL. */
83
+ hasAddress(address: string): Promise<boolean>;
84
+ /** Associate an on-chain deposit address with the Stripe PaymentIntent that minted it. */
85
+ cachePaymentIntent(depositAddress: string, paymentIntentId: string): void;
86
+ /** Get the Stripe PaymentIntent id for a previously-minted deposit address, or undefined. */
87
+ getPaymentIntentId(depositAddress: string): string | undefined;
88
+ /** Associate a PaymentIntent id with the full set of sibling deposit addresses (one per network). */
89
+ cacheNetworkAddresses(paymentIntentId: string, addresses: Record<string, string>): void;
90
+ /** Look up the deposit address Stripe minted on a specific network for a given PaymentIntent. */
91
+ getNetworkDepositAddress(paymentIntentId: string, network: string): string | undefined;
92
+ /** Stop the background TTL-eviction loop. Call from server shutdown handlers. */
93
+ stop(): void;
94
+ }
95
+ declare function createPiCache({ redisUrl, ttlSeconds, keyPrefix, }?: {
96
+ /** Redis connection URL (e.g. `rediss://…cache.amazonaws.com:6379`). When omitted,
97
+ * the cache falls back to in-process Maps with the same API. */
98
+ redisUrl?: string;
99
+ /** TTL for cached entries in seconds. Default 300. */
100
+ ttlSeconds?: number;
101
+ /** Prefix for Redis keys. Default `'payto:'`. */
102
+ keyPrefix?: string;
103
+ }): PiCache;
104
+
105
+ /**
106
+ * Per-order Stripe-multichain `pay_to` resolver.
107
+ *
108
+ * Stripe-multichain merchants need ONE function for their `mintRecipients`
109
+ * (or per-request payTo) hook that does the right thing on both legs:
110
+ *
111
+ * - **Discovery leg** (no payment header): mint a fresh PaymentIntent so
112
+ * the 402 advertises a stable per-order deposit address.
113
+ * - **Settle leg** (MPP credential attached): reuse the buyer's
114
+ * signed-against payTo from the credential (after verifying it's in the
115
+ * local cache) — otherwise the verify leg would compare against a
116
+ * freshly-rotated address and reject the credential.
117
+ *
118
+ * Stripe SPT and card methods don't carry an on-chain recipient, so the
119
+ * settle leg still mints a fresh PaymentIntent for them.
120
+ *
121
+ * Mirrors the hand-rolled `createPayToAddress` block consumers wrote in
122
+ * `lib/payment.ts`. Lifts the structural branching; merchants keep
123
+ * env-driven config (network list, default network, metadata) at the call site.
124
+ */
125
+
126
+ interface CreatePayToAddressFromStripePIOptions {
127
+ /** Inbound HTTP request — header is read for an Authorization Payment credential. */
128
+ request: Request;
129
+ /** Order amount in cents. */
130
+ amountCents: number;
131
+ /** Configured Stripe client (peer dep). */
132
+ stripe: StripeClientLike;
133
+ /** Cache backing the merchant's minted addresses + PI lookups. */
134
+ piCache: PiCache;
135
+ /** Networks to advertise to Stripe `deposit_options`. Default ['tempo', 'base', 'solana']. */
136
+ networks?: string[];
137
+ /** Optional Stripe metadata. */
138
+ metadata?: Record<string, string>;
139
+ /** Pending-order id; used as the Stripe idempotency key seed so retries
140
+ * of the same order don't mint duplicate PaymentIntents. */
141
+ orderId?: string;
142
+ /** Preferred network for the returned address when minting fresh. Default
143
+ * picks `tempo`, falls back to `base`. */
144
+ preferredNetwork?: string;
145
+ }
146
+ /** Returns the on-chain `pay_to` address the agent should be told to pay
147
+ * (in the 402 challenge or the bound MPP credential).
148
+ *
149
+ * On the settle leg, when the inbound `Authorization: Payment` credential
150
+ * binds a `tempo` or `solana` recipient, the helper returns THAT address
151
+ * (after verifying it's still in `piCache`). Otherwise it mints a fresh
152
+ * `createMultichainPaymentIntent` and caches the addresses + PI mapping. */
153
+ declare function createPayToAddressFromStripePI(opts: CreatePayToAddressFromStripePIOptions): Promise<string>;
154
+
55
155
  /**
56
156
  * Stripe's documented magic test_helpers transaction hash that resolves the
57
157
  * PaymentIntent to `succeeded` within 15 seconds. Same value across all networks —
@@ -157,54 +257,57 @@ declare function createMppxStripe({ profileId, secretKey, paymentMethodTypes, }:
157
257
  paymentMethodTypes?: string[];
158
258
  }): Promise<unknown>;
159
259
 
160
- /**
161
- * Stripe PaymentIntent + deposit-address cache.
162
- *
163
- * Stripe-multichain merchants need three lookups during a request lifecycle:
260
+ /** Settle-outcome → simulator dispatch. Replaces the 3-branch rail/railKey
261
+ * switch + thin `simulateDepositIfTestnet(addr, network)` wrapper that
262
+ * consumer codebases (sayer, martin, sandbox) hand-rolled in their own
263
+ * `lib/payment.ts` files.
164
264
  *
165
- * 1. **Is this on-chain `pay_to` address one we minted?** — when an MPP credential
166
- * arrives with a `recipient`, verify it matches a recently-minted Stripe deposit
167
- * address. Validates the credential's deposit address against the addresses the
168
- * merchant has actually minted.
169
- *
170
- * 2. **Which PaymentIntent owns this deposit address?** — when settling, the
171
- * `simulate_crypto_deposit` test_helpers call needs the PaymentIntent id for the
172
- * deposit address that was paid to.
173
- *
174
- * 3. **Which sibling deposit addresses belong to the same PaymentIntent?** — when
175
- * enriching a 402 with x402 entries, the merchant needs the Base + Solana addresses
176
- * Stripe minted alongside the original Tempo address (one PI carries up to three).
177
- *
178
- * All three are TTL-bounded (default 300s — long enough for an agent to retry, short
179
- * enough to bound memory). Backed by Redis when `redisUrl` is set, falls back to
180
- * in-process Map otherwise. Single-instance servers can use the in-memory cache;
181
- * multi-instance deployments need a shared cache (Redis) so a deposit lands on
182
- * whichever instance settles it.
265
+ * Folding the dispatch into the SDK removes the consumer-wrap anti-pattern
266
+ * (`feedback_no_consumer_sdk_rewrapping`): merchants call this directly from
267
+ * `onSettled`, no per-merchant payment.ts wrapper needed.
183
268
  */
184
- interface PiCache {
185
- /** Mark an on-chain address as one this merchant minted. Idempotent + TTL-bounded. */
186
- cacheAddress(address: string): Promise<void>;
187
- /** Return true when the address was minted by this merchant within TTL. */
188
- hasAddress(address: string): Promise<boolean>;
189
- /** Associate an on-chain deposit address with the Stripe PaymentIntent that minted it. */
190
- cachePaymentIntent(depositAddress: string, paymentIntentId: string): void;
191
- /** Get the Stripe PaymentIntent id for a previously-minted deposit address, or undefined. */
192
- getPaymentIntentId(depositAddress: string): string | undefined;
193
- /** Associate a PaymentIntent id with the full set of sibling deposit addresses (one per network). */
194
- cacheNetworkAddresses(paymentIntentId: string, addresses: Record<string, string>): void;
195
- /** Look up the deposit address Stripe minted on a specific network for a given PaymentIntent. */
196
- getNetworkDepositAddress(paymentIntentId: string, network: string): string | undefined;
197
- /** Stop the background TTL-eviction loop. Call from server shutdown handlers. */
198
- stop(): void;
269
+ type SimulateNetwork = 'tempo' | 'base' | 'solana';
270
+ /** Map a settle outcome (from `Checkout.onSettled` or
271
+ * `computeFirstCheckout.onSettled`) to the `network` arg consumed by
272
+ * `simulateDepositIfTestMode`. Returns null for:
273
+ * - Stripe SPT (no on-chain deposit)
274
+ * - rails we don't recognize
275
+ *
276
+ * Accepts both Checkout-shaped outcomes (`rail` + `railKey`) and
277
+ * computeFirstCheckout-shaped outcomes (`rail` + `mppMethod`). The settle
278
+ * outcomes diverged historically; this helper canonicalizes them. */
279
+ declare function networkForOutcome(outcome: {
280
+ rail?: string;
281
+ railKey?: string;
282
+ mppMethod?: string;
283
+ }): SimulateNetwork | null;
284
+ interface SimulateDepositForOutcomeOptions {
285
+ /** The settle outcome handed to `onSettled`. Reads `rail` / `railKey` /
286
+ * `mppMethod` to pick the network arg. */
287
+ outcome: {
288
+ rail?: string;
289
+ railKey?: string;
290
+ mppMethod?: string;
291
+ };
292
+ /** On-chain deposit address that was paid to. For Stripe-multichain
293
+ * merchants this is the per-order minted address; for static-recipient
294
+ * merchants it's the merchant treasury. */
295
+ depositAddress: string;
296
+ /** PI-id resolver. Typically `piCache.getPaymentIntentId` from
297
+ * `createPiCache(...)`. */
298
+ getPaymentIntentId: (depositAddress: string) => string | undefined;
299
+ /** Stripe secret key. The wrapper checks `sk_test_` and no-ops on live. */
300
+ stripeSecretKey: string;
301
+ /** Stripe API version (defaults to the deposit-mode preview). */
302
+ stripeVersion?: string;
303
+ /** Optional simulated buyer wallet. */
304
+ buyerWallet?: string;
199
305
  }
200
- declare function createPiCache({ redisUrl, ttlSeconds, keyPrefix, }?: {
201
- /** Redis connection URL (e.g. `rediss://…cache.amazonaws.com:6379`). When omitted,
202
- * the cache falls back to in-process Maps with the same API. */
203
- redisUrl?: string;
204
- /** TTL for cached entries in seconds. Default 300. */
205
- ttlSeconds?: number;
206
- /** Prefix for Redis keys. Default `'payto:'`. */
207
- keyPrefix?: string;
208
- }): PiCache;
306
+ /** Dispatch `simulateDepositIfTestMode` based on the outcome's rail. Calls
307
+ * through to the SDK simulator; no-op for Stripe SPT or unknown rails.
308
+ *
309
+ * Use this in `onSettled` to replace the hand-rolled rail switch +
310
+ * `simulateDepositIfTestnet` wrapper pattern. */
311
+ declare function simulateDepositForOutcome(opts: SimulateDepositForOutcomeOptions): Promise<void>;
209
312
 
210
- export { type MultichainPaymentIntentResult, type PiCache, STRIPE_TEST_TX_HASH_FAILED, STRIPE_TEST_TX_HASH_SUCCESS, type StripeClientLike, type StripePaymentIntent, createMppxStripe, createMultichainPaymentIntent, createPiCache, simulateCryptoDeposit, simulateDepositIfTestMode };
313
+ export { type CreatePayToAddressFromStripePIOptions, type MultichainPaymentIntentResult, type PiCache, STRIPE_TEST_TX_HASH_FAILED, STRIPE_TEST_TX_HASH_SUCCESS, type SimulateDepositForOutcomeOptions, type SimulateNetwork, type StripeClientLike, type StripePaymentIntent, createMppxStripe, createMultichainPaymentIntent, createPayToAddressFromStripePI, createPiCache, networkForOutcome, simulateCryptoDeposit, simulateDepositForOutcome, simulateDepositIfTestMode };