@blocklet/payment-react-headless 1.26.4 → 1.26.5
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/es/checkout/core/index.d.ts +1 -1
- package/es/checkout/core/index.js +1 -0
- package/es/checkout/core/submit.d.ts +1 -0
- package/es/checkout/core/submit.js +4 -0
- package/es/checkout/hooks/useSubmit.d.ts +1 -0
- package/es/checkout/hooks/useSubmit.js +27 -3
- package/es/checkout/types.d.ts +2 -0
- package/es/shared/api.d.ts +1 -0
- package/es/shared/api.js +1 -0
- package/lib/checkout/core/index.d.ts +1 -1
- package/lib/checkout/core/index.js +6 -0
- package/lib/checkout/core/submit.d.ts +1 -0
- package/lib/checkout/core/submit.js +7 -0
- package/lib/checkout/hooks/useSubmit.d.ts +1 -0
- package/lib/checkout/hooks/useSubmit.js +38 -2
- package/lib/checkout/types.d.ts +2 -0
- package/lib/shared/api.d.ts +1 -0
- package/lib/shared/api.js +1 -0
- package/package.json +3 -3
- package/src/checkout/core/index.ts +1 -0
- package/src/checkout/core/submit.ts +5 -0
- package/src/checkout/hooks/useSubmit.ts +29 -2
- package/src/checkout/types.ts +7 -1
- package/src/shared/api.ts +1 -0
|
@@ -7,4 +7,4 @@ export { recalculatePromotionIfNeeded, adjustQuantity, performUpsell, performDow
|
|
|
7
7
|
export { addCrossSellItem, removeCrossSellItem, fetchCrossSellItem } from './crossSell';
|
|
8
8
|
export { parseBillingInterval, type BillingIntervalType, type BillingIntervalOption, type BillingIntervalInfo, } from './billingInterval';
|
|
9
9
|
export { buildFields, createInitialValues, setNestedValue } from './customerForm';
|
|
10
|
-
export { QUOTE_ERROR_CODES, RELAY_SOCKET_PREFIX, getAppId, getRelayChannel, getRelayProtocol, getSocketHost, buildSubmitPayload, isQuoteError, abortStripePayment, submitCheckout, confirmFastCheckout, updateSlippage, } from './submit';
|
|
10
|
+
export { QUOTE_ERROR_CODES, RELAY_SOCKET_PREFIX, getAppId, getRelayChannel, getRelayProtocol, getSocketHost, buildSubmitPayload, isQuoteError, abortStripePayment, submitCheckout, confirmFastCheckout, skipPaymentMethod, updateSlippage, } from './submit';
|
|
@@ -34,6 +34,7 @@ export declare function isQuoteError(errorCode: string): boolean;
|
|
|
34
34
|
export declare function abortStripePayment(sessionId: string): Promise<void>;
|
|
35
35
|
export declare function submitCheckout(sessionId: string, isDonation: boolean, payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
36
36
|
export declare function confirmFastCheckout(sessionId: string): Promise<Record<string, unknown>>;
|
|
37
|
+
export declare function skipPaymentMethod(sessionId: string): Promise<Record<string, unknown>>;
|
|
37
38
|
export declare function updateSlippage(sessionId: string, config: {
|
|
38
39
|
mode: string;
|
|
39
40
|
percent: number;
|
|
@@ -58,6 +58,10 @@ export async function confirmFastCheckout(sessionId) {
|
|
|
58
58
|
const { data } = await api.post(API.FAST_CHECKOUT_CONFIRM(sessionId), {});
|
|
59
59
|
return data;
|
|
60
60
|
}
|
|
61
|
+
export async function skipPaymentMethod(sessionId) {
|
|
62
|
+
const { data } = await api.post(API.SKIP_PAYMENT_METHOD(sessionId), {});
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
61
65
|
export async function updateSlippage(sessionId, config) {
|
|
62
66
|
const { data } = await api.put(API.SLIPPAGE(sessionId), {
|
|
63
67
|
slippage_config: config
|
|
@@ -29,6 +29,7 @@ export interface UseSubmitReturn {
|
|
|
29
29
|
reset: () => void;
|
|
30
30
|
stripeConfirm: () => Promise<void>;
|
|
31
31
|
stripeCancel: () => Promise<void>;
|
|
32
|
+
stripeSkip: () => Promise<void>;
|
|
32
33
|
vendorStatus: VendorOrderStatus | null;
|
|
33
34
|
/** Whether the checkout config is locked (user clicked "Connect and Pay", pending login/submit) */
|
|
34
35
|
locked: boolean;
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
isQuoteError,
|
|
16
16
|
abortStripePayment,
|
|
17
17
|
submitCheckout,
|
|
18
|
-
confirmFastCheckout
|
|
18
|
+
confirmFastCheckout,
|
|
19
|
+
skipPaymentMethod
|
|
19
20
|
} from "../core/submit.js";
|
|
20
21
|
export function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit, isDonation, formValues, validateForm, refreshSession, updateSessionData) {
|
|
21
22
|
const session = sessionData?.checkoutSession;
|
|
@@ -177,7 +178,7 @@ export function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit
|
|
|
177
178
|
if (clientSecret) {
|
|
178
179
|
const intentType = stripeCtx?.intent_type || "payment_intent";
|
|
179
180
|
setStatus("waiting_stripe");
|
|
180
|
-
setContext({ type: "stripe", clientSecret, intentType });
|
|
181
|
+
setContext({ type: "stripe", clientSecret, intentType, noPaymentRequired: !!data.noPaymentRequired });
|
|
181
182
|
return;
|
|
182
183
|
}
|
|
183
184
|
if (data.noPaymentRequired) {
|
|
@@ -349,7 +350,11 @@ export function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit
|
|
|
349
350
|
const execute = useMemoizedFn(async (force = false) => {
|
|
350
351
|
if (!force && status !== "idle") return;
|
|
351
352
|
if (!force) quoteRetryCountRef.current = 0;
|
|
352
|
-
if (!await validateForm())
|
|
353
|
+
if (!await validateForm()) {
|
|
354
|
+
setStatus("failed");
|
|
355
|
+
setContext({ type: "error", message: "Please complete the required fields", code: "VALIDATION_FAILED" });
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
353
358
|
const user = session?.user;
|
|
354
359
|
if (!user?.sourceAppPid) {
|
|
355
360
|
const hasVendorConfig = session?.line_items?.some(
|
|
@@ -480,6 +485,24 @@ export function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit
|
|
|
480
485
|
setStatus("idle");
|
|
481
486
|
setContext(null);
|
|
482
487
|
});
|
|
488
|
+
const stripeSkip = useMemoizedFn(async () => {
|
|
489
|
+
if (status !== "waiting_stripe") return;
|
|
490
|
+
try {
|
|
491
|
+
setStatus("submitting");
|
|
492
|
+
const data = await skipPaymentMethod(sessionId);
|
|
493
|
+
if (!mountedRef.current) return;
|
|
494
|
+
if (data.skipped || data.checkoutSession?.status === "complete") {
|
|
495
|
+
await handleCompletion();
|
|
496
|
+
} else {
|
|
497
|
+
setStatus("failed");
|
|
498
|
+
setContext({ type: "error", message: "Failed to skip payment method", code: "SKIP_FAILED" });
|
|
499
|
+
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
if (!mountedRef.current) return;
|
|
502
|
+
setStatus("failed");
|
|
503
|
+
setContext({ type: "error", message: err.message || "Failed to skip payment method", code: "SKIP_FAILED" });
|
|
504
|
+
}
|
|
505
|
+
});
|
|
483
506
|
return {
|
|
484
507
|
status,
|
|
485
508
|
context,
|
|
@@ -491,6 +514,7 @@ export function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit
|
|
|
491
514
|
reset,
|
|
492
515
|
stripeConfirm,
|
|
493
516
|
stripeCancel,
|
|
517
|
+
stripeSkip,
|
|
494
518
|
vendorStatus,
|
|
495
519
|
locked,
|
|
496
520
|
lock
|
package/es/checkout/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type SubmitContext = {
|
|
|
16
16
|
type: 'stripe';
|
|
17
17
|
clientSecret: string;
|
|
18
18
|
intentType?: 'payment_intent' | 'setup_intent';
|
|
19
|
+
noPaymentRequired?: boolean;
|
|
19
20
|
} | {
|
|
20
21
|
type: 'did_connect';
|
|
21
22
|
action: string;
|
|
@@ -212,6 +213,7 @@ export interface UseCheckoutReturn {
|
|
|
212
213
|
reset: () => void;
|
|
213
214
|
stripeConfirm: () => Promise<void>;
|
|
214
215
|
stripeCancel: () => Promise<void>;
|
|
216
|
+
stripeSkip: () => Promise<void>;
|
|
215
217
|
vendorStatus: {
|
|
216
218
|
payment_status: string;
|
|
217
219
|
session_status: string;
|
package/es/shared/api.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare const API: {
|
|
|
33
33
|
readonly CHANGE_AMOUNT: (id: string) => string;
|
|
34
34
|
readonly SLIPPAGE: (id: string) => string;
|
|
35
35
|
readonly ABORT_STRIPE: (id: string) => string;
|
|
36
|
+
readonly SKIP_PAYMENT_METHOD: (id: string) => string;
|
|
36
37
|
readonly RECALCULATE_PROMOTION_SESSION: (id: string) => string;
|
|
37
38
|
readonly APPLY_PROMOTION: (id: string) => string;
|
|
38
39
|
readonly REMOVE_PROMOTION: (id: string) => string;
|
package/es/shared/api.js
CHANGED
|
@@ -73,6 +73,7 @@ export const API = {
|
|
|
73
73
|
CHANGE_AMOUNT: (id) => `/api/checkout-sessions/${id}/amount`,
|
|
74
74
|
SLIPPAGE: (id) => `/api/checkout-sessions/${id}/slippage`,
|
|
75
75
|
ABORT_STRIPE: (id) => `/api/checkout-sessions/${id}/abort-stripe`,
|
|
76
|
+
SKIP_PAYMENT_METHOD: (id) => `/api/checkout-sessions/${id}/skip-payment-method`,
|
|
76
77
|
RECALCULATE_PROMOTION_SESSION: (id) => `/api/checkout-sessions/${id}/recalculate-promotion`,
|
|
77
78
|
APPLY_PROMOTION: (id) => `/api/checkout-sessions/${id}/apply-promotion`,
|
|
78
79
|
REMOVE_PROMOTION: (id) => `/api/checkout-sessions/${id}/remove-promotion`,
|
|
@@ -7,4 +7,4 @@ export { recalculatePromotionIfNeeded, adjustQuantity, performUpsell, performDow
|
|
|
7
7
|
export { addCrossSellItem, removeCrossSellItem, fetchCrossSellItem } from './crossSell';
|
|
8
8
|
export { parseBillingInterval, type BillingIntervalType, type BillingIntervalOption, type BillingIntervalInfo, } from './billingInterval';
|
|
9
9
|
export { buildFields, createInitialValues, setNestedValue } from './customerForm';
|
|
10
|
-
export { QUOTE_ERROR_CODES, RELAY_SOCKET_PREFIX, getAppId, getRelayChannel, getRelayProtocol, getSocketHost, buildSubmitPayload, isQuoteError, abortStripePayment, submitCheckout, confirmFastCheckout, updateSlippage, } from './submit';
|
|
10
|
+
export { QUOTE_ERROR_CODES, RELAY_SOCKET_PREFIX, getAppId, getRelayChannel, getRelayProtocol, getSocketHost, buildSubmitPayload, isQuoteError, abortStripePayment, submitCheckout, confirmFastCheckout, skipPaymentMethod, updateSlippage, } from './submit';
|
|
@@ -273,6 +273,12 @@ Object.defineProperty(exports, "setNestedValue", {
|
|
|
273
273
|
return _customerForm.setNestedValue;
|
|
274
274
|
}
|
|
275
275
|
});
|
|
276
|
+
Object.defineProperty(exports, "skipPaymentMethod", {
|
|
277
|
+
enumerable: true,
|
|
278
|
+
get: function () {
|
|
279
|
+
return _submit.skipPaymentMethod;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
276
282
|
Object.defineProperty(exports, "submitCheckout", {
|
|
277
283
|
enumerable: true,
|
|
278
284
|
get: function () {
|
|
@@ -34,6 +34,7 @@ export declare function isQuoteError(errorCode: string): boolean;
|
|
|
34
34
|
export declare function abortStripePayment(sessionId: string): Promise<void>;
|
|
35
35
|
export declare function submitCheckout(sessionId: string, isDonation: boolean, payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
36
36
|
export declare function confirmFastCheckout(sessionId: string): Promise<Record<string, unknown>>;
|
|
37
|
+
export declare function skipPaymentMethod(sessionId: string): Promise<Record<string, unknown>>;
|
|
37
38
|
export declare function updateSlippage(sessionId: string, config: {
|
|
38
39
|
mode: string;
|
|
39
40
|
percent: number;
|
|
@@ -13,6 +13,7 @@ exports.getRelayProtocol = getRelayProtocol;
|
|
|
13
13
|
exports.getSessionFingerprint = getSessionFingerprint;
|
|
14
14
|
exports.getSocketHost = getSocketHost;
|
|
15
15
|
exports.isQuoteError = isQuoteError;
|
|
16
|
+
exports.skipPaymentMethod = skipPaymentMethod;
|
|
16
17
|
exports.submitCheckout = submitCheckout;
|
|
17
18
|
exports.updateSlippage = updateSlippage;
|
|
18
19
|
var _api = _interopRequireWildcard(require("../../shared/api"));
|
|
@@ -74,6 +75,12 @@ async function confirmFastCheckout(sessionId) {
|
|
|
74
75
|
} = await _api.default.post(_api.API.FAST_CHECKOUT_CONFIRM(sessionId), {});
|
|
75
76
|
return data;
|
|
76
77
|
}
|
|
78
|
+
async function skipPaymentMethod(sessionId) {
|
|
79
|
+
const {
|
|
80
|
+
data
|
|
81
|
+
} = await _api.default.post(_api.API.SKIP_PAYMENT_METHOD(sessionId), {});
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
77
84
|
async function updateSlippage(sessionId, config) {
|
|
78
85
|
const {
|
|
79
86
|
data
|
|
@@ -29,6 +29,7 @@ export interface UseSubmitReturn {
|
|
|
29
29
|
reset: () => void;
|
|
30
30
|
stripeConfirm: () => Promise<void>;
|
|
31
31
|
stripeCancel: () => Promise<void>;
|
|
32
|
+
stripeSkip: () => Promise<void>;
|
|
32
33
|
vendorStatus: VendorOrderStatus | null;
|
|
33
34
|
/** Whether the checkout config is locked (user clicked "Connect and Pay", pending login/submit) */
|
|
34
35
|
locked: boolean;
|
|
@@ -178,7 +178,8 @@ function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit, isDon
|
|
|
178
178
|
setContext({
|
|
179
179
|
type: "stripe",
|
|
180
180
|
clientSecret,
|
|
181
|
-
intentType
|
|
181
|
+
intentType,
|
|
182
|
+
noPaymentRequired: !!data.noPaymentRequired
|
|
182
183
|
});
|
|
183
184
|
return;
|
|
184
185
|
}
|
|
@@ -357,7 +358,15 @@ function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit, isDon
|
|
|
357
358
|
const execute = (0, _ahooks.useMemoizedFn)(async (force = false) => {
|
|
358
359
|
if (!force && status !== "idle") return;
|
|
359
360
|
if (!force) quoteRetryCountRef.current = 0;
|
|
360
|
-
if (!(await validateForm()))
|
|
361
|
+
if (!(await validateForm())) {
|
|
362
|
+
setStatus("failed");
|
|
363
|
+
setContext({
|
|
364
|
+
type: "error",
|
|
365
|
+
message: "Please complete the required fields",
|
|
366
|
+
code: "VALIDATION_FAILED"
|
|
367
|
+
});
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
361
370
|
const user = session?.user;
|
|
362
371
|
if (!user?.sourceAppPid) {
|
|
363
372
|
const hasVendorConfig = session?.line_items?.some(item => !!item?.price?.product?.vendor_config?.length);
|
|
@@ -493,6 +502,32 @@ function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit, isDon
|
|
|
493
502
|
setStatus("idle");
|
|
494
503
|
setContext(null);
|
|
495
504
|
});
|
|
505
|
+
const stripeSkip = (0, _ahooks.useMemoizedFn)(async () => {
|
|
506
|
+
if (status !== "waiting_stripe") return;
|
|
507
|
+
try {
|
|
508
|
+
setStatus("submitting");
|
|
509
|
+
const data = await (0, _submit.skipPaymentMethod)(sessionId);
|
|
510
|
+
if (!mountedRef.current) return;
|
|
511
|
+
if (data.skipped || data.checkoutSession?.status === "complete") {
|
|
512
|
+
await handleCompletion();
|
|
513
|
+
} else {
|
|
514
|
+
setStatus("failed");
|
|
515
|
+
setContext({
|
|
516
|
+
type: "error",
|
|
517
|
+
message: "Failed to skip payment method",
|
|
518
|
+
code: "SKIP_FAILED"
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
} catch (err) {
|
|
522
|
+
if (!mountedRef.current) return;
|
|
523
|
+
setStatus("failed");
|
|
524
|
+
setContext({
|
|
525
|
+
type: "error",
|
|
526
|
+
message: err.message || "Failed to skip payment method",
|
|
527
|
+
code: "SKIP_FAILED"
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
});
|
|
496
531
|
return {
|
|
497
532
|
status,
|
|
498
533
|
context,
|
|
@@ -504,6 +539,7 @@ function useSubmit(sessionData, sessionId, currencyId, isStripe, isCredit, isDon
|
|
|
504
539
|
reset,
|
|
505
540
|
stripeConfirm,
|
|
506
541
|
stripeCancel,
|
|
542
|
+
stripeSkip,
|
|
507
543
|
vendorStatus,
|
|
508
544
|
locked,
|
|
509
545
|
lock
|
package/lib/checkout/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type SubmitContext = {
|
|
|
16
16
|
type: 'stripe';
|
|
17
17
|
clientSecret: string;
|
|
18
18
|
intentType?: 'payment_intent' | 'setup_intent';
|
|
19
|
+
noPaymentRequired?: boolean;
|
|
19
20
|
} | {
|
|
20
21
|
type: 'did_connect';
|
|
21
22
|
action: string;
|
|
@@ -212,6 +213,7 @@ export interface UseCheckoutReturn {
|
|
|
212
213
|
reset: () => void;
|
|
213
214
|
stripeConfirm: () => Promise<void>;
|
|
214
215
|
stripeCancel: () => Promise<void>;
|
|
216
|
+
stripeSkip: () => Promise<void>;
|
|
215
217
|
vendorStatus: {
|
|
216
218
|
payment_status: string;
|
|
217
219
|
session_status: string;
|
package/lib/shared/api.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare const API: {
|
|
|
33
33
|
readonly CHANGE_AMOUNT: (id: string) => string;
|
|
34
34
|
readonly SLIPPAGE: (id: string) => string;
|
|
35
35
|
readonly ABORT_STRIPE: (id: string) => string;
|
|
36
|
+
readonly SKIP_PAYMENT_METHOD: (id: string) => string;
|
|
36
37
|
readonly RECALCULATE_PROMOTION_SESSION: (id: string) => string;
|
|
37
38
|
readonly APPLY_PROMOTION: (id: string) => string;
|
|
38
39
|
readonly REMOVE_PROMOTION: (id: string) => string;
|
package/lib/shared/api.js
CHANGED
|
@@ -80,6 +80,7 @@ const API = exports.API = {
|
|
|
80
80
|
CHANGE_AMOUNT: id => `/api/checkout-sessions/${id}/amount`,
|
|
81
81
|
SLIPPAGE: id => `/api/checkout-sessions/${id}/slippage`,
|
|
82
82
|
ABORT_STRIPE: id => `/api/checkout-sessions/${id}/abort-stripe`,
|
|
83
|
+
SKIP_PAYMENT_METHOD: id => `/api/checkout-sessions/${id}/skip-payment-method`,
|
|
83
84
|
RECALCULATE_PROMOTION_SESSION: id => `/api/checkout-sessions/${id}/recalculate-promotion`,
|
|
84
85
|
APPLY_PROMOTION: id => `/api/checkout-sessions/${id}/apply-promotion`,
|
|
85
86
|
REMOVE_PROMOTION: id => `/api/checkout-sessions/${id}/remove-promotion`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react-headless",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.5",
|
|
4
4
|
"description": "Headless React hooks for payment-kit checkout",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@arcblock/ws": "^1.28.5",
|
|
37
37
|
"@blocklet/js-sdk": "workspace:*",
|
|
38
|
-
"@blocklet/payment-types": "1.26.
|
|
38
|
+
"@blocklet/payment-types": "1.26.5",
|
|
39
39
|
"@ocap/util": "^1.28.5",
|
|
40
40
|
"ahooks": "^3.8.5",
|
|
41
41
|
"google-libphonenumber": "^3.2.42",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "2d208818d218494407989dd8cd460ab51d075952"
|
|
64
64
|
}
|
|
@@ -98,6 +98,11 @@ export async function confirmFastCheckout(sessionId: string): Promise<Record<str
|
|
|
98
98
|
return data;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
export async function skipPaymentMethod(sessionId: string): Promise<Record<string, unknown>> {
|
|
102
|
+
const { data } = await api.post(API.SKIP_PAYMENT_METHOD(sessionId), {});
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
|
|
101
106
|
export async function updateSlippage(
|
|
102
107
|
sessionId: string,
|
|
103
108
|
config: { mode: string; percent: number }
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
abortStripePayment,
|
|
23
23
|
submitCheckout,
|
|
24
24
|
confirmFastCheckout,
|
|
25
|
+
skipPaymentMethod,
|
|
25
26
|
} from '../core/submit';
|
|
26
27
|
|
|
27
28
|
export interface VendorStatus {
|
|
@@ -55,6 +56,7 @@ export interface UseSubmitReturn {
|
|
|
55
56
|
reset: () => void;
|
|
56
57
|
stripeConfirm: () => Promise<void>;
|
|
57
58
|
stripeCancel: () => Promise<void>;
|
|
59
|
+
stripeSkip: () => Promise<void>;
|
|
58
60
|
vendorStatus: VendorOrderStatus | null;
|
|
59
61
|
/** Whether the checkout config is locked (user clicked "Connect and Pay", pending login/submit) */
|
|
60
62
|
locked: boolean;
|
|
@@ -293,7 +295,7 @@ export function useSubmit(
|
|
|
293
295
|
if (clientSecret) {
|
|
294
296
|
const intentType = (stripeCtx?.intent_type || 'payment_intent') as 'payment_intent' | 'setup_intent';
|
|
295
297
|
setStatus('waiting_stripe');
|
|
296
|
-
setContext({ type: 'stripe', clientSecret, intentType });
|
|
298
|
+
setContext({ type: 'stripe', clientSecret, intentType, noPaymentRequired: !!data.noPaymentRequired });
|
|
297
299
|
return;
|
|
298
300
|
}
|
|
299
301
|
|
|
@@ -510,7 +512,11 @@ export function useSubmit(
|
|
|
510
512
|
// Reset quote retry counter on fresh user-initiated submit
|
|
511
513
|
if (!force) quoteRetryCountRef.current = 0;
|
|
512
514
|
|
|
513
|
-
if (!(await validateForm()))
|
|
515
|
+
if (!(await validateForm())) {
|
|
516
|
+
setStatus('failed');
|
|
517
|
+
setContext({ type: 'error', message: 'Please complete the required fields', code: 'VALIDATION_FAILED' });
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
514
520
|
|
|
515
521
|
// Vendor account validation
|
|
516
522
|
const user = (session as CheckoutSessionRuntime | undefined)?.user;
|
|
@@ -678,6 +684,26 @@ export function useSubmit(
|
|
|
678
684
|
setContext(null);
|
|
679
685
|
});
|
|
680
686
|
|
|
687
|
+
// Stripe skip — user chose "Skip, bind later" for $0 subscription
|
|
688
|
+
const stripeSkip = useMemoizedFn(async () => {
|
|
689
|
+
if (status !== 'waiting_stripe') return;
|
|
690
|
+
try {
|
|
691
|
+
setStatus('submitting');
|
|
692
|
+
const data = await skipPaymentMethod(sessionId);
|
|
693
|
+
if (!mountedRef.current) return;
|
|
694
|
+
if (data.skipped || (data.checkoutSession as Record<string, unknown> | undefined)?.status === 'complete') {
|
|
695
|
+
await handleCompletion();
|
|
696
|
+
} else {
|
|
697
|
+
setStatus('failed');
|
|
698
|
+
setContext({ type: 'error', message: 'Failed to skip payment method', code: 'SKIP_FAILED' });
|
|
699
|
+
}
|
|
700
|
+
} catch (err: any) {
|
|
701
|
+
if (!mountedRef.current) return;
|
|
702
|
+
setStatus('failed');
|
|
703
|
+
setContext({ type: 'error', message: err.message || 'Failed to skip payment method', code: 'SKIP_FAILED' });
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
|
|
681
707
|
return {
|
|
682
708
|
status,
|
|
683
709
|
context,
|
|
@@ -689,6 +715,7 @@ export function useSubmit(
|
|
|
689
715
|
reset,
|
|
690
716
|
stripeConfirm,
|
|
691
717
|
stripeCancel,
|
|
718
|
+
stripeSkip,
|
|
692
719
|
vendorStatus,
|
|
693
720
|
locked,
|
|
694
721
|
lock,
|
package/src/checkout/types.ts
CHANGED
|
@@ -26,7 +26,12 @@ export type SubmitContext =
|
|
|
26
26
|
| { type: 'price_change'; changePercent: number }
|
|
27
27
|
| { type: 'fast_pay'; payType: 'balance' | 'delegation' | 'credit'; amount: string; payer: string }
|
|
28
28
|
| { type: 'credit_insufficient'; available: string; required: string }
|
|
29
|
-
| {
|
|
29
|
+
| {
|
|
30
|
+
type: 'stripe';
|
|
31
|
+
clientSecret: string;
|
|
32
|
+
intentType?: 'payment_intent' | 'setup_intent';
|
|
33
|
+
noPaymentRequired?: boolean;
|
|
34
|
+
}
|
|
30
35
|
| { type: 'did_connect'; action: string; checkpointId: string; extraParams: Record<string, unknown> }
|
|
31
36
|
| { type: 'error'; message: string; code?: string }
|
|
32
37
|
| { type: 'service_suspended' }
|
|
@@ -235,6 +240,7 @@ export interface UseCheckoutReturn {
|
|
|
235
240
|
reset: () => void;
|
|
236
241
|
stripeConfirm: () => Promise<void>;
|
|
237
242
|
stripeCancel: () => Promise<void>;
|
|
243
|
+
stripeSkip: () => Promise<void>;
|
|
238
244
|
vendorStatus: {
|
|
239
245
|
payment_status: string;
|
|
240
246
|
session_status: string;
|
package/src/shared/api.ts
CHANGED
|
@@ -110,6 +110,7 @@ export const API = {
|
|
|
110
110
|
CHANGE_AMOUNT: (id: string) => `/api/checkout-sessions/${id}/amount`,
|
|
111
111
|
SLIPPAGE: (id: string) => `/api/checkout-sessions/${id}/slippage`,
|
|
112
112
|
ABORT_STRIPE: (id: string) => `/api/checkout-sessions/${id}/abort-stripe`,
|
|
113
|
+
SKIP_PAYMENT_METHOD: (id: string) => `/api/checkout-sessions/${id}/skip-payment-method`,
|
|
113
114
|
RECALCULATE_PROMOTION_SESSION: (id: string) => `/api/checkout-sessions/${id}/recalculate-promotion`,
|
|
114
115
|
APPLY_PROMOTION: (id: string) => `/api/checkout-sessions/${id}/apply-promotion`,
|
|
115
116
|
REMOVE_PROMOTION: (id: string) => `/api/checkout-sessions/${id}/remove-promotion`,
|