recur-tw 0.14.0 → 0.16.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/dist/index.cjs +269 -1
- package/dist/index.d.cts +214 -2
- package/dist/index.d.ts +214 -2
- package/dist/index.js +270 -4
- package/dist/recur.umd.js +1 -1
- package/dist/server.cjs +102 -1
- package/dist/server.d.cts +99 -1
- package/dist/server.d.ts +99 -1
- package/dist/server.js +98 -2
- package/package.json +9 -2
package/dist/index.cjs
CHANGED
|
@@ -2814,7 +2814,7 @@ function toCamelCase(obj) {
|
|
|
2814
2814
|
|
|
2815
2815
|
// package.json
|
|
2816
2816
|
var package_default = {
|
|
2817
|
-
version: "0.
|
|
2817
|
+
version: "0.15.0"};
|
|
2818
2818
|
var SDK_VERSION = package_default.version;
|
|
2819
2819
|
var SDK_TYPE = "react";
|
|
2820
2820
|
var RecurContext = React.createContext(null);
|
|
@@ -3748,6 +3748,272 @@ function useSubscribe(options = {}) {
|
|
|
3748
3748
|
reset
|
|
3749
3749
|
};
|
|
3750
3750
|
}
|
|
3751
|
+
var DEFAULT_BASE_URL = "https://api.recur.tw";
|
|
3752
|
+
var SDK_TYPE2 = "react";
|
|
3753
|
+
var SDK_VERSION2 = package_default.version;
|
|
3754
|
+
function usePromoCode(options = {}) {
|
|
3755
|
+
const context = React.useContext(RecurContext);
|
|
3756
|
+
const publishableKey = options.publishableKey || context?.config?.publishableKey;
|
|
3757
|
+
const baseUrl = options.baseUrl || context?.config?.baseUrl || DEFAULT_BASE_URL;
|
|
3758
|
+
if (!publishableKey) {
|
|
3759
|
+
throw new Error(
|
|
3760
|
+
'usePromoCode requires a publishableKey. Either wrap your app in <RecurProvider> or pass publishableKey directly: usePromoCode({ publishableKey: "pk_..." })'
|
|
3761
|
+
);
|
|
3762
|
+
}
|
|
3763
|
+
const [status, setStatus] = React.useState("idle");
|
|
3764
|
+
const [code, setCode] = React.useState(null);
|
|
3765
|
+
const [discount, setDiscount] = React.useState(null);
|
|
3766
|
+
const [appliesToAllProducts, setAppliesToAllProducts] = React.useState(false);
|
|
3767
|
+
const [applicableProducts, setApplicableProducts] = React.useState([]);
|
|
3768
|
+
const [error, setError] = React.useState(null);
|
|
3769
|
+
const abortControllerRef = React.useRef(null);
|
|
3770
|
+
const apply = React.useCallback(
|
|
3771
|
+
async (inputCode) => {
|
|
3772
|
+
const trimmed = inputCode.trim();
|
|
3773
|
+
if (!trimmed) {
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
abortControllerRef.current?.abort();
|
|
3777
|
+
const controller = new AbortController();
|
|
3778
|
+
abortControllerRef.current = controller;
|
|
3779
|
+
setStatus("validating");
|
|
3780
|
+
setError(null);
|
|
3781
|
+
setDiscount(null);
|
|
3782
|
+
setCode(null);
|
|
3783
|
+
setAppliesToAllProducts(false);
|
|
3784
|
+
setApplicableProducts([]);
|
|
3785
|
+
try {
|
|
3786
|
+
const body = { code: trimmed };
|
|
3787
|
+
if (options.customerId) {
|
|
3788
|
+
body.external_id = options.customerId;
|
|
3789
|
+
}
|
|
3790
|
+
if (options.recurCustomerId) {
|
|
3791
|
+
body.customer_id = options.recurCustomerId;
|
|
3792
|
+
}
|
|
3793
|
+
const response = await fetch(`${baseUrl}/v1/promotion-codes/validate`, {
|
|
3794
|
+
method: "POST",
|
|
3795
|
+
headers: {
|
|
3796
|
+
"Content-Type": "application/json",
|
|
3797
|
+
"X-Recur-Publishable-Key": publishableKey,
|
|
3798
|
+
"X-Recur-SDK-Type": SDK_TYPE2,
|
|
3799
|
+
"X-Recur-SDK-Version": SDK_VERSION2,
|
|
3800
|
+
"X-Recur-Source": typeof window !== "undefined" ? window.location.origin : "server"
|
|
3801
|
+
},
|
|
3802
|
+
body: JSON.stringify(body),
|
|
3803
|
+
signal: controller.signal
|
|
3804
|
+
});
|
|
3805
|
+
if (controller.signal.aborted) return;
|
|
3806
|
+
if (!response.ok) {
|
|
3807
|
+
const errorData = await response.json().catch(() => ({}));
|
|
3808
|
+
const message = errorData?.error?.message || "\u9A57\u8B49\u5931\u6557\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
|
|
3809
|
+
setStatus("invalid");
|
|
3810
|
+
setError(message);
|
|
3811
|
+
return;
|
|
3812
|
+
}
|
|
3813
|
+
const raw = await response.json();
|
|
3814
|
+
const result = toCamelCase(raw);
|
|
3815
|
+
if (controller.signal.aborted) return;
|
|
3816
|
+
if (result.valid) {
|
|
3817
|
+
setStatus("valid");
|
|
3818
|
+
setCode(trimmed.toUpperCase());
|
|
3819
|
+
setDiscount(result.discount || null);
|
|
3820
|
+
setAppliesToAllProducts(result.appliesToAllProducts ?? false);
|
|
3821
|
+
setApplicableProducts(result.applicableProducts ?? []);
|
|
3822
|
+
} else {
|
|
3823
|
+
const errorStatus = result.errorCode === "PROMO_CODE_ALREADY_USED" ? "already_used" : "invalid";
|
|
3824
|
+
setStatus(errorStatus);
|
|
3825
|
+
setError(result.errorMessage || "\u6B64\u512A\u60E0\u78BC\u7121\u6548");
|
|
3826
|
+
}
|
|
3827
|
+
} catch (err) {
|
|
3828
|
+
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
3829
|
+
setStatus("invalid");
|
|
3830
|
+
setError("\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66");
|
|
3831
|
+
}
|
|
3832
|
+
},
|
|
3833
|
+
[publishableKey, baseUrl, options.customerId, options.recurCustomerId]
|
|
3834
|
+
);
|
|
3835
|
+
const clear = React.useCallback(() => {
|
|
3836
|
+
abortControllerRef.current?.abort();
|
|
3837
|
+
setStatus("idle");
|
|
3838
|
+
setCode(null);
|
|
3839
|
+
setDiscount(null);
|
|
3840
|
+
setAppliesToAllProducts(false);
|
|
3841
|
+
setApplicableProducts([]);
|
|
3842
|
+
setError(null);
|
|
3843
|
+
}, []);
|
|
3844
|
+
return {
|
|
3845
|
+
apply,
|
|
3846
|
+
clear,
|
|
3847
|
+
status,
|
|
3848
|
+
isLoading: status === "validating",
|
|
3849
|
+
isValid: status === "valid",
|
|
3850
|
+
code,
|
|
3851
|
+
discount,
|
|
3852
|
+
appliesToAllProducts,
|
|
3853
|
+
applicableProducts,
|
|
3854
|
+
error
|
|
3855
|
+
};
|
|
3856
|
+
}
|
|
3857
|
+
var defaultStyles = {
|
|
3858
|
+
container: {
|
|
3859
|
+
display: "flex",
|
|
3860
|
+
flexDirection: "column",
|
|
3861
|
+
gap: "6px",
|
|
3862
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
3863
|
+
fontSize: "14px"
|
|
3864
|
+
},
|
|
3865
|
+
row: {
|
|
3866
|
+
display: "flex",
|
|
3867
|
+
gap: "8px",
|
|
3868
|
+
alignItems: "center"
|
|
3869
|
+
},
|
|
3870
|
+
input: {
|
|
3871
|
+
flex: 1,
|
|
3872
|
+
padding: "8px 12px",
|
|
3873
|
+
border: "1px solid #d1d5db",
|
|
3874
|
+
borderRadius: "6px",
|
|
3875
|
+
fontSize: "14px",
|
|
3876
|
+
fontFamily: "inherit",
|
|
3877
|
+
outline: "none",
|
|
3878
|
+
textTransform: "uppercase",
|
|
3879
|
+
letterSpacing: "0.05em"
|
|
3880
|
+
},
|
|
3881
|
+
button: {
|
|
3882
|
+
padding: "8px 16px",
|
|
3883
|
+
border: "1px solid #d1d5db",
|
|
3884
|
+
borderRadius: "6px",
|
|
3885
|
+
fontSize: "14px",
|
|
3886
|
+
fontFamily: "inherit",
|
|
3887
|
+
cursor: "pointer",
|
|
3888
|
+
backgroundColor: "#fff",
|
|
3889
|
+
whiteSpace: "nowrap"
|
|
3890
|
+
},
|
|
3891
|
+
buttonDisabled: {
|
|
3892
|
+
opacity: 0.5,
|
|
3893
|
+
cursor: "not-allowed"
|
|
3894
|
+
},
|
|
3895
|
+
messageSuccess: {
|
|
3896
|
+
color: "#16a34a",
|
|
3897
|
+
fontSize: "13px",
|
|
3898
|
+
margin: 0
|
|
3899
|
+
},
|
|
3900
|
+
messageError: {
|
|
3901
|
+
color: "#dc2626",
|
|
3902
|
+
fontSize: "13px",
|
|
3903
|
+
margin: 0
|
|
3904
|
+
}
|
|
3905
|
+
};
|
|
3906
|
+
function PromoCodeInput({
|
|
3907
|
+
promo,
|
|
3908
|
+
placeholder = "\u512A\u60E0\u78BC",
|
|
3909
|
+
applyText = "\u5957\u7528",
|
|
3910
|
+
clearText = "\u79FB\u9664",
|
|
3911
|
+
disabled = false,
|
|
3912
|
+
className,
|
|
3913
|
+
style: containerStyle,
|
|
3914
|
+
inputClassName,
|
|
3915
|
+
inputStyle,
|
|
3916
|
+
buttonClassName,
|
|
3917
|
+
buttonStyle,
|
|
3918
|
+
messageClassName,
|
|
3919
|
+
messageStyle,
|
|
3920
|
+
unstyled = false
|
|
3921
|
+
}) {
|
|
3922
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
3923
|
+
const handleApply = React.useCallback(() => {
|
|
3924
|
+
if (inputValue.trim()) {
|
|
3925
|
+
promo.apply(inputValue.trim());
|
|
3926
|
+
}
|
|
3927
|
+
}, [inputValue, promo]);
|
|
3928
|
+
const handleClear = React.useCallback(() => {
|
|
3929
|
+
setInputValue("");
|
|
3930
|
+
promo.clear();
|
|
3931
|
+
}, [promo]);
|
|
3932
|
+
const handleKeyDown = React.useCallback(
|
|
3933
|
+
(e) => {
|
|
3934
|
+
if (e.key === "Enter") {
|
|
3935
|
+
e.preventDefault();
|
|
3936
|
+
handleApply();
|
|
3937
|
+
}
|
|
3938
|
+
},
|
|
3939
|
+
[handleApply]
|
|
3940
|
+
);
|
|
3941
|
+
const isApplied = promo.isValid;
|
|
3942
|
+
const isDisabled = disabled || promo.isLoading;
|
|
3943
|
+
const s = unstyled ? null : defaultStyles;
|
|
3944
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3945
|
+
"div",
|
|
3946
|
+
{
|
|
3947
|
+
className,
|
|
3948
|
+
style: s ? { ...s.container, ...containerStyle } : containerStyle,
|
|
3949
|
+
children: [
|
|
3950
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: s?.row, children: [
|
|
3951
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3952
|
+
"input",
|
|
3953
|
+
{
|
|
3954
|
+
type: "text",
|
|
3955
|
+
value: isApplied ? promo.code || "" : inputValue,
|
|
3956
|
+
onChange: (e) => setInputValue(e.target.value),
|
|
3957
|
+
onKeyDown: handleKeyDown,
|
|
3958
|
+
placeholder,
|
|
3959
|
+
disabled: isDisabled || isApplied,
|
|
3960
|
+
className: inputClassName,
|
|
3961
|
+
style: s ? { ...s.input, ...inputStyle } : inputStyle,
|
|
3962
|
+
"aria-label": placeholder
|
|
3963
|
+
}
|
|
3964
|
+
),
|
|
3965
|
+
isApplied ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3966
|
+
"button",
|
|
3967
|
+
{
|
|
3968
|
+
type: "button",
|
|
3969
|
+
onClick: handleClear,
|
|
3970
|
+
disabled,
|
|
3971
|
+
className: buttonClassName,
|
|
3972
|
+
style: s ? { ...s.button, ...buttonStyle } : buttonStyle,
|
|
3973
|
+
children: clearText
|
|
3974
|
+
}
|
|
3975
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3976
|
+
"button",
|
|
3977
|
+
{
|
|
3978
|
+
type: "button",
|
|
3979
|
+
onClick: handleApply,
|
|
3980
|
+
disabled: isDisabled || !inputValue.trim(),
|
|
3981
|
+
className: buttonClassName,
|
|
3982
|
+
style: s ? {
|
|
3983
|
+
...s.button,
|
|
3984
|
+
...isDisabled || !inputValue.trim() ? s.buttonDisabled : {},
|
|
3985
|
+
...buttonStyle
|
|
3986
|
+
} : buttonStyle,
|
|
3987
|
+
children: promo.isLoading ? "\u9A57\u8B49\u4E2D..." : applyText
|
|
3988
|
+
}
|
|
3989
|
+
)
|
|
3990
|
+
] }),
|
|
3991
|
+
promo.isValid && promo.discount && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3992
|
+
"p",
|
|
3993
|
+
{
|
|
3994
|
+
className: messageClassName,
|
|
3995
|
+
style: s ? { ...s.messageSuccess, ...messageStyle } : messageStyle,
|
|
3996
|
+
role: "status",
|
|
3997
|
+
children: [
|
|
3998
|
+
promo.discount.label,
|
|
3999
|
+
promo.discount.bonusTrialDays && promo.discount.bonusTrialDays > 0 ? ` + ${promo.discount.bonusTrialDays} \u5929\u514D\u8CBB\u8A66\u7528` : "",
|
|
4000
|
+
promo.discount.bonusMonths && promo.discount.bonusMonths > 0 ? ` + \u8D08\u9001 ${promo.discount.bonusMonths} \u500B\u6708` : ""
|
|
4001
|
+
]
|
|
4002
|
+
}
|
|
4003
|
+
),
|
|
4004
|
+
promo.error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4005
|
+
"p",
|
|
4006
|
+
{
|
|
4007
|
+
className: messageClassName,
|
|
4008
|
+
style: s ? { ...s.messageError, ...messageStyle } : messageStyle,
|
|
4009
|
+
role: "alert",
|
|
4010
|
+
children: promo.error
|
|
4011
|
+
}
|
|
4012
|
+
)
|
|
4013
|
+
]
|
|
4014
|
+
}
|
|
4015
|
+
);
|
|
4016
|
+
}
|
|
3751
4017
|
function useCustomer() {
|
|
3752
4018
|
const context = React.useContext(CustomerContext);
|
|
3753
4019
|
if (!context) {
|
|
@@ -3824,9 +4090,11 @@ function useCustomer() {
|
|
|
3824
4090
|
};
|
|
3825
4091
|
}
|
|
3826
4092
|
|
|
4093
|
+
exports.PromoCodeInput = PromoCodeInput;
|
|
3827
4094
|
exports.RecurProvider = RecurProvider;
|
|
3828
4095
|
exports.useCustomer = useCustomer;
|
|
3829
4096
|
exports.usePlans = useProducts;
|
|
3830
4097
|
exports.useProducts = useProducts;
|
|
4098
|
+
exports.usePromoCode = usePromoCode;
|
|
3831
4099
|
exports.useRecur = useRecur;
|
|
3832
4100
|
exports.useSubscribe = useSubscribe;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import React from 'react';
|
|
2
|
+
import React, { CSSProperties } from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Recur Loading Spinner Web Component
|
|
@@ -1313,6 +1313,218 @@ interface UseSubscribeResult {
|
|
|
1313
1313
|
*/
|
|
1314
1314
|
declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult;
|
|
1315
1315
|
|
|
1316
|
+
interface UsePromoCodeOptions {
|
|
1317
|
+
/**
|
|
1318
|
+
* Customer identifier from your system (external ID).
|
|
1319
|
+
* Required for publishable key validation — the API rejects requests without it.
|
|
1320
|
+
*/
|
|
1321
|
+
customerId?: string;
|
|
1322
|
+
/**
|
|
1323
|
+
* Recur internal customer ID (alternative to customerId).
|
|
1324
|
+
* Use this if you already have the Recur customer ID.
|
|
1325
|
+
*/
|
|
1326
|
+
recurCustomerId?: string;
|
|
1327
|
+
/**
|
|
1328
|
+
* Publishable API key. If omitted, falls back to RecurProvider context.
|
|
1329
|
+
* This allows standalone usage without wrapping in RecurProvider.
|
|
1330
|
+
*
|
|
1331
|
+
* @example 'pk_test_abc123...'
|
|
1332
|
+
*/
|
|
1333
|
+
publishableKey?: string;
|
|
1334
|
+
/**
|
|
1335
|
+
* Base URL for API calls. Defaults to 'https://api.recur.tw'.
|
|
1336
|
+
*/
|
|
1337
|
+
baseUrl?: string;
|
|
1338
|
+
}
|
|
1339
|
+
type PromoCodeStatus = 'idle' | 'validating' | 'valid' | 'invalid' | 'already_used';
|
|
1340
|
+
interface PromoCodeDiscount {
|
|
1341
|
+
/** Discount type */
|
|
1342
|
+
type: 'PERCENTAGE' | 'FIXED_AMOUNT' | 'FIRST_PERIOD_PRICE';
|
|
1343
|
+
/** Raw discount value (basis points for %, TWD for fixed) */
|
|
1344
|
+
amount: number;
|
|
1345
|
+
/** Final amount after discount (0 if no amount was provided) */
|
|
1346
|
+
discountedAmount: number;
|
|
1347
|
+
/** Human-readable label (e.g., "20% off", "NT$100 off") */
|
|
1348
|
+
label: string;
|
|
1349
|
+
/** Bonus trial days */
|
|
1350
|
+
bonusTrialDays?: number;
|
|
1351
|
+
/** Bonus months */
|
|
1352
|
+
bonusMonths?: number;
|
|
1353
|
+
}
|
|
1354
|
+
interface ApplicableProduct {
|
|
1355
|
+
id: string;
|
|
1356
|
+
name: string;
|
|
1357
|
+
price: number;
|
|
1358
|
+
priceAfterDiscount: number;
|
|
1359
|
+
interval?: string | null;
|
|
1360
|
+
intervalCount?: number | null;
|
|
1361
|
+
}
|
|
1362
|
+
interface UsePromoCodeReturn {
|
|
1363
|
+
/**
|
|
1364
|
+
* Validate and apply a promo code.
|
|
1365
|
+
* Call this on button click or input blur.
|
|
1366
|
+
*/
|
|
1367
|
+
apply: (code: string) => Promise<void>;
|
|
1368
|
+
/**
|
|
1369
|
+
* Clear the current promo code state.
|
|
1370
|
+
*/
|
|
1371
|
+
clear: () => void;
|
|
1372
|
+
/** Current status */
|
|
1373
|
+
status: PromoCodeStatus;
|
|
1374
|
+
/** Whether a validation request is in progress */
|
|
1375
|
+
isLoading: boolean;
|
|
1376
|
+
/** Whether the current code is valid */
|
|
1377
|
+
isValid: boolean;
|
|
1378
|
+
/**
|
|
1379
|
+
* The validated promo code string.
|
|
1380
|
+
* Pass this directly to subscribe() — null if no valid code.
|
|
1381
|
+
*/
|
|
1382
|
+
code: string | null;
|
|
1383
|
+
/** Discount details (when valid) */
|
|
1384
|
+
discount: PromoCodeDiscount | null;
|
|
1385
|
+
/** Whether the discount applies to all products */
|
|
1386
|
+
appliesToAllProducts: boolean;
|
|
1387
|
+
/** Applicable products with discounted prices (when restricted to specific products) */
|
|
1388
|
+
applicableProducts: ApplicableProduct[];
|
|
1389
|
+
/** Error message (when invalid) */
|
|
1390
|
+
error: string | null;
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* usePromoCode Hook
|
|
1394
|
+
*
|
|
1395
|
+
* Validate promotion codes and get discount information.
|
|
1396
|
+
* Works with or without RecurProvider.
|
|
1397
|
+
*
|
|
1398
|
+
* @example With RecurProvider (publishableKey from context)
|
|
1399
|
+
* ```tsx
|
|
1400
|
+
* <RecurProvider config={{ publishableKey: 'pk_test_xxx' }}>
|
|
1401
|
+
* <PricingPage />
|
|
1402
|
+
* </RecurProvider>
|
|
1403
|
+
*
|
|
1404
|
+
* function PricingPage() {
|
|
1405
|
+
* const promo = usePromoCode({ customerId: 'user_123' })
|
|
1406
|
+
*
|
|
1407
|
+
* return (
|
|
1408
|
+
* <div>
|
|
1409
|
+
* <input onBlur={(e) => promo.apply(e.target.value)} />
|
|
1410
|
+
* {promo.isValid && <span>{promo.discount.label}</span>}
|
|
1411
|
+
* {promo.error && <span>{promo.error}</span>}
|
|
1412
|
+
* </div>
|
|
1413
|
+
* )
|
|
1414
|
+
* }
|
|
1415
|
+
* ```
|
|
1416
|
+
*
|
|
1417
|
+
* @example Standalone (no RecurProvider needed)
|
|
1418
|
+
* ```tsx
|
|
1419
|
+
* function PricingPage() {
|
|
1420
|
+
* const promo = usePromoCode({
|
|
1421
|
+
* publishableKey: 'pk_test_xxx',
|
|
1422
|
+
* customerId: 'user_123',
|
|
1423
|
+
* })
|
|
1424
|
+
* // ... same usage
|
|
1425
|
+
* }
|
|
1426
|
+
* ```
|
|
1427
|
+
*/
|
|
1428
|
+
declare function usePromoCode(options?: UsePromoCodeOptions): UsePromoCodeReturn;
|
|
1429
|
+
|
|
1430
|
+
interface PromoCodeInputProps {
|
|
1431
|
+
/**
|
|
1432
|
+
* Return value from usePromoCode().
|
|
1433
|
+
* The component reads state and calls apply/clear from this object.
|
|
1434
|
+
*/
|
|
1435
|
+
promo: UsePromoCodeReturn;
|
|
1436
|
+
/**
|
|
1437
|
+
* Input placeholder text
|
|
1438
|
+
* @default "優惠碼"
|
|
1439
|
+
*/
|
|
1440
|
+
placeholder?: string;
|
|
1441
|
+
/**
|
|
1442
|
+
* Apply button text
|
|
1443
|
+
* @default "套用"
|
|
1444
|
+
*/
|
|
1445
|
+
applyText?: string;
|
|
1446
|
+
/**
|
|
1447
|
+
* Clear button text (shown when a code is applied)
|
|
1448
|
+
* @default "移除"
|
|
1449
|
+
*/
|
|
1450
|
+
clearText?: string;
|
|
1451
|
+
/**
|
|
1452
|
+
* Disable the input and button
|
|
1453
|
+
*/
|
|
1454
|
+
disabled?: boolean;
|
|
1455
|
+
/**
|
|
1456
|
+
* CSS class for the outer container
|
|
1457
|
+
*/
|
|
1458
|
+
className?: string;
|
|
1459
|
+
/**
|
|
1460
|
+
* Inline styles for the outer container
|
|
1461
|
+
*/
|
|
1462
|
+
style?: CSSProperties;
|
|
1463
|
+
/**
|
|
1464
|
+
* CSS class for the input element
|
|
1465
|
+
*/
|
|
1466
|
+
inputClassName?: string;
|
|
1467
|
+
/**
|
|
1468
|
+
* Inline styles for the input element
|
|
1469
|
+
*/
|
|
1470
|
+
inputStyle?: CSSProperties;
|
|
1471
|
+
/**
|
|
1472
|
+
* CSS class for the apply/clear button
|
|
1473
|
+
*/
|
|
1474
|
+
buttonClassName?: string;
|
|
1475
|
+
/**
|
|
1476
|
+
* Inline styles for the apply/clear button
|
|
1477
|
+
*/
|
|
1478
|
+
buttonStyle?: CSSProperties;
|
|
1479
|
+
/**
|
|
1480
|
+
* CSS class for the status message (success/error)
|
|
1481
|
+
*/
|
|
1482
|
+
messageClassName?: string;
|
|
1483
|
+
/**
|
|
1484
|
+
* Inline styles for the status message
|
|
1485
|
+
*/
|
|
1486
|
+
messageStyle?: CSSProperties;
|
|
1487
|
+
/**
|
|
1488
|
+
* When true, renders without any default inline styles.
|
|
1489
|
+
* Use this when you want full control via className and style props.
|
|
1490
|
+
* @default false
|
|
1491
|
+
*/
|
|
1492
|
+
unstyled?: boolean;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* PromoCodeInput
|
|
1496
|
+
*
|
|
1497
|
+
* A drop-in promo code input component that works with usePromoCode().
|
|
1498
|
+
*
|
|
1499
|
+
* @example Minimal usage
|
|
1500
|
+
* ```tsx
|
|
1501
|
+
* const promo = usePromoCode({ customerId: 'user_123' });
|
|
1502
|
+
* <PromoCodeInput promo={promo} />
|
|
1503
|
+
* ```
|
|
1504
|
+
*
|
|
1505
|
+
* @example Custom styling with className (Tailwind)
|
|
1506
|
+
* ```tsx
|
|
1507
|
+
* <PromoCodeInput
|
|
1508
|
+
* promo={promo}
|
|
1509
|
+
* unstyled
|
|
1510
|
+
* className="flex flex-col gap-1"
|
|
1511
|
+
* inputClassName="border rounded px-3 py-2 uppercase"
|
|
1512
|
+
* buttonClassName="bg-blue-500 text-white px-4 py-2 rounded"
|
|
1513
|
+
* messageClassName="text-sm"
|
|
1514
|
+
* />
|
|
1515
|
+
* ```
|
|
1516
|
+
*
|
|
1517
|
+
* @example Custom styling with style props
|
|
1518
|
+
* ```tsx
|
|
1519
|
+
* <PromoCodeInput
|
|
1520
|
+
* promo={promo}
|
|
1521
|
+
* inputStyle={{ border: '2px solid #3b82f6', borderRadius: 8 }}
|
|
1522
|
+
* buttonStyle={{ backgroundColor: '#3b82f6', color: 'white' }}
|
|
1523
|
+
* />
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
declare function PromoCodeInput({ promo, placeholder, applyText, clearText, disabled, className, style: containerStyle, inputClassName, inputStyle, buttonClassName, buttonStyle, messageClassName, messageStyle, unstyled, }: PromoCodeInputProps): react_jsx_runtime.JSX.Element;
|
|
1527
|
+
|
|
1316
1528
|
/**
|
|
1317
1529
|
* useCustomer Hook
|
|
1318
1530
|
*
|
|
@@ -1460,4 +1672,4 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1460
1672
|
*/
|
|
1461
1673
|
declare function useCustomer(): UseCustomerResult;
|
|
1462
1674
|
|
|
1463
|
-
export { type ApiErrorCode, type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutErrorDetails, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type PaymentFailedAction, type PaymentFailureCode, type PaymentFailureDetails, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
|
1675
|
+
export { type ApiErrorCode, type ApplicableProduct, type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutErrorDetails, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type PaymentFailedAction, type PaymentFailureCode, type PaymentFailureDetails, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type PromoCodeDiscount, PromoCodeInput, type PromoCodeInputProps, type PromoCodeStatus, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UsePromoCodeOptions, type UsePromoCodeReturn, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, usePromoCode, useRecur, useSubscribe };
|