@blocklet/payment-react-headless 1.26.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.
- package/.eslintrc.js +18 -0
- package/build.config.ts +30 -0
- package/es/checkout/context/CheckoutProvider.d.ts +6 -0
- package/es/checkout/context/CheckoutProvider.js +209 -0
- package/es/checkout/context/CustomerFormContext.d.ts +4 -0
- package/es/checkout/context/CustomerFormContext.js +9 -0
- package/es/checkout/context/ExchangeRateContext.d.ts +11 -0
- package/es/checkout/context/ExchangeRateContext.js +9 -0
- package/es/checkout/context/PaymentMethodContext.d.ts +26 -0
- package/es/checkout/context/PaymentMethodContext.js +9 -0
- package/es/checkout/context/SessionContext.d.ts +45 -0
- package/es/checkout/context/SessionContext.js +9 -0
- package/es/checkout/context/SubmitContext.d.ts +4 -0
- package/es/checkout/context/SubmitContext.js +9 -0
- package/es/checkout/context/index.d.ts +6 -0
- package/es/checkout/context/index.js +6 -0
- package/es/checkout/core/billingInterval.d.ts +15 -0
- package/es/checkout/core/billingInterval.js +36 -0
- package/es/checkout/core/crossSell.d.ts +4 -0
- package/es/checkout/core/crossSell.js +30 -0
- package/es/checkout/core/customerForm.d.ts +5 -0
- package/es/checkout/core/customerForm.js +105 -0
- package/es/checkout/core/exchangeRate.d.ts +11 -0
- package/es/checkout/core/exchangeRate.js +25 -0
- package/es/checkout/core/index.d.ts +10 -0
- package/es/checkout/core/index.js +55 -0
- package/es/checkout/core/lineItems.d.ts +7 -0
- package/es/checkout/core/lineItems.js +59 -0
- package/es/checkout/core/paymentMethod.d.ts +23 -0
- package/es/checkout/core/paymentMethod.js +85 -0
- package/es/checkout/core/pricing.d.ts +32 -0
- package/es/checkout/core/pricing.js +221 -0
- package/es/checkout/core/promotion.d.ts +10 -0
- package/es/checkout/core/promotion.js +39 -0
- package/es/checkout/core/session.d.ts +26 -0
- package/es/checkout/core/session.js +50 -0
- package/es/checkout/core/submit.d.ts +40 -0
- package/es/checkout/core/submit.js +66 -0
- package/es/checkout/hooks/index.d.ts +34 -0
- package/es/checkout/hooks/index.js +19 -0
- package/es/checkout/hooks/useBillingInterval.d.ts +14 -0
- package/es/checkout/hooks/useBillingInterval.js +50 -0
- package/es/checkout/hooks/useCheckout.d.ts +2 -0
- package/es/checkout/hooks/useCheckout.js +212 -0
- package/es/checkout/hooks/useCheckoutSession.d.ts +58 -0
- package/es/checkout/hooks/useCheckoutSession.js +107 -0
- package/es/checkout/hooks/useCheckoutStatus.d.ts +10 -0
- package/es/checkout/hooks/useCheckoutStatus.js +16 -0
- package/es/checkout/hooks/useCrossSell.d.ts +8 -0
- package/es/checkout/hooks/useCrossSell.js +57 -0
- package/es/checkout/hooks/useCustomerForm.d.ts +14 -0
- package/es/checkout/hooks/useCustomerForm.js +116 -0
- package/es/checkout/hooks/useCustomerFormFeature.d.ts +2 -0
- package/es/checkout/hooks/useCustomerFormFeature.js +4 -0
- package/es/checkout/hooks/useExchangeRate.d.ts +11 -0
- package/es/checkout/hooks/useExchangeRate.js +15 -0
- package/es/checkout/hooks/useLineItems.d.ts +22 -0
- package/es/checkout/hooks/useLineItems.js +139 -0
- package/es/checkout/hooks/usePaymentMethod.d.ts +26 -0
- package/es/checkout/hooks/usePaymentMethod.js +101 -0
- package/es/checkout/hooks/usePaymentMethodFeature.d.ts +2 -0
- package/es/checkout/hooks/usePaymentMethodFeature.js +4 -0
- package/es/checkout/hooks/usePricing.d.ts +57 -0
- package/es/checkout/hooks/usePricing.js +174 -0
- package/es/checkout/hooks/usePricingFeature.d.ts +28 -0
- package/es/checkout/hooks/usePricingFeature.js +36 -0
- package/es/checkout/hooks/useProduct.d.ts +32 -0
- package/es/checkout/hooks/useProduct.js +5 -0
- package/es/checkout/hooks/usePromotion.d.ts +12 -0
- package/es/checkout/hooks/usePromotion.js +48 -0
- package/es/checkout/hooks/useSlippage.d.ts +8 -0
- package/es/checkout/hooks/useSlippage.js +29 -0
- package/es/checkout/hooks/useSubmit.d.ts +38 -0
- package/es/checkout/hooks/useSubmit.js +493 -0
- package/es/checkout/hooks/useSubmitFeature.d.ts +2 -0
- package/es/checkout/hooks/useSubmitFeature.js +4 -0
- package/es/checkout/hooks/useUpsell.d.ts +5 -0
- package/es/checkout/hooks/useUpsell.js +25 -0
- package/es/checkout/index.d.ts +37 -0
- package/es/checkout/index.js +28 -0
- package/es/checkout/types.d.ts +262 -0
- package/es/checkout/types.js +0 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +28 -0
- package/es/shared/api.d.ts +41 -0
- package/es/shared/api.js +81 -0
- package/es/shared/format.d.ts +38 -0
- package/es/shared/format.js +229 -0
- package/es/shared/polling.d.ts +15 -0
- package/es/shared/polling.js +20 -0
- package/es/shared/types.d.ts +10 -0
- package/es/shared/types.js +0 -0
- package/es/shared/validation.d.ts +38 -0
- package/es/shared/validation.js +190 -0
- package/es/types/checkout-augmented.d.ts +42 -0
- package/es/types/checkout-augmented.js +17 -0
- package/es/types/external.d.ts +18 -0
- package/examples/01-basic-checkout.tsx +159 -0
- package/examples/01-credit-recharge.tsx +19 -0
- package/examples/02-subscription.tsx +40 -0
- package/examples/03-upsell.tsx +60 -0
- package/examples/04-cross-sell.tsx +54 -0
- package/examples/05-full-checkout.tsx +126 -0
- package/jest.config.js +15 -0
- package/lib/checkout/context/CheckoutProvider.d.ts +6 -0
- package/lib/checkout/context/CheckoutProvider.js +181 -0
- package/lib/checkout/context/CustomerFormContext.d.ts +4 -0
- package/lib/checkout/context/CustomerFormContext.js +16 -0
- package/lib/checkout/context/ExchangeRateContext.d.ts +11 -0
- package/lib/checkout/context/ExchangeRateContext.js +16 -0
- package/lib/checkout/context/PaymentMethodContext.d.ts +26 -0
- package/lib/checkout/context/PaymentMethodContext.js +16 -0
- package/lib/checkout/context/SessionContext.d.ts +45 -0
- package/lib/checkout/context/SessionContext.js +16 -0
- package/lib/checkout/context/SubmitContext.d.ts +4 -0
- package/lib/checkout/context/SubmitContext.js +16 -0
- package/lib/checkout/context/index.d.ts +6 -0
- package/lib/checkout/context/index.js +77 -0
- package/lib/checkout/core/billingInterval.d.ts +15 -0
- package/lib/checkout/core/billingInterval.js +42 -0
- package/lib/checkout/core/crossSell.d.ts +4 -0
- package/lib/checkout/core/crossSell.js +43 -0
- package/lib/checkout/core/customerForm.d.ts +5 -0
- package/lib/checkout/core/customerForm.js +106 -0
- package/lib/checkout/core/exchangeRate.d.ts +11 -0
- package/lib/checkout/core/exchangeRate.js +45 -0
- package/lib/checkout/core/index.d.ts +10 -0
- package/lib/checkout/core/index.js +297 -0
- package/lib/checkout/core/lineItems.d.ts +7 -0
- package/lib/checkout/core/lineItems.js +76 -0
- package/lib/checkout/core/paymentMethod.d.ts +23 -0
- package/lib/checkout/core/paymentMethod.js +114 -0
- package/lib/checkout/core/pricing.d.ts +32 -0
- package/lib/checkout/core/pricing.js +216 -0
- package/lib/checkout/core/promotion.d.ts +10 -0
- package/lib/checkout/core/promotion.js +62 -0
- package/lib/checkout/core/session.d.ts +26 -0
- package/lib/checkout/core/session.js +58 -0
- package/lib/checkout/core/submit.d.ts +40 -0
- package/lib/checkout/core/submit.js +84 -0
- package/lib/checkout/hooks/index.d.ts +34 -0
- package/lib/checkout/hooks/index.js +138 -0
- package/lib/checkout/hooks/useBillingInterval.d.ts +14 -0
- package/lib/checkout/hooks/useBillingInterval.js +63 -0
- package/lib/checkout/hooks/useCheckout.d.ts +2 -0
- package/lib/checkout/hooks/useCheckout.js +190 -0
- package/lib/checkout/hooks/useCheckoutSession.d.ts +58 -0
- package/lib/checkout/hooks/useCheckoutSession.js +119 -0
- package/lib/checkout/hooks/useCheckoutStatus.d.ts +10 -0
- package/lib/checkout/hooks/useCheckoutStatus.js +28 -0
- package/lib/checkout/hooks/useCrossSell.d.ts +8 -0
- package/lib/checkout/hooks/useCrossSell.js +75 -0
- package/lib/checkout/hooks/useCustomerForm.d.ts +14 -0
- package/lib/checkout/hooks/useCustomerForm.js +135 -0
- package/lib/checkout/hooks/useCustomerFormFeature.d.ts +2 -0
- package/lib/checkout/hooks/useCustomerFormFeature.js +10 -0
- package/lib/checkout/hooks/useExchangeRate.d.ts +11 -0
- package/lib/checkout/hooks/useExchangeRate.js +29 -0
- package/lib/checkout/hooks/useLineItems.d.ts +22 -0
- package/lib/checkout/hooks/useLineItems.js +142 -0
- package/lib/checkout/hooks/usePaymentMethod.d.ts +26 -0
- package/lib/checkout/hooks/usePaymentMethod.js +101 -0
- package/lib/checkout/hooks/usePaymentMethodFeature.d.ts +2 -0
- package/lib/checkout/hooks/usePaymentMethodFeature.js +10 -0
- package/lib/checkout/hooks/usePricing.d.ts +57 -0
- package/lib/checkout/hooks/usePricing.js +168 -0
- package/lib/checkout/hooks/usePricingFeature.d.ts +28 -0
- package/lib/checkout/hooks/usePricingFeature.js +48 -0
- package/lib/checkout/hooks/useProduct.d.ts +32 -0
- package/lib/checkout/hooks/useProduct.js +21 -0
- package/lib/checkout/hooks/usePromotion.d.ts +12 -0
- package/lib/checkout/hooks/usePromotion.js +57 -0
- package/lib/checkout/hooks/useSlippage.d.ts +8 -0
- package/lib/checkout/hooks/useSlippage.js +39 -0
- package/lib/checkout/hooks/useSubmit.d.ts +38 -0
- package/lib/checkout/hooks/useSubmit.js +504 -0
- package/lib/checkout/hooks/useSubmitFeature.d.ts +2 -0
- package/lib/checkout/hooks/useSubmitFeature.js +10 -0
- package/lib/checkout/hooks/useUpsell.d.ts +5 -0
- package/lib/checkout/hooks/useUpsell.js +40 -0
- package/lib/checkout/index.d.ts +37 -0
- package/lib/checkout/index.js +182 -0
- package/lib/checkout/types.d.ts +262 -0
- package/lib/checkout/types.js +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +162 -0
- package/lib/shared/api.d.ts +41 -0
- package/lib/shared/api.js +88 -0
- package/lib/shared/format.d.ts +38 -0
- package/lib/shared/format.js +262 -0
- package/lib/shared/polling.d.ts +15 -0
- package/lib/shared/polling.js +32 -0
- package/lib/shared/types.d.ts +10 -0
- package/lib/shared/types.js +1 -0
- package/lib/shared/validation.d.ts +38 -0
- package/lib/shared/validation.js +212 -0
- package/lib/types/checkout-augmented.d.ts +42 -0
- package/lib/types/checkout-augmented.js +24 -0
- package/lib/types/external.d.ts +18 -0
- package/package.json +64 -0
- package/src/checkout/context/CheckoutProvider.tsx +269 -0
- package/src/checkout/context/CustomerFormContext.ts +14 -0
- package/src/checkout/context/ExchangeRateContext.ts +21 -0
- package/src/checkout/context/PaymentMethodContext.ts +36 -0
- package/src/checkout/context/SessionContext.ts +49 -0
- package/src/checkout/context/SubmitContext.ts +14 -0
- package/src/checkout/context/index.ts +6 -0
- package/src/checkout/core/billingInterval.ts +62 -0
- package/src/checkout/core/crossSell.ts +52 -0
- package/src/checkout/core/customerForm.ts +122 -0
- package/src/checkout/core/exchangeRate.ts +38 -0
- package/src/checkout/core/index.ts +60 -0
- package/src/checkout/core/lineItems.ts +106 -0
- package/src/checkout/core/paymentMethod.ts +113 -0
- package/src/checkout/core/pricing.ts +347 -0
- package/src/checkout/core/promotion.ts +59 -0
- package/src/checkout/core/session.ts +62 -0
- package/src/checkout/core/submit.ts +109 -0
- package/src/checkout/hooks/index.ts +41 -0
- package/src/checkout/hooks/useBillingInterval.ts +71 -0
- package/src/checkout/hooks/useCheckout.ts +267 -0
- package/src/checkout/hooks/useCheckoutSession.ts +217 -0
- package/src/checkout/hooks/useCheckoutStatus.ts +31 -0
- package/src/checkout/hooks/useCrossSell.ts +80 -0
- package/src/checkout/hooks/useCustomerForm.ts +156 -0
- package/src/checkout/hooks/useCustomerFormFeature.ts +7 -0
- package/src/checkout/hooks/useExchangeRate.ts +28 -0
- package/src/checkout/hooks/useLineItems.ts +191 -0
- package/src/checkout/hooks/usePaymentMethod.ts +165 -0
- package/src/checkout/hooks/usePaymentMethodFeature.ts +8 -0
- package/src/checkout/hooks/usePricing.ts +274 -0
- package/src/checkout/hooks/usePricingFeature.ts +73 -0
- package/src/checkout/hooks/useProduct.ts +32 -0
- package/src/checkout/hooks/usePromotion.ts +67 -0
- package/src/checkout/hooks/useSlippage.ts +39 -0
- package/src/checkout/hooks/useSubmit.ts +684 -0
- package/src/checkout/hooks/useSubmitFeature.ts +7 -0
- package/src/checkout/hooks/useUpsell.ts +35 -0
- package/src/checkout/index.ts +65 -0
- package/src/checkout/types.ts +292 -0
- package/src/index.ts +64 -0
- package/src/shared/api.ts +118 -0
- package/src/shared/format.ts +318 -0
- package/src/shared/polling.ts +49 -0
- package/src/shared/types.ts +13 -0
- package/src/shared/validation.ts +254 -0
- package/src/types/checkout-augmented.ts +77 -0
- package/src/types/external.d.ts +18 -0
- package/tools/jest.js +1 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemoizedFn } from 'ahooks';
|
|
2
|
+
|
|
3
|
+
import { getErrorMessage } from '../../types/checkout-augmented';
|
|
4
|
+
import { useSessionContext } from '../context/SessionContext';
|
|
5
|
+
import { usePaymentMethodContext } from '../context/PaymentMethodContext';
|
|
6
|
+
import { performUpsell, performDownsell } from '../core/lineItems';
|
|
7
|
+
|
|
8
|
+
export interface UseUpsellReturn {
|
|
9
|
+
upsell: (fromId: string, toId: string) => Promise<void>;
|
|
10
|
+
downsell: (priceId: string) => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useUpsell(): UseUpsellReturn {
|
|
14
|
+
const { session, effectiveSessionId, refresh } = useSessionContext();
|
|
15
|
+
const { currency } = usePaymentMethodContext();
|
|
16
|
+
const currencyId = currency?.id || null;
|
|
17
|
+
|
|
18
|
+
const upsell = useMemoizedFn(async (fromId: string, toId: string) => {
|
|
19
|
+
try {
|
|
20
|
+
await performUpsell(effectiveSessionId, fromId, toId, session, currencyId, refresh);
|
|
21
|
+
} catch (err: unknown) {
|
|
22
|
+
console.error('Failed to upsell:', getErrorMessage(err));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const downsell = useMemoizedFn(async (priceId: string) => {
|
|
27
|
+
try {
|
|
28
|
+
await performDownsell(effectiveSessionId, priceId, session, currencyId, refresh);
|
|
29
|
+
} catch (err: unknown) {
|
|
30
|
+
console.error('Failed to downsell:', getErrorMessage(err));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return { upsell, downsell };
|
|
35
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Checkout domain exports
|
|
2
|
+
|
|
3
|
+
// Types
|
|
4
|
+
export type {
|
|
5
|
+
UseCheckoutReturn,
|
|
6
|
+
SubmitStatus,
|
|
7
|
+
SubmitContext,
|
|
8
|
+
FieldConfig,
|
|
9
|
+
CheckoutFormData,
|
|
10
|
+
CheckoutResult,
|
|
11
|
+
} from './types';
|
|
12
|
+
|
|
13
|
+
// Context & Provider
|
|
14
|
+
export {
|
|
15
|
+
CheckoutProvider,
|
|
16
|
+
type CheckoutProviderProps,
|
|
17
|
+
useSessionContext,
|
|
18
|
+
usePaymentMethodContext,
|
|
19
|
+
useExchangeRateContext,
|
|
20
|
+
useCustomerFormContext,
|
|
21
|
+
useSubmitContext,
|
|
22
|
+
} from './context';
|
|
23
|
+
|
|
24
|
+
// Backward-compatible orchestrator
|
|
25
|
+
export { useCheckout } from './hooks/useCheckout';
|
|
26
|
+
|
|
27
|
+
// Param-based hooks
|
|
28
|
+
export { useCheckoutSession } from './hooks/useCheckoutSession';
|
|
29
|
+
export type { UseCheckoutSessionReturn, SessionData } from './hooks/useCheckoutSession';
|
|
30
|
+
export { usePaymentMethod } from './hooks/usePaymentMethod';
|
|
31
|
+
export type { UsePaymentMethodReturn } from './hooks/usePaymentMethod';
|
|
32
|
+
export { usePricing } from './hooks/usePricing';
|
|
33
|
+
export type { UsePricingReturn } from './hooks/usePricing';
|
|
34
|
+
export { useCustomerForm } from './hooks/useCustomerForm';
|
|
35
|
+
export type { UseCustomerFormReturn } from './hooks/useCustomerForm';
|
|
36
|
+
export { useSubmit } from './hooks/useSubmit';
|
|
37
|
+
export type { UseSubmitReturn } from './hooks/useSubmit';
|
|
38
|
+
|
|
39
|
+
// NEW: Feature hooks (require <CheckoutProvider>)
|
|
40
|
+
export { useProduct } from './hooks/useProduct';
|
|
41
|
+
export type { UseProductReturn } from './hooks/useProduct';
|
|
42
|
+
export { useLineItems } from './hooks/useLineItems';
|
|
43
|
+
export type { UseLineItemsReturn } from './hooks/useLineItems';
|
|
44
|
+
export { useBillingInterval } from './hooks/useBillingInterval';
|
|
45
|
+
export type { UseBillingIntervalReturn, BillingIntervalData } from './hooks/useBillingInterval';
|
|
46
|
+
export { useUpsell } from './hooks/useUpsell';
|
|
47
|
+
export type { UseUpsellReturn } from './hooks/useUpsell';
|
|
48
|
+
export { useCrossSell } from './hooks/useCrossSell';
|
|
49
|
+
export type { UseCrossSellReturn } from './hooks/useCrossSell';
|
|
50
|
+
export { usePromotion } from './hooks/usePromotion';
|
|
51
|
+
export type { UsePromotionReturn } from './hooks/usePromotion';
|
|
52
|
+
export { useExchangeRate } from './hooks/useExchangeRate';
|
|
53
|
+
export type { UseExchangeRateReturn } from './hooks/useExchangeRate';
|
|
54
|
+
export { useSlippage } from './hooks/useSlippage';
|
|
55
|
+
export type { UseSlippageReturn } from './hooks/useSlippage';
|
|
56
|
+
export { useCheckoutStatus } from './hooks/useCheckoutStatus';
|
|
57
|
+
export type { UseCheckoutStatusReturn } from './hooks/useCheckoutStatus';
|
|
58
|
+
export { usePricingFeature } from './hooks/usePricingFeature';
|
|
59
|
+
export type { UsePricingFeatureReturn } from './hooks/usePricingFeature';
|
|
60
|
+
export { usePaymentMethodFeature } from './hooks/usePaymentMethodFeature';
|
|
61
|
+
export { useCustomerFormFeature } from './hooks/useCustomerFormFeature';
|
|
62
|
+
export { useSubmitFeature } from './hooks/useSubmitFeature';
|
|
63
|
+
|
|
64
|
+
// Shared utilities
|
|
65
|
+
export { getPriceUnitAmountByCurrency } from '../shared/format';
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TLineItemExpanded,
|
|
3
|
+
TPaymentMethodExpanded,
|
|
4
|
+
TPaymentCurrency,
|
|
5
|
+
TPrice,
|
|
6
|
+
TCheckoutSessionExpanded,
|
|
7
|
+
TPaymentIntent,
|
|
8
|
+
TCustomer,
|
|
9
|
+
} from '@blocklet/payment-types';
|
|
10
|
+
|
|
11
|
+
// ── Submit State Machine ──
|
|
12
|
+
|
|
13
|
+
export type SubmitStatus =
|
|
14
|
+
| 'idle'
|
|
15
|
+
| 'submitting'
|
|
16
|
+
| 'confirming_price'
|
|
17
|
+
| 'confirming_fast_pay'
|
|
18
|
+
| 'credit_insufficient'
|
|
19
|
+
| 'waiting_did'
|
|
20
|
+
| 'waiting_stripe'
|
|
21
|
+
| 'completed'
|
|
22
|
+
| 'failed';
|
|
23
|
+
|
|
24
|
+
export type SubmitContext =
|
|
25
|
+
| { type: 'price_change'; changePercent: number }
|
|
26
|
+
| { type: 'fast_pay'; payType: 'balance' | 'delegation' | 'credit'; amount: string; payer: string }
|
|
27
|
+
| { type: 'credit_insufficient'; available: string; required: string }
|
|
28
|
+
| { type: 'stripe'; clientSecret: string; intentType?: 'payment_intent' | 'setup_intent' }
|
|
29
|
+
| { type: 'did_connect'; action: string; checkpointId: string; extraParams: Record<string, unknown> }
|
|
30
|
+
| { type: 'error'; message: string; code?: string }
|
|
31
|
+
| null;
|
|
32
|
+
|
|
33
|
+
// ── Form ──
|
|
34
|
+
|
|
35
|
+
export interface FieldConfig {
|
|
36
|
+
name: string;
|
|
37
|
+
type: 'text' | 'email' | 'phone' | 'select' | 'country';
|
|
38
|
+
required: boolean;
|
|
39
|
+
options?: Array<{ value: string; label: string }>;
|
|
40
|
+
group?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type CheckoutFormData = {
|
|
44
|
+
customer_name: string;
|
|
45
|
+
customer_email?: string;
|
|
46
|
+
customer_phone?: string;
|
|
47
|
+
payment_method: string;
|
|
48
|
+
payment_currency: string;
|
|
49
|
+
billing_address?: {
|
|
50
|
+
country: string;
|
|
51
|
+
state?: string;
|
|
52
|
+
city?: string;
|
|
53
|
+
line1?: string;
|
|
54
|
+
line2?: string;
|
|
55
|
+
postal_code: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// ── Checkout Context (result after completion) ──
|
|
60
|
+
|
|
61
|
+
export type CheckoutResult = {
|
|
62
|
+
checkoutSession: TCheckoutSessionExpanded;
|
|
63
|
+
paymentMethods: TPaymentMethodExpanded[];
|
|
64
|
+
paymentIntent?: TPaymentIntent;
|
|
65
|
+
customer?: TCustomer;
|
|
66
|
+
quotes?: Record<
|
|
67
|
+
string,
|
|
68
|
+
{
|
|
69
|
+
quote_id: string;
|
|
70
|
+
expires_at: number;
|
|
71
|
+
quoted_amount: string;
|
|
72
|
+
exchange_rate?: string;
|
|
73
|
+
rate_provider_name?: string;
|
|
74
|
+
rate_provider_id?: string;
|
|
75
|
+
}
|
|
76
|
+
>;
|
|
77
|
+
rateUnavailable?: boolean;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// ── UseCheckoutReturn ──
|
|
81
|
+
|
|
82
|
+
export interface UseCheckoutReturn {
|
|
83
|
+
// Loading state
|
|
84
|
+
isLoading: boolean;
|
|
85
|
+
error: string | null;
|
|
86
|
+
/** Structured error code: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | null */
|
|
87
|
+
errorCode: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | null;
|
|
88
|
+
refresh: () => Promise<void>;
|
|
89
|
+
|
|
90
|
+
// Vendor count (for post-payment vendor order polling)
|
|
91
|
+
vendorCount: number;
|
|
92
|
+
|
|
93
|
+
// Product
|
|
94
|
+
product: {
|
|
95
|
+
name: string;
|
|
96
|
+
description: string;
|
|
97
|
+
images: string[];
|
|
98
|
+
features: Array<{ name: string; icon?: string }>;
|
|
99
|
+
billing: {
|
|
100
|
+
mode: 'payment' | 'subscription';
|
|
101
|
+
interval: 'month' | 'year' | 'week' | 'day' | null;
|
|
102
|
+
intervalCount: number;
|
|
103
|
+
displayInterval: string;
|
|
104
|
+
};
|
|
105
|
+
metadata: Record<string, string>;
|
|
106
|
+
} | null;
|
|
107
|
+
|
|
108
|
+
// Line items
|
|
109
|
+
lineItems: {
|
|
110
|
+
items: Array<
|
|
111
|
+
TLineItemExpanded & {
|
|
112
|
+
adjustable_quantity?: {
|
|
113
|
+
enabled: boolean;
|
|
114
|
+
minimum?: number;
|
|
115
|
+
maximum?: number;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
>;
|
|
119
|
+
updateQuantity: (itemId: string, qty: number) => Promise<void>;
|
|
120
|
+
upsell: (fromId: string, toId: string) => Promise<void>;
|
|
121
|
+
downsell: (priceId: string) => Promise<void>;
|
|
122
|
+
billingInterval: {
|
|
123
|
+
current: 'month' | 'year' | 'week' | 'day' | null;
|
|
124
|
+
available: Array<{
|
|
125
|
+
interval: 'month' | 'year' | 'week' | 'day';
|
|
126
|
+
priceId: string;
|
|
127
|
+
amount: string;
|
|
128
|
+
savings: string | null;
|
|
129
|
+
}>;
|
|
130
|
+
switch: (interval: 'month' | 'year' | 'week' | 'day') => Promise<void>;
|
|
131
|
+
} | null;
|
|
132
|
+
crossSellItem: TPrice | null;
|
|
133
|
+
crossSellRequired: boolean;
|
|
134
|
+
addCrossSell: () => Promise<void>;
|
|
135
|
+
removeCrossSell: () => Promise<void>;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Payment method
|
|
139
|
+
paymentMethod: {
|
|
140
|
+
current: TPaymentMethodExpanded;
|
|
141
|
+
currency: TPaymentCurrency;
|
|
142
|
+
available: TPaymentMethodExpanded[];
|
|
143
|
+
currencies: TPaymentCurrency[];
|
|
144
|
+
isStripe: boolean;
|
|
145
|
+
isCrypto: boolean;
|
|
146
|
+
isCredit: boolean;
|
|
147
|
+
setType: (type: 'stripe' | 'crypto') => Promise<void>;
|
|
148
|
+
types: Array<{
|
|
149
|
+
type: 'stripe' | 'crypto';
|
|
150
|
+
label: string;
|
|
151
|
+
currencies: TPaymentCurrency[];
|
|
152
|
+
active: boolean;
|
|
153
|
+
}>;
|
|
154
|
+
setCurrency: (currencyId: string) => Promise<void>;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Stripe config
|
|
158
|
+
stripe: {
|
|
159
|
+
publishableKey: string | null;
|
|
160
|
+
clientSecret: string | null;
|
|
161
|
+
status: 'idle' | 'ready' | 'processing' | 'succeeded' | 'failed';
|
|
162
|
+
} | null;
|
|
163
|
+
|
|
164
|
+
// Pricing
|
|
165
|
+
pricing: {
|
|
166
|
+
subtotal: string;
|
|
167
|
+
discount: string | null;
|
|
168
|
+
tax: {
|
|
169
|
+
amount: string;
|
|
170
|
+
rate: number;
|
|
171
|
+
inclusive: boolean;
|
|
172
|
+
} | null;
|
|
173
|
+
staking: string | null;
|
|
174
|
+
total: string;
|
|
175
|
+
usdEquivalent: string | null;
|
|
176
|
+
hasDynamicPricing: boolean;
|
|
177
|
+
rate: {
|
|
178
|
+
value: string | null;
|
|
179
|
+
display: string | null;
|
|
180
|
+
provider: string | null;
|
|
181
|
+
providerDisplay: string | null;
|
|
182
|
+
fetchedAt: number | null;
|
|
183
|
+
status: 'loading' | 'available' | 'unavailable';
|
|
184
|
+
refresh: () => Promise<void>;
|
|
185
|
+
};
|
|
186
|
+
quote: {
|
|
187
|
+
locked: boolean;
|
|
188
|
+
lockedAt: number | null;
|
|
189
|
+
expiresAt: number | null;
|
|
190
|
+
expired: boolean;
|
|
191
|
+
baseCurrency: string | null;
|
|
192
|
+
};
|
|
193
|
+
slippage: {
|
|
194
|
+
percent: number;
|
|
195
|
+
set: (config: { mode: string; percent: number }) => Promise<void>;
|
|
196
|
+
};
|
|
197
|
+
promotion: {
|
|
198
|
+
applied: boolean;
|
|
199
|
+
code: string | null;
|
|
200
|
+
active: boolean;
|
|
201
|
+
inactiveReason: string | null;
|
|
202
|
+
apply: (code: string) => Promise<{ success: boolean; error?: string }>;
|
|
203
|
+
remove: () => Promise<void>;
|
|
204
|
+
};
|
|
205
|
+
trial: {
|
|
206
|
+
active: boolean;
|
|
207
|
+
days: number;
|
|
208
|
+
afterTrialPrice: string | null;
|
|
209
|
+
afterTrialInterval: string | null;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Form
|
|
214
|
+
form: {
|
|
215
|
+
fields: FieldConfig[];
|
|
216
|
+
values: CheckoutFormData;
|
|
217
|
+
onChange: (field: string, value: string | boolean | Record<string, string>) => void;
|
|
218
|
+
errors: Partial<Record<string, string>>;
|
|
219
|
+
touched: Record<string, boolean>;
|
|
220
|
+
validate: () => Promise<boolean>;
|
|
221
|
+
validateField: (field: string) => Promise<void>;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Submit
|
|
225
|
+
submit: {
|
|
226
|
+
status: SubmitStatus;
|
|
227
|
+
context: SubmitContext;
|
|
228
|
+
execute: () => Promise<void>;
|
|
229
|
+
confirm: () => Promise<void>;
|
|
230
|
+
cancel: () => void;
|
|
231
|
+
result: CheckoutResult | null;
|
|
232
|
+
retry: () => Promise<void>;
|
|
233
|
+
reset: () => void;
|
|
234
|
+
stripeConfirm: () => Promise<void>;
|
|
235
|
+
stripeCancel: () => Promise<void>;
|
|
236
|
+
vendorStatus: {
|
|
237
|
+
payment_status: string;
|
|
238
|
+
session_status: string;
|
|
239
|
+
vendors: Array<{
|
|
240
|
+
success: boolean;
|
|
241
|
+
status: 'delivered' | 'pending' | 'failed';
|
|
242
|
+
progress: number;
|
|
243
|
+
message: string;
|
|
244
|
+
appUrl?: string;
|
|
245
|
+
title?: string;
|
|
246
|
+
name?: string;
|
|
247
|
+
vendorType: string;
|
|
248
|
+
}>;
|
|
249
|
+
error: string | null;
|
|
250
|
+
isAllCompleted: boolean;
|
|
251
|
+
hasFailed: boolean;
|
|
252
|
+
} | null;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Subscription config
|
|
256
|
+
subscription: {
|
|
257
|
+
mode: 'payment' | 'subscription' | 'setup';
|
|
258
|
+
showStake: boolean;
|
|
259
|
+
confirmMessage: string;
|
|
260
|
+
} | null;
|
|
261
|
+
|
|
262
|
+
// Page config
|
|
263
|
+
pageInfo: {
|
|
264
|
+
formPurposeDescription?: { en: string; zh: string };
|
|
265
|
+
showProductFeatures: boolean;
|
|
266
|
+
} | null;
|
|
267
|
+
|
|
268
|
+
// Convenience
|
|
269
|
+
canSubmit: boolean;
|
|
270
|
+
isCompleted: boolean;
|
|
271
|
+
isDonation: boolean;
|
|
272
|
+
setDonationAmount: (priceId: string, amount: string) => Promise<void>;
|
|
273
|
+
|
|
274
|
+
// Customer info (from API, available when logged in)
|
|
275
|
+
customer: {
|
|
276
|
+
name: string;
|
|
277
|
+
email: string;
|
|
278
|
+
phone: string;
|
|
279
|
+
address: {
|
|
280
|
+
country: string;
|
|
281
|
+
state: string;
|
|
282
|
+
city: string;
|
|
283
|
+
line1: string;
|
|
284
|
+
line2: string;
|
|
285
|
+
postal_code: string;
|
|
286
|
+
};
|
|
287
|
+
} | null;
|
|
288
|
+
|
|
289
|
+
// Raw session (for advanced UI access)
|
|
290
|
+
livemode: boolean;
|
|
291
|
+
session: TCheckoutSessionExpanded | null;
|
|
292
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Root re-export: checkout domain
|
|
2
|
+
export {
|
|
3
|
+
// Types
|
|
4
|
+
type UseCheckoutReturn,
|
|
5
|
+
type SubmitStatus,
|
|
6
|
+
type SubmitContext,
|
|
7
|
+
type FieldConfig,
|
|
8
|
+
type CheckoutFormData,
|
|
9
|
+
type CheckoutResult,
|
|
10
|
+
type UseCheckoutSessionReturn,
|
|
11
|
+
type SessionData,
|
|
12
|
+
type UsePaymentMethodReturn,
|
|
13
|
+
type UsePricingReturn,
|
|
14
|
+
type UseCustomerFormReturn,
|
|
15
|
+
type UseSubmitReturn,
|
|
16
|
+
type UseProductReturn,
|
|
17
|
+
type UseLineItemsReturn,
|
|
18
|
+
type UseBillingIntervalReturn,
|
|
19
|
+
type BillingIntervalData,
|
|
20
|
+
type UseUpsellReturn,
|
|
21
|
+
type UseCrossSellReturn,
|
|
22
|
+
type UsePromotionReturn,
|
|
23
|
+
type UseExchangeRateReturn,
|
|
24
|
+
type UseSlippageReturn,
|
|
25
|
+
type UseCheckoutStatusReturn,
|
|
26
|
+
type UsePricingFeatureReturn,
|
|
27
|
+
type CheckoutProviderProps,
|
|
28
|
+
|
|
29
|
+
// Context & Provider
|
|
30
|
+
CheckoutProvider,
|
|
31
|
+
useSessionContext,
|
|
32
|
+
usePaymentMethodContext,
|
|
33
|
+
useExchangeRateContext,
|
|
34
|
+
useCustomerFormContext,
|
|
35
|
+
useSubmitContext,
|
|
36
|
+
|
|
37
|
+
// Backward-compatible orchestrator
|
|
38
|
+
useCheckout,
|
|
39
|
+
|
|
40
|
+
// Param-based hooks
|
|
41
|
+
useCheckoutSession,
|
|
42
|
+
usePaymentMethod,
|
|
43
|
+
usePricing,
|
|
44
|
+
useCustomerForm,
|
|
45
|
+
useSubmit,
|
|
46
|
+
|
|
47
|
+
// Feature hooks (require <CheckoutProvider>)
|
|
48
|
+
useProduct,
|
|
49
|
+
useLineItems,
|
|
50
|
+
useBillingInterval,
|
|
51
|
+
useUpsell,
|
|
52
|
+
useCrossSell,
|
|
53
|
+
usePromotion,
|
|
54
|
+
useExchangeRate,
|
|
55
|
+
useSlippage,
|
|
56
|
+
useCheckoutStatus,
|
|
57
|
+
usePricingFeature,
|
|
58
|
+
usePaymentMethodFeature,
|
|
59
|
+
useCustomerFormFeature,
|
|
60
|
+
useSubmitFeature,
|
|
61
|
+
|
|
62
|
+
// Shared utilities
|
|
63
|
+
getPriceUnitAmountByCurrency,
|
|
64
|
+
} from './checkout';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createAxios } from '@blocklet/js-sdk';
|
|
2
|
+
import type { InternalAxiosRequestConfig } from 'axios';
|
|
3
|
+
import isNull from 'lodash/isNull';
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
blocklet?: {
|
|
8
|
+
prefix?: string;
|
|
9
|
+
componentId?: string;
|
|
10
|
+
componentMountPoints?: Array<{ did: string; mountPoint: string }>;
|
|
11
|
+
appName?: string;
|
|
12
|
+
languages?: string[];
|
|
13
|
+
};
|
|
14
|
+
__PAYMENT_KIT_AUTH_TOKEN?: string;
|
|
15
|
+
__PAYMENT_KIT_BASE_URL?: string;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const PAYMENT_KIT_DID = 'z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk';
|
|
20
|
+
|
|
21
|
+
export function getPrefix(): string {
|
|
22
|
+
const prefix = window.blocklet?.prefix || '/';
|
|
23
|
+
const baseUrl = window.location?.origin;
|
|
24
|
+
|
|
25
|
+
if (window.__PAYMENT_KIT_BASE_URL) {
|
|
26
|
+
try {
|
|
27
|
+
const tmp = new URL(window.__PAYMENT_KIT_BASE_URL);
|
|
28
|
+
if (tmp.origin !== window.location.origin) {
|
|
29
|
+
return window.__PAYMENT_KIT_BASE_URL;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
// Invalid baseUrl, fallback
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const componentId = (window.blocklet?.componentId || '').split('/').pop();
|
|
37
|
+
if (componentId === PAYMENT_KIT_DID) {
|
|
38
|
+
return `${baseUrl}${prefix}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const component = (window.blocklet?.componentMountPoints || []).find((x) => x?.did === PAYMENT_KIT_DID);
|
|
42
|
+
if (component) {
|
|
43
|
+
return `${baseUrl}${component.mountPoint}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return `${baseUrl}${prefix}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getLocale(languages?: string[]): string {
|
|
50
|
+
if (typeof window === 'undefined') return 'en';
|
|
51
|
+
try {
|
|
52
|
+
// Try to get locale from localStorage (common pattern in ArcBlock apps)
|
|
53
|
+
const stored = localStorage.getItem('locale');
|
|
54
|
+
if (stored) return stored;
|
|
55
|
+
} catch {
|
|
56
|
+
// localStorage not available
|
|
57
|
+
}
|
|
58
|
+
if (languages && languages.length > 0) return languages[0];
|
|
59
|
+
return navigator.language?.split('-')[0] || 'en';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function createApi() {
|
|
63
|
+
const api = createAxios();
|
|
64
|
+
|
|
65
|
+
api.interceptors.request.use(
|
|
66
|
+
(config: InternalAxiosRequestConfig) => {
|
|
67
|
+
const prefix = getPrefix();
|
|
68
|
+
config.baseURL = prefix || '';
|
|
69
|
+
|
|
70
|
+
const locale = getLocale(window.blocklet?.languages);
|
|
71
|
+
const query = new URLSearchParams(config.url?.split('?').pop());
|
|
72
|
+
|
|
73
|
+
config.params = { ...(config.params || {}), locale };
|
|
74
|
+
|
|
75
|
+
const authToken = window.__PAYMENT_KIT_AUTH_TOKEN;
|
|
76
|
+
if (authToken && typeof config.params.authToken === 'undefined' && !query.has('authToken')) {
|
|
77
|
+
config.params.authToken = authToken;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof config.params.livemode === 'undefined' && query.has('livemode') === false) {
|
|
81
|
+
const livemode = localStorage.getItem('livemode');
|
|
82
|
+
config.params.livemode = isNull(livemode) ? true : JSON.parse(livemode);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return config;
|
|
86
|
+
},
|
|
87
|
+
(err: unknown) => Promise.reject(err)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return api;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const api = createApi();
|
|
94
|
+
|
|
95
|
+
export default api;
|
|
96
|
+
|
|
97
|
+
// API endpoint constants
|
|
98
|
+
export const API = {
|
|
99
|
+
START_PAYMENT_LINK: (id: string) => `/api/checkout-sessions/start/${id}`,
|
|
100
|
+
RETRIEVE_SESSION: (id: string) => `/api/checkout-sessions/retrieve/${id}`,
|
|
101
|
+
SWITCH_CURRENCY: (id: string) => `/api/checkout-sessions/${id}/switch-currency`,
|
|
102
|
+
SUBMIT: (id: string) => `/api/checkout-sessions/${id}/submit`,
|
|
103
|
+
DONATE_SUBMIT: (id: string) => `/api/checkout-sessions/${id}/donate-submit`,
|
|
104
|
+
FAST_CHECKOUT_CONFIRM: (id: string) => `/api/checkout-sessions/${id}/fast-checkout-confirm`,
|
|
105
|
+
EXCHANGE_RATE: (id: string) => `/api/checkout-sessions/${id}/exchange-rate`,
|
|
106
|
+
ADJUST_QUANTITY: (id: string) => `/api/checkout-sessions/${id}/adjust-quantity`,
|
|
107
|
+
UPSELL: (id: string) => `/api/checkout-sessions/${id}/upsell`,
|
|
108
|
+
DOWNSELL: (id: string) => `/api/checkout-sessions/${id}/downsell`,
|
|
109
|
+
CROSS_SELL: (id: string) => `/api/checkout-sessions/${id}/cross-sell`,
|
|
110
|
+
CHANGE_AMOUNT: (id: string) => `/api/checkout-sessions/${id}/amount`,
|
|
111
|
+
SLIPPAGE: (id: string) => `/api/checkout-sessions/${id}/slippage`,
|
|
112
|
+
ABORT_STRIPE: (id: string) => `/api/checkout-sessions/${id}/abort-stripe`,
|
|
113
|
+
RECALCULATE_PROMOTION_SESSION: (id: string) => `/api/checkout-sessions/${id}/recalculate-promotion`,
|
|
114
|
+
APPLY_PROMOTION: (id: string) => `/api/checkout-sessions/${id}/apply-promotion`,
|
|
115
|
+
REMOVE_PROMOTION: (id: string) => `/api/checkout-sessions/${id}/remove-promotion`,
|
|
116
|
+
CUSTOMER_ME: '/api/customers/me',
|
|
117
|
+
VENDOR_ORDER_STATUS: (id: string) => `/api/vendors/order/${id}/status`,
|
|
118
|
+
} as const;
|