@blocklet/payment-react 1.20.11 → 1.20.13
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/components/promotion-code.d.ts +19 -0
- package/es/components/promotion-code.js +153 -0
- package/es/contexts/payment.d.ts +8 -0
- package/es/contexts/payment.js +10 -1
- package/es/index.d.ts +2 -1
- package/es/index.js +3 -1
- package/es/libs/util.d.ts +5 -1
- package/es/libs/util.js +23 -0
- package/es/locales/en.js +25 -0
- package/es/locales/zh.js +29 -0
- package/es/payment/form/index.js +7 -1
- package/es/payment/index.js +19 -0
- package/es/payment/product-item.js +32 -3
- package/es/payment/summary.d.ts +5 -2
- package/es/payment/summary.js +193 -16
- package/lib/components/promotion-code.d.ts +19 -0
- package/lib/components/promotion-code.js +155 -0
- package/lib/contexts/payment.d.ts +8 -0
- package/lib/contexts/payment.js +13 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +8 -0
- package/lib/libs/util.d.ts +5 -1
- package/lib/libs/util.js +29 -0
- package/lib/locales/en.js +25 -0
- package/lib/locales/zh.js +29 -0
- package/lib/payment/form/index.js +8 -1
- package/lib/payment/index.js +23 -0
- package/lib/payment/product-item.js +46 -0
- package/lib/payment/summary.d.ts +5 -2
- package/lib/payment/summary.js +153 -11
- package/package.json +9 -9
- package/src/components/promotion-code.tsx +184 -0
- package/src/contexts/payment.tsx +15 -0
- package/src/index.ts +2 -0
- package/src/libs/util.ts +35 -0
- package/src/locales/en.tsx +25 -0
- package/src/locales/zh.tsx +29 -0
- package/src/payment/form/index.tsx +10 -1
- package/src/payment/index.tsx +22 -0
- package/src/payment/product-item.tsx +37 -2
- package/src/payment/summary.tsx +201 -16
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface AppliedPromoCode {
|
|
2
|
+
id: string;
|
|
3
|
+
code: string;
|
|
4
|
+
discount_amount?: string;
|
|
5
|
+
}
|
|
6
|
+
interface PromotionCodeProps {
|
|
7
|
+
checkoutSessionId: string;
|
|
8
|
+
initialAppliedCodes?: AppliedPromoCode[];
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
currencyId: string;
|
|
13
|
+
onUpdate?: (data: {
|
|
14
|
+
appliedCodes: AppliedPromoCode[];
|
|
15
|
+
discountAmount: string;
|
|
16
|
+
}) => void;
|
|
17
|
+
}
|
|
18
|
+
export default function PromotionCode({ checkoutSessionId, initialAppliedCodes, disabled, className, placeholder, onUpdate, currencyId, }: PromotionCodeProps): import("react").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
|
+
import { TextField, Button, Alert, Box, InputAdornment } from "@mui/material";
|
|
4
|
+
import { Add } from "@mui/icons-material";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import LoadingButton from "./loading-button.js";
|
|
7
|
+
import api from "../libs/api.js";
|
|
8
|
+
import { usePaymentContext } from "../contexts/payment.js";
|
|
9
|
+
export default function PromotionCode({
|
|
10
|
+
checkoutSessionId,
|
|
11
|
+
initialAppliedCodes = [],
|
|
12
|
+
disabled = false,
|
|
13
|
+
className = "",
|
|
14
|
+
placeholder = "",
|
|
15
|
+
onUpdate = void 0,
|
|
16
|
+
currencyId
|
|
17
|
+
}) {
|
|
18
|
+
const { t } = useLocaleContext();
|
|
19
|
+
const [showInput, setShowInput] = useState(false);
|
|
20
|
+
const [code, setCode] = useState("");
|
|
21
|
+
const [error, setError] = useState("");
|
|
22
|
+
const [applying, setApplying] = useState(false);
|
|
23
|
+
const [appliedCodes, setAppliedCodes] = useState(initialAppliedCodes);
|
|
24
|
+
const { session, paymentState } = usePaymentContext();
|
|
25
|
+
const handleLoginCheck = () => {
|
|
26
|
+
if (!session.user) {
|
|
27
|
+
session?.login(() => {
|
|
28
|
+
handleApply();
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
handleApply();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handleApply = async () => {
|
|
35
|
+
if (!code.trim()) return;
|
|
36
|
+
if (paymentState.paying || paymentState.stripePaying) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
setApplying(true);
|
|
40
|
+
setError("");
|
|
41
|
+
try {
|
|
42
|
+
const response = await api.post(`/api/checkout-sessions/${checkoutSessionId}/apply-promotion`, {
|
|
43
|
+
promotion_code: code.trim(),
|
|
44
|
+
currency_id: currencyId
|
|
45
|
+
});
|
|
46
|
+
const discounts = response.data.discounts || [];
|
|
47
|
+
const appliedDiscount = discounts[0];
|
|
48
|
+
if (appliedDiscount) {
|
|
49
|
+
const newCode = {
|
|
50
|
+
id: appliedDiscount.promotion_code || appliedDiscount.coupon,
|
|
51
|
+
code: code.trim(),
|
|
52
|
+
discount_amount: appliedDiscount.discount_amount
|
|
53
|
+
};
|
|
54
|
+
setAppliedCodes([newCode]);
|
|
55
|
+
setCode("");
|
|
56
|
+
setShowInput(false);
|
|
57
|
+
onUpdate?.({
|
|
58
|
+
appliedCodes: [newCode],
|
|
59
|
+
discountAmount: appliedDiscount.discount_amount
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const errorMessage = err.response?.data?.error || err.message;
|
|
64
|
+
setError(errorMessage);
|
|
65
|
+
} finally {
|
|
66
|
+
setApplying(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const handleKeyPress = (event) => {
|
|
70
|
+
if (event.key === "Enter" && !applying && code.trim()) {
|
|
71
|
+
handleLoginCheck();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const isPaymentInProgress = paymentState.paying || paymentState.stripePaying;
|
|
75
|
+
return /* @__PURE__ */ jsx(Box, { className, children: appliedCodes.length === 0 && !disabled && !isPaymentInProgress && (showInput ? /* @__PURE__ */ jsxs(
|
|
76
|
+
Box,
|
|
77
|
+
{
|
|
78
|
+
onBlur: () => {
|
|
79
|
+
if (!code.trim()) {
|
|
80
|
+
setShowInput(false);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ jsx(
|
|
85
|
+
TextField,
|
|
86
|
+
{
|
|
87
|
+
fullWidth: true,
|
|
88
|
+
value: code,
|
|
89
|
+
onChange: (e) => setCode(e.target.value),
|
|
90
|
+
onKeyPress: handleKeyPress,
|
|
91
|
+
placeholder: placeholder || t("payment.checkout.promotion.placeholder"),
|
|
92
|
+
variant: "outlined",
|
|
93
|
+
size: "small",
|
|
94
|
+
disabled: applying,
|
|
95
|
+
autoFocus: true,
|
|
96
|
+
slotProps: {
|
|
97
|
+
input: {
|
|
98
|
+
endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx(
|
|
99
|
+
LoadingButton,
|
|
100
|
+
{
|
|
101
|
+
size: "small",
|
|
102
|
+
onClick: handleLoginCheck,
|
|
103
|
+
loading: applying,
|
|
104
|
+
disabled: !code.trim(),
|
|
105
|
+
variant: "text",
|
|
106
|
+
sx: {
|
|
107
|
+
color: "primary.main",
|
|
108
|
+
fontSize: "small"
|
|
109
|
+
},
|
|
110
|
+
children: t("payment.checkout.promotion.apply")
|
|
111
|
+
}
|
|
112
|
+
) })
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
sx: {
|
|
116
|
+
"& .MuiOutlinedInput-root": {
|
|
117
|
+
pr: 1
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
error && /* @__PURE__ */ jsx(
|
|
123
|
+
Alert,
|
|
124
|
+
{
|
|
125
|
+
severity: "error",
|
|
126
|
+
sx: {
|
|
127
|
+
my: 1
|
|
128
|
+
},
|
|
129
|
+
children: error
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
) : /* @__PURE__ */ jsx(
|
|
135
|
+
Button,
|
|
136
|
+
{
|
|
137
|
+
onClick: () => setShowInput(true),
|
|
138
|
+
startIcon: /* @__PURE__ */ jsx(Add, { fontSize: "small" }),
|
|
139
|
+
variant: "text",
|
|
140
|
+
sx: {
|
|
141
|
+
fontWeight: "normal",
|
|
142
|
+
textTransform: "none",
|
|
143
|
+
justifyContent: "flex-start",
|
|
144
|
+
p: 0,
|
|
145
|
+
"&:hover": {
|
|
146
|
+
backgroundColor: "transparent",
|
|
147
|
+
textDecoration: "underline"
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
children: t("payment.checkout.promotion.add_code")
|
|
151
|
+
}
|
|
152
|
+
)) });
|
|
153
|
+
}
|
package/es/contexts/payment.d.ts
CHANGED
|
@@ -19,6 +19,14 @@ export type PaymentContextType = {
|
|
|
19
19
|
api: Axios;
|
|
20
20
|
payable: boolean;
|
|
21
21
|
setPayable: (status: boolean) => void;
|
|
22
|
+
paymentState: {
|
|
23
|
+
paying: boolean;
|
|
24
|
+
stripePaying: boolean;
|
|
25
|
+
};
|
|
26
|
+
setPaymentState: (state: Partial<{
|
|
27
|
+
paying: boolean;
|
|
28
|
+
stripePaying: boolean;
|
|
29
|
+
}>) => void;
|
|
22
30
|
};
|
|
23
31
|
export type PaymentContextProps = {
|
|
24
32
|
session: import('@arcblock/did-connect-react/lib/types').SessionContext['session'];
|
package/es/contexts/payment.js
CHANGED
|
@@ -125,6 +125,13 @@ function PaymentProvider({
|
|
|
125
125
|
}, [session?.user]);
|
|
126
126
|
const prefix = getPrefix();
|
|
127
127
|
const [payable, setPayable] = useState(true);
|
|
128
|
+
const [paymentState, setPaymentState] = useState({
|
|
129
|
+
paying: false,
|
|
130
|
+
stripePaying: false
|
|
131
|
+
});
|
|
132
|
+
const updatePaymentState = (state) => {
|
|
133
|
+
setPaymentState((prev) => ({ ...prev, ...state }));
|
|
134
|
+
};
|
|
128
135
|
if (error) {
|
|
129
136
|
return /* @__PURE__ */ jsx(Alert, { severity: "error", children: error.message });
|
|
130
137
|
}
|
|
@@ -146,7 +153,9 @@ function PaymentProvider({
|
|
|
146
153
|
setLivemode,
|
|
147
154
|
api,
|
|
148
155
|
payable,
|
|
149
|
-
setPayable
|
|
156
|
+
setPayable,
|
|
157
|
+
paymentState,
|
|
158
|
+
setPaymentState: updatePaymentState
|
|
150
159
|
},
|
|
151
160
|
children
|
|
152
161
|
}
|
package/es/index.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ import DateRangePicker from './components/date-range-picker';
|
|
|
39
39
|
import AutoTopupModal from './components/auto-topup/modal';
|
|
40
40
|
import AutoTopup from './components/auto-topup';
|
|
41
41
|
import Collapse from './components/collapse';
|
|
42
|
+
import PromotionCode from './components/promotion-code';
|
|
42
43
|
export { PaymentThemeProvider } from './theme';
|
|
43
44
|
export * from './libs/util';
|
|
44
45
|
export * from './libs/connect';
|
|
@@ -53,4 +54,4 @@ export * from './hooks/scroll';
|
|
|
53
54
|
export * from './hooks/keyboard';
|
|
54
55
|
export * from './libs/validator';
|
|
55
56
|
export { translations, createTranslator } from './locales';
|
|
56
|
-
export { createLazyComponent, api, dayjs, FormInput, FormLabel, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, CreditGrantsList, CreditTransactionsList, DateRangePicker, CreditStatusChip, AutoTopupModal, AutoTopup, Collapse, };
|
|
57
|
+
export { createLazyComponent, api, dayjs, FormInput, FormLabel, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, CreditGrantsList, CreditTransactionsList, DateRangePicker, CreditStatusChip, AutoTopupModal, AutoTopup, Collapse, PromotionCode, };
|
package/es/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import DateRangePicker from "./components/date-range-picker.js";
|
|
|
39
39
|
import AutoTopupModal from "./components/auto-topup/modal.js";
|
|
40
40
|
import AutoTopup from "./components/auto-topup/index.js";
|
|
41
41
|
import Collapse from "./components/collapse.js";
|
|
42
|
+
import PromotionCode from "./components/promotion-code.js";
|
|
42
43
|
export { PaymentThemeProvider } from "./theme/index.js";
|
|
43
44
|
export * from "./libs/util.js";
|
|
44
45
|
export * from "./libs/connect.js";
|
|
@@ -96,5 +97,6 @@ export {
|
|
|
96
97
|
CreditStatusChip,
|
|
97
98
|
AutoTopupModal,
|
|
98
99
|
AutoTopup,
|
|
99
|
-
Collapse
|
|
100
|
+
Collapse,
|
|
101
|
+
PromotionCode
|
|
100
102
|
};
|
package/es/libs/util.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type { PaymentDetails, PriceCurrency, PriceRecurring, TInvoiceExpanded, TLineItemExpanded, TPaymentCurrency, TPaymentCurrencyExpanded, TPaymentMethod, TPaymentMethodExpanded, TPrice, TProductExpanded, TSubscriptionExpanded, TSubscriptionItemExpanded } from '@blocklet/payment-types';
|
|
1
|
+
import type { PaymentDetails, PriceCurrency, PriceRecurring, TCoupon, TInvoiceExpanded, TLineItemExpanded, TPaymentCurrency, TPaymentCurrencyExpanded, TPaymentMethod, TPaymentMethodExpanded, TPrice, TProductExpanded, TSubscriptionExpanded, TSubscriptionItemExpanded } from '@blocklet/payment-types';
|
|
2
2
|
import type { ActionProps, PricingRenderProps } from '../types';
|
|
3
3
|
export declare const PAYMENT_KIT_DID = "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk";
|
|
4
|
+
/**
|
|
5
|
+
* Format coupon discount terms for display
|
|
6
|
+
*/
|
|
7
|
+
export declare const formatCouponTerms: (coupon: TCoupon, currency: TPaymentCurrency, locale?: string) => string;
|
|
4
8
|
export declare const isPaymentKitMounted: () => any;
|
|
5
9
|
export declare const getPrefix: () => string;
|
|
6
10
|
export declare function isCrossOrigin(): boolean;
|
package/es/libs/util.js
CHANGED
|
@@ -8,6 +8,29 @@ import { joinURL, withQuery } from "ufo";
|
|
|
8
8
|
import { t } from "../locales/index.js";
|
|
9
9
|
import dayjs from "./dayjs.js";
|
|
10
10
|
export const PAYMENT_KIT_DID = "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk";
|
|
11
|
+
export const formatCouponTerms = (coupon, currency, locale = "en") => {
|
|
12
|
+
let couponOff = "";
|
|
13
|
+
if (coupon.percent_off && coupon.percent_off > 0) {
|
|
14
|
+
couponOff = t("payment.checkout.coupon.percentage", locale, { percent: coupon.percent_off });
|
|
15
|
+
}
|
|
16
|
+
if (coupon.amount_off && coupon.amount_off !== "0") {
|
|
17
|
+
const { symbol } = currency;
|
|
18
|
+
couponOff = coupon.currency_id === currency.id ? coupon.amount_off || "" : coupon.currency_options?.[currency.id]?.amount_off || "";
|
|
19
|
+
if (couponOff) {
|
|
20
|
+
couponOff = t("payment.checkout.coupon.fixedAmount", locale, {
|
|
21
|
+
amount: formatAmount(couponOff, currency.decimal),
|
|
22
|
+
symbol
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!couponOff) {
|
|
27
|
+
return t("payment.checkout.coupon.noDiscount");
|
|
28
|
+
}
|
|
29
|
+
return t(`payment.checkout.coupon.terms.${coupon.duration}`, locale, {
|
|
30
|
+
couponOff,
|
|
31
|
+
months: coupon.duration_in_months || 0
|
|
32
|
+
});
|
|
33
|
+
};
|
|
11
34
|
export const isPaymentKitMounted = () => {
|
|
12
35
|
return (window.blocklet?.componentMountPoints || []).some((x) => x.did === PAYMENT_KIT_DID);
|
|
13
36
|
};
|
package/es/locales/en.js
CHANGED
|
@@ -238,6 +238,31 @@ export default flat({
|
|
|
238
238
|
orderSummary: "Order Summary",
|
|
239
239
|
paymentDetails: "Payment Details",
|
|
240
240
|
productListTotal: "Includes {total} items",
|
|
241
|
+
promotion: {
|
|
242
|
+
add_code: "Add promotion code",
|
|
243
|
+
enter_code: "Enter promotion code",
|
|
244
|
+
placeholder: "Enter code",
|
|
245
|
+
apply: "Apply",
|
|
246
|
+
applied: "Applied promotion codes",
|
|
247
|
+
dialog: {
|
|
248
|
+
title: "Add promotion code"
|
|
249
|
+
},
|
|
250
|
+
error: {
|
|
251
|
+
unknown: "Unknown error",
|
|
252
|
+
network: "Network error occurred",
|
|
253
|
+
removal: "Failed to remove code"
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
coupon: {
|
|
257
|
+
noDiscount: "No discount",
|
|
258
|
+
percentage: "{percent}% off",
|
|
259
|
+
fixedAmount: "{amount} {symbol} off",
|
|
260
|
+
terms: {
|
|
261
|
+
forever: "{couponOff} forever",
|
|
262
|
+
once: "{couponOff} once",
|
|
263
|
+
repeating: "{couponOff} for {months} month{months > 1 ? 's' : ''}"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
241
266
|
connectModal: {
|
|
242
267
|
title: "{action}",
|
|
243
268
|
scan: "Use the following methods to complete this payment",
|
package/es/locales/zh.js
CHANGED
|
@@ -226,6 +226,35 @@ export default flat({
|
|
|
226
226
|
add: "\u6DFB\u52A0\u5230\u8BA2\u5355",
|
|
227
227
|
remove: "\u4ECE\u8BA2\u5355\u79FB\u9664"
|
|
228
228
|
},
|
|
229
|
+
promotion: {
|
|
230
|
+
add_code: "\u6DFB\u52A0\u4FC3\u9500\u7801",
|
|
231
|
+
enter_code: "\u8F93\u5165\u4FC3\u9500\u7801",
|
|
232
|
+
apply: "\u5E94\u7528",
|
|
233
|
+
applied: "\u5DF2\u5E94\u7528\u7684\u4FC3\u9500\u7801",
|
|
234
|
+
placeholder: "\u8F93\u5165\u4FC3\u9500\u7801",
|
|
235
|
+
duration_once: "1\u6B21\u4F18\u60E0 {amount} {symbol}",
|
|
236
|
+
duration_repeating: "{months}\u4E2A\u6708\u4F18\u60E0 {amount} {symbol}",
|
|
237
|
+
duration_forever: "\u6C38\u4E45\u4F18\u60E0 {amount} {symbol}",
|
|
238
|
+
dialog: {
|
|
239
|
+
title: "\u6DFB\u52A0\u4FC3\u9500\u7801"
|
|
240
|
+
},
|
|
241
|
+
error: {
|
|
242
|
+
invalid: "\u65E0\u6548\u7684\u4FC3\u9500\u7801",
|
|
243
|
+
expired: "\u4FC3\u9500\u7801\u5DF2\u8FC7\u671F",
|
|
244
|
+
used: "\u4FC3\u9500\u7801\u5DF2\u88AB\u4F7F\u7528",
|
|
245
|
+
not_applicable: "\u4FC3\u9500\u7801\u4E0D\u9002\u7528\u4E8E\u6B64\u8BA2\u5355"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
coupon: {
|
|
249
|
+
noDiscount: "\u65E0\u4F18\u60E0",
|
|
250
|
+
percentage: "{percent}%",
|
|
251
|
+
fixedAmount: "{amount} {symbol}",
|
|
252
|
+
terms: {
|
|
253
|
+
forever: "\u6C38\u4E45\u4EAB {couponOff} \u6298\u6263",
|
|
254
|
+
once: "\u5355\u6B21\u4EAB {couponOff} \u6298\u6263",
|
|
255
|
+
repeating: "{months} \u4E2A\u6708\u5185\u4EAB {couponOff} \u6298\u6263"
|
|
256
|
+
}
|
|
257
|
+
},
|
|
229
258
|
credit: {
|
|
230
259
|
oneTimeInfo: "\u4ED8\u6B3E\u5B8C\u6210\u540E\u60A8\u5C06\u83B7\u5F97 {amount} {symbol} \u989D\u5EA6",
|
|
231
260
|
recurringInfo: "\u60A8\u5C06{period}\u83B7\u5F97 {amount} {symbol} \u989D\u5EA6",
|
package/es/payment/form/index.js
CHANGED
|
@@ -103,7 +103,7 @@ export default function PaymentForm({
|
|
|
103
103
|
}) {
|
|
104
104
|
const { t, locale } = useLocaleContext();
|
|
105
105
|
const { isMobile } = useMobile();
|
|
106
|
-
const { session, connect, payable } = usePaymentContext();
|
|
106
|
+
const { session, connect, payable, setPaymentState } = usePaymentContext();
|
|
107
107
|
const subscription = useSubscription("events");
|
|
108
108
|
const formErrorPosition = "bottom";
|
|
109
109
|
const {
|
|
@@ -150,6 +150,12 @@ export default function PaymentForm({
|
|
|
150
150
|
subscription.on("checkout.session.completed", onCheckoutComplete);
|
|
151
151
|
}
|
|
152
152
|
}, [subscription]);
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
setPaymentState({
|
|
155
|
+
paying: state.submitting || state.paying,
|
|
156
|
+
stripePaying: state.stripePaying
|
|
157
|
+
});
|
|
158
|
+
}, [state.submitting, state.paying, state.stripePaying]);
|
|
153
159
|
const mergeUserInfo = (customerInfo, userInfo) => {
|
|
154
160
|
return {
|
|
155
161
|
...userInfo || {},
|
package/es/payment/index.js
CHANGED
|
@@ -139,6 +139,13 @@ function PaymentInner({
|
|
|
139
139
|
if (onChange) {
|
|
140
140
|
onChange(methods.getValues());
|
|
141
141
|
}
|
|
142
|
+
if (state.checkoutSession?.discounts?.length) {
|
|
143
|
+
api.post(`/api/checkout-sessions/${state.checkoutSession.id}/recalculate-promotion`, {
|
|
144
|
+
currency_id: currencyId
|
|
145
|
+
}).then(() => {
|
|
146
|
+
onPromotionUpdate();
|
|
147
|
+
});
|
|
148
|
+
}
|
|
142
149
|
}, [currencyId]);
|
|
143
150
|
const onUpsell = async (from, to) => {
|
|
144
151
|
try {
|
|
@@ -200,6 +207,15 @@ function PaymentInner({
|
|
|
200
207
|
Toast.error(formatError(err));
|
|
201
208
|
}
|
|
202
209
|
};
|
|
210
|
+
const onPromotionUpdate = async () => {
|
|
211
|
+
try {
|
|
212
|
+
const { data } = await api.get(`/api/checkout-sessions/retrieve/${state.checkoutSession.id}`);
|
|
213
|
+
setState({ checkoutSession: data.checkoutSession });
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error(err);
|
|
216
|
+
Toast.error(formatError(err));
|
|
217
|
+
}
|
|
218
|
+
};
|
|
203
219
|
const handlePaid = (result) => {
|
|
204
220
|
setState({ checkoutSession: result.checkoutSession });
|
|
205
221
|
onPaid(result);
|
|
@@ -238,6 +254,9 @@ function PaymentInner({
|
|
|
238
254
|
donationSettings: paymentLink?.donation_settings,
|
|
239
255
|
action,
|
|
240
256
|
completed,
|
|
257
|
+
checkoutSession: state.checkoutSession,
|
|
258
|
+
onPromotionUpdate,
|
|
259
|
+
paymentMethods,
|
|
241
260
|
showFeatures
|
|
242
261
|
}
|
|
243
262
|
),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
|
-
import { Box, Stack, Typography, IconButton, TextField, Alert } from "@mui/material";
|
|
4
|
-
import { Add, Remove } from "@mui/icons-material";
|
|
3
|
+
import { Box, Stack, Typography, IconButton, TextField, Alert, Chip } from "@mui/material";
|
|
4
|
+
import { Add, Remove, LocalOffer } from "@mui/icons-material";
|
|
5
5
|
import { useMemo, useState } from "react";
|
|
6
6
|
import Status from "../components/status.js";
|
|
7
7
|
import Switch from "../components/switch-button.js";
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
formatPrice,
|
|
13
13
|
formatQuantityInventory,
|
|
14
14
|
formatRecurring,
|
|
15
|
-
formatUpsellSaving
|
|
15
|
+
formatUpsellSaving,
|
|
16
|
+
formatAmount
|
|
16
17
|
} from "../libs/util.js";
|
|
17
18
|
import ProductCard from "./product-card.js";
|
|
18
19
|
import dayjs from "../libs/dayjs.js";
|
|
@@ -183,6 +184,34 @@ export default function ProductItem({
|
|
|
183
184
|
]
|
|
184
185
|
}
|
|
185
186
|
),
|
|
187
|
+
item.discount_amounts && item.discount_amounts.length > 0 && /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 1, sx: { mt: 1, alignItems: "center" }, children: item.discount_amounts.map((discountAmount) => /* @__PURE__ */ jsx(
|
|
188
|
+
Chip,
|
|
189
|
+
{
|
|
190
|
+
icon: /* @__PURE__ */ jsx(LocalOffer, { sx: { fontSize: "0.8rem !important" } }),
|
|
191
|
+
label: /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
192
|
+
/* @__PURE__ */ jsx(Typography, { component: "span", sx: { fontSize: "0.75rem", fontWeight: "medium" }, children: discountAmount.promotion_code?.code || "DISCOUNT" }),
|
|
193
|
+
/* @__PURE__ */ jsxs(Typography, { component: "span", sx: { fontSize: "0.75rem" }, children: [
|
|
194
|
+
"(-",
|
|
195
|
+
formatAmount(discountAmount.amount || "0", currency.decimal),
|
|
196
|
+
" ",
|
|
197
|
+
currency.symbol,
|
|
198
|
+
")"
|
|
199
|
+
] })
|
|
200
|
+
] }),
|
|
201
|
+
size: "small",
|
|
202
|
+
variant: "filled",
|
|
203
|
+
sx: {
|
|
204
|
+
height: 20,
|
|
205
|
+
"& .MuiChip-icon": {
|
|
206
|
+
color: "warning.main"
|
|
207
|
+
},
|
|
208
|
+
"& .MuiChip-label": {
|
|
209
|
+
px: 1
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
discountAmount.promotion_code
|
|
214
|
+
)) }),
|
|
186
215
|
showFeatures && features.length > 0 && /* @__PURE__ */ jsx(
|
|
187
216
|
Box,
|
|
188
217
|
{
|
package/es/payment/summary.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DonationSettings, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
|
|
1
|
+
import type { DonationSettings, TLineItemExpanded, TPaymentCurrency, TCheckoutSession, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
2
2
|
type Props = {
|
|
3
3
|
items: TLineItemExpanded[];
|
|
4
4
|
currency: TPaymentCurrency;
|
|
@@ -17,7 +17,10 @@ type Props = {
|
|
|
17
17
|
donationSettings?: DonationSettings;
|
|
18
18
|
action?: string;
|
|
19
19
|
completed?: boolean;
|
|
20
|
+
checkoutSession?: TCheckoutSession;
|
|
21
|
+
onPromotionUpdate?: () => void;
|
|
22
|
+
paymentMethods?: TPaymentMethodExpanded[];
|
|
20
23
|
showFeatures?: boolean;
|
|
21
24
|
};
|
|
22
|
-
export default function PaymentSummary({ items, currency, trialInDays, billingThreshold, onUpsell, onDownsell, onQuantityChange, onApplyCrossSell, onCancelCrossSell, onChangeAmount, checkoutSessionId, crossSellBehavior, showStaking, donationSettings, action, trialEnd, completed, showFeatures, ...rest }: Props): import("react").JSX.Element;
|
|
25
|
+
export default function PaymentSummary({ items, currency, trialInDays, billingThreshold, onUpsell, onDownsell, onQuantityChange, onApplyCrossSell, onCancelCrossSell, onChangeAmount, checkoutSessionId, crossSellBehavior, showStaking, donationSettings, action, trialEnd, completed, checkoutSession, paymentMethods, onPromotionUpdate, showFeatures, ...rest }: Props): import("react").JSX.Element;
|
|
23
26
|
export {};
|