mppx 0.9.1 → 0.9.3

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 (73) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +26 -0
  3. package/dist/Method.d.ts +2 -0
  4. package/dist/cli/Extension.d.ts +20 -0
  5. package/dist/cli/Extension.js +5 -0
  6. package/dist/cli/cli.d.ts +1 -0
  7. package/dist/cli/cli.js +135 -135
  8. package/dist/cli/config.d.ts +4 -0
  9. package/dist/cli/config.js +1 -0
  10. package/dist/cli/internal.d.ts +3 -0
  11. package/dist/cli/internal.js +15 -2
  12. package/dist/cli/plugins/evm.js +2 -0
  13. package/dist/cli/plugins/index.d.ts +1 -0
  14. package/dist/cli/plugins/index.js +13 -13
  15. package/dist/cli/plugins/plugin.d.ts +2 -2
  16. package/dist/cli/plugins/x402.d.ts +3 -0
  17. package/dist/cli/plugins/x402.js +61 -0
  18. package/dist/cli/sessions/request.d.ts +1 -0
  19. package/dist/cli/sessions/request.js +1 -0
  20. package/dist/cli/validate/payment.js +14 -8
  21. package/dist/evm/client/Charge.d.ts +2 -2
  22. package/dist/evm/client/Charge.js +1 -1
  23. package/dist/evm/server/Charge.d.ts +7 -2
  24. package/dist/evm/server/Charge.js +28 -2
  25. package/dist/evm/server/Methods.d.ts +1 -1
  26. package/dist/internal/version.d.ts +5 -0
  27. package/dist/internal/version.js +6 -0
  28. package/dist/middlewares/internal/mppx.js +17 -3
  29. package/dist/server/Mppx.d.ts +7 -6
  30. package/dist/server/Mppx.js +84 -2
  31. package/dist/server/internal/html/config.js +1 -3
  32. package/dist/stripe/Methods.d.ts +24 -5
  33. package/dist/stripe/Methods.js +14 -11
  34. package/dist/stripe/client/Charge.d.ts +23 -5
  35. package/dist/stripe/client/Methods.d.ts +23 -5
  36. package/dist/stripe/internal/constants.d.ts +4 -0
  37. package/dist/stripe/internal/constants.js +2 -0
  38. package/dist/stripe/internal/parse-units.d.ts +8 -0
  39. package/dist/stripe/internal/parse-units.js +20 -0
  40. package/dist/stripe/internal/payment-intent.d.ts +39 -1
  41. package/dist/stripe/internal/payment-intent.js +16 -1
  42. package/dist/stripe/internal/types.d.ts +4 -0
  43. package/dist/stripe/server/Charge.d.ts +25 -5
  44. package/dist/stripe/server/Charge.js +35 -23
  45. package/dist/stripe/server/Methods.d.ts +8 -0
  46. package/dist/stripe/server/Methods.js +54 -9
  47. package/dist/stripe/server/internal/analytics.d.ts +6 -0
  48. package/dist/stripe/server/internal/analytics.js +16 -0
  49. package/dist/stripe/server/internal/hosted-fee-payer.d.ts +10 -0
  50. package/dist/stripe/server/internal/hosted-fee-payer.js +54 -0
  51. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  52. package/dist/stripe/server/internal/html.gen.js +1 -1
  53. package/dist/stripe/server/internal/record-payment.d.ts +4 -1
  54. package/dist/stripe/server/internal/record-payment.js +35 -5
  55. package/dist/tempo/client/Subscription.js +6 -2
  56. package/dist/tempo/internal/account.d.ts +1 -0
  57. package/dist/tempo/internal/fee-payer.d.ts +2 -0
  58. package/dist/tempo/internal/fee-payer.js +4 -0
  59. package/dist/tempo/internal/remote-fee-payer.d.ts +2 -0
  60. package/dist/tempo/server/Subscription.js +1 -0
  61. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  62. package/dist/tempo/server/internal/html.gen.js +1 -1
  63. package/dist/tempo/session/client/SessionManager.d.ts +5 -0
  64. package/dist/tempo/session/client/SessionManager.js +16 -2
  65. package/dist/tempo/session/precompile/Chain.js +3 -0
  66. package/dist/tempo/subscription/KeyAuthorization.d.ts +7 -1
  67. package/dist/tempo/subscription/KeyAuthorization.js +21 -3
  68. package/dist/viem/Client.js +1 -0
  69. package/dist/x402/client/Exact.d.ts +2 -2
  70. package/dist/x402/client/Exact.js +1 -1
  71. package/dist/x402/server/EvmCharge.d.ts +2 -0
  72. package/dist/x402/server/EvmCharge.js +34 -26
  73. package/package.json +1 -1
@@ -55,9 +55,18 @@ export declare function charge(parameters: charge.Parameters): Method.Client<{
55
55
  externalId: z.ZodMiniOptional<z.ZodMiniString<string>>;
56
56
  metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
57
57
  networkId: z.ZodMiniString<string>;
58
- paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniObject<{
59
- metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
60
- }, z.core.$strip>>;
58
+ paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
59
+ customer: z.ZodMiniOptional<z.ZodMiniString<string>>;
60
+ hooks: z.ZodMiniOptional<z.ZodMiniObject<{
61
+ inputs: z.ZodMiniObject<{
62
+ tax: z.ZodMiniObject<{
63
+ calculation: z.ZodMiniString<string>;
64
+ }, z.core.$strip>;
65
+ }, z.core.$strip>;
66
+ }, z.core.$strip>>;
67
+ metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
68
+ receipt_email: z.ZodMiniOptional<z.ZodMiniString<string>>;
69
+ }, z.core.$strip>, z.ZodMiniCustom<import("../internal/payment-intent.js").ResolveOptions, import("../internal/payment-intent.js").ResolveOptions>]>>;
61
70
  paymentMethodTypes: z.ZodMiniArray<z.ZodMiniString<string>>;
62
71
  recipient: z.ZodMiniOptional<z.ZodMiniString<string>>;
63
72
  }, z.core.$strip>, z.ZodMiniTransform<{
@@ -79,8 +88,17 @@ export declare function charge(parameters: charge.Parameters): Method.Client<{
79
88
  externalId?: string | undefined;
80
89
  metadata?: Record<string, string> | undefined;
81
90
  networkId: string;
82
- paymentIntentOptions?: {
83
- metadata: Record<string, string>;
91
+ paymentIntentOptions?: import("../internal/payment-intent.js").ResolveOptions | {
92
+ customer?: string | undefined;
93
+ hooks?: {
94
+ inputs: {
95
+ tax: {
96
+ calculation: string;
97
+ };
98
+ };
99
+ } | undefined;
100
+ metadata?: Record<string, string> | undefined;
101
+ receipt_email?: string | undefined;
84
102
  } | undefined;
85
103
  paymentMethodTypes: string[];
86
104
  recipient?: string | undefined;
@@ -42,9 +42,18 @@ export declare function stripe(parameters: stripe.Parameters): readonly [import(
42
42
  externalId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
43
43
  metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>>;
44
44
  networkId: import("zod/mini").ZodMiniString<string>;
45
- paymentIntentOptions: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
46
- metadata: import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>;
47
- }, import("zod/v4/core").$strip>>;
45
+ paymentIntentOptions: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniObject<{
46
+ customer: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
47
+ hooks: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
48
+ inputs: import("zod/mini").ZodMiniObject<{
49
+ tax: import("zod/mini").ZodMiniObject<{
50
+ calculation: import("zod/mini").ZodMiniString<string>;
51
+ }, import("zod/v4/core").$strip>;
52
+ }, import("zod/v4/core").$strip>;
53
+ }, import("zod/v4/core").$strip>>;
54
+ metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>>;
55
+ receipt_email: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
56
+ }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniCustom<import("../internal/payment-intent.js").ResolveOptions, import("../internal/payment-intent.js").ResolveOptions>]>>;
48
57
  paymentMethodTypes: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniString<string>>;
49
58
  recipient: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
50
59
  }, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniTransform<{
@@ -66,8 +75,17 @@ export declare function stripe(parameters: stripe.Parameters): readonly [import(
66
75
  externalId?: string | undefined;
67
76
  metadata?: Record<string, string> | undefined;
68
77
  networkId: string;
69
- paymentIntentOptions?: {
70
- metadata: Record<string, string>;
78
+ paymentIntentOptions?: import("../internal/payment-intent.js").ResolveOptions | {
79
+ customer?: string | undefined;
80
+ hooks?: {
81
+ inputs: {
82
+ tax: {
83
+ calculation: string;
84
+ };
85
+ };
86
+ } | undefined;
87
+ metadata?: Record<string, string> | undefined;
88
+ receipt_email?: string | undefined;
71
89
  } | undefined;
72
90
  paymentMethodTypes: string[];
73
91
  recipient?: string | undefined;
@@ -5,4 +5,8 @@
5
5
  * Bump this when upgrading to a newer Stripe API version.
6
6
  */
7
7
  export declare const stripePreviewVersion = "2026-07-29.preview";
8
+ /** Metadata identifying a Stripe PaymentIntent as a machine payment. */
9
+ export declare const machinePaymentMetadata: {
10
+ readonly machine_payment: 'true';
11
+ };
8
12
  //# sourceMappingURL=constants.d.ts.map
@@ -5,4 +5,6 @@
5
5
  * Bump this when upgrading to a newer Stripe API version.
6
6
  */
7
7
  export const stripePreviewVersion = '2026-07-29.preview';
8
+ /** Metadata identifying a Stripe PaymentIntent as a machine payment. */
9
+ export const machinePaymentMetadata = { machine_payment: 'true' };
8
10
  //# sourceMappingURL=constants.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Converts a decimal string to its integer representation at the given precision.
3
+ * Fractional digits beyond the precision are rounded half away from zero.
4
+ *
5
+ * @see https://github.com/wevm/viem/pull/4859
6
+ */
7
+ export declare function parseUnits(value: string, decimals: number): bigint;
8
+ //# sourceMappingURL=parse-units.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Converts a decimal string to its integer representation at the given precision.
3
+ * Fractional digits beyond the precision are rounded half away from zero.
4
+ *
5
+ * @see https://github.com/wevm/viem/pull/4859
6
+ */
7
+ export function parseUnits(value, decimals) {
8
+ if (!Number.isInteger(decimals) || decimals < 0)
9
+ throw new RangeError('Decimals must be a non-negative integer.');
10
+ if (!/^-?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value))
11
+ throw new TypeError(`Value \`${value}\` is not a valid decimal number.`);
12
+ const negative = value.startsWith('-');
13
+ const [integer = '0', fraction = ''] = (negative ? value.slice(1) : value).split('.');
14
+ const keptFraction = fraction.slice(0, decimals).padEnd(decimals, '0');
15
+ let result = BigInt(`${integer || '0'}${keptFraction}`);
16
+ if (fraction.length > decimals && fraction[decimals] >= '5')
17
+ result += 1n;
18
+ return negative ? -result : result;
19
+ }
20
+ //# sourceMappingURL=parse-units.js.map
@@ -1,7 +1,45 @@
1
+ import type * as Challenge from '../../Challenge.js';
2
+ import type * as Credential from '../../Credential.js';
3
+ import type { MaybePromise } from '../../internal/types.js';
4
+ import type * as Method from '../../Method.js';
1
5
  import * as z from '../../zod.js';
2
6
  /** Stripe PaymentIntent options accepted only by server-side method input. */
3
7
  export declare const Schema: z.ZodMiniObject<{
4
- metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
8
+ customer: z.ZodMiniOptional<z.ZodMiniString<string>>;
9
+ hooks: z.ZodMiniOptional<z.ZodMiniObject<{
10
+ inputs: z.ZodMiniObject<{
11
+ tax: z.ZodMiniObject<{
12
+ calculation: z.ZodMiniString<string>;
13
+ }, z.core.$strip>;
14
+ }, z.core.$strip>;
15
+ }, z.core.$strip>>;
16
+ metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
17
+ receipt_email: z.ZodMiniOptional<z.ZodMiniString<string>>;
5
18
  }, z.core.$strip>;
6
19
  export type Options = z.infer<typeof Schema>;
20
+ /** Context provided when resolving request-scoped PaymentIntent options. */
21
+ export type ResolveOptionsContext = {
22
+ challenge: Challenge.Challenge;
23
+ credential: Credential.Credential;
24
+ envelope?: Method.VerifiedChallengeEnvelope | undefined;
25
+ request: Record<string, unknown>;
26
+ };
27
+ /** Lazily resolves request-scoped PaymentIntent options before the terminal payment operation. */
28
+ export type ResolveOptions = (context: ResolveOptionsContext) => MaybePromise<Options | undefined>;
29
+ export type OptionsInput = Options | ResolveOptions;
30
+ /** PaymentIntent options accepted only by server-side method input. */
31
+ export declare const InputSchema: z.ZodMiniUnion<readonly [z.ZodMiniObject<{
32
+ customer: z.ZodMiniOptional<z.ZodMiniString<string>>;
33
+ hooks: z.ZodMiniOptional<z.ZodMiniObject<{
34
+ inputs: z.ZodMiniObject<{
35
+ tax: z.ZodMiniObject<{
36
+ calculation: z.ZodMiniString<string>;
37
+ }, z.core.$strip>;
38
+ }, z.core.$strip>;
39
+ }, z.core.$strip>>;
40
+ metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
41
+ receipt_email: z.ZodMiniOptional<z.ZodMiniString<string>>;
42
+ }, z.core.$strip>, z.ZodMiniCustom<ResolveOptions, ResolveOptions>]>;
43
+ /** Resolves and validates request-scoped PaymentIntent options. */
44
+ export declare function resolve(input: OptionsInput | undefined, context: ResolveOptionsContext): Promise<Options | undefined>;
7
45
  //# sourceMappingURL=payment-intent.d.ts.map
@@ -1,6 +1,21 @@
1
1
  import * as z from '../../zod.js';
2
2
  /** Stripe PaymentIntent options accepted only by server-side method input. */
3
3
  export const Schema = z.object({
4
- metadata: z.record(z.string(), z.string()),
4
+ customer: z.optional(z.string().check(z.minLength(1))),
5
+ hooks: z.optional(z.object({
6
+ inputs: z.object({
7
+ tax: z.object({ calculation: z.string().check(z.minLength(1)) }),
8
+ }),
9
+ })),
10
+ metadata: z.optional(z.record(z.string(), z.string())),
11
+ receipt_email: z.optional(z.string().check(z.minLength(1))),
5
12
  });
13
+ const ResolveOptionsSchema = z.custom((value) => typeof value === 'function');
14
+ /** PaymentIntent options accepted only by server-side method input. */
15
+ export const InputSchema = z.union([Schema, ResolveOptionsSchema]);
16
+ /** Resolves and validates request-scoped PaymentIntent options. */
17
+ export async function resolve(input, context) {
18
+ const options = typeof input === 'function' ? await input(context) : input;
19
+ return z.optional(Schema).parse(options);
20
+ }
6
21
  //# sourceMappingURL=payment-intent.js.map
@@ -6,6 +6,10 @@ import * as StripeJsTypes from '../../stripe/server/internal/html/types.js';
6
6
  * Uses loose signatures so any Stripe SDK version is assignable.
7
7
  */
8
8
  export type StripeClient = {
9
+ /** Stripe-node request transport used for hosted fee-payer requests. */
10
+ _requestSender?: {
11
+ _request(...args: any[]): void;
12
+ } | undefined;
9
13
  paymentIntents: {
10
14
  create(...args: any[]): Promise<{
11
15
  id: string;
@@ -4,6 +4,7 @@ import type { ConfiguredDefaults, LooseOmit, MaybePromise, OneOf } from '../../i
4
4
  import * as Method from '../../Method.js';
5
5
  import type * as Html from '../../server/internal/html/config.ts';
6
6
  import type * as z from '../../zod.js';
7
+ import * as PaymentIntent from '../internal/payment-intent.js';
7
8
  import type { StripeClient, CreatePaymentMethodFromElements, StripeElementsOptionsMode, StripePaymentElementOptions } from '../internal/types.js';
8
9
  import * as Methods from '../Methods.js';
9
10
  /**
@@ -49,9 +50,18 @@ export declare function charge<const parameters extends charge.Parameters>(param
49
50
  externalId: z.ZodMiniOptional<z.ZodMiniString<string>>;
50
51
  metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
51
52
  networkId: z.ZodMiniString<string>;
52
- paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniObject<{
53
- metadata: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
54
- }, z.core.$strip>>;
53
+ paymentIntentOptions: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
54
+ customer: z.ZodMiniOptional<z.ZodMiniString<string>>;
55
+ hooks: z.ZodMiniOptional<z.ZodMiniObject<{
56
+ inputs: z.ZodMiniObject<{
57
+ tax: z.ZodMiniObject<{
58
+ calculation: z.ZodMiniString<string>;
59
+ }, z.core.$strip>;
60
+ }, z.core.$strip>;
61
+ }, z.core.$strip>>;
62
+ metadata: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
63
+ receipt_email: z.ZodMiniOptional<z.ZodMiniString<string>>;
64
+ }, z.core.$strip>, z.ZodMiniCustom<PaymentIntent.ResolveOptions, PaymentIntent.ResolveOptions>]>>;
55
65
  paymentMethodTypes: z.ZodMiniArray<z.ZodMiniString<string>>;
56
66
  recipient: z.ZodMiniOptional<z.ZodMiniString<string>>;
57
67
  }, z.core.$strip>, z.ZodMiniTransform<{
@@ -73,8 +83,17 @@ export declare function charge<const parameters extends charge.Parameters>(param
73
83
  externalId?: string | undefined;
74
84
  metadata?: Record<string, string> | undefined;
75
85
  networkId: string;
76
- paymentIntentOptions?: {
77
- metadata: Record<string, string>;
86
+ paymentIntentOptions?: PaymentIntent.ResolveOptions | {
87
+ customer?: string | undefined;
88
+ hooks?: {
89
+ inputs: {
90
+ tax: {
91
+ calculation: string;
92
+ };
93
+ };
94
+ } | undefined;
95
+ metadata?: Record<string, string> | undefined;
96
+ receipt_email?: string | undefined;
78
97
  } | undefined;
79
98
  paymentMethodTypes: string[];
80
99
  recipient?: string | undefined;
@@ -112,6 +131,7 @@ export declare namespace charge {
112
131
  * `decimals` remains required when it was not configured on `stripe.charge()`.
113
132
  */
114
133
  type DeriveDefaults<parameters extends Parameters> = ConfiguredDefaults<parameters, Defaults>;
134
+ type ResolvePaymentIntentOptionsContext = PaymentIntent.ResolveOptionsContext;
115
135
  /**
116
136
  * Server-side Stripe Connect settlement parameters.
117
137
  *
@@ -2,7 +2,9 @@ import { InvalidChallengeError, PaymentActionRequiredError, VerificationFailedEr
2
2
  import * as Expires from '../../Expires.js';
3
3
  import * as Method from '../../Method.js';
4
4
  import { stripePreviewVersion } from '../internal/constants.js';
5
+ import * as PaymentIntent from '../internal/payment-intent.js';
5
6
  import * as Methods from '../Methods.js';
7
+ import { buildAnalytics } from './internal/analytics.js';
6
8
  import { html as htmlContent } from './internal/html.gen.js';
7
9
  /**
8
10
  * Stripe's general minimum charge amounts in each currency's smallest unit.
@@ -117,7 +119,7 @@ export function charge(parameters) {
117
119
  onPaymentSuccess,
118
120
  async verify({ credential, envelope, request }) {
119
121
  const { challenge } = credential;
120
- const { paymentIntentOptions, ...methodRequest } = request;
122
+ const { paymentIntentOptions: paymentIntentOptionsInput, ...methodRequest } = request;
121
123
  const resolvedRequest = (() => {
122
124
  const parsed = Methods.charge.schema.request.safeParse(methodRequest);
123
125
  if (parsed.success)
@@ -139,32 +141,44 @@ export function charge(parameters) {
139
141
  });
140
142
  }
141
143
  const userMetadata = resolvedRequest.methodDetails?.metadata;
142
- const resolvedMetadata = {
143
- ...buildAnalytics({ credential }),
144
- ...userMetadata,
145
- ...paymentIntentOptions?.metadata,
146
- };
147
144
  const settlement = validateConnectSettlement({
148
145
  amount: resolvedRequest.amount,
149
146
  settlement: typeof connect === 'function'
150
147
  ? await connect({ challenge, credential, envelope, request: resolvedRequest })
151
148
  : connect,
152
149
  });
150
+ const paymentIntentOptions = await PaymentIntent.resolve(paymentIntentOptionsInput, {
151
+ challenge,
152
+ credential,
153
+ envelope,
154
+ request: resolvedRequest,
155
+ });
156
+ const { customer, hooks, metadata: optionMetadata, receipt_email, } = paymentIntentOptions ?? {};
157
+ const resolvedPaymentIntentOptions = {
158
+ ...(customer !== undefined && { customer }),
159
+ ...(hooks !== undefined && { hooks }),
160
+ metadata: {
161
+ ...buildAnalytics({ challenge }),
162
+ ...userMetadata,
163
+ ...optionMetadata,
164
+ },
165
+ ...(receipt_email !== undefined && { receipt_email }),
166
+ };
153
167
  const pi = client
154
168
  ? await createWithClient({
155
169
  client,
156
170
  challenge,
171
+ paymentIntentOptions: resolvedPaymentIntentOptions,
157
172
  request: resolvedRequest,
158
173
  spt,
159
- metadata: resolvedMetadata,
160
174
  settlement,
161
175
  })
162
176
  : await createWithSecretKey({
163
177
  secretKey: secretKey,
164
178
  challenge,
179
+ paymentIntentOptions: resolvedPaymentIntentOptions,
165
180
  request: resolvedRequest,
166
181
  spt,
167
- metadata: resolvedMetadata,
168
182
  settlement,
169
183
  });
170
184
  if (pi.replayed)
@@ -186,14 +200,18 @@ export function charge(parameters) {
186
200
  }
187
201
  /** Creates a PaymentIntent using the Stripe SDK client. */
188
202
  async function createWithClient(parameters) {
189
- const { client, challenge, metadata, request, settlement, spt } = parameters;
203
+ const { client, challenge, paymentIntentOptions, request, settlement, spt } = parameters;
204
+ const { customer, hooks, metadata, receipt_email } = paymentIntentOptions;
190
205
  try {
191
206
  const paymentIntentParams = {
192
207
  amount: Number(request.amount),
193
208
  automatic_payment_methods: { allow_redirects: 'never', enabled: true },
194
209
  confirm: true,
195
210
  currency: request.currency,
211
+ ...(customer !== undefined && { customer }),
212
+ ...(hooks !== undefined && { hooks }),
196
213
  metadata,
214
+ ...(receipt_email !== undefined && { receipt_email }),
197
215
  ...(settlement?.applicationFeeAmount !== undefined && {
198
216
  application_fee_amount: settlement.applicationFeeAmount,
199
217
  }),
@@ -210,12 +228,12 @@ async function createWithClient(parameters) {
210
228
  // `shared_payment_granted_token` is not yet in the Stripe SDK types (SPTs are in private preview).
211
229
  shared_payment_granted_token: spt,
212
230
  };
213
- const paymentIntentOptions = {
231
+ const requestOptions = {
214
232
  apiVersion: stripePreviewVersion,
215
233
  idempotencyKey: `mpp_${challenge.id}_${spt}`,
216
234
  ...(settlement?.stripeAccount !== undefined && { stripeAccount: settlement.stripeAccount }),
217
235
  };
218
- const result = await client.paymentIntents.create(paymentIntentParams, paymentIntentOptions);
236
+ const result = await client.paymentIntents.create(paymentIntentParams, requestOptions);
219
237
  // https://docs.stripe.com/error-low-level#idempotency
220
238
  const replayed = result.lastResponse?.headers?.['idempotent-replayed'] === 'true';
221
239
  return { id: result.id, status: result.status, replayed };
@@ -229,18 +247,23 @@ async function createWithClient(parameters) {
229
247
  }
230
248
  /** Creates a PaymentIntent using a raw secret key and fetch. */
231
249
  async function createWithSecretKey(parameters) {
232
- const { secretKey, challenge, metadata, request, settlement, spt } = parameters;
250
+ const { secretKey, challenge, paymentIntentOptions, request, settlement, spt } = parameters;
251
+ const { customer, hooks, metadata, receipt_email } = paymentIntentOptions;
233
252
  const body = new URLSearchParams({
234
253
  amount: request.amount,
235
254
  'automatic_payment_methods[allow_redirects]': 'never',
236
255
  'automatic_payment_methods[enabled]': 'true',
237
256
  confirm: 'true',
238
257
  currency: request.currency,
258
+ ...(customer !== undefined && { customer }),
259
+ ...(receipt_email !== undefined && { receipt_email }),
239
260
  shared_payment_granted_token: spt,
240
261
  });
241
262
  for (const [key, value] of Object.entries(metadata)) {
242
263
  body.set(`metadata[${key}]`, value);
243
264
  }
265
+ if (hooks !== undefined)
266
+ body.set('hooks[inputs][tax][calculation]', hooks.inputs.tax.calculation);
244
267
  if (settlement?.applicationFeeAmount !== undefined)
245
268
  body.set('application_fee_amount', String(settlement.applicationFeeAmount));
246
269
  if (settlement?.onBehalfOf !== undefined)
@@ -284,17 +307,6 @@ async function createWithSecretKey(parameters) {
284
307
  const result = (await response.json());
285
308
  return { ...result, replayed };
286
309
  }
287
- /** @internal */
288
- function buildAnalytics(parameters) {
289
- const { credential } = parameters;
290
- const { challenge } = credential;
291
- return {
292
- mpp_intent: challenge.intent,
293
- mpp_challenge_id: challenge.id,
294
- mpp_server_id: challenge.realm,
295
- ...(credential.source ? { mpp_client_id: credential.source } : {}),
296
- };
297
- }
298
310
  function validateConnectSettlement(parameters) {
299
311
  const { amount, settlement } = parameters;
300
312
  if (settlement === undefined)
@@ -2,12 +2,14 @@ import { charge as evmCharge } from '../../evm/server/Charge.js';
2
2
  import type * as Method from '../../Method.js';
3
3
  import { charge as tempoCharge } from '../../tempo/server/Charge.js';
4
4
  import { session as tempoSession } from '../../tempo/session/server/Session.js';
5
+ import * as PaymentIntent from '../internal/payment-intent.js';
5
6
  import type { StripeClient } from '../internal/types.js';
6
7
  import { charge as charge_ } from './Charge.js';
7
8
  import { findOrCreateDepositAddress as _findOrCreateDepositAddress } from './internal/deposit-address.js';
8
9
  import { type ConnectConfig } from './internal/request.js';
9
10
  export declare namespace stripe {
10
11
  type Network = 'tempo' | 'base' | 'solana';
12
+ type ResolvePaymentIntentOptionsContext = PaymentIntent.ResolveOptionsContext;
11
13
  type DepositAddress<N extends Network = Network> = string & {
12
14
  readonly __brand: 'StripeDepositAddress';
13
15
  readonly __network: N;
@@ -16,6 +18,7 @@ export declare namespace stripe {
16
18
  client: StripeClient;
17
19
  networkId: string;
18
20
  livemode: boolean;
21
+ hostedFeePayer?: boolean | undefined;
19
22
  connect?: ConnectConfig;
20
23
  depositAddresses?: Partial<Record<Network, string>> | ((network: Network) => Promise<string>);
21
24
  metadata?: Record<string, string> | undefined;
@@ -139,6 +142,11 @@ declare class DefaultMethodsBuilder implements PromiseLike<DefaultMethods> {
139
142
  * secretKey: mppSecretKey,
140
143
  * })
141
144
  * ```
145
+ *
146
+ * PaymentIntent option resolvers run after non-mutating method validation, when
147
+ * available, and before the terminal payment operation. If Stripe later rejects
148
+ * resolved optional fields while recording a completed crypto payment, mppx
149
+ * retries the recording once without them. SPT payments do not use this fallback.
142
150
  */
143
151
  export declare function stripe<const P extends stripe.Parameters>(parameters: P): StripeMachinePayments<P>;
144
152
  export declare namespace stripe {
@@ -6,7 +6,9 @@ import { session as tempoSession } from '../../tempo/session/server/Session.js';
6
6
  import * as z from '../../zod.js';
7
7
  import * as PaymentIntent from '../internal/payment-intent.js';
8
8
  import { charge as charge_ } from './Charge.js';
9
+ import { buildAnalytics } from './internal/analytics.js';
9
10
  import { findOrCreateDepositAddress as _findOrCreateDepositAddress } from './internal/deposit-address.js';
11
+ import * as HostedFeePayer from './internal/hosted-fee-payer.js';
10
12
  import { recordCryptoPayment } from './internal/record-payment.js';
11
13
  const defaultMethodNames = ['tempo', 'spt'];
12
14
  const additionalBuiltinKeys = ['base', 'tempo'];
@@ -71,12 +73,22 @@ class DefaultMethodsBuilder {
71
73
  * secretKey: mppSecretKey,
72
74
  * })
73
75
  * ```
76
+ *
77
+ * PaymentIntent option resolvers run after non-mutating method validation, when
78
+ * available, and before the terminal payment operation. If Stripe later rejects
79
+ * resolved optional fields while recording a completed crypto payment, mppx
80
+ * retries the recording once without them. SPT payments do not use this fallback.
74
81
  */
75
82
  export function stripe(parameters) {
76
- const { client, networkId, livemode, connect, depositAddresses, metadata } = parameters;
83
+ const { client, networkId, livemode, hostedFeePayer, connect, depositAddresses, metadata } = parameters;
77
84
  if (!client.rawRequest)
78
85
  throw new Error('stripe.create() requires a Stripe SDK client with rawRequest() (v15+)');
86
+ if (hostedFeePayer && !livemode)
87
+ throw new Error('Stripe hosted fee payer requires a live-mode integration.');
88
+ if (hostedFeePayer && connect)
89
+ throw new Error('Stripe hosted fee payer does not support Connect account routing.');
79
90
  const tempoCurrency = (livemode ? tempoDefaults.tokens.usdc : tempoDefaults.tokens.pathUsd);
91
+ const hostedTempoFeePayer = hostedFeePayer ? HostedFeePayer.create(client) : undefined;
80
92
  const tempoPaymentHandler = createPaymentSuccessHandler(client, 'tempo', connect, metadata);
81
93
  const basePaymentHandler = createPaymentSuccessHandler(client, 'base', connect, metadata);
82
94
  // All stripe-managed crypto rails use 6-decimal stablecoins. Reject amounts
@@ -102,6 +114,7 @@ export function stripe(parameters) {
102
114
  currency: tempoCurrency,
103
115
  recipient,
104
116
  ...(!livemode && { testnet: true }),
117
+ ...(hostedTempoFeePayer && { feePayer: hostedTempoFeePayer }),
105
118
  canOffer: cryptoCanOffer,
106
119
  onPaymentSuccess: handler,
107
120
  ...rest,
@@ -113,6 +126,7 @@ export function stripe(parameters) {
113
126
  currency: tempoCurrency,
114
127
  recipient,
115
128
  ...(!livemode && { testnet: true }),
129
+ ...(hostedTempoFeePayer && { feePayer: hostedTempoFeePayer }),
116
130
  ...rest,
117
131
  });
118
132
  }
@@ -269,18 +283,27 @@ function customRails(additional) {
269
283
  }
270
284
  function createPaymentSuccessHandler(client, network, connect, metadata) {
271
285
  return (params) => {
272
- const { receipt, request, requestInput } = params;
286
+ const { challenge, receipt, request, requestInput } = params;
273
287
  if (receipt?.reference && request?.amount) {
288
+ const paymentIntentOptionsInput = requestInput?.paymentIntentOptions;
289
+ const paymentIntentOptions = typeof paymentIntentOptionsInput === 'function' ? undefined : paymentIntentOptionsInput;
274
290
  const resolvedMetadata = {
275
291
  ...metadata,
276
- ...requestInput?.paymentIntentOptions?.metadata,
292
+ ...paymentIntentOptions?.metadata,
293
+ };
294
+ const resolvedPaymentIntentOptions = {
295
+ ...paymentIntentOptions,
296
+ ...(Object.keys(resolvedMetadata).length > 0 && { metadata: resolvedMetadata }),
277
297
  };
278
298
  return recordCryptoPayment(client, {
279
299
  network,
280
300
  reference: receipt.reference,
281
301
  amount: String(request.amount),
282
302
  ...(connect && { connect }),
283
- ...(Object.keys(resolvedMetadata).length > 0 && { metadata: resolvedMetadata }),
303
+ analyticsMetadata: buildAnalytics({ challenge }),
304
+ ...(Object.keys(resolvedPaymentIntentOptions).length > 0 && {
305
+ paymentIntentOptions: resolvedPaymentIntentOptions,
306
+ }),
284
307
  });
285
308
  }
286
309
  };
@@ -289,8 +312,9 @@ function createPaymentSuccessHandler(client, network, connect, metadata) {
289
312
  * Extends a rail's server-only input with Stripe PaymentIntent options while
290
313
  * keeping its canonical request unchanged. The schema strips the options
291
314
  * before challenge serialization, and delegated lifecycle hooks receive the
292
- * underlying rail request without Stripe-only fields. The wrapper retains the
293
- * options only in `requestInput` for payment-success recording.
315
+ * underlying rail request without Stripe-only fields. After method validation,
316
+ * the wrapper resolves the options immediately before the terminal operation
317
+ * and retains the result in `requestInput` for payment-success recording.
294
318
  */
295
319
  function withPaymentIntentInput(method) {
296
320
  const baseSchema = method.schema.request;
@@ -306,7 +330,7 @@ function withPaymentIntentInput(method) {
306
330
  ...method.schema,
307
331
  request: z.pipe(z.custom(), z.transform((input) => {
308
332
  const { paymentIntentOptions, ...request } = input;
309
- z.optional(PaymentIntent.Schema).parse(paymentIntentOptions);
333
+ z.optional(PaymentIntent.InputSchema).parse(paymentIntentOptions);
310
334
  // Only the base schema's output is included in the challenge.
311
335
  return baseSchema.parse(request);
312
336
  })),
@@ -314,22 +338,43 @@ function withPaymentIntentInput(method) {
314
338
  async request(context) {
315
339
  const { paymentIntentOptions, ...request } = context.request;
316
340
  const resolved = baseRequest ? await baseRequest({ ...context, request }) : request;
341
+ // Keep the server-only value in requestInput for the success hook. The
342
+ // schema transform above still excludes it from the signed challenge.
317
343
  return { ...resolved, paymentIntentOptions };
318
344
  },
319
345
  ...(baseRespond && {
320
346
  respond: (context) => baseRespond(withoutPaymentIntentOptions(context)),
321
347
  }),
322
348
  ...(baseBroadcast && {
323
- broadcast: (context) => baseBroadcast(withoutPaymentIntentOptions(context)),
349
+ async broadcast(context) {
350
+ await resolvePaymentIntentOptions(context);
351
+ return baseBroadcast(withoutPaymentIntentOptions(context));
352
+ },
324
353
  }),
325
354
  ...(baseValidate && {
326
355
  validate: (context) => baseValidate(withoutPaymentIntentOptions(context)),
327
356
  }),
328
357
  ...(baseVerify && {
329
- verify: (context) => baseVerify(withoutPaymentIntentOptions(context)),
358
+ async verify(context) {
359
+ await resolvePaymentIntentOptions(context);
360
+ return baseVerify(withoutPaymentIntentOptions(context));
361
+ },
330
362
  }),
331
363
  };
332
364
  }
365
+ /** Resolves Stripe-only options immediately before the terminal payment operation. */
366
+ async function resolvePaymentIntentOptions(context) {
367
+ const { paymentIntentOptions } = context.request;
368
+ const resolved = await PaymentIntent.resolve(paymentIntentOptions, {
369
+ challenge: context.credential.challenge,
370
+ credential: context.credential,
371
+ envelope: context.envelope,
372
+ request: context.envelope?.request ?? context.credential.challenge.request,
373
+ });
374
+ // MPPX later snapshots this request-local input for onPaymentSuccess. Replacing
375
+ // only the schema-stripped field keeps the canonical challenge unchanged.
376
+ context.request.paymentIntentOptions = resolved;
377
+ }
333
378
  function withoutPaymentIntentOptions(context) {
334
379
  const { paymentIntentOptions: _, ...request } = context.request;
335
380
  return { ...context, request };
@@ -0,0 +1,6 @@
1
+ import type * as Challenge from '../../../Challenge.js';
2
+ /** Builds Stripe metadata used to identify and analyze MPP payments. */
3
+ export declare function buildAnalytics(parameters: {
4
+ challenge?: Pick<Challenge.Challenge, 'id' | 'intent'> | undefined;
5
+ }): Record<string, string>;
6
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1,16 @@
1
+ import { sdkIdentifier } from '../../../internal/version.js';
2
+ import { machinePaymentMetadata } from '../../internal/constants.js';
3
+ /** Builds Stripe metadata used to identify and analyze MPP payments. */
4
+ export function buildAnalytics(parameters) {
5
+ const { challenge } = parameters;
6
+ const metadata = {
7
+ ...machinePaymentMetadata,
8
+ mpp_sdk: sdkIdentifier,
9
+ ...(challenge && {
10
+ mpp_challenge_id: challenge.id,
11
+ mpp_intent: challenge.intent,
12
+ }),
13
+ };
14
+ return Object.fromEntries(Object.entries(metadata).map(([key, value]) => [key, [...value].slice(0, 500).join('')]));
15
+ }
16
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1,10 @@
1
+ import type * as RemoteFeePayer from '../../../tempo/internal/remote-fee-payer.js';
2
+ import type { StripeClient } from '../../internal/types.js';
3
+ /**
4
+ * Creates an instance of a Stripe feepayer using an authenticated Stripe client.
5
+ *
6
+ * This compatibility path uses the Stripe client so that requests to the feepayer
7
+ * retain their authentication, timeouts, telemetry, and request events.
8
+ */
9
+ export declare function create(client: StripeClient): RemoteFeePayer.Config;
10
+ //# sourceMappingURL=hosted-fee-payer.d.ts.map