@proxy-checkout/stripe-server-js 0.0.1 → 0.1.0-prx-124.137.1
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.
- package/README.md +18 -14
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/open-checkout.d.cts +22 -1
- package/dist/cjs/open-checkout.d.ts +22 -1
- package/dist/cjs/open-checkout.js +63 -0
- package/dist/cjs/types.d.cts +5 -0
- package/dist/cjs/types.d.ts +5 -0
- package/dist/cjs/version.d.cts +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/open-checkout.d.ts +22 -1
- package/dist/esm/open-checkout.js +63 -0
- package/dist/esm/types.d.ts +5 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Server-side Stripe adapter for Proxy Checkout. It owns the Stripe-specific integ
|
|
|
4
4
|
|
|
5
5
|
- **`requiredMetadata(proxySession)`** — the `proxy_session_id` metadata Proxy needs on the Checkout Session, Subscription, and PaymentIntent.
|
|
6
6
|
- **`assertCheckoutSessionBelongsToProxy` / `assertSubscriptionBelongsToProxy` / `assertPaymentIntentBelongsToProxy`** — provider-object ownership checks.
|
|
7
|
-
- **`openCheckout(...)`** — records payer-opened, validates/reconciles the typed cart with rollback, injects required metadata, creates the Stripe Checkout Session with deterministic idempotency, records the Proxy provider binding
|
|
7
|
+
- **`openCheckout(...)`** — records payer-opened, validates/reconciles the typed cart with rollback, injects required metadata, creates or joins the Stripe Checkout Session with deterministic idempotency, and records the Proxy provider binding. It returns provider-create material only for a `ready` outcome; terminal outcomes retain the shared cart without starting Stripe.
|
|
8
8
|
- **`syncCheckoutCart(...)`** — validates the stored Proxy provider binding, then updates the Proxy cart and the Stripe Checkout `line_items` in the safest order with deterministic rollback/reconciliation (`ProxyStripeCartSyncError`).
|
|
9
9
|
|
|
10
10
|
PSP-agnostic primitives (sessions, subscriptions, webhooks, lifecycle) live in [`@proxy-checkout/server-js`](https://www.npmjs.com/package/@proxy-checkout/server-js). This package is an additive adapter on top and is dependency-free: you inject your own `Stripe` instance and your `@proxy-checkout/server-js` client.
|
|
@@ -38,19 +38,23 @@ const checkout = await openCheckout({
|
|
|
38
38
|
ui_mode: "custom",
|
|
39
39
|
}),
|
|
40
40
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
if (checkout.outcome === "ready") {
|
|
42
|
+
// checkout.clientSecret -> render Stripe Elements
|
|
43
|
+
|
|
44
|
+
// Cart edits go through the binding-validated sync helper.
|
|
45
|
+
await syncCheckoutCart({
|
|
46
|
+
proxy,
|
|
47
|
+
stripe,
|
|
48
|
+
proxySessionId,
|
|
49
|
+
checkoutSessionId: checkout.checkoutSession.id,
|
|
50
|
+
cartSchema,
|
|
51
|
+
input: { interval: "year" },
|
|
52
|
+
buildNextCart: ({ currentCart, input }) => buildMerchantCart(currentCart, input),
|
|
53
|
+
buildStripeLineItems: (cart) => cart.lineItems,
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
// Render checkout.cart plus checkout.outcome/sessionStatus. Do not start Stripe.
|
|
57
|
+
}
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
Use the delegated-checkout guide from your Proxy onboarding materials for the
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { type ProxyStripeCartSyncCode, ProxyStripeCartSyncError, type ProxyStripeOpenCheckoutCode, ProxyStripeOpenCheckoutError, } from "./errors.js";
|
|
2
2
|
export { assertCheckoutSessionBelongsToProxy, assertPaymentIntentBelongsToProxy, assertSubscriptionBelongsToProxy, injectRequiredCheckoutSessionMetadata, PROXY_SESSION_METADATA_KEY, type ProxySessionMetadata, type ProxyStripeRequiredMetadata, requiredMetadata, } from "./metadata.js";
|
|
3
|
-
export { type OpenCheckoutOptions, type OpenCheckoutReconciledCart, type OpenCheckoutResult, openCheckout, } from "./open-checkout.js";
|
|
3
|
+
export { type OpenCheckoutOptions, type OpenCheckoutReconciledCart, type OpenCheckoutResponse, type OpenCheckoutResult, type OpenCheckoutTerminalResult, openCheckout, } from "./open-checkout.js";
|
|
4
4
|
export { normalizePsp } from "./psp.js";
|
|
5
5
|
export { type SyncCheckoutCartOptions, type SyncCheckoutCartResult, type SyncedCart, syncCheckoutCart, } from "./sync-checkout-cart.js";
|
|
6
6
|
export type { StripeCheckoutSessionCreateParams, StripeCheckoutSessionLike, StripeCheckoutSessionsApiLike, StripeCheckoutSessionUpdateParams, StripeClientLike, StripeMetadata, StripePaymentIntentLike, StripeRequestOptionsLike, StripeSubscriptionLike, } from "./types.js";
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { type ProxyStripeCartSyncCode, ProxyStripeCartSyncError, type ProxyStripeOpenCheckoutCode, ProxyStripeOpenCheckoutError, } from "./errors.js";
|
|
2
2
|
export { assertCheckoutSessionBelongsToProxy, assertPaymentIntentBelongsToProxy, assertSubscriptionBelongsToProxy, injectRequiredCheckoutSessionMetadata, PROXY_SESSION_METADATA_KEY, type ProxySessionMetadata, type ProxyStripeRequiredMetadata, requiredMetadata, } from "./metadata.js";
|
|
3
|
-
export { type OpenCheckoutOptions, type OpenCheckoutReconciledCart, type OpenCheckoutResult, openCheckout, } from "./open-checkout.js";
|
|
3
|
+
export { type OpenCheckoutOptions, type OpenCheckoutReconciledCart, type OpenCheckoutResponse, type OpenCheckoutResult, type OpenCheckoutTerminalResult, openCheckout, } from "./open-checkout.js";
|
|
4
4
|
export { normalizePsp } from "./psp.js";
|
|
5
5
|
export { type SyncCheckoutCartOptions, type SyncCheckoutCartResult, type SyncedCart, syncCheckoutCart, } from "./sync-checkout-cart.js";
|
|
6
6
|
export type { StripeCheckoutSessionCreateParams, StripeCheckoutSessionLike, StripeCheckoutSessionsApiLike, StripeCheckoutSessionUpdateParams, StripeClientLike, StripeMetadata, StripePaymentIntentLike, StripeRequestOptionsLike, StripeSubscriptionLike, } from "./types.js";
|
|
@@ -54,7 +54,28 @@ export interface OpenCheckoutResult<TCart, TOffer, TSession> {
|
|
|
54
54
|
readonly checkoutSession: TSession;
|
|
55
55
|
readonly clientSecret: string | null;
|
|
56
56
|
readonly offer: TOffer;
|
|
57
|
+
readonly outcome: "ready";
|
|
57
58
|
readonly proxySession: PayerOpenedResult;
|
|
58
59
|
readonly proxySessionId: string;
|
|
59
60
|
}
|
|
60
|
-
export
|
|
61
|
+
export type OpenCheckoutTerminalResult<TCart> = {
|
|
62
|
+
readonly cart: TCart;
|
|
63
|
+
readonly outcome: "already_paid";
|
|
64
|
+
readonly proxySession: PayerOpenedResult;
|
|
65
|
+
readonly proxySessionId: string;
|
|
66
|
+
readonly sessionStatus: "paid" | "provisionable" | "provisioned" | "provisioning_failed";
|
|
67
|
+
} | {
|
|
68
|
+
readonly cart: TCart;
|
|
69
|
+
readonly outcome: "action_required";
|
|
70
|
+
readonly proxySession: PayerOpenedResult;
|
|
71
|
+
readonly proxySessionId: string;
|
|
72
|
+
readonly sessionStatus: "merchant_action_required";
|
|
73
|
+
} | {
|
|
74
|
+
readonly cart: TCart;
|
|
75
|
+
readonly outcome: "unavailable";
|
|
76
|
+
readonly proxySession: PayerOpenedResult;
|
|
77
|
+
readonly proxySessionId: string;
|
|
78
|
+
readonly sessionStatus: "cancelled" | "created" | "expired" | "failed";
|
|
79
|
+
};
|
|
80
|
+
export type OpenCheckoutResponse<TCart, TOffer, TSession> = OpenCheckoutResult<TCart, TOffer, TSession> | OpenCheckoutTerminalResult<TCart>;
|
|
81
|
+
export declare function openCheckout<TStripe extends StripeClientLike, TCart = unknown, TOffer = undefined, TSession extends StripeCheckoutSessionLike = StripeCheckoutSessionLike>(options: OpenCheckoutOptions<TStripe, TCart, TOffer>): Promise<OpenCheckoutResponse<TCart, TOffer, TSession>>;
|
|
@@ -54,7 +54,28 @@ export interface OpenCheckoutResult<TCart, TOffer, TSession> {
|
|
|
54
54
|
readonly checkoutSession: TSession;
|
|
55
55
|
readonly clientSecret: string | null;
|
|
56
56
|
readonly offer: TOffer;
|
|
57
|
+
readonly outcome: "ready";
|
|
57
58
|
readonly proxySession: PayerOpenedResult;
|
|
58
59
|
readonly proxySessionId: string;
|
|
59
60
|
}
|
|
60
|
-
export
|
|
61
|
+
export type OpenCheckoutTerminalResult<TCart> = {
|
|
62
|
+
readonly cart: TCart;
|
|
63
|
+
readonly outcome: "already_paid";
|
|
64
|
+
readonly proxySession: PayerOpenedResult;
|
|
65
|
+
readonly proxySessionId: string;
|
|
66
|
+
readonly sessionStatus: "paid" | "provisionable" | "provisioned" | "provisioning_failed";
|
|
67
|
+
} | {
|
|
68
|
+
readonly cart: TCart;
|
|
69
|
+
readonly outcome: "action_required";
|
|
70
|
+
readonly proxySession: PayerOpenedResult;
|
|
71
|
+
readonly proxySessionId: string;
|
|
72
|
+
readonly sessionStatus: "merchant_action_required";
|
|
73
|
+
} | {
|
|
74
|
+
readonly cart: TCart;
|
|
75
|
+
readonly outcome: "unavailable";
|
|
76
|
+
readonly proxySession: PayerOpenedResult;
|
|
77
|
+
readonly proxySessionId: string;
|
|
78
|
+
readonly sessionStatus: "cancelled" | "created" | "expired" | "failed";
|
|
79
|
+
};
|
|
80
|
+
export type OpenCheckoutResponse<TCart, TOffer, TSession> = OpenCheckoutResult<TCart, TOffer, TSession> | OpenCheckoutTerminalResult<TCart>;
|
|
81
|
+
export declare function openCheckout<TStripe extends StripeClientLike, TCart = unknown, TOffer = undefined, TSession extends StripeCheckoutSessionLike = StripeCheckoutSessionLike>(options: OpenCheckoutOptions<TStripe, TCart, TOffer>): Promise<OpenCheckoutResponse<TCart, TOffer, TSession>>;
|
|
@@ -27,6 +27,13 @@ async function openCheckout(options) {
|
|
|
27
27
|
let cart = options.cartSchema || options.getCartBuyerReference
|
|
28
28
|
? options.proxy.sessions.parseCart(proxySession, options.cartSchema ?? ((value) => value), { getBuyerReference: options.getCartBuyerReference })
|
|
29
29
|
: proxySession.cartSnapshot;
|
|
30
|
+
if (proxySession.presentationState !== "checkout_available") {
|
|
31
|
+
return terminalResultFromSession(proxySession, cart);
|
|
32
|
+
}
|
|
33
|
+
if (proxySession.status === "payment_pending" &&
|
|
34
|
+
proxySession.providerCheckoutSessionId === null) {
|
|
35
|
+
throw new server_js_1.ProxyCheckoutValidationError(`Proxy session ${proxySession.id} already has payment preparation in progress without a bound Stripe Checkout Session.`, { code: "provider_binding_missing", field: "provider_checkout_session_id" });
|
|
36
|
+
}
|
|
30
37
|
let offer = options.validateCart
|
|
31
38
|
? await options.validateCart({ cart, session: proxySession })
|
|
32
39
|
: undefined;
|
|
@@ -40,10 +47,14 @@ async function openCheckout(options) {
|
|
|
40
47
|
checkoutSession,
|
|
41
48
|
clientSecret: checkoutSession.client_secret ?? null,
|
|
42
49
|
offer,
|
|
50
|
+
outcome: "ready",
|
|
43
51
|
proxySession,
|
|
44
52
|
proxySessionId: proxySession.id,
|
|
45
53
|
};
|
|
46
54
|
}
|
|
55
|
+
if (proxySession.currentAcquisitionAttempt !== null) {
|
|
56
|
+
throw new server_js_1.ProxyCheckoutValidationError(`Proxy session ${proxySession.id} already has acquisition ${proxySession.currentAcquisitionAttempt.id} in progress; openCheckout will not create an unrelated Stripe Checkout Session.`, { code: "provider_acquisition_in_progress", field: "current_acquisition_attempt" });
|
|
57
|
+
}
|
|
47
58
|
let rollbackCart;
|
|
48
59
|
const reconciledCart = await options.reconcileCart?.({ cart, offer, session: proxySession });
|
|
49
60
|
if (reconciledCart) {
|
|
@@ -87,11 +98,14 @@ async function openCheckout(options) {
|
|
|
87
98
|
proxySession,
|
|
88
99
|
stripe: options.stripe,
|
|
89
100
|
});
|
|
101
|
+
assertCheckoutAutomaticRecoveryDisabled(params, proxySession.id);
|
|
102
|
+
const commercialMode = checkoutCommercialMode(params, proxySession.id);
|
|
90
103
|
const checkoutSession = (await options.stripe.checkout.sessions.create((0, metadata_js_1.injectRequiredCheckoutSessionMetadata)(params, proxySession.id), buildStripeRequestOptions(options, proxySession.id, psp)));
|
|
91
104
|
// Fail closed if the merchant forgot to place proxy_session_id metadata.
|
|
92
105
|
(0, metadata_js_1.assertCheckoutSessionBelongsToProxy)(checkoutSession, proxySession.id);
|
|
93
106
|
const binding = await recordOrReconcileProviderBinding({
|
|
94
107
|
checkoutSessionId: checkoutSession.id,
|
|
108
|
+
commercialMode,
|
|
95
109
|
proxy: options.proxy,
|
|
96
110
|
proxySessionId: proxySession.id,
|
|
97
111
|
psp,
|
|
@@ -105,6 +119,7 @@ async function openCheckout(options) {
|
|
|
105
119
|
checkoutSession,
|
|
106
120
|
clientSecret: checkoutSession.client_secret ?? null,
|
|
107
121
|
offer,
|
|
122
|
+
outcome: "ready",
|
|
108
123
|
proxySession,
|
|
109
124
|
proxySessionId: proxySession.id,
|
|
110
125
|
};
|
|
@@ -122,6 +137,39 @@ async function openCheckout(options) {
|
|
|
122
137
|
throw cause;
|
|
123
138
|
}
|
|
124
139
|
}
|
|
140
|
+
function terminalResultFromSession(proxySession, cart) {
|
|
141
|
+
const common = {
|
|
142
|
+
cart,
|
|
143
|
+
proxySession,
|
|
144
|
+
proxySessionId: proxySession.id,
|
|
145
|
+
};
|
|
146
|
+
switch (proxySession.presentationState) {
|
|
147
|
+
case "already_paid":
|
|
148
|
+
if (proxySession.status !== "paid" &&
|
|
149
|
+
proxySession.status !== "provisionable" &&
|
|
150
|
+
proxySession.status !== "provisioned" &&
|
|
151
|
+
proxySession.status !== "provisioning_failed") {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
return { ...common, outcome: "already_paid", sessionStatus: proxySession.status };
|
|
155
|
+
case "action_required":
|
|
156
|
+
if (proxySession.status !== "merchant_action_required") {
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
return { ...common, outcome: "action_required", sessionStatus: proxySession.status };
|
|
160
|
+
case "unavailable":
|
|
161
|
+
if (proxySession.status !== "cancelled" &&
|
|
162
|
+
proxySession.status !== "created" &&
|
|
163
|
+
proxySession.status !== "expired" &&
|
|
164
|
+
proxySession.status !== "failed") {
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
return { ...common, outcome: "unavailable", sessionStatus: proxySession.status };
|
|
168
|
+
case "checkout_available":
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
throw new server_js_1.ProxyCheckoutValidationError(`Proxy session ${proxySession.id} returned an inconsistent presentation and lifecycle status.`, { code: "invalid_session_presentation", field: "presentation_state" });
|
|
172
|
+
}
|
|
125
173
|
function buildStripeRequestOptions(options, proxySessionId, psp) {
|
|
126
174
|
return {
|
|
127
175
|
...options.stripeRequestOptions,
|
|
@@ -170,6 +218,7 @@ async function rollbackReconciledCart(input) {
|
|
|
170
218
|
async function recordOrReconcileProviderBinding(input) {
|
|
171
219
|
try {
|
|
172
220
|
return await input.proxy.sessions.recordProviderBinding(input.proxySessionId, {
|
|
221
|
+
commercialMode: input.commercialMode,
|
|
173
222
|
providerCheckoutSessionId: input.checkoutSessionId,
|
|
174
223
|
psp: input.psp,
|
|
175
224
|
requestId: input.requestId,
|
|
@@ -190,6 +239,20 @@ async function recordOrReconcileProviderBinding(input) {
|
|
|
190
239
|
});
|
|
191
240
|
}
|
|
192
241
|
}
|
|
242
|
+
function checkoutCommercialMode(params, proxySessionId) {
|
|
243
|
+
if (params.mode === "payment") {
|
|
244
|
+
return "one_time";
|
|
245
|
+
}
|
|
246
|
+
if (params.mode === "subscription") {
|
|
247
|
+
return "subscription";
|
|
248
|
+
}
|
|
249
|
+
throw new server_js_1.ProxyCheckoutValidationError(`Proxy session ${proxySessionId} requires Stripe Checkout mode payment or subscription.`, { code: "unsupported_provider_option", field: "mode" });
|
|
250
|
+
}
|
|
251
|
+
function assertCheckoutAutomaticRecoveryDisabled(params, proxySessionId) {
|
|
252
|
+
if (params.after_expiration?.recovery?.enabled === true) {
|
|
253
|
+
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" });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
193
256
|
async function readReconciledBinding(input) {
|
|
194
257
|
const current = await input.proxy.sessions.retrieve(input.proxySessionId, {
|
|
195
258
|
requestId: input.requestId,
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -27,6 +27,11 @@ export interface StripeCheckoutSessionUpdateParams {
|
|
|
27
27
|
readonly metadata?: StripeMetadataParam;
|
|
28
28
|
}
|
|
29
29
|
export type StripeCheckoutSessionCreateParams = object & {
|
|
30
|
+
readonly after_expiration?: {
|
|
31
|
+
readonly recovery?: {
|
|
32
|
+
readonly enabled?: boolean;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
30
35
|
readonly metadata?: StripeMetadataParam;
|
|
31
36
|
readonly mode?: "payment" | "setup" | "subscription";
|
|
32
37
|
readonly payment_intent_data?: unknown;
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -27,6 +27,11 @@ export interface StripeCheckoutSessionUpdateParams {
|
|
|
27
27
|
readonly metadata?: StripeMetadataParam;
|
|
28
28
|
}
|
|
29
29
|
export type StripeCheckoutSessionCreateParams = object & {
|
|
30
|
+
readonly after_expiration?: {
|
|
31
|
+
readonly recovery?: {
|
|
32
|
+
readonly enabled?: boolean;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
30
35
|
readonly metadata?: StripeMetadataParam;
|
|
31
36
|
readonly mode?: "payment" | "setup" | "subscription";
|
|
32
37
|
readonly payment_intent_data?: unknown;
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const proxyCheckoutStripeServerSdkName = "@proxy-checkout/stripe-server-js";
|
|
2
|
-
export declare const proxyCheckoutStripeServerSdkVersion = "0.0.1";
|
|
2
|
+
export declare const proxyCheckoutStripeServerSdkVersion = "0.1.0-prx-124.137.1";
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const proxyCheckoutStripeServerSdkName = "@proxy-checkout/stripe-server-js";
|
|
2
|
-
export declare const proxyCheckoutStripeServerSdkVersion = "0.0.1";
|
|
2
|
+
export declare const proxyCheckoutStripeServerSdkVersion = "0.1.0-prx-124.137.1";
|
package/dist/cjs/version.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.proxyCheckoutStripeServerSdkVersion = exports.proxyCheckoutStripeServerSdkName = void 0;
|
|
4
4
|
exports.proxyCheckoutStripeServerSdkName = "@proxy-checkout/stripe-server-js";
|
|
5
|
-
exports.proxyCheckoutStripeServerSdkVersion = "0.0.1";
|
|
5
|
+
exports.proxyCheckoutStripeServerSdkVersion = "0.1.0-prx-124.137.1";
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { type ProxyStripeCartSyncCode, ProxyStripeCartSyncError, type ProxyStripeOpenCheckoutCode, ProxyStripeOpenCheckoutError, } from "./errors.js";
|
|
2
2
|
export { assertCheckoutSessionBelongsToProxy, assertPaymentIntentBelongsToProxy, assertSubscriptionBelongsToProxy, injectRequiredCheckoutSessionMetadata, PROXY_SESSION_METADATA_KEY, type ProxySessionMetadata, type ProxyStripeRequiredMetadata, requiredMetadata, } from "./metadata.js";
|
|
3
|
-
export { type OpenCheckoutOptions, type OpenCheckoutReconciledCart, type OpenCheckoutResult, openCheckout, } from "./open-checkout.js";
|
|
3
|
+
export { type OpenCheckoutOptions, type OpenCheckoutReconciledCart, type OpenCheckoutResponse, type OpenCheckoutResult, type OpenCheckoutTerminalResult, openCheckout, } from "./open-checkout.js";
|
|
4
4
|
export { normalizePsp } from "./psp.js";
|
|
5
5
|
export { type SyncCheckoutCartOptions, type SyncCheckoutCartResult, type SyncedCart, syncCheckoutCart, } from "./sync-checkout-cart.js";
|
|
6
6
|
export type { StripeCheckoutSessionCreateParams, StripeCheckoutSessionLike, StripeCheckoutSessionsApiLike, StripeCheckoutSessionUpdateParams, StripeClientLike, StripeMetadata, StripePaymentIntentLike, StripeRequestOptionsLike, StripeSubscriptionLike, } from "./types.js";
|
|
@@ -54,7 +54,28 @@ export interface OpenCheckoutResult<TCart, TOffer, TSession> {
|
|
|
54
54
|
readonly checkoutSession: TSession;
|
|
55
55
|
readonly clientSecret: string | null;
|
|
56
56
|
readonly offer: TOffer;
|
|
57
|
+
readonly outcome: "ready";
|
|
57
58
|
readonly proxySession: PayerOpenedResult;
|
|
58
59
|
readonly proxySessionId: string;
|
|
59
60
|
}
|
|
60
|
-
export
|
|
61
|
+
export type OpenCheckoutTerminalResult<TCart> = {
|
|
62
|
+
readonly cart: TCart;
|
|
63
|
+
readonly outcome: "already_paid";
|
|
64
|
+
readonly proxySession: PayerOpenedResult;
|
|
65
|
+
readonly proxySessionId: string;
|
|
66
|
+
readonly sessionStatus: "paid" | "provisionable" | "provisioned" | "provisioning_failed";
|
|
67
|
+
} | {
|
|
68
|
+
readonly cart: TCart;
|
|
69
|
+
readonly outcome: "action_required";
|
|
70
|
+
readonly proxySession: PayerOpenedResult;
|
|
71
|
+
readonly proxySessionId: string;
|
|
72
|
+
readonly sessionStatus: "merchant_action_required";
|
|
73
|
+
} | {
|
|
74
|
+
readonly cart: TCart;
|
|
75
|
+
readonly outcome: "unavailable";
|
|
76
|
+
readonly proxySession: PayerOpenedResult;
|
|
77
|
+
readonly proxySessionId: string;
|
|
78
|
+
readonly sessionStatus: "cancelled" | "created" | "expired" | "failed";
|
|
79
|
+
};
|
|
80
|
+
export type OpenCheckoutResponse<TCart, TOffer, TSession> = OpenCheckoutResult<TCart, TOffer, TSession> | OpenCheckoutTerminalResult<TCart>;
|
|
81
|
+
export declare function openCheckout<TStripe extends StripeClientLike, TCart = unknown, TOffer = undefined, TSession extends StripeCheckoutSessionLike = StripeCheckoutSessionLike>(options: OpenCheckoutOptions<TStripe, TCart, TOffer>): Promise<OpenCheckoutResponse<TCart, TOffer, TSession>>;
|
|
@@ -24,6 +24,13 @@ export async function openCheckout(options) {
|
|
|
24
24
|
let cart = options.cartSchema || options.getCartBuyerReference
|
|
25
25
|
? options.proxy.sessions.parseCart(proxySession, options.cartSchema ?? ((value) => value), { getBuyerReference: options.getCartBuyerReference })
|
|
26
26
|
: proxySession.cartSnapshot;
|
|
27
|
+
if (proxySession.presentationState !== "checkout_available") {
|
|
28
|
+
return terminalResultFromSession(proxySession, cart);
|
|
29
|
+
}
|
|
30
|
+
if (proxySession.status === "payment_pending" &&
|
|
31
|
+
proxySession.providerCheckoutSessionId === null) {
|
|
32
|
+
throw new ProxyCheckoutValidationError(`Proxy session ${proxySession.id} already has payment preparation in progress without a bound Stripe Checkout Session.`, { code: "provider_binding_missing", field: "provider_checkout_session_id" });
|
|
33
|
+
}
|
|
27
34
|
let offer = options.validateCart
|
|
28
35
|
? await options.validateCart({ cart, session: proxySession })
|
|
29
36
|
: undefined;
|
|
@@ -37,10 +44,14 @@ export async function openCheckout(options) {
|
|
|
37
44
|
checkoutSession,
|
|
38
45
|
clientSecret: checkoutSession.client_secret ?? null,
|
|
39
46
|
offer,
|
|
47
|
+
outcome: "ready",
|
|
40
48
|
proxySession,
|
|
41
49
|
proxySessionId: proxySession.id,
|
|
42
50
|
};
|
|
43
51
|
}
|
|
52
|
+
if (proxySession.currentAcquisitionAttempt !== null) {
|
|
53
|
+
throw new ProxyCheckoutValidationError(`Proxy session ${proxySession.id} already has acquisition ${proxySession.currentAcquisitionAttempt.id} in progress; openCheckout will not create an unrelated Stripe Checkout Session.`, { code: "provider_acquisition_in_progress", field: "current_acquisition_attempt" });
|
|
54
|
+
}
|
|
44
55
|
let rollbackCart;
|
|
45
56
|
const reconciledCart = await options.reconcileCart?.({ cart, offer, session: proxySession });
|
|
46
57
|
if (reconciledCart) {
|
|
@@ -84,11 +95,14 @@ export async function openCheckout(options) {
|
|
|
84
95
|
proxySession,
|
|
85
96
|
stripe: options.stripe,
|
|
86
97
|
});
|
|
98
|
+
assertCheckoutAutomaticRecoveryDisabled(params, proxySession.id);
|
|
99
|
+
const commercialMode = checkoutCommercialMode(params, proxySession.id);
|
|
87
100
|
const checkoutSession = (await options.stripe.checkout.sessions.create(injectRequiredCheckoutSessionMetadata(params, proxySession.id), buildStripeRequestOptions(options, proxySession.id, psp)));
|
|
88
101
|
// Fail closed if the merchant forgot to place proxy_session_id metadata.
|
|
89
102
|
assertCheckoutSessionBelongsToProxy(checkoutSession, proxySession.id);
|
|
90
103
|
const binding = await recordOrReconcileProviderBinding({
|
|
91
104
|
checkoutSessionId: checkoutSession.id,
|
|
105
|
+
commercialMode,
|
|
92
106
|
proxy: options.proxy,
|
|
93
107
|
proxySessionId: proxySession.id,
|
|
94
108
|
psp,
|
|
@@ -102,6 +116,7 @@ export async function openCheckout(options) {
|
|
|
102
116
|
checkoutSession,
|
|
103
117
|
clientSecret: checkoutSession.client_secret ?? null,
|
|
104
118
|
offer,
|
|
119
|
+
outcome: "ready",
|
|
105
120
|
proxySession,
|
|
106
121
|
proxySessionId: proxySession.id,
|
|
107
122
|
};
|
|
@@ -119,6 +134,39 @@ export async function openCheckout(options) {
|
|
|
119
134
|
throw cause;
|
|
120
135
|
}
|
|
121
136
|
}
|
|
137
|
+
function terminalResultFromSession(proxySession, cart) {
|
|
138
|
+
const common = {
|
|
139
|
+
cart,
|
|
140
|
+
proxySession,
|
|
141
|
+
proxySessionId: proxySession.id,
|
|
142
|
+
};
|
|
143
|
+
switch (proxySession.presentationState) {
|
|
144
|
+
case "already_paid":
|
|
145
|
+
if (proxySession.status !== "paid" &&
|
|
146
|
+
proxySession.status !== "provisionable" &&
|
|
147
|
+
proxySession.status !== "provisioned" &&
|
|
148
|
+
proxySession.status !== "provisioning_failed") {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
return { ...common, outcome: "already_paid", sessionStatus: proxySession.status };
|
|
152
|
+
case "action_required":
|
|
153
|
+
if (proxySession.status !== "merchant_action_required") {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
return { ...common, outcome: "action_required", sessionStatus: proxySession.status };
|
|
157
|
+
case "unavailable":
|
|
158
|
+
if (proxySession.status !== "cancelled" &&
|
|
159
|
+
proxySession.status !== "created" &&
|
|
160
|
+
proxySession.status !== "expired" &&
|
|
161
|
+
proxySession.status !== "failed") {
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
return { ...common, outcome: "unavailable", sessionStatus: proxySession.status };
|
|
165
|
+
case "checkout_available":
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
throw new ProxyCheckoutValidationError(`Proxy session ${proxySession.id} returned an inconsistent presentation and lifecycle status.`, { code: "invalid_session_presentation", field: "presentation_state" });
|
|
169
|
+
}
|
|
122
170
|
function buildStripeRequestOptions(options, proxySessionId, psp) {
|
|
123
171
|
return {
|
|
124
172
|
...options.stripeRequestOptions,
|
|
@@ -167,6 +215,7 @@ async function rollbackReconciledCart(input) {
|
|
|
167
215
|
async function recordOrReconcileProviderBinding(input) {
|
|
168
216
|
try {
|
|
169
217
|
return await input.proxy.sessions.recordProviderBinding(input.proxySessionId, {
|
|
218
|
+
commercialMode: input.commercialMode,
|
|
170
219
|
providerCheckoutSessionId: input.checkoutSessionId,
|
|
171
220
|
psp: input.psp,
|
|
172
221
|
requestId: input.requestId,
|
|
@@ -187,6 +236,20 @@ async function recordOrReconcileProviderBinding(input) {
|
|
|
187
236
|
});
|
|
188
237
|
}
|
|
189
238
|
}
|
|
239
|
+
function checkoutCommercialMode(params, proxySessionId) {
|
|
240
|
+
if (params.mode === "payment") {
|
|
241
|
+
return "one_time";
|
|
242
|
+
}
|
|
243
|
+
if (params.mode === "subscription") {
|
|
244
|
+
return "subscription";
|
|
245
|
+
}
|
|
246
|
+
throw new ProxyCheckoutValidationError(`Proxy session ${proxySessionId} requires Stripe Checkout mode payment or subscription.`, { code: "unsupported_provider_option", field: "mode" });
|
|
247
|
+
}
|
|
248
|
+
function assertCheckoutAutomaticRecoveryDisabled(params, proxySessionId) {
|
|
249
|
+
if (params.after_expiration?.recovery?.enabled === true) {
|
|
250
|
+
throw new 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" });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
190
253
|
async function readReconciledBinding(input) {
|
|
191
254
|
const current = await input.proxy.sessions.retrieve(input.proxySessionId, {
|
|
192
255
|
requestId: input.requestId,
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -27,6 +27,11 @@ export interface StripeCheckoutSessionUpdateParams {
|
|
|
27
27
|
readonly metadata?: StripeMetadataParam;
|
|
28
28
|
}
|
|
29
29
|
export type StripeCheckoutSessionCreateParams = object & {
|
|
30
|
+
readonly after_expiration?: {
|
|
31
|
+
readonly recovery?: {
|
|
32
|
+
readonly enabled?: boolean;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
30
35
|
readonly metadata?: StripeMetadataParam;
|
|
31
36
|
readonly mode?: "payment" | "setup" | "subscription";
|
|
32
37
|
readonly payment_intent_data?: unknown;
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const proxyCheckoutStripeServerSdkName = "@proxy-checkout/stripe-server-js";
|
|
2
|
-
export declare const proxyCheckoutStripeServerSdkVersion = "0.0.1";
|
|
2
|
+
export declare const proxyCheckoutStripeServerSdkVersion = "0.1.0-prx-124.137.1";
|
package/dist/esm/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const proxyCheckoutStripeServerSdkName = "@proxy-checkout/stripe-server-js";
|
|
2
|
-
export const proxyCheckoutStripeServerSdkVersion = "0.0.1";
|
|
2
|
+
export const proxyCheckoutStripeServerSdkVersion = "0.1.0-prx-124.137.1";
|
package/package.json
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"type": "module",
|
|
9
9
|
"types": "./dist/esm/index.d.ts",
|
|
10
|
-
"version": "0.0.1",
|
|
10
|
+
"version": "0.1.0-prx-124.137.1",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@types/node": "^24.12.4",
|
|
13
13
|
"@vitest/coverage-v8": "4.1.7",
|
|
14
14
|
"stripe": "^21.0.1",
|
|
15
15
|
"typescript": "^6.0.3",
|
|
16
16
|
"vitest": "4.1.7",
|
|
17
|
-
"@proxy-checkout/server-js": "0.0.
|
|
17
|
+
"@proxy-checkout/server-js": "0.1.0-prx-124.137.1"
|
|
18
18
|
},
|
|
19
19
|
"exports": {
|
|
20
20
|
".": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"sdk"
|
|
42
42
|
],
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@proxy-checkout/server-js": ">=0.0
|
|
44
|
+
"@proxy-checkout/server-js": ">=0.1.0-0 <0.2.0"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"access": "public"
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "node ../../scripts/sdk/build-dual-package.mjs",
|
|
56
56
|
"test:all": "vitest run",
|
|
57
|
+
"test:ci": "pnpm run test:coverage",
|
|
57
58
|
"test:coverage": "vitest run --coverage.enabled --coverage.reportsDirectory=coverage/all",
|
|
58
59
|
"test:coverage:changed": "vitest run --coverage.enabled --coverage.reportsDirectory=coverage/changed",
|
|
59
60
|
"test:coverage:integration": "vitest run --project integration --coverage.enabled --coverage.reportsDirectory=coverage/integration",
|