@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,25 @@
|
|
|
1
|
+
import api, { API } from "../../shared/api.js";
|
|
2
|
+
export const BASE_INTERVAL = 3e4;
|
|
3
|
+
export const MAX_INTERVAL = 12e4;
|
|
4
|
+
export function checkHasDynamicPricing(items) {
|
|
5
|
+
return items.some((item) => (item.upsell_price || item.price)?.pricing_type === "dynamic");
|
|
6
|
+
}
|
|
7
|
+
export async function fetchExchangeRate(sessionId, currencyId) {
|
|
8
|
+
const { data } = await api.get(API.EXCHANGE_RATE(sessionId), {
|
|
9
|
+
params: currencyId ? { currency_id: currencyId } : void 0
|
|
10
|
+
});
|
|
11
|
+
const rateValue = data.rate || data.exchange_rate;
|
|
12
|
+
if (rateValue) {
|
|
13
|
+
const providerDisplay = data.provider_display || data.provider_name || data.provider_id || null;
|
|
14
|
+
return {
|
|
15
|
+
rate: rateValue,
|
|
16
|
+
provider: data.provider_name || data.rate_provider_name || null,
|
|
17
|
+
providerDisplay,
|
|
18
|
+
fetchedAt: data.fetched_at || data.timestamp_ms || null
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return { rate: null, provider: null, providerDisplay: null, fetchedAt: null };
|
|
22
|
+
}
|
|
23
|
+
export function getBackoffInterval(failCount) {
|
|
24
|
+
return Math.min(BASE_INTERVAL * 2 ** failCount, MAX_INTERVAL);
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { parseProduct, parseSubscription, parsePageInfo } from './session';
|
|
2
|
+
export { flattenCurrencies, hasDidWallet, getInitialCurrencyId, findMethodAndCurrency, buildPaymentTypes, } from './paymentMethod';
|
|
3
|
+
export { BASE_INTERVAL, MAX_INTERVAL, checkHasDynamicPricing, fetchExchangeRate, getBackoffInterval, } from './exchangeRate';
|
|
4
|
+
export { calculateAmounts, calculateQuoteMeta, calculateTrial, type CalculatedAmounts, type QuoteMeta, } from './pricing';
|
|
5
|
+
export { extractPromotionCodeFromSession, applyPromotionCode, removePromotionCode, recalculatePromotion, isPromotionActive, hasAppliedDiscounts, } from './promotion';
|
|
6
|
+
export { recalculatePromotionIfNeeded, adjustQuantity, performUpsell, performDownsell, changeDonationAmount, getCrossSellItem, } from './lineItems';
|
|
7
|
+
export { addCrossSellItem, removeCrossSellItem, fetchCrossSellItem } from './crossSell';
|
|
8
|
+
export { parseBillingInterval, type BillingIntervalType, type BillingIntervalOption, type BillingIntervalInfo, } from './billingInterval';
|
|
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';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export { parseProduct, parseSubscription, parsePageInfo } from "./session.js";
|
|
2
|
+
export {
|
|
3
|
+
flattenCurrencies,
|
|
4
|
+
hasDidWallet,
|
|
5
|
+
getInitialCurrencyId,
|
|
6
|
+
findMethodAndCurrency,
|
|
7
|
+
buildPaymentTypes
|
|
8
|
+
} from "./paymentMethod.js";
|
|
9
|
+
export {
|
|
10
|
+
BASE_INTERVAL,
|
|
11
|
+
MAX_INTERVAL,
|
|
12
|
+
checkHasDynamicPricing,
|
|
13
|
+
fetchExchangeRate,
|
|
14
|
+
getBackoffInterval
|
|
15
|
+
} from "./exchangeRate.js";
|
|
16
|
+
export {
|
|
17
|
+
calculateAmounts,
|
|
18
|
+
calculateQuoteMeta,
|
|
19
|
+
calculateTrial
|
|
20
|
+
} from "./pricing.js";
|
|
21
|
+
export {
|
|
22
|
+
extractPromotionCodeFromSession,
|
|
23
|
+
applyPromotionCode,
|
|
24
|
+
removePromotionCode,
|
|
25
|
+
recalculatePromotion,
|
|
26
|
+
isPromotionActive,
|
|
27
|
+
hasAppliedDiscounts
|
|
28
|
+
} from "./promotion.js";
|
|
29
|
+
export {
|
|
30
|
+
recalculatePromotionIfNeeded,
|
|
31
|
+
adjustQuantity,
|
|
32
|
+
performUpsell,
|
|
33
|
+
performDownsell,
|
|
34
|
+
changeDonationAmount,
|
|
35
|
+
getCrossSellItem
|
|
36
|
+
} from "./lineItems.js";
|
|
37
|
+
export { addCrossSellItem, removeCrossSellItem, fetchCrossSellItem } from "./crossSell.js";
|
|
38
|
+
export {
|
|
39
|
+
parseBillingInterval
|
|
40
|
+
} from "./billingInterval.js";
|
|
41
|
+
export { buildFields, createInitialValues, setNestedValue } from "./customerForm.js";
|
|
42
|
+
export {
|
|
43
|
+
QUOTE_ERROR_CODES,
|
|
44
|
+
RELAY_SOCKET_PREFIX,
|
|
45
|
+
getAppId,
|
|
46
|
+
getRelayChannel,
|
|
47
|
+
getRelayProtocol,
|
|
48
|
+
getSocketHost,
|
|
49
|
+
buildSubmitPayload,
|
|
50
|
+
isQuoteError,
|
|
51
|
+
abortStripePayment,
|
|
52
|
+
submitCheckout,
|
|
53
|
+
confirmFastCheckout,
|
|
54
|
+
updateSlippage
|
|
55
|
+
} from "./submit.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TCheckoutSessionExpanded, TLineItemExpanded, TPrice } from '@blocklet/payment-types';
|
|
2
|
+
export declare function recalculatePromotionIfNeeded(session: TCheckoutSessionExpanded | undefined | null, sessionId: string, currencyId: string | null | undefined): Promise<void>;
|
|
3
|
+
export declare function adjustQuantity(sessionId: string, itemId: string, qty: number, currencyId: string | null | undefined, session: TCheckoutSessionExpanded | undefined | null, refresh: (force?: boolean) => Promise<void>): Promise<void>;
|
|
4
|
+
export declare function performUpsell(sessionId: string, fromId: string, toId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
|
|
5
|
+
export declare function performDownsell(sessionId: string, priceId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
|
|
6
|
+
export declare function changeDonationAmount(sessionId: string, priceId: string, amount: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
|
|
7
|
+
export declare function getCrossSellItem(items: TLineItemExpanded[]): TPrice | null;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import api, { API } from "../../shared/api.js";
|
|
2
|
+
import { recalculatePromotion, hasAppliedDiscounts } from "./promotion.js";
|
|
3
|
+
export async function recalculatePromotionIfNeeded(session, sessionId, currencyId) {
|
|
4
|
+
if (!hasAppliedDiscounts(session)) return;
|
|
5
|
+
try {
|
|
6
|
+
await recalculatePromotion(sessionId, currencyId);
|
|
7
|
+
} catch {
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export async function adjustQuantity(sessionId, itemId, qty, currencyId, session, refresh) {
|
|
11
|
+
await api.put(API.ADJUST_QUANTITY(sessionId), {
|
|
12
|
+
itemId,
|
|
13
|
+
quantity: qty,
|
|
14
|
+
currency_id: currencyId
|
|
15
|
+
});
|
|
16
|
+
await recalculatePromotionIfNeeded(session, sessionId, currencyId);
|
|
17
|
+
await refresh(true);
|
|
18
|
+
}
|
|
19
|
+
export async function performUpsell(sessionId, fromId, toId, session, currencyId, refresh) {
|
|
20
|
+
if ((session?.line_items?.length || 0) > 1) {
|
|
21
|
+
try {
|
|
22
|
+
await api.delete(API.CROSS_SELL(sessionId));
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
await api.put(API.UPSELL(sessionId), { from: fromId, to: toId });
|
|
27
|
+
await recalculatePromotionIfNeeded(session, sessionId, currencyId);
|
|
28
|
+
await refresh(true);
|
|
29
|
+
}
|
|
30
|
+
export async function performDownsell(sessionId, priceId, session, currencyId, refresh) {
|
|
31
|
+
if ((session?.line_items?.length || 0) > 1) {
|
|
32
|
+
try {
|
|
33
|
+
await api.delete(API.CROSS_SELL(sessionId));
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await api.put(API.DOWNSELL(sessionId), { from: priceId });
|
|
38
|
+
await recalculatePromotionIfNeeded(session, sessionId, currencyId);
|
|
39
|
+
await refresh(true);
|
|
40
|
+
}
|
|
41
|
+
export async function changeDonationAmount(sessionId, priceId, amount, session, currencyId, refresh) {
|
|
42
|
+
const { data } = await api.put(API.CHANGE_AMOUNT(sessionId), {
|
|
43
|
+
priceId,
|
|
44
|
+
amount
|
|
45
|
+
});
|
|
46
|
+
if (data?.discounts?.length) {
|
|
47
|
+
await recalculatePromotionIfNeeded(session, sessionId, currencyId);
|
|
48
|
+
}
|
|
49
|
+
await refresh(true);
|
|
50
|
+
}
|
|
51
|
+
export function getCrossSellItem(items) {
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
const crossSell = item.price?.cross_sell;
|
|
54
|
+
if (crossSell?.cross_sells_to) {
|
|
55
|
+
return crossSell.cross_sells_to;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TPaymentMethodExpanded, TPaymentCurrency, TCheckoutSessionExpanded } from '@blocklet/payment-types';
|
|
2
|
+
import type { CheckoutSessionUser } from '../../types/checkout-augmented';
|
|
3
|
+
export type CurrencyWithMethod = TPaymentCurrency & {
|
|
4
|
+
method?: {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
type: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare function flattenCurrencies(methods: TPaymentMethodExpanded[]): CurrencyWithMethod[];
|
|
11
|
+
export declare function hasDidWallet(user: CheckoutSessionUser | undefined): boolean;
|
|
12
|
+
export declare function getCurrencyStorageKey(did?: string): string;
|
|
13
|
+
export declare function getInitialCurrencyId(session: TCheckoutSessionExpanded | undefined | null, methods: TPaymentMethodExpanded[]): string | null;
|
|
14
|
+
export declare function findMethodAndCurrency(methods: TPaymentMethodExpanded[], currencyId: string | null): {
|
|
15
|
+
method: TPaymentMethodExpanded | null;
|
|
16
|
+
currency: TPaymentCurrency | null;
|
|
17
|
+
};
|
|
18
|
+
export declare function buildPaymentTypes(methods: TPaymentMethodExpanded[], currentMethod: TPaymentMethodExpanded | null): {
|
|
19
|
+
type: "stripe" | "crypto";
|
|
20
|
+
label: string;
|
|
21
|
+
currencies: CurrencyWithMethod[];
|
|
22
|
+
active: boolean;
|
|
23
|
+
}[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function flattenCurrencies(methods) {
|
|
2
|
+
const out = [];
|
|
3
|
+
methods.forEach((method) => {
|
|
4
|
+
const currencies = method.payment_currencies || [];
|
|
5
|
+
currencies.forEach((currency) => {
|
|
6
|
+
const enriched = {
|
|
7
|
+
...currency,
|
|
8
|
+
method: { id: method.id, name: method.name, type: method.type }
|
|
9
|
+
};
|
|
10
|
+
out.push(enriched);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
export function hasDidWallet(user) {
|
|
16
|
+
const connected = user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
|
|
17
|
+
return connected.some((x) => x.provider === "wallet");
|
|
18
|
+
}
|
|
19
|
+
const CURRENCY_PREFERENCE_KEY = "payment-currency-preference";
|
|
20
|
+
export function getCurrencyStorageKey(did) {
|
|
21
|
+
return did ? `${CURRENCY_PREFERENCE_KEY}:${did}` : CURRENCY_PREFERENCE_KEY;
|
|
22
|
+
}
|
|
23
|
+
export function getInitialCurrencyId(session, methods) {
|
|
24
|
+
if (typeof window !== "undefined") {
|
|
25
|
+
try {
|
|
26
|
+
const params = new URLSearchParams(window.location.search);
|
|
27
|
+
const urlCurrency = params.get("currencyId") || params.get("currency_id");
|
|
28
|
+
if (urlCurrency) return urlCurrency;
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
const user = session?.user;
|
|
32
|
+
if (user && !hasDidWallet(user)) {
|
|
33
|
+
const stripeMethod = methods.find((m) => m.type === "stripe");
|
|
34
|
+
const stripeCurrency = stripeMethod?.payment_currencies?.[0];
|
|
35
|
+
if (stripeCurrency) return stripeCurrency.id;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const did = session?.user?.did;
|
|
39
|
+
const stored = localStorage.getItem(getCurrencyStorageKey(did));
|
|
40
|
+
if (stored) return stored;
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return session?.currency_id || null;
|
|
45
|
+
}
|
|
46
|
+
export function findMethodAndCurrency(methods, currencyId) {
|
|
47
|
+
if (!currencyId) {
|
|
48
|
+
const first2 = methods[0];
|
|
49
|
+
const firstCurrency = first2?.payment_currencies?.[0] || null;
|
|
50
|
+
return { method: first2 || null, currency: firstCurrency };
|
|
51
|
+
}
|
|
52
|
+
for (const method of methods) {
|
|
53
|
+
for (const currency of method.payment_currencies || []) {
|
|
54
|
+
if (currency.id === currencyId) {
|
|
55
|
+
return { method, currency };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const first = methods[0];
|
|
60
|
+
return { method: first || null, currency: first?.payment_currencies?.[0] || null };
|
|
61
|
+
}
|
|
62
|
+
export function buildPaymentTypes(methods, currentMethod) {
|
|
63
|
+
const typeMap = /* @__PURE__ */ new Map();
|
|
64
|
+
methods.forEach((method) => {
|
|
65
|
+
const type = method.type === "stripe" ? "stripe" : "crypto";
|
|
66
|
+
if (!typeMap.has(type)) {
|
|
67
|
+
typeMap.set(type, { currencies: [], active: false });
|
|
68
|
+
}
|
|
69
|
+
const entry = typeMap.get(type);
|
|
70
|
+
const enriched = (method.payment_currencies || []).map((cur) => ({
|
|
71
|
+
...cur,
|
|
72
|
+
method: { id: method.id, name: method.name, type: method.type }
|
|
73
|
+
}));
|
|
74
|
+
entry.currencies.push(...enriched);
|
|
75
|
+
if (method === currentMethod) {
|
|
76
|
+
entry.active = true;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return Array.from(typeMap.entries()).map(([type, data]) => ({
|
|
80
|
+
type,
|
|
81
|
+
label: type === "stripe" ? "Card" : "Crypto",
|
|
82
|
+
currencies: data.currencies,
|
|
83
|
+
active: data.active
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TCheckoutSessionExpanded, TPaymentCurrency, TLineItemExpanded } from '@blocklet/payment-types';
|
|
2
|
+
import type { SessionData } from '../hooks/useCheckoutSession';
|
|
3
|
+
export interface CalculatedAmounts {
|
|
4
|
+
subtotal: string;
|
|
5
|
+
paymentAmount: string;
|
|
6
|
+
total: string;
|
|
7
|
+
discount: string | null;
|
|
8
|
+
tax: {
|
|
9
|
+
amount: string;
|
|
10
|
+
rate: number;
|
|
11
|
+
inclusive: boolean;
|
|
12
|
+
} | null;
|
|
13
|
+
staking: string | null;
|
|
14
|
+
usdEquivalent: string | null;
|
|
15
|
+
subtotalUsdEquivalent?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export declare function calculateAmounts(items: TLineItemExpanded[], currency: TPaymentCurrency | null, session: TCheckoutSessionExpanded | undefined | null, exchangeRate: string | null, hasDynamicPricing: boolean, paymentMethodType?: string | null): CalculatedAmounts;
|
|
18
|
+
export interface QuoteMeta {
|
|
19
|
+
locked: boolean;
|
|
20
|
+
lockedAt: number | null;
|
|
21
|
+
expiresAt: number | null;
|
|
22
|
+
expired: boolean;
|
|
23
|
+
baseCurrency: string | null;
|
|
24
|
+
slippagePercent: number | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function calculateQuoteMeta(items: TLineItemExpanded[], hasDynamicPricing: boolean, sessionData: SessionData | null): QuoteMeta;
|
|
27
|
+
export declare function calculateTrial(session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, items: TLineItemExpanded[], currency: TPaymentCurrency | null, hasDynamicPricing: boolean, exchangeRate: string | null): {
|
|
28
|
+
active: boolean;
|
|
29
|
+
days: number;
|
|
30
|
+
afterTrialPrice: string | null;
|
|
31
|
+
afterTrialInterval: string | null;
|
|
32
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { BN, fromUnitToToken, fromTokenToUnit } from "@ocap/util";
|
|
2
|
+
import {
|
|
3
|
+
formatDynamicPrice,
|
|
4
|
+
formatUsdAmount,
|
|
5
|
+
getUsdAmountFromTokenUnits,
|
|
6
|
+
getCheckoutAmount,
|
|
7
|
+
getPriceUnitAmountByCurrency
|
|
8
|
+
} from "../../shared/format.js";
|
|
9
|
+
function getStakingSetup(items, currency, billingThreshold = 0) {
|
|
10
|
+
const staking = { licensed: new BN(0), metered: new BN(0) };
|
|
11
|
+
const recurringItems = items.map((x) => x.upsell_price || x.price).filter((x) => x?.type === "recurring" && x?.recurring);
|
|
12
|
+
if (recurringItems.length > 0) {
|
|
13
|
+
if (+billingThreshold) {
|
|
14
|
+
return fromTokenToUnit(billingThreshold, currency.decimal).toString();
|
|
15
|
+
}
|
|
16
|
+
items.forEach((x) => {
|
|
17
|
+
const price = x.upsell_price || x.price;
|
|
18
|
+
const unit = getPriceUnitAmountByCurrency(price, currency);
|
|
19
|
+
const amount = new BN(unit).mul(new BN(x.quantity));
|
|
20
|
+
if (price?.type === "recurring" && price?.recurring) {
|
|
21
|
+
if (price.recurring.usage_type === "licensed") {
|
|
22
|
+
staking.licensed = staking.licensed.add(amount);
|
|
23
|
+
}
|
|
24
|
+
if (price.recurring.usage_type === "metered") {
|
|
25
|
+
staking.metered = staking.metered.add(amount);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return staking.licensed.add(staking.metered).toString();
|
|
30
|
+
}
|
|
31
|
+
return "0";
|
|
32
|
+
}
|
|
33
|
+
function calculateCouponDiscount(items, currency, session, hasDynamicPricing, exchangeRate, trialing, recurringOnly) {
|
|
34
|
+
const discounts = session?.discounts || [];
|
|
35
|
+
if (discounts.length === 0) return new BN(0);
|
|
36
|
+
const coupon = discounts[0].coupon_details || {};
|
|
37
|
+
if (recurringOnly && coupon.duration === "once") return new BN(0);
|
|
38
|
+
const discountableItems = items.filter((item) => {
|
|
39
|
+
if (item.discountable === false) return false;
|
|
40
|
+
const price = item.upsell_price || item.price;
|
|
41
|
+
if (price?.recurring?.usage_type === "metered") return false;
|
|
42
|
+
return true;
|
|
43
|
+
});
|
|
44
|
+
const discountableResult = getCheckoutAmount(discountableItems, currency, trialing, true, {
|
|
45
|
+
exchangeRate: hasDynamicPricing ? exchangeRate : null
|
|
46
|
+
});
|
|
47
|
+
const discountableSubtotalBN = new BN(discountableResult.total);
|
|
48
|
+
if (discountableSubtotalBN.lte(new BN(0))) return new BN(0);
|
|
49
|
+
if (coupon.percent_off && coupon.percent_off > 0) {
|
|
50
|
+
return discountableSubtotalBN.mul(new BN(coupon.percent_off)).div(new BN(100));
|
|
51
|
+
}
|
|
52
|
+
if (coupon.amount_off && coupon.amount_off !== "0") {
|
|
53
|
+
const amountOff = coupon.currency_options?.[currency.id]?.amount_off || coupon.amount_off;
|
|
54
|
+
if (amountOff && amountOff !== "0") {
|
|
55
|
+
const amountOffBN = new BN(amountOff);
|
|
56
|
+
return amountOffBN.lt(discountableSubtotalBN) ? amountOffBN : discountableSubtotalBN;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return new BN(0);
|
|
60
|
+
}
|
|
61
|
+
export function calculateAmounts(items, currency, session, exchangeRate, hasDynamicPricing, paymentMethodType) {
|
|
62
|
+
if (!currency || items.length === 0) {
|
|
63
|
+
return {
|
|
64
|
+
subtotal: "0",
|
|
65
|
+
paymentAmount: "0",
|
|
66
|
+
total: "0",
|
|
67
|
+
discount: null,
|
|
68
|
+
tax: null,
|
|
69
|
+
staking: null,
|
|
70
|
+
usdEquivalent: null
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const subData = session?.subscription_data;
|
|
74
|
+
const trialDays = Number(subData?.trial_period_days || 0);
|
|
75
|
+
const trialing = trialDays > 0;
|
|
76
|
+
const result = getCheckoutAmount(items, currency, trialing, true, {
|
|
77
|
+
exchangeRate: hasDynamicPricing ? exchangeRate : null
|
|
78
|
+
});
|
|
79
|
+
const subtotalBN = new BN(result.total);
|
|
80
|
+
const subtotalFormatted = formatDynamicPrice(fromUnitToToken(subtotalBN, currency.decimal), hasDynamicPricing);
|
|
81
|
+
const discountBN = calculateCouponDiscount(items, currency, session, hasDynamicPricing, exchangeRate, trialing, false);
|
|
82
|
+
const discount = discountBN.gt(new BN(0)) ? formatDynamicPrice(fromUnitToToken(discountBN.toString(), currency.decimal), hasDynamicPricing) : null;
|
|
83
|
+
const taxAmount = session?.total_details?.amount_tax;
|
|
84
|
+
const taxBN = new BN(taxAmount || "0");
|
|
85
|
+
const totalDetails = session?.total_details;
|
|
86
|
+
const tax = taxAmount && taxAmount !== "0" ? {
|
|
87
|
+
amount: fromUnitToToken(taxAmount, currency.decimal),
|
|
88
|
+
rate: totalDetails?.tax_rate || 0,
|
|
89
|
+
inclusive: totalDetails?.tax_inclusive || false
|
|
90
|
+
} : null;
|
|
91
|
+
const noStake = subData?.no_stake || false;
|
|
92
|
+
const billingThreshold = Math.max(
|
|
93
|
+
Number(subData?.billing_threshold_amount || 0),
|
|
94
|
+
Number(subData?.min_stake_amount || 0)
|
|
95
|
+
);
|
|
96
|
+
const shouldShowStaking = paymentMethodType === "arcblock" && currency.type !== "credit" && !noStake;
|
|
97
|
+
const stakingUnitStr = shouldShowStaking ? getStakingSetup(items, currency, billingThreshold) : "0";
|
|
98
|
+
const stakingBN = new BN(stakingUnitStr);
|
|
99
|
+
const subtotalWithStakingBN = subtotalBN.add(stakingBN);
|
|
100
|
+
let totalBN = subtotalWithStakingBN.sub(discountBN);
|
|
101
|
+
if (taxBN.gt(new BN(0)) && !tax?.inclusive) {
|
|
102
|
+
totalBN = totalBN.add(taxBN);
|
|
103
|
+
}
|
|
104
|
+
if (totalBN.isNeg()) {
|
|
105
|
+
totalBN = new BN(0);
|
|
106
|
+
}
|
|
107
|
+
const totalFormatted = formatDynamicPrice(fromUnitToToken(totalBN, currency.decimal), hasDynamicPricing);
|
|
108
|
+
const displaySubtotalBN = subtotalWithStakingBN;
|
|
109
|
+
const displaySubtotalFormatted = formatDynamicPrice(
|
|
110
|
+
fromUnitToToken(displaySubtotalBN, currency.decimal),
|
|
111
|
+
hasDynamicPricing
|
|
112
|
+
);
|
|
113
|
+
let usdEquivalent = null;
|
|
114
|
+
let subtotalUsdEquivalent = null;
|
|
115
|
+
if (hasDynamicPricing && exchangeRate && currency) {
|
|
116
|
+
const totalUsd = getUsdAmountFromTokenUnits(totalBN.toString(), currency.decimal, exchangeRate);
|
|
117
|
+
if (totalUsd) {
|
|
118
|
+
usdEquivalent = formatUsdAmount(totalUsd) || null;
|
|
119
|
+
}
|
|
120
|
+
const subtotalUsd = getUsdAmountFromTokenUnits(displaySubtotalBN.toString(), currency.decimal, exchangeRate);
|
|
121
|
+
if (subtotalUsd) {
|
|
122
|
+
subtotalUsdEquivalent = formatUsdAmount(subtotalUsd) || null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const stakingFormatted = stakingBN.gt(new BN(0)) ? `${formatDynamicPrice(fromUnitToToken(stakingBN.toString(), currency.decimal), hasDynamicPricing)} ${currency.symbol}` : null;
|
|
126
|
+
return {
|
|
127
|
+
subtotal: `${displaySubtotalFormatted} ${currency.symbol}`,
|
|
128
|
+
paymentAmount: `${subtotalFormatted} ${currency.symbol}`,
|
|
129
|
+
total: `${totalFormatted} ${currency.symbol}`,
|
|
130
|
+
discount: discount ? `${discount} ${currency.symbol}` : null,
|
|
131
|
+
tax,
|
|
132
|
+
staking: stakingFormatted,
|
|
133
|
+
usdEquivalent: usdEquivalent ? `$${usdEquivalent}` : null,
|
|
134
|
+
subtotalUsdEquivalent: subtotalUsdEquivalent ? `$${subtotalUsdEquivalent}` : null
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export function calculateQuoteMeta(items, hasDynamicPricing, sessionData) {
|
|
138
|
+
if (!items.length || !hasDynamicPricing) {
|
|
139
|
+
return {
|
|
140
|
+
locked: false,
|
|
141
|
+
lockedAt: null,
|
|
142
|
+
expiresAt: null,
|
|
143
|
+
expired: false,
|
|
144
|
+
baseCurrency: null,
|
|
145
|
+
slippagePercent: null
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const session = sessionData?.checkoutSession;
|
|
149
|
+
let baseCurrency = null;
|
|
150
|
+
let expiresAt = null;
|
|
151
|
+
let slippagePercent = null;
|
|
152
|
+
items.forEach((item) => {
|
|
153
|
+
const price = item.upsell_price || item.price;
|
|
154
|
+
if (!baseCurrency && price?.base_currency) baseCurrency = price.base_currency;
|
|
155
|
+
const expires = item.expires_at;
|
|
156
|
+
if (expires) {
|
|
157
|
+
expiresAt = expiresAt === null ? expires : Math.min(expiresAt, expires);
|
|
158
|
+
}
|
|
159
|
+
const itemSlippage = item.slippage_percent;
|
|
160
|
+
if (slippagePercent === null && Number.isFinite(Number(itemSlippage))) {
|
|
161
|
+
slippagePercent = Number(itemSlippage);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
const piCreated = sessionData?.paymentIntent?.created_at ? new Date(sessionData.paymentIntent.created_at).getTime() : null;
|
|
165
|
+
const sessionLock = session?.quote_locked_at;
|
|
166
|
+
const lockedAt = piCreated || sessionLock || null;
|
|
167
|
+
const locked = !!lockedAt;
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
const expired = expiresAt ? now > expiresAt : false;
|
|
170
|
+
return { locked, lockedAt, expiresAt, expired, baseCurrency, slippagePercent };
|
|
171
|
+
}
|
|
172
|
+
export function calculateTrial(session, currencyId, items, currency, hasDynamicPricing, exchangeRate) {
|
|
173
|
+
const subData = session?.subscription_data;
|
|
174
|
+
let trialDays = Number(subData?.trial_period_days || 0);
|
|
175
|
+
if (trialDays > 0 && currencyId) {
|
|
176
|
+
const trialCurrencyIds = (subData?.trial_currency || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
177
|
+
if (trialCurrencyIds.length > 0 && !trialCurrencyIds.includes(currencyId)) {
|
|
178
|
+
trialDays = 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const trialActive = trialDays > 0;
|
|
182
|
+
let afterTrialPrice = null;
|
|
183
|
+
let afterTrialInterval = null;
|
|
184
|
+
if (trialActive && currency && items.length > 0) {
|
|
185
|
+
const noTrialResult = getCheckoutAmount(items, currency, false, true, {
|
|
186
|
+
exchangeRate: hasDynamicPricing ? exchangeRate : null
|
|
187
|
+
});
|
|
188
|
+
let afterTrialBN = new BN(noTrialResult.total);
|
|
189
|
+
const recurringDiscount = calculateCouponDiscount(
|
|
190
|
+
items,
|
|
191
|
+
currency,
|
|
192
|
+
session,
|
|
193
|
+
hasDynamicPricing,
|
|
194
|
+
exchangeRate,
|
|
195
|
+
false,
|
|
196
|
+
true
|
|
197
|
+
);
|
|
198
|
+
if (recurringDiscount.gt(new BN(0))) {
|
|
199
|
+
afterTrialBN = afterTrialBN.sub(recurringDiscount);
|
|
200
|
+
if (afterTrialBN.isNeg()) afterTrialBN = new BN(0);
|
|
201
|
+
}
|
|
202
|
+
afterTrialPrice = `${formatDynamicPrice(
|
|
203
|
+
fromUnitToToken(afterTrialBN, currency.decimal),
|
|
204
|
+
hasDynamicPricing
|
|
205
|
+
)} ${currency.symbol}`;
|
|
206
|
+
const recurringItem = items.find((item) => {
|
|
207
|
+
const price = item.upsell_price || item.price;
|
|
208
|
+
return price?.type === "recurring" && price?.recurring;
|
|
209
|
+
});
|
|
210
|
+
if (recurringItem) {
|
|
211
|
+
const price = recurringItem.upsell_price || recurringItem.price;
|
|
212
|
+
afterTrialInterval = price?.recurring?.interval || null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
active: trialActive,
|
|
217
|
+
days: trialDays,
|
|
218
|
+
afterTrialPrice,
|
|
219
|
+
afterTrialInterval
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TCheckoutSessionExpanded } from '@blocklet/payment-types';
|
|
2
|
+
export declare function extractPromotionCodeFromSession(session: TCheckoutSessionExpanded | undefined | null): string | null;
|
|
3
|
+
export declare function applyPromotionCode(sessionId: string, code: string, currencyId: string | null | undefined): Promise<{
|
|
4
|
+
success: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function removePromotionCode(sessionId: string): Promise<void>;
|
|
8
|
+
export declare function recalculatePromotion(sessionId: string, currencyId: string | null | undefined): Promise<void>;
|
|
9
|
+
export declare function isPromotionActive(session: TCheckoutSessionExpanded | undefined | null): boolean;
|
|
10
|
+
export declare function hasAppliedDiscounts(session: TCheckoutSessionExpanded | undefined | null): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import api, { API } from "../../shared/api.js";
|
|
2
|
+
import { getErrorMessage } from "../../types/checkout-augmented.js";
|
|
3
|
+
export function extractPromotionCodeFromSession(session) {
|
|
4
|
+
const discounts = session?.discounts || [];
|
|
5
|
+
if (discounts.length === 0) return null;
|
|
6
|
+
const firstDiscount = discounts[0];
|
|
7
|
+
const details = firstDiscount.promotion_code_details;
|
|
8
|
+
const verification = firstDiscount.verification_data;
|
|
9
|
+
return details?.code || verification?.code || firstDiscount.promotion_code || firstDiscount.coupon || null;
|
|
10
|
+
}
|
|
11
|
+
export async function applyPromotionCode(sessionId, code, currencyId) {
|
|
12
|
+
try {
|
|
13
|
+
const { data } = await api.post(API.APPLY_PROMOTION(sessionId), {
|
|
14
|
+
promotion_code: code,
|
|
15
|
+
currency_id: currencyId
|
|
16
|
+
});
|
|
17
|
+
if (data.error) {
|
|
18
|
+
return { success: false, error: data.error };
|
|
19
|
+
}
|
|
20
|
+
return { success: true };
|
|
21
|
+
} catch (err) {
|
|
22
|
+
return { success: false, error: getErrorMessage(err) || "Invalid promotion code" };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function removePromotionCode(sessionId) {
|
|
26
|
+
await api.delete(API.REMOVE_PROMOTION(sessionId));
|
|
27
|
+
}
|
|
28
|
+
export async function recalculatePromotion(sessionId, currencyId) {
|
|
29
|
+
await api.post(API.RECALCULATE_PROMOTION_SESSION(sessionId), {
|
|
30
|
+
currency_id: currencyId
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function isPromotionActive(session) {
|
|
34
|
+
return session?.allow_promotion_codes !== false;
|
|
35
|
+
}
|
|
36
|
+
export function hasAppliedDiscounts(session) {
|
|
37
|
+
const discounts = session?.discounts || [];
|
|
38
|
+
return discounts.length > 0;
|
|
39
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TCheckoutSessionExpanded } from '@blocklet/payment-types';
|
|
2
|
+
export declare function parseProduct(session: TCheckoutSessionExpanded): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
images: any[];
|
|
6
|
+
features: {
|
|
7
|
+
name: string;
|
|
8
|
+
icon: string | undefined;
|
|
9
|
+
}[];
|
|
10
|
+
billing: {
|
|
11
|
+
mode: "payment" | "subscription";
|
|
12
|
+
interval: "month" | "year" | "week" | "day" | null;
|
|
13
|
+
intervalCount: number;
|
|
14
|
+
displayInterval: string;
|
|
15
|
+
};
|
|
16
|
+
metadata: Record<string, any>;
|
|
17
|
+
} | null;
|
|
18
|
+
export declare function parseSubscription(session: TCheckoutSessionExpanded): {
|
|
19
|
+
mode: "payment" | "subscription" | "setup";
|
|
20
|
+
showStake: boolean;
|
|
21
|
+
confirmMessage: string;
|
|
22
|
+
} | null;
|
|
23
|
+
export declare function parsePageInfo(session: TCheckoutSessionExpanded): {
|
|
24
|
+
formPurposeDescription: any;
|
|
25
|
+
showProductFeatures: boolean;
|
|
26
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export function parseProduct(session) {
|
|
2
|
+
const items = session.line_items || [];
|
|
3
|
+
if (items.length === 0) return null;
|
|
4
|
+
const firstItem = items[0];
|
|
5
|
+
const price = firstItem.upsell_price || firstItem.price;
|
|
6
|
+
const product = price?.product;
|
|
7
|
+
if (!product) return null;
|
|
8
|
+
const recurring = price?.recurring;
|
|
9
|
+
const mode = session.mode === "subscription" ? "subscription" : "payment";
|
|
10
|
+
const intervalMap = {
|
|
11
|
+
day: "daily",
|
|
12
|
+
week: "weekly",
|
|
13
|
+
month: "monthly",
|
|
14
|
+
year: "yearly"
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
name: product.name || "",
|
|
18
|
+
description: product.description || "",
|
|
19
|
+
images: product.images || [],
|
|
20
|
+
features: (product.features || []).map((f) => ({
|
|
21
|
+
name: f.name || "",
|
|
22
|
+
icon: f.icon
|
|
23
|
+
})),
|
|
24
|
+
billing: {
|
|
25
|
+
mode,
|
|
26
|
+
interval: recurring?.interval || null,
|
|
27
|
+
intervalCount: recurring?.interval_count || 1,
|
|
28
|
+
displayInterval: recurring ? intervalMap[recurring.interval] || recurring.interval : ""
|
|
29
|
+
},
|
|
30
|
+
metadata: product.metadata || {}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function parseSubscription(session) {
|
|
34
|
+
const subData = session.subscription_data;
|
|
35
|
+
if (!subData && session.mode === "payment") return null;
|
|
36
|
+
return {
|
|
37
|
+
mode: session.mode || "payment",
|
|
38
|
+
showStake: !subData?.no_stake,
|
|
39
|
+
confirmMessage: ""
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function parsePageInfo(session) {
|
|
43
|
+
const metadata = session.metadata;
|
|
44
|
+
const pageInfo = metadata?.page_info;
|
|
45
|
+
const showRaw = metadata?.show_product_features ?? pageInfo?.show_product_features;
|
|
46
|
+
return {
|
|
47
|
+
formPurposeDescription: pageInfo?.form_purpose_description,
|
|
48
|
+
showProductFeatures: `${showRaw}` === "true"
|
|
49
|
+
};
|
|
50
|
+
}
|