@proxy-checkout/stripe-server-js 0.1.0-prx-146.161.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +15 -113
  2. package/dist/cjs/errors.d.cts +1 -68
  3. package/dist/cjs/errors.d.ts +1 -68
  4. package/dist/cjs/errors.js +1 -86
  5. package/dist/cjs/index.d.cts +4 -10
  6. package/dist/cjs/index.d.ts +4 -10
  7. package/dist/cjs/index.js +1 -24
  8. package/dist/cjs/metadata.d.cts +2 -26
  9. package/dist/cjs/metadata.d.ts +2 -26
  10. package/dist/cjs/metadata.js +3 -73
  11. package/dist/cjs/open-checkout.d.cts +0 -21
  12. package/dist/cjs/open-checkout.d.ts +0 -21
  13. package/dist/cjs/open-checkout.js +38 -310
  14. package/dist/cjs/sync-checkout-cart.d.cts +3 -3
  15. package/dist/cjs/sync-checkout-cart.d.ts +3 -3
  16. package/dist/cjs/sync-checkout-cart.js +22 -92
  17. package/dist/cjs/types.d.cts +0 -199
  18. package/dist/cjs/types.d.ts +0 -199
  19. package/dist/cjs/version.d.cts +1 -1
  20. package/dist/cjs/version.d.ts +1 -1
  21. package/dist/cjs/version.js +1 -1
  22. package/dist/esm/errors.d.ts +1 -68
  23. package/dist/esm/errors.js +0 -80
  24. package/dist/esm/index.d.ts +4 -10
  25. package/dist/esm/index.js +2 -7
  26. package/dist/esm/metadata.d.ts +2 -26
  27. package/dist/esm/metadata.js +2 -69
  28. package/dist/esm/open-checkout.d.ts +0 -21
  29. package/dist/esm/open-checkout.js +40 -312
  30. package/dist/esm/sync-checkout-cart.d.ts +3 -3
  31. package/dist/esm/sync-checkout-cart.js +23 -93
  32. package/dist/esm/types.d.ts +0 -199
  33. package/dist/esm/version.d.ts +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/package.json +3 -3
  36. package/dist/cjs/acquisition.d.cts +0 -189
  37. package/dist/cjs/acquisition.d.ts +0 -189
  38. package/dist/cjs/acquisition.js +0 -789
  39. package/dist/cjs/compatibility.d.cts +0 -66
  40. package/dist/cjs/compatibility.d.ts +0 -66
  41. package/dist/cjs/compatibility.js +0 -174
  42. package/dist/cjs/open-payment-intent.d.cts +0 -35
  43. package/dist/cjs/open-payment-intent.d.ts +0 -35
  44. package/dist/cjs/open-payment-intent.js +0 -157
  45. package/dist/cjs/open-setup-intent.d.cts +0 -49
  46. package/dist/cjs/open-setup-intent.d.ts +0 -49
  47. package/dist/cjs/open-setup-intent.js +0 -157
  48. package/dist/cjs/open-subscription.d.cts +0 -75
  49. package/dist/cjs/open-subscription.d.ts +0 -75
  50. package/dist/cjs/open-subscription.js +0 -307
  51. package/dist/cjs/subscription-params.d.cts +0 -33
  52. package/dist/cjs/subscription-params.d.ts +0 -33
  53. package/dist/cjs/subscription-params.js +0 -237
  54. package/dist/esm/acquisition.d.ts +0 -189
  55. package/dist/esm/acquisition.js +0 -763
  56. package/dist/esm/compatibility.d.ts +0 -66
  57. package/dist/esm/compatibility.js +0 -168
  58. package/dist/esm/open-payment-intent.d.ts +0 -35
  59. package/dist/esm/open-payment-intent.js +0 -154
  60. package/dist/esm/open-setup-intent.d.ts +0 -49
  61. package/dist/esm/open-setup-intent.js +0 -153
  62. package/dist/esm/open-subscription.d.ts +0 -75
  63. package/dist/esm/open-subscription.js +0 -304
  64. package/dist/esm/subscription-params.d.ts +0 -33
  65. package/dist/esm/subscription-params.js +0 -232
@@ -8,22 +8,17 @@
8
8
  * provides assertions that a Stripe object actually belongs to a Proxy session.
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.PROXY_CONTRACT_VERSION_METADATA_KEY = exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY = exports.PROXY_INTEGRATION_PATH_METADATA_KEY = exports.PROXY_SESSION_METADATA_KEY = void 0;
11
+ exports.PROXY_INTEGRATION_PATH_METADATA_KEY = exports.PROXY_SESSION_METADATA_KEY = void 0;
12
12
  exports.requiredMetadata = requiredMetadata;
13
13
  exports.injectRequiredCheckoutSessionMetadata = injectRequiredCheckoutSessionMetadata;
14
- exports.assertCheckoutSessionMetadataPreflight = assertCheckoutSessionMetadataPreflight;
15
- exports.injectRequiredPaymentIntentMetadata = injectRequiredPaymentIntentMetadata;
16
14
  exports.assertCheckoutSessionBelongsToProxy = assertCheckoutSessionBelongsToProxy;
17
15
  exports.assertSubscriptionBelongsToProxy = assertSubscriptionBelongsToProxy;
18
16
  exports.assertPaymentIntentBelongsToProxy = assertPaymentIntentBelongsToProxy;
19
- exports.assertProviderObjectBelongsToAcquisition = assertProviderObjectBelongsToAcquisition;
20
17
  const server_js_1 = require("@proxy-checkout/server-js");
21
18
  /** The single metadata key Proxy reads to correlate a Stripe object to a session. */
22
19
  exports.PROXY_SESSION_METADATA_KEY = "proxy_session_id";
23
20
  /** Server-owned marker that distinguishes Checkout-created child objects from direct objects. */
24
21
  exports.PROXY_INTEGRATION_PATH_METADATA_KEY = "proxy_integration_path";
25
- exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY = "proxy_acquisition_attempt_id";
26
- exports.PROXY_CONTRACT_VERSION_METADATA_KEY = "proxy_contract_version";
27
22
  /** Build the required Stripe metadata placements for a Proxy session. */
28
23
  function requiredMetadata(proxySession) {
29
24
  const metadata = { proxy_session_id: proxySession.id };
@@ -40,8 +35,8 @@ function requiredMetadata(proxySession) {
40
35
  * every object Proxy may later need to correlate. Conflicting merchant-provided
41
36
  * proxy metadata fails before any Stripe side effect.
42
37
  */
43
- function injectRequiredCheckoutSessionMetadata(params, proxySessionId, acquisition) {
44
- const metadata = acquisitionMetadata(proxySessionId, acquisition);
38
+ function injectRequiredCheckoutSessionMetadata(params, proxySessionId) {
39
+ const metadata = { proxy_session_id: proxySessionId };
45
40
  const checkoutMetadata = {
46
41
  ...metadata,
47
42
  [exports.PROXY_INTEGRATION_PATH_METADATA_KEY]: "checkout_session",
@@ -67,44 +62,6 @@ function injectRequiredCheckoutSessionMetadata(params, proxySessionId, acquisiti
67
62
  : {}),
68
63
  };
69
64
  }
70
- /**
71
- * Validate merchant-authored Checkout metadata before reserving an acquisition.
72
- * The acquisition ID does not exist yet, so those reservation-owned keys must
73
- * be absent until the shared create owner injects their exact values.
74
- */
75
- function assertCheckoutSessionMetadataPreflight(params, proxySessionId) {
76
- for (const [field, metadata] of [
77
- ["metadata", params.metadata],
78
- ["payment_intent_data.metadata", metadataFromNestedParams(params.payment_intent_data)],
79
- ["subscription_data.metadata", metadataFromNestedParams(params.subscription_data)],
80
- ]) {
81
- for (const key of [
82
- exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY,
83
- exports.PROXY_CONTRACT_VERSION_METADATA_KEY,
84
- ]) {
85
- if (metadata?.[key] !== undefined) {
86
- throw new server_js_1.ProxyCheckoutValidationError(`Stripe ${field}.${key} is owned by the Proxy acquisition reservation and must not be supplied by the merchant.`, { code: "provider_binding_mismatch", field: `${field}.${key}` });
87
- }
88
- }
89
- }
90
- // Pure construction performs every session/path conflict check without
91
- // contacting Proxy or Stripe. The returned params are intentionally ignored;
92
- // the reservation-derived injection still happens exactly once in create.
93
- injectRequiredCheckoutSessionMetadata(params, proxySessionId);
94
- }
95
- /** Inject the server-owned acquisition identity into a direct PaymentIntent. */
96
- function injectRequiredPaymentIntentMetadata(params, input) {
97
- const required = {
98
- [exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY]: input.acquisitionAttemptId,
99
- [exports.PROXY_CONTRACT_VERSION_METADATA_KEY]: "2",
100
- [exports.PROXY_INTEGRATION_PATH_METADATA_KEY]: "payment_intent",
101
- [exports.PROXY_SESSION_METADATA_KEY]: input.proxySessionId,
102
- };
103
- return {
104
- ...params,
105
- metadata: mergeProxyMetadata(params.metadata, required, "metadata"),
106
- };
107
- }
108
65
  /** Assert a Stripe Checkout Session carries the expected `proxy_session_id` metadata. */
109
66
  function assertCheckoutSessionBelongsToProxy(checkoutSession, proxySessionId) {
110
67
  assertProxyMetadata(checkoutSession.metadata, proxySessionId, "Stripe checkout session");
@@ -117,39 +74,12 @@ function assertSubscriptionBelongsToProxy(subscription, proxySessionId) {
117
74
  function assertPaymentIntentBelongsToProxy(paymentIntent, proxySessionId) {
118
75
  assertProxyMetadata(paymentIntent.metadata, proxySessionId, "Stripe payment intent");
119
76
  }
120
- /** Assert the provider root carries the exact reserved acquisition identity. */
121
- function assertProviderObjectBelongsToAcquisition(object, input) {
122
- assertProxyMetadata(object.metadata, input.proxySessionId, "Stripe provider object");
123
- const acquisitionId = object.metadata?.[exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY];
124
- const contractVersion = object.metadata?.[exports.PROXY_CONTRACT_VERSION_METADATA_KEY];
125
- const integrationPath = object.metadata?.[exports.PROXY_INTEGRATION_PATH_METADATA_KEY];
126
- if (acquisitionId !== input.acquisitionAttemptId) {
127
- throw new server_js_1.ProxyCheckoutValidationError(`Stripe provider object metadata.${exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY} does not match the reserved Proxy acquisition.`, { code: "provider_binding_mismatch", field: exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY });
128
- }
129
- if (contractVersion !== "2") {
130
- throw new server_js_1.ProxyCheckoutValidationError(`Stripe provider object metadata.${exports.PROXY_CONTRACT_VERSION_METADATA_KEY} must be 2.`, { code: "provider_binding_mismatch", field: exports.PROXY_CONTRACT_VERSION_METADATA_KEY });
131
- }
132
- if (integrationPath !== input.integrationPath) {
133
- throw new server_js_1.ProxyCheckoutValidationError(`Stripe provider object metadata.${exports.PROXY_INTEGRATION_PATH_METADATA_KEY} does not match ${input.integrationPath}.`, { code: "provider_binding_mismatch", field: exports.PROXY_INTEGRATION_PATH_METADATA_KEY });
134
- }
135
- }
136
77
  function assertProxyMetadata(metadata, proxySessionId, objectLabel) {
137
78
  const value = metadata?.[exports.PROXY_SESSION_METADATA_KEY];
138
79
  if (value !== proxySessionId) {
139
80
  throw new server_js_1.ProxyCheckoutValidationError(`${objectLabel} metadata.${exports.PROXY_SESSION_METADATA_KEY} (${value ?? "missing"}) does not match Proxy session ${proxySessionId}.`, { code: "provider_binding_mismatch", field: `metadata.${exports.PROXY_SESSION_METADATA_KEY}` });
140
81
  }
141
82
  }
142
- function acquisitionMetadata(proxySessionId, acquisition) {
143
- return {
144
- ...(acquisition
145
- ? {
146
- [exports.PROXY_ACQUISITION_ATTEMPT_METADATA_KEY]: acquisition.id,
147
- [exports.PROXY_CONTRACT_VERSION_METADATA_KEY]: acquisition.contractVersion,
148
- }
149
- : {}),
150
- [exports.PROXY_SESSION_METADATA_KEY]: proxySessionId,
151
- };
152
- }
153
83
  function mergeProxyMetadata(existing, required, field) {
154
84
  for (const [key, requiredValue] of Object.entries(required)) {
155
85
  const existingValue = existing?.[key];
@@ -7,7 +7,6 @@
7
7
  * metadata placement, idempotency, and binding reconciliation.
8
8
  */
9
9
  import type { JsonObject, PayerOpenedResult, ProxyCartValidator, ProxyCheckoutServerClient, ProxySessionProviderBinding } from "@proxy-checkout/server-js";
10
- import { type StripeCompatibilityDescriptor } from "./compatibility.js";
11
10
  import type { StripeCheckoutSessionCreateParams, StripeCheckoutSessionLike, StripeClientLike, StripeRequestOptionsWithoutIdempotencyLike } from "./types.js";
12
11
  export interface OpenCheckoutOptions<TStripe extends StripeClientLike, TCart, TOffer> {
13
12
  /** Builds app-specific Stripe Checkout Session params. Proxy metadata is injected by the SDK. */
@@ -19,13 +18,8 @@ export interface OpenCheckoutOptions<TStripe extends StripeClientLike, TCart, TO
19
18
  }) => Promise<StripeCheckoutSessionCreateParams> | StripeCheckoutSessionCreateParams;
20
19
  /** Optional schema validating the session cart snapshot into a typed cart. */
21
20
  readonly cartSchema?: ProxyCartValidator<TCart>;
22
- /** New acquisition contract. Omit with the other acquisition fields only for 0.0.x compatibility. */
23
- readonly commercialMode?: "one_time" | "subscription";
24
- readonly compatibility?: StripeCompatibilityDescriptor;
25
21
  /** Optional extractor used to assert the cart buyer reference matches the session. */
26
22
  readonly getCartBuyerReference?: (cart: TCart) => string | null | undefined;
27
- /** New acquisition contract. Defaults are intentionally not guessed for explicit callers. */
28
- readonly pricingMode?: "fixed" | "provider_finalized";
29
23
  /** A Proxy server SDK client. */
30
24
  readonly proxy: ProxyCheckoutServerClient;
31
25
  readonly proxySessionId: string;
@@ -40,12 +34,8 @@ export interface OpenCheckoutOptions<TStripe extends StripeClientLike, TCart, TO
40
34
  readonly requestId?: string;
41
35
  /** The merchant's Stripe instance. */
42
36
  readonly stripe: TStripe;
43
- /** Convenience alias for compatibility.apiVersion. */
44
- readonly stripeApiVersion?: string;
45
37
  /** Optional Stripe request options. The SDK owns the deterministic idempotency key. */
46
38
  readonly stripeRequestOptions?: StripeRequestOptionsWithoutIdempotencyLike;
47
- /** Semantic Checkout presentation. Provider aliases are normalized per Stripe generation. */
48
- readonly uiMode?: "custom" | "embedded" | "hosted";
49
39
  /** Optional merchant cart validation that derives an app-specific offer. */
50
40
  readonly validateCart?: (input: {
51
41
  cart: TCart;
@@ -59,26 +49,15 @@ export interface OpenCheckoutReconciledCart<TCart> {
59
49
  readonly currency: string;
60
50
  }
61
51
  export interface OpenCheckoutResult<TCart, TOffer, TSession> {
62
- readonly acquisitionAttemptId?: string;
63
52
  readonly binding: ProxySessionProviderBinding;
64
53
  readonly cart: TCart;
65
54
  readonly checkoutSession: TSession;
66
- readonly checkoutSessionId?: string;
67
55
  readonly clientSecret: string | null;
68
56
  readonly offer: TOffer;
69
57
  readonly outcome: "ready";
70
- readonly presentation?: {
71
- readonly kind: "client_secret";
72
- readonly clientSecret: string;
73
- readonly uiMode: "custom" | "embedded";
74
- } | {
75
- readonly kind: "redirect";
76
- readonly url: string;
77
- };
78
58
  readonly proxySession: PayerOpenedResult;
79
59
  readonly proxySessionId: string;
80
60
  }
81
- export type OpenCheckoutPresentation = NonNullable<OpenCheckoutResult<unknown, unknown, unknown>["presentation"]>;
82
61
  export type OpenCheckoutTerminalResult<TCart> = {
83
62
  readonly cart: TCart;
84
63
  readonly outcome: "already_paid";
@@ -7,7 +7,6 @@
7
7
  * metadata placement, idempotency, and binding reconciliation.
8
8
  */
9
9
  import type { JsonObject, PayerOpenedResult, ProxyCartValidator, ProxyCheckoutServerClient, ProxySessionProviderBinding } from "@proxy-checkout/server-js";
10
- import { type StripeCompatibilityDescriptor } from "./compatibility.js";
11
10
  import type { StripeCheckoutSessionCreateParams, StripeCheckoutSessionLike, StripeClientLike, StripeRequestOptionsWithoutIdempotencyLike } from "./types.js";
12
11
  export interface OpenCheckoutOptions<TStripe extends StripeClientLike, TCart, TOffer> {
13
12
  /** Builds app-specific Stripe Checkout Session params. Proxy metadata is injected by the SDK. */
@@ -19,13 +18,8 @@ export interface OpenCheckoutOptions<TStripe extends StripeClientLike, TCart, TO
19
18
  }) => Promise<StripeCheckoutSessionCreateParams> | StripeCheckoutSessionCreateParams;
20
19
  /** Optional schema validating the session cart snapshot into a typed cart. */
21
20
  readonly cartSchema?: ProxyCartValidator<TCart>;
22
- /** New acquisition contract. Omit with the other acquisition fields only for 0.0.x compatibility. */
23
- readonly commercialMode?: "one_time" | "subscription";
24
- readonly compatibility?: StripeCompatibilityDescriptor;
25
21
  /** Optional extractor used to assert the cart buyer reference matches the session. */
26
22
  readonly getCartBuyerReference?: (cart: TCart) => string | null | undefined;
27
- /** New acquisition contract. Defaults are intentionally not guessed for explicit callers. */
28
- readonly pricingMode?: "fixed" | "provider_finalized";
29
23
  /** A Proxy server SDK client. */
30
24
  readonly proxy: ProxyCheckoutServerClient;
31
25
  readonly proxySessionId: string;
@@ -40,12 +34,8 @@ export interface OpenCheckoutOptions<TStripe extends StripeClientLike, TCart, TO
40
34
  readonly requestId?: string;
41
35
  /** The merchant's Stripe instance. */
42
36
  readonly stripe: TStripe;
43
- /** Convenience alias for compatibility.apiVersion. */
44
- readonly stripeApiVersion?: string;
45
37
  /** Optional Stripe request options. The SDK owns the deterministic idempotency key. */
46
38
  readonly stripeRequestOptions?: StripeRequestOptionsWithoutIdempotencyLike;
47
- /** Semantic Checkout presentation. Provider aliases are normalized per Stripe generation. */
48
- readonly uiMode?: "custom" | "embedded" | "hosted";
49
39
  /** Optional merchant cart validation that derives an app-specific offer. */
50
40
  readonly validateCart?: (input: {
51
41
  cart: TCart;
@@ -59,26 +49,15 @@ export interface OpenCheckoutReconciledCart<TCart> {
59
49
  readonly currency: string;
60
50
  }
61
51
  export interface OpenCheckoutResult<TCart, TOffer, TSession> {
62
- readonly acquisitionAttemptId?: string;
63
52
  readonly binding: ProxySessionProviderBinding;
64
53
  readonly cart: TCart;
65
54
  readonly checkoutSession: TSession;
66
- readonly checkoutSessionId?: string;
67
55
  readonly clientSecret: string | null;
68
56
  readonly offer: TOffer;
69
57
  readonly outcome: "ready";
70
- readonly presentation?: {
71
- readonly kind: "client_secret";
72
- readonly clientSecret: string;
73
- readonly uiMode: "custom" | "embedded";
74
- } | {
75
- readonly kind: "redirect";
76
- readonly url: string;
77
- };
78
58
  readonly proxySession: PayerOpenedResult;
79
59
  readonly proxySessionId: string;
80
60
  }
81
- export type OpenCheckoutPresentation = NonNullable<OpenCheckoutResult<unknown, unknown, unknown>["presentation"]>;
82
61
  export type OpenCheckoutTerminalResult<TCart> = {
83
62
  readonly cart: TCart;
84
63
  readonly outcome: "already_paid";
@@ -10,15 +10,10 @@
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.openCheckout = openCheckout;
12
12
  const server_js_1 = require("@proxy-checkout/server-js");
13
- const acquisition_js_1 = require("./acquisition.js");
14
- const compatibility_js_1 = require("./compatibility.js");
15
13
  const errors_js_1 = require("./errors.js");
16
14
  const metadata_js_1 = require("./metadata.js");
17
15
  const psp_js_1 = require("./psp.js");
18
16
  async function openCheckout(options) {
19
- if (usesAcquisitionContract(options)) {
20
- return openAcquisitionCheckout(options);
21
- }
22
17
  // Normalize the same way the API does on store, so the binding is recorded
23
18
  // under the normalized psp and later syncCheckoutCart validations compare
24
19
  // normalized-to-normalized.
@@ -138,7 +133,7 @@ async function openCheckout(options) {
138
133
  const checkoutSession = (await options.stripe.checkout.sessions.create((0, metadata_js_1.injectRequiredCheckoutSessionMetadata)(params, proxySession.id), buildStripeRequestOptions(options, proxySession.id, psp)));
139
134
  // Fail closed if the merchant forgot to place proxy_session_id metadata.
140
135
  (0, metadata_js_1.assertCheckoutSessionBelongsToProxy)(checkoutSession, proxySession.id);
141
- const binding = await (0, acquisition_js_1.recordOrReconcileLegacyCheckoutBinding)({
136
+ const binding = await recordOrReconcileProviderBinding({
142
137
  checkoutSessionId: checkoutSession.id,
143
138
  expectedCartVersion: proxySession.cartVersion,
144
139
  commercialMode,
@@ -160,310 +155,6 @@ async function openCheckout(options) {
160
155
  proxySessionId: proxySession.id,
161
156
  };
162
157
  }
163
- async function openAcquisitionCheckout(options) {
164
- if (options.commercialMode !== "one_time" ||
165
- (options.uiMode !== "hosted" && options.uiMode !== "embedded" && options.uiMode !== "custom") ||
166
- options.pricingMode === undefined) {
167
- throw new errors_js_1.UnsupportedStripeOptionError("The explicit Checkout acquisition contract supports one_time hosted, embedded, and custom presentations only.", "uiMode");
168
- }
169
- if ((0, psp_js_1.normalizePsp)(options.psp ?? "stripe") !== "stripe") {
170
- throw new errors_js_1.UnsupportedStripeOptionError("The Stripe acquisition helper cannot route provider creation through another PSP.", "psp");
171
- }
172
- (0, acquisition_js_1.assertNoConnectRequestOptions)(options.stripeRequestOptions);
173
- const compatibility = compatibilityDescriptor(options);
174
- (0, compatibility_js_1.assertStripePathCompatibility)(checkoutCompatibilityPath(options.uiMode), compatibility);
175
- let proxySession = await options.proxy.sessions.payerOpened(options.proxySessionId, {
176
- requestId: options.requestId,
177
- });
178
- let cart = parseCheckoutCart(options, proxySession);
179
- if (proxySession.presentationState !== "checkout_available") {
180
- return terminalResultFromSession(proxySession, cart);
181
- }
182
- let offer = options.validateCart
183
- ? await options.validateCart({ cart, session: proxySession })
184
- : undefined;
185
- const reconciledCart = await options.reconcileCart?.({ cart, offer, session: proxySession });
186
- if (reconciledCart) {
187
- if (options.getCartBuyerReference) {
188
- (0, server_js_1.assertCartBuyerReference)(options.getCartBuyerReference(reconciledCart.cart), proxySession);
189
- }
190
- try {
191
- const updated = await options.proxy.sessions.cart.set(proxySession.id, {
192
- amountMinor: reconciledCart.amountMinor,
193
- cartSnapshot: reconciledCart.cartSnapshot,
194
- currency: reconciledCart.currency,
195
- expectedCartVersion: proxySession.cartVersion,
196
- requestId: options.requestId,
197
- });
198
- proxySession = {
199
- ...proxySession,
200
- amountMinor: updated.amountMinor,
201
- cartSnapshot: updated.cartSnapshot,
202
- cartVersion: updated.cartVersion,
203
- currency: updated.currency,
204
- };
205
- }
206
- catch (cause) {
207
- if (!isProxyConflict(cause))
208
- throw cause;
209
- const joined = await options.proxy.sessions.payerOpened(proxySession.id, {
210
- requestId: options.requestId,
211
- });
212
- if (!sameCheckoutCart(joined, reconciledCart))
213
- throw cause;
214
- proxySession = joined;
215
- }
216
- cart = reconciledCart.cart;
217
- if (options.validateCart)
218
- offer = await options.validateCart({ cart, session: proxySession });
219
- }
220
- const current = await options.proxy.sessions.payerOpened(proxySession.id, {
221
- requestId: options.requestId,
222
- });
223
- if (current.presentationState !== "checkout_available") {
224
- return terminalResultFromSession(current, cart);
225
- }
226
- if (!sameCheckoutCart(current, proxySession)) {
227
- throw new server_js_1.ProxyCheckoutValidationError(`Proxy session ${proxySession.id} cart changed while Checkout was being prepared.`, { code: "cart_version_conflict", field: "cart_version" });
228
- }
229
- proxySession = current;
230
- const builtParams = await options.buildCheckoutSessionParams({
231
- cart,
232
- offer,
233
- proxySession,
234
- stripe: options.stripe,
235
- });
236
- validateAcquisitionCheckoutParams(builtParams, options, proxySession);
237
- (0, metadata_js_1.assertCheckoutSessionMetadataPreflight)(builtParams, proxySession.id);
238
- // Stripe 12 has no hosted ui_mode create field, while newer Stripe responses
239
- // name the same provider value hosted_page. Omit only the hosted provider
240
- // alias; client-secret paths must send their exact generation-specific mode.
241
- const params = options.uiMode === "hosted" ? withoutHostedProviderUiMode(builtParams) : builtParams;
242
- if (options.pricingMode === "fixed" && proxySession.amountMinor === 0) {
243
- (0, compatibility_js_1.assertHostedNoCostCompatibility)(compatibility);
244
- }
245
- const providerOptionsFingerprint = (0, acquisition_js_1.fingerprintHostedCheckoutProviderOptions)(params);
246
- const acquisitionResult = await (0, acquisition_js_1.acquireStripeRoot)({
247
- checkoutUiMode: options.uiMode,
248
- commercialMode: "one_time",
249
- integrationPath: "checkout_session",
250
- objectType: "checkout_session",
251
- pricingMode: options.pricingMode,
252
- create: async (reservation) => {
253
- const created = (await options.stripe.checkout.sessions.create((0, metadata_js_1.injectRequiredCheckoutSessionMetadata)(params, proxySession.id, {
254
- contractVersion: "2",
255
- id: reservation.acquisitionAttempt.id,
256
- }), {
257
- ...options.stripeRequestOptions,
258
- idempotencyKey: reservation.stripe.idempotencyKeys.root,
259
- }));
260
- return (await (0, acquisition_js_1.retrieveCheckoutSessionWithLineEvidence)({
261
- id: created.id,
262
- initial: created,
263
- requestOptions: options.stripeRequestOptions,
264
- stripe: options.stripe,
265
- }));
266
- },
267
- providerOptionsFingerprint,
268
- proxy: options.proxy,
269
- proxySession,
270
- requestId: options.requestId,
271
- retrieve: async (id) => (await (0, acquisition_js_1.retrieveCheckoutSessionWithLineEvidence)({
272
- id,
273
- requestOptions: options.stripeRequestOptions,
274
- stripe: options.stripe,
275
- })),
276
- toConfirmation: (session) => (0, acquisition_js_1.checkoutCartConfirmation)({
277
- cartVersion: proxySession.cartVersion,
278
- checkoutSession: session,
279
- expectedLineItems: params.line_items,
280
- }),
281
- validateObject: (session, acquisition) => {
282
- (0, metadata_js_1.assertProviderObjectBelongsToAcquisition)(session, {
283
- acquisitionAttemptId: acquisition.id,
284
- integrationPath: "checkout_session",
285
- proxySessionId: proxySession.id,
286
- });
287
- assertCheckoutProviderCart(session, proxySession, options.pricingMode ?? "fixed", params.line_items, options.uiMode ?? "hosted");
288
- },
289
- });
290
- if (acquisitionResult.outcome === "terminal") {
291
- return terminalResultFromSession(acquisitionResult.proxySession, cart);
292
- }
293
- const acquired = acquisitionResult;
294
- const presentation = checkoutPresentation(acquired.object, options.uiMode);
295
- return {
296
- acquisitionAttemptId: acquired.acquisition.id,
297
- binding: {
298
- providerCheckoutSessionId: acquired.object.id,
299
- proxySessionId: proxySession.id,
300
- psp: "stripe",
301
- updatedAt: acquired.acquisition.updatedAt,
302
- },
303
- cart,
304
- checkoutSession: acquired.object,
305
- checkoutSessionId: acquired.object.id,
306
- clientSecret: presentation.kind === "client_secret" ? presentation.clientSecret : null,
307
- offer,
308
- outcome: "ready",
309
- presentation,
310
- proxySession,
311
- proxySessionId: proxySession.id,
312
- };
313
- }
314
- function usesAcquisitionContract(options) {
315
- const values = [options.commercialMode, options.pricingMode, options.uiMode];
316
- const count = values.filter((value) => value !== undefined).length;
317
- if (count !== 0 && count !== values.length) {
318
- throw new server_js_1.ProxyCheckoutValidationError("commercialMode, pricingMode, and uiMode must be supplied together for the acquisition contract.", { code: "unsupported_provider_option", field: "commercialMode" });
319
- }
320
- return count === values.length;
321
- }
322
- function parseCheckoutCart(options, proxySession) {
323
- return options.cartSchema || options.getCartBuyerReference
324
- ? options.proxy.sessions.parseCart(proxySession, options.cartSchema ?? ((value) => value), { getBuyerReference: options.getCartBuyerReference })
325
- : proxySession.cartSnapshot;
326
- }
327
- function compatibilityDescriptor(options) {
328
- if (!options.compatibility && !options.stripeApiVersion)
329
- return undefined;
330
- return {
331
- ...options.compatibility,
332
- ...(options.stripeApiVersion ? { apiVersion: options.stripeApiVersion } : {}),
333
- };
334
- }
335
- function validateAcquisitionCheckoutParams(params, options, proxySession) {
336
- assertCheckoutAutomaticRecoveryDisabled(params, proxySession.id);
337
- if (checkoutCommercialMode(params, proxySession.id) !== options.commercialMode) {
338
- throw new errors_js_1.UnsupportedStripeOptionError("Stripe Checkout mode does not match commercialMode.", "mode");
339
- }
340
- const uiMode = normalizeProviderUiMode(params.ui_mode);
341
- if (uiMode !== options.uiMode) {
342
- throw new errors_js_1.UnsupportedStripeOptionError(`Stripe Checkout ui_mode ${uiMode} does not match requested ${options.uiMode}.`, "ui_mode");
343
- }
344
- if (uiMode === "hosted" && !params.success_url) {
345
- throw new errors_js_1.UnsupportedStripeOptionError("Hosted Stripe Checkout requires success_url.", "success_url");
346
- }
347
- if (uiMode === "hosted" && params.return_url !== undefined) {
348
- throw new errors_js_1.UnsupportedStripeOptionError("Hosted Stripe Checkout uses success_url and must not set return_url.", "return_url");
349
- }
350
- if (uiMode !== "hosted" && !params.return_url) {
351
- throw new errors_js_1.UnsupportedStripeOptionError(`${uiMode} Stripe Checkout requires return_url.`, "return_url");
352
- }
353
- if (uiMode !== "hosted" && params.success_url !== undefined) {
354
- throw new errors_js_1.UnsupportedStripeOptionError(`${uiMode} Stripe Checkout uses return_url and must not set success_url.`, "success_url");
355
- }
356
- const paymentIntentData = objectRecord(params.payment_intent_data);
357
- for (const field of [
358
- "application_fee_amount",
359
- "application_fee_percent",
360
- "capture_method",
361
- "on_behalf_of",
362
- "transfer_data",
363
- "transfer_group",
364
- ]) {
365
- if (paymentIntentData[field] !== undefined) {
366
- throw new errors_js_1.UnsupportedStripeOptionError(`One-time Checkout does not support payment_intent_data.${field}.`, `payment_intent_data.${field}`);
367
- }
368
- }
369
- if (options.pricingMode === "fixed") {
370
- for (const field of [
371
- "allow_promotion_codes",
372
- "shipping_address_collection",
373
- "shipping_options",
374
- ]) {
375
- const value = params[field];
376
- if (value !== undefined && value !== false) {
377
- throw new errors_js_1.UnsupportedStripeOptionError(`Fixed-price Checkout cannot use provider-finalized option ${field}.`, field);
378
- }
379
- }
380
- for (const field of ["adaptive_pricing", "automatic_tax"]) {
381
- const value = objectRecord(params[field]);
382
- if (value.enabled === true) {
383
- throw new errors_js_1.UnsupportedStripeOptionError(`Fixed-price Checkout cannot enable provider-finalized option ${field}.`, `${field}.enabled`);
384
- }
385
- }
386
- const lineItems = Array.isArray(params.line_items) ? params.line_items : [];
387
- if (lineItems.some((line) => objectRecord(objectRecord(line).adjustable_quantity).enabled === true)) {
388
- throw new errors_js_1.UnsupportedStripeOptionError("Fixed-price Checkout cannot enable adjustable line-item quantities.", "line_items.adjustable_quantity.enabled");
389
- }
390
- }
391
- }
392
- function assertCheckoutProviderCart(checkoutSession, proxySession, pricingMode, expectedLineItems, requestedUiMode) {
393
- const confirmation = (0, acquisition_js_1.checkoutCartConfirmation)({
394
- cartVersion: 0,
395
- checkoutSession,
396
- expectedLineItems,
397
- });
398
- if (checkoutSession.mode !== undefined &&
399
- checkoutSession.mode !== null &&
400
- checkoutSession.mode !== "payment") {
401
- throw new errors_js_1.StripeResponseShapeError("Stripe Checkout response is not payment mode.", "mode");
402
- }
403
- if (normalizeProviderUiMode(checkoutSession.ui_mode) !== requestedUiMode) {
404
- throw new errors_js_1.StripeResponseShapeError(`Stripe Checkout response is not ${requestedUiMode} UI mode.`, "ui_mode");
405
- }
406
- if (confirmation.currency !== proxySession.currency ||
407
- (pricingMode === "fixed" && confirmation.amountMinor !== proxySession.amountMinor)) {
408
- throw new server_js_1.ProxyCheckoutValidationError(`Stripe Checkout Session ${checkoutSession.id} does not represent the current Proxy cart amount and currency.`, { code: "cart_version_conflict", field: "provider_amount" });
409
- }
410
- }
411
- function normalizeProviderUiMode(value) {
412
- if (value === undefined || value === null || value === "hosted" || value === "hosted_page") {
413
- return "hosted";
414
- }
415
- if (value === "embedded" || value === "embedded_page")
416
- return "embedded";
417
- if (value === "custom" || value === "elements")
418
- return "custom";
419
- return value;
420
- }
421
- function checkoutCompatibilityPath(uiMode) {
422
- return `one_time.checkout.${uiMode}`;
423
- }
424
- function checkoutPresentation(checkoutSession, uiMode) {
425
- const clientSecret = checkoutSession.client_secret;
426
- const url = checkoutSession.url;
427
- if (uiMode === "hosted") {
428
- if (!isSafeHostedCheckoutUrl(url)) {
429
- throw new errors_js_1.StripeResponseShapeError("Hosted Stripe Checkout response did not contain a safe HTTPS redirect URL.", "url");
430
- }
431
- if (clientSecret !== undefined && clientSecret !== null) {
432
- throw new errors_js_1.StripeResponseShapeError("Hosted Stripe Checkout response unexpectedly contained a client secret.", "client_secret");
433
- }
434
- return { kind: "redirect", url };
435
- }
436
- if (!isCheckoutSessionClientSecret(clientSecret)) {
437
- throw new errors_js_1.StripeResponseShapeError(`${uiMode} Stripe Checkout response did not contain a valid Checkout Session client secret.`, "client_secret");
438
- }
439
- if (url !== undefined && url !== null) {
440
- throw new errors_js_1.StripeResponseShapeError(`${uiMode} Stripe Checkout response unexpectedly contained a hosted redirect URL.`, "url");
441
- }
442
- return { clientSecret, kind: "client_secret", uiMode };
443
- }
444
- function isCheckoutSessionClientSecret(value) {
445
- return typeof value === "string" && /^cs_(?:test|live)_[A-Za-z0-9_]+$/.test(value);
446
- }
447
- function isSafeHostedCheckoutUrl(value) {
448
- if (typeof value !== "string" || value !== value.trim())
449
- return false;
450
- try {
451
- const parsed = new URL(value);
452
- return parsed.protocol === "https:" && parsed.username === "" && parsed.password === "";
453
- }
454
- catch {
455
- return false;
456
- }
457
- }
458
- function withoutHostedProviderUiMode(params) {
459
- if (params.ui_mode === undefined)
460
- return params;
461
- const { ui_mode: _providerUiMode, ...providerParams } = params;
462
- return providerParams;
463
- }
464
- function objectRecord(value) {
465
- return typeof value === "object" && value !== null ? value : {};
466
- }
467
158
  function terminalResultFromSession(proxySession, cart) {
468
159
  const common = {
469
160
  cart,
@@ -523,6 +214,31 @@ function bindingFromSession(proxySession) {
523
214
  updatedAt: proxySession.updatedAt,
524
215
  };
525
216
  }
217
+ async function recordOrReconcileProviderBinding(input) {
218
+ try {
219
+ return await input.proxy.sessions.recordProviderBinding(input.proxySessionId, {
220
+ commercialMode: input.commercialMode,
221
+ expectedCartVersion: input.expectedCartVersion,
222
+ providerCheckoutSessionId: input.checkoutSessionId,
223
+ psp: input.psp,
224
+ requestId: input.requestId,
225
+ });
226
+ }
227
+ catch (cause) {
228
+ const reconciled = await readReconciledBinding(input).catch(() => undefined);
229
+ if (reconciled) {
230
+ return reconciled;
231
+ }
232
+ if (isProxyConflict(cause)) {
233
+ throw cause;
234
+ }
235
+ throw new errors_js_1.ProxyStripeOpenCheckoutError("Stripe Checkout Session was created but Proxy provider binding could not be recorded.", {
236
+ cause,
237
+ code: "provider_binding_failed",
238
+ providerCheckoutSessionId: input.checkoutSessionId,
239
+ });
240
+ }
241
+ }
526
242
  function sameCheckoutCart(actual, expected) {
527
243
  return (actual.amountMinor === expected.amountMinor &&
528
244
  actual.currency === expected.currency &&
@@ -554,6 +270,18 @@ function assertCheckoutAutomaticRecoveryDisabled(params, proxySessionId) {
554
270
  throw new server_js_1.ProxyCheckoutValidationError(`Proxy session ${proxySessionId} does not allow Stripe Checkout automatic recovery because replacement roots require explicit Proxy supersession.`, { code: "unsupported_provider_option", field: "after_expiration.recovery.enabled" });
555
271
  }
556
272
  }
273
+ async function readReconciledBinding(input) {
274
+ const current = await input.proxy.sessions.retrieve(input.proxySessionId, {
275
+ requestId: input.requestId,
276
+ });
277
+ if (current.providerCheckoutSessionId !== input.checkoutSessionId ||
278
+ current.providerCheckoutSessionPsp !== input.psp) {
279
+ return undefined;
280
+ }
281
+ const checkoutSession = await input.stripe.checkout.sessions.retrieve(input.checkoutSessionId, undefined, input.stripeRequestOptions);
282
+ (0, metadata_js_1.assertCheckoutSessionBelongsToProxy)(checkoutSession, input.proxySessionId);
283
+ return bindingFromSession(current);
284
+ }
557
285
  function isProxyConflict(error) {
558
286
  return (typeof error === "object" &&
559
287
  error !== null &&
@@ -6,7 +6,7 @@
6
6
  * safest order with deterministic rollback/reconciliation on failure.
7
7
  */
8
8
  import { type JsonObject, type MerchantProxySession, type ProxyCartValidator, type ProxyCheckoutServerClient } from "@proxy-checkout/server-js";
9
- import type { StripeCheckoutSessionLike, StripeMutableCheckoutClientLike, StripeRequestOptionsWithoutIdempotencyLike } from "./types.js";
9
+ import type { StripeCheckoutSessionLike, StripeClientLike, StripeRequestOptionsWithoutIdempotencyLike } from "./types.js";
10
10
  /** The next cart, expressed both as Proxy state and the typed merchant cart. */
11
11
  export interface SyncedCart<TCart> {
12
12
  readonly amountMinor: number;
@@ -14,7 +14,7 @@ export interface SyncedCart<TCart> {
14
14
  readonly cartSnapshot: JsonObject;
15
15
  readonly currency: string;
16
16
  }
17
- export interface SyncCheckoutCartOptions<TStripe extends StripeMutableCheckoutClientLike, TCart, TInput> {
17
+ export interface SyncCheckoutCartOptions<TStripe extends StripeClientLike, TCart, TInput> {
18
18
  /** Compute the next cart from current state and the merchant input. */
19
19
  readonly buildNextCart: (input: {
20
20
  currentCart: TCart;
@@ -44,4 +44,4 @@ export interface SyncCheckoutCartResult<TCart> {
44
44
  readonly checkoutSession: StripeCheckoutSessionLike;
45
45
  readonly currency: string;
46
46
  }
47
- export declare function syncCheckoutCart<TStripe extends StripeMutableCheckoutClientLike, TCart, TInput>(options: SyncCheckoutCartOptions<TStripe, TCart, TInput>): Promise<SyncCheckoutCartResult<TCart>>;
47
+ export declare function syncCheckoutCart<TStripe extends StripeClientLike, TCart, TInput>(options: SyncCheckoutCartOptions<TStripe, TCart, TInput>): Promise<SyncCheckoutCartResult<TCart>>;