recur-tw 0.10.7 → 0.10.8
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 +69 -5
- package/dist/index.d.cts +89 -4
- package/dist/index.d.ts +89 -4
- package/dist/index.js +69 -5
- package/dist/recur.umd.js +12 -3
- package/dist/server.cjs +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -247,7 +247,7 @@ var init_error_display = __esm({
|
|
|
247
247
|
"src/components/base/error-display.ts"() {
|
|
248
248
|
RecurErrorDisplay = class extends HTMLElement {
|
|
249
249
|
static get observedAttributes() {
|
|
250
|
-
return ["error", "dismissible"];
|
|
250
|
+
return ["error", "error-title", "dismissible"];
|
|
251
251
|
}
|
|
252
252
|
constructor() {
|
|
253
253
|
super();
|
|
@@ -264,6 +264,9 @@ var init_error_display = __esm({
|
|
|
264
264
|
get error() {
|
|
265
265
|
return this.getAttribute("error") || "";
|
|
266
266
|
}
|
|
267
|
+
get errorTitle() {
|
|
268
|
+
return this.getAttribute("error-title") || "";
|
|
269
|
+
}
|
|
267
270
|
get isDismissible() {
|
|
268
271
|
return this.getAttribute("dismissible") === "true";
|
|
269
272
|
}
|
|
@@ -308,6 +311,14 @@ var init_error_display = __esm({
|
|
|
308
311
|
flex: 1;
|
|
309
312
|
}
|
|
310
313
|
|
|
314
|
+
.recur-sdk__error-title {
|
|
315
|
+
margin: 0 0 4px 0;
|
|
316
|
+
font-size: 14px;
|
|
317
|
+
font-weight: 600;
|
|
318
|
+
color: var(--recur-error-text, #991b1b);
|
|
319
|
+
line-height: 1.4;
|
|
320
|
+
}
|
|
321
|
+
|
|
311
322
|
.recur-sdk__error-message {
|
|
312
323
|
margin: 0;
|
|
313
324
|
font-size: 14px;
|
|
@@ -359,6 +370,7 @@ var init_error_display = __esm({
|
|
|
359
370
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
360
371
|
</svg>
|
|
361
372
|
<div class="recur-sdk__error-content">
|
|
373
|
+
${this.errorTitle ? `<p class="recur-sdk__error-title">${this.errorTitle}</p>` : ""}
|
|
362
374
|
<p class="recur-sdk__error-message">${this.error}</p>
|
|
363
375
|
</div>
|
|
364
376
|
${this.isDismissible ? `
|
|
@@ -1968,7 +1980,13 @@ var init_payment_form = __esm({
|
|
|
1968
1980
|
this.setButtonLoading(false);
|
|
1969
1981
|
}
|
|
1970
1982
|
}
|
|
1971
|
-
|
|
1983
|
+
/**
|
|
1984
|
+
* Show error message in the form
|
|
1985
|
+
* @param options - Either a string message or an object with title and message
|
|
1986
|
+
*/
|
|
1987
|
+
showError(options) {
|
|
1988
|
+
const message = typeof options === "string" ? options : options.message;
|
|
1989
|
+
const title = typeof options === "object" ? options.title : void 0;
|
|
1972
1990
|
let errorContainer = this.querySelector(".recur-sdk__error-container");
|
|
1973
1991
|
if (!errorContainer) {
|
|
1974
1992
|
errorContainer = document.createElement("div");
|
|
@@ -1978,6 +1996,9 @@ var init_payment_form = __esm({
|
|
|
1978
1996
|
}
|
|
1979
1997
|
const errorDisplay = document.createElement("recur-error-display");
|
|
1980
1998
|
errorDisplay.setAttribute("error", message);
|
|
1999
|
+
if (title) {
|
|
2000
|
+
errorDisplay.setAttribute("error-title", title);
|
|
2001
|
+
}
|
|
1981
2002
|
errorDisplay.setAttribute("dismissible", "true");
|
|
1982
2003
|
errorContainer.innerHTML = "";
|
|
1983
2004
|
errorContainer.appendChild(errorDisplay);
|
|
@@ -2653,7 +2674,7 @@ function toCamelCase(obj) {
|
|
|
2653
2674
|
|
|
2654
2675
|
// package.json
|
|
2655
2676
|
var package_default = {
|
|
2656
|
-
version: "0.10.
|
|
2677
|
+
version: "0.10.8"};
|
|
2657
2678
|
var SDK_VERSION = package_default.version;
|
|
2658
2679
|
var SDK_TYPE = "react";
|
|
2659
2680
|
var RecurContext = React.createContext(null);
|
|
@@ -3051,6 +3072,46 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
3051
3072
|
const rawPaymentResult = await paymentResponse.json();
|
|
3052
3073
|
const paymentResult = toCamelCase(rawPaymentResult);
|
|
3053
3074
|
console.log("[Recur SDK] Payment executed:", paymentResult);
|
|
3075
|
+
if (paymentResult.success === false && paymentResult.failure) {
|
|
3076
|
+
console.log("[Recur SDK] Payment failed:", paymentResult.failure);
|
|
3077
|
+
const failureError = {
|
|
3078
|
+
code: "PAYMENT_FAILED",
|
|
3079
|
+
message: paymentResult.failure.message || "\u4ED8\u6B3E\u5931\u6557",
|
|
3080
|
+
details: {
|
|
3081
|
+
failure_code: paymentResult.failure.code,
|
|
3082
|
+
failure_message: paymentResult.failure.message,
|
|
3083
|
+
can_retry: paymentResult.failure.canRetry
|
|
3084
|
+
}
|
|
3085
|
+
};
|
|
3086
|
+
let action;
|
|
3087
|
+
if (options.onPaymentFailed) {
|
|
3088
|
+
action = options.onPaymentFailed(failureError);
|
|
3089
|
+
}
|
|
3090
|
+
if (!action) {
|
|
3091
|
+
action = paymentResult.failure.canRetry ? { action: "retry" } : { action: "retry" };
|
|
3092
|
+
}
|
|
3093
|
+
if (action.action === "close") {
|
|
3094
|
+
options.onError?.(failureError);
|
|
3095
|
+
paymentForm.resetButton?.();
|
|
3096
|
+
closeDialog();
|
|
3097
|
+
setIsCheckingOut(false);
|
|
3098
|
+
return;
|
|
3099
|
+
} else if (action.action === "custom") {
|
|
3100
|
+
paymentForm.showError?.({
|
|
3101
|
+
title: action.customTitle || "\u4ED8\u6B3E\u5931\u6557",
|
|
3102
|
+
message: action.customMessage || failureError.message
|
|
3103
|
+
});
|
|
3104
|
+
paymentForm.resetButton?.();
|
|
3105
|
+
return;
|
|
3106
|
+
} else {
|
|
3107
|
+
paymentForm.showError?.({
|
|
3108
|
+
title: "\u4ED8\u6B3E\u5931\u6557",
|
|
3109
|
+
message: failureError.details?.failure_message || failureError.message
|
|
3110
|
+
});
|
|
3111
|
+
paymentForm.resetButton?.();
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3054
3115
|
if (paymentResult.requires3D && paymentResult.redirectUrl) {
|
|
3055
3116
|
console.log("[Recur SDK] 3D verification required");
|
|
3056
3117
|
const isMobileOrWebView = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || // Detect common WebView user agents
|
|
@@ -3401,7 +3462,7 @@ function useProducts(options = {}) {
|
|
|
3401
3462
|
};
|
|
3402
3463
|
}
|
|
3403
3464
|
function useSubscribe(options = {}) {
|
|
3404
|
-
const { onSuccess, onError, onPaymentComplete, onPaymentCancel } = options;
|
|
3465
|
+
const { onSuccess, onError, onPaymentComplete, onPaymentCancel, onPaymentFailed } = options;
|
|
3405
3466
|
const { checkout, isCheckingOut } = useRecur();
|
|
3406
3467
|
const [error, setError] = React.useState(null);
|
|
3407
3468
|
const mutate = React.useCallback(
|
|
@@ -3420,6 +3481,9 @@ function useSubscribe(options = {}) {
|
|
|
3420
3481
|
setError(err);
|
|
3421
3482
|
onError?.(err);
|
|
3422
3483
|
},
|
|
3484
|
+
onPaymentFailed: (err) => {
|
|
3485
|
+
return onPaymentFailed?.(err);
|
|
3486
|
+
},
|
|
3423
3487
|
onPaymentCancel: () => {
|
|
3424
3488
|
onPaymentCancel?.();
|
|
3425
3489
|
}
|
|
@@ -3433,7 +3497,7 @@ function useSubscribe(options = {}) {
|
|
|
3433
3497
|
onError?.(checkoutError);
|
|
3434
3498
|
}
|
|
3435
3499
|
},
|
|
3436
|
-
[checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel]
|
|
3500
|
+
[checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel, onPaymentFailed]
|
|
3437
3501
|
);
|
|
3438
3502
|
const reset = React.useCallback(() => {
|
|
3439
3503
|
setError(null);
|
package/dist/index.d.cts
CHANGED
|
@@ -55,6 +55,7 @@ declare class RecurErrorDisplay extends HTMLElement {
|
|
|
55
55
|
connectedCallback(): void;
|
|
56
56
|
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
57
57
|
private get error();
|
|
58
|
+
private get errorTitle();
|
|
58
59
|
private get isDismissible();
|
|
59
60
|
private handleDismiss;
|
|
60
61
|
private render;
|
|
@@ -275,6 +276,30 @@ interface CheckoutOptions {
|
|
|
275
276
|
* Callback when checkout fails
|
|
276
277
|
*/
|
|
277
278
|
onError?: (error: CheckoutError) => void;
|
|
279
|
+
/**
|
|
280
|
+
* Callback when payment fails (after user submits payment)
|
|
281
|
+
*
|
|
282
|
+
* This allows you to customize behavior based on the specific failure reason.
|
|
283
|
+
* Return an action object to override default behavior, or return nothing/undefined
|
|
284
|
+
* to use the SDK's default handling.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* onPaymentFailed: (error) => {
|
|
289
|
+
* // Custom handling for insufficient funds
|
|
290
|
+
* if (error.details?.failure_code === 'INSUFFICIENT_FUNDS') {
|
|
291
|
+
* return {
|
|
292
|
+
* action: 'custom',
|
|
293
|
+
* customTitle: '餘額不足',
|
|
294
|
+
* customMessage: '請使用其他付款方式或聯繫客服'
|
|
295
|
+
* };
|
|
296
|
+
* }
|
|
297
|
+
* // Use default behavior for other errors
|
|
298
|
+
* return undefined;
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
onPaymentFailed?: (error: CheckoutError) => PaymentFailedAction | void | undefined;
|
|
278
303
|
/**
|
|
279
304
|
* Callback when payment is completed
|
|
280
305
|
* (only works in popup mode)
|
|
@@ -323,6 +348,31 @@ interface CheckoutResult {
|
|
|
323
348
|
*/
|
|
324
349
|
sdkTokenExpiresAt: string;
|
|
325
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Payment failure codes from payment provider
|
|
353
|
+
* These indicate the specific reason a payment was declined
|
|
354
|
+
*/
|
|
355
|
+
type PaymentFailureCode = 'PAYUNI_DECLINED' | 'UNAPPROVED' | 'INSUFFICIENT_FUNDS' | 'CARD_DECLINED' | 'EXPIRED_CARD' | 'NETWORK_ERROR' | 'TIMEOUT' | 'INVALID_CARD' | 'UNKNOWN';
|
|
356
|
+
/**
|
|
357
|
+
* Extended error details for payment failures
|
|
358
|
+
*/
|
|
359
|
+
interface PaymentFailureDetails {
|
|
360
|
+
/**
|
|
361
|
+
* Specific failure code from payment provider
|
|
362
|
+
* Use this to show appropriate error messages or take specific actions
|
|
363
|
+
*/
|
|
364
|
+
failure_code?: PaymentFailureCode | string;
|
|
365
|
+
/**
|
|
366
|
+
* Raw failure message from payment provider
|
|
367
|
+
*/
|
|
368
|
+
failure_message?: string;
|
|
369
|
+
/**
|
|
370
|
+
* Whether the user can retry with the same payment method
|
|
371
|
+
* - true: Temporary error (network, timeout) - retry may succeed
|
|
372
|
+
* - false: Permanent error (card declined, expired) - different payment method needed
|
|
373
|
+
*/
|
|
374
|
+
can_retry?: boolean;
|
|
375
|
+
}
|
|
326
376
|
interface CheckoutError {
|
|
327
377
|
/**
|
|
328
378
|
* Error code
|
|
@@ -335,7 +385,27 @@ interface CheckoutError {
|
|
|
335
385
|
/**
|
|
336
386
|
* Additional error details
|
|
337
387
|
*/
|
|
338
|
-
details?: Record<string, unknown
|
|
388
|
+
details?: Record<string, unknown> & PaymentFailureDetails;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Action to take after payment failure
|
|
392
|
+
*/
|
|
393
|
+
interface PaymentFailedAction {
|
|
394
|
+
/**
|
|
395
|
+
* What action to take
|
|
396
|
+
* - 'retry': Keep dialog open, allow user to retry (default for can_retry=true)
|
|
397
|
+
* - 'close': Close dialog, trigger onError callback
|
|
398
|
+
* - 'custom': Show custom message in dialog
|
|
399
|
+
*/
|
|
400
|
+
action: 'retry' | 'close' | 'custom';
|
|
401
|
+
/**
|
|
402
|
+
* Custom message to display (only used when action is 'custom')
|
|
403
|
+
*/
|
|
404
|
+
customMessage?: string;
|
|
405
|
+
/**
|
|
406
|
+
* Custom title to display (only used when action is 'custom')
|
|
407
|
+
*/
|
|
408
|
+
customTitle?: string;
|
|
339
409
|
}
|
|
340
410
|
interface SubscriptionResult {
|
|
341
411
|
id: string;
|
|
@@ -556,7 +626,14 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
556
626
|
private setButtonLoading;
|
|
557
627
|
resetButton(): void;
|
|
558
628
|
setVerifying(verifying: boolean): void;
|
|
559
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Show error message in the form
|
|
631
|
+
* @param options - Either a string message or an object with title and message
|
|
632
|
+
*/
|
|
633
|
+
showError(options: string | {
|
|
634
|
+
title?: string;
|
|
635
|
+
message: string;
|
|
636
|
+
}): void;
|
|
560
637
|
private clearError;
|
|
561
638
|
private updateComplete;
|
|
562
639
|
getFormData(): {
|
|
@@ -794,6 +871,14 @@ interface UseSubscribeOptions {
|
|
|
794
871
|
* Callback when an error occurs
|
|
795
872
|
*/
|
|
796
873
|
onError?: (error: CheckoutError) => void;
|
|
874
|
+
/**
|
|
875
|
+
* Callback when payment fails (after user submits payment)
|
|
876
|
+
*
|
|
877
|
+
* This allows you to customize behavior based on the specific failure reason.
|
|
878
|
+
* Return an action object to override default behavior, or return nothing/undefined
|
|
879
|
+
* to use the SDK's default handling.
|
|
880
|
+
*/
|
|
881
|
+
onPaymentFailed?: (error: CheckoutError) => PaymentFailedAction | void | undefined;
|
|
797
882
|
/**
|
|
798
883
|
* Callback when payment is cancelled
|
|
799
884
|
*/
|
|
@@ -803,11 +888,11 @@ interface UseSubscribeResult {
|
|
|
803
888
|
/**
|
|
804
889
|
* Function to initiate subscription checkout
|
|
805
890
|
*/
|
|
806
|
-
mutate: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;
|
|
891
|
+
mutate: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel' | 'onPaymentFailed'>) => Promise<void>;
|
|
807
892
|
/**
|
|
808
893
|
* Alias for mutate (for better DX)
|
|
809
894
|
*/
|
|
810
|
-
subscribe: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;
|
|
895
|
+
subscribe: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel' | 'onPaymentFailed'>) => Promise<void>;
|
|
811
896
|
/**
|
|
812
897
|
* Whether subscription is in progress
|
|
813
898
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -55,6 +55,7 @@ declare class RecurErrorDisplay extends HTMLElement {
|
|
|
55
55
|
connectedCallback(): void;
|
|
56
56
|
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
|
|
57
57
|
private get error();
|
|
58
|
+
private get errorTitle();
|
|
58
59
|
private get isDismissible();
|
|
59
60
|
private handleDismiss;
|
|
60
61
|
private render;
|
|
@@ -275,6 +276,30 @@ interface CheckoutOptions {
|
|
|
275
276
|
* Callback when checkout fails
|
|
276
277
|
*/
|
|
277
278
|
onError?: (error: CheckoutError) => void;
|
|
279
|
+
/**
|
|
280
|
+
* Callback when payment fails (after user submits payment)
|
|
281
|
+
*
|
|
282
|
+
* This allows you to customize behavior based on the specific failure reason.
|
|
283
|
+
* Return an action object to override default behavior, or return nothing/undefined
|
|
284
|
+
* to use the SDK's default handling.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* onPaymentFailed: (error) => {
|
|
289
|
+
* // Custom handling for insufficient funds
|
|
290
|
+
* if (error.details?.failure_code === 'INSUFFICIENT_FUNDS') {
|
|
291
|
+
* return {
|
|
292
|
+
* action: 'custom',
|
|
293
|
+
* customTitle: '餘額不足',
|
|
294
|
+
* customMessage: '請使用其他付款方式或聯繫客服'
|
|
295
|
+
* };
|
|
296
|
+
* }
|
|
297
|
+
* // Use default behavior for other errors
|
|
298
|
+
* return undefined;
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
onPaymentFailed?: (error: CheckoutError) => PaymentFailedAction | void | undefined;
|
|
278
303
|
/**
|
|
279
304
|
* Callback when payment is completed
|
|
280
305
|
* (only works in popup mode)
|
|
@@ -323,6 +348,31 @@ interface CheckoutResult {
|
|
|
323
348
|
*/
|
|
324
349
|
sdkTokenExpiresAt: string;
|
|
325
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Payment failure codes from payment provider
|
|
353
|
+
* These indicate the specific reason a payment was declined
|
|
354
|
+
*/
|
|
355
|
+
type PaymentFailureCode = 'PAYUNI_DECLINED' | 'UNAPPROVED' | 'INSUFFICIENT_FUNDS' | 'CARD_DECLINED' | 'EXPIRED_CARD' | 'NETWORK_ERROR' | 'TIMEOUT' | 'INVALID_CARD' | 'UNKNOWN';
|
|
356
|
+
/**
|
|
357
|
+
* Extended error details for payment failures
|
|
358
|
+
*/
|
|
359
|
+
interface PaymentFailureDetails {
|
|
360
|
+
/**
|
|
361
|
+
* Specific failure code from payment provider
|
|
362
|
+
* Use this to show appropriate error messages or take specific actions
|
|
363
|
+
*/
|
|
364
|
+
failure_code?: PaymentFailureCode | string;
|
|
365
|
+
/**
|
|
366
|
+
* Raw failure message from payment provider
|
|
367
|
+
*/
|
|
368
|
+
failure_message?: string;
|
|
369
|
+
/**
|
|
370
|
+
* Whether the user can retry with the same payment method
|
|
371
|
+
* - true: Temporary error (network, timeout) - retry may succeed
|
|
372
|
+
* - false: Permanent error (card declined, expired) - different payment method needed
|
|
373
|
+
*/
|
|
374
|
+
can_retry?: boolean;
|
|
375
|
+
}
|
|
326
376
|
interface CheckoutError {
|
|
327
377
|
/**
|
|
328
378
|
* Error code
|
|
@@ -335,7 +385,27 @@ interface CheckoutError {
|
|
|
335
385
|
/**
|
|
336
386
|
* Additional error details
|
|
337
387
|
*/
|
|
338
|
-
details?: Record<string, unknown
|
|
388
|
+
details?: Record<string, unknown> & PaymentFailureDetails;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Action to take after payment failure
|
|
392
|
+
*/
|
|
393
|
+
interface PaymentFailedAction {
|
|
394
|
+
/**
|
|
395
|
+
* What action to take
|
|
396
|
+
* - 'retry': Keep dialog open, allow user to retry (default for can_retry=true)
|
|
397
|
+
* - 'close': Close dialog, trigger onError callback
|
|
398
|
+
* - 'custom': Show custom message in dialog
|
|
399
|
+
*/
|
|
400
|
+
action: 'retry' | 'close' | 'custom';
|
|
401
|
+
/**
|
|
402
|
+
* Custom message to display (only used when action is 'custom')
|
|
403
|
+
*/
|
|
404
|
+
customMessage?: string;
|
|
405
|
+
/**
|
|
406
|
+
* Custom title to display (only used when action is 'custom')
|
|
407
|
+
*/
|
|
408
|
+
customTitle?: string;
|
|
339
409
|
}
|
|
340
410
|
interface SubscriptionResult {
|
|
341
411
|
id: string;
|
|
@@ -556,7 +626,14 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
556
626
|
private setButtonLoading;
|
|
557
627
|
resetButton(): void;
|
|
558
628
|
setVerifying(verifying: boolean): void;
|
|
559
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Show error message in the form
|
|
631
|
+
* @param options - Either a string message or an object with title and message
|
|
632
|
+
*/
|
|
633
|
+
showError(options: string | {
|
|
634
|
+
title?: string;
|
|
635
|
+
message: string;
|
|
636
|
+
}): void;
|
|
560
637
|
private clearError;
|
|
561
638
|
private updateComplete;
|
|
562
639
|
getFormData(): {
|
|
@@ -794,6 +871,14 @@ interface UseSubscribeOptions {
|
|
|
794
871
|
* Callback when an error occurs
|
|
795
872
|
*/
|
|
796
873
|
onError?: (error: CheckoutError) => void;
|
|
874
|
+
/**
|
|
875
|
+
* Callback when payment fails (after user submits payment)
|
|
876
|
+
*
|
|
877
|
+
* This allows you to customize behavior based on the specific failure reason.
|
|
878
|
+
* Return an action object to override default behavior, or return nothing/undefined
|
|
879
|
+
* to use the SDK's default handling.
|
|
880
|
+
*/
|
|
881
|
+
onPaymentFailed?: (error: CheckoutError) => PaymentFailedAction | void | undefined;
|
|
797
882
|
/**
|
|
798
883
|
* Callback when payment is cancelled
|
|
799
884
|
*/
|
|
@@ -803,11 +888,11 @@ interface UseSubscribeResult {
|
|
|
803
888
|
/**
|
|
804
889
|
* Function to initiate subscription checkout
|
|
805
890
|
*/
|
|
806
|
-
mutate: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;
|
|
891
|
+
mutate: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel' | 'onPaymentFailed'>) => Promise<void>;
|
|
807
892
|
/**
|
|
808
893
|
* Alias for mutate (for better DX)
|
|
809
894
|
*/
|
|
810
|
-
subscribe: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;
|
|
895
|
+
subscribe: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel' | 'onPaymentFailed'>) => Promise<void>;
|
|
811
896
|
/**
|
|
812
897
|
* Whether subscription is in progress
|
|
813
898
|
*/
|
package/dist/index.js
CHANGED
|
@@ -241,7 +241,7 @@ var init_error_display = __esm({
|
|
|
241
241
|
"src/components/base/error-display.ts"() {
|
|
242
242
|
RecurErrorDisplay = class extends HTMLElement {
|
|
243
243
|
static get observedAttributes() {
|
|
244
|
-
return ["error", "dismissible"];
|
|
244
|
+
return ["error", "error-title", "dismissible"];
|
|
245
245
|
}
|
|
246
246
|
constructor() {
|
|
247
247
|
super();
|
|
@@ -258,6 +258,9 @@ var init_error_display = __esm({
|
|
|
258
258
|
get error() {
|
|
259
259
|
return this.getAttribute("error") || "";
|
|
260
260
|
}
|
|
261
|
+
get errorTitle() {
|
|
262
|
+
return this.getAttribute("error-title") || "";
|
|
263
|
+
}
|
|
261
264
|
get isDismissible() {
|
|
262
265
|
return this.getAttribute("dismissible") === "true";
|
|
263
266
|
}
|
|
@@ -302,6 +305,14 @@ var init_error_display = __esm({
|
|
|
302
305
|
flex: 1;
|
|
303
306
|
}
|
|
304
307
|
|
|
308
|
+
.recur-sdk__error-title {
|
|
309
|
+
margin: 0 0 4px 0;
|
|
310
|
+
font-size: 14px;
|
|
311
|
+
font-weight: 600;
|
|
312
|
+
color: var(--recur-error-text, #991b1b);
|
|
313
|
+
line-height: 1.4;
|
|
314
|
+
}
|
|
315
|
+
|
|
305
316
|
.recur-sdk__error-message {
|
|
306
317
|
margin: 0;
|
|
307
318
|
font-size: 14px;
|
|
@@ -353,6 +364,7 @@ var init_error_display = __esm({
|
|
|
353
364
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
354
365
|
</svg>
|
|
355
366
|
<div class="recur-sdk__error-content">
|
|
367
|
+
${this.errorTitle ? `<p class="recur-sdk__error-title">${this.errorTitle}</p>` : ""}
|
|
356
368
|
<p class="recur-sdk__error-message">${this.error}</p>
|
|
357
369
|
</div>
|
|
358
370
|
${this.isDismissible ? `
|
|
@@ -1962,7 +1974,13 @@ var init_payment_form = __esm({
|
|
|
1962
1974
|
this.setButtonLoading(false);
|
|
1963
1975
|
}
|
|
1964
1976
|
}
|
|
1965
|
-
|
|
1977
|
+
/**
|
|
1978
|
+
* Show error message in the form
|
|
1979
|
+
* @param options - Either a string message or an object with title and message
|
|
1980
|
+
*/
|
|
1981
|
+
showError(options) {
|
|
1982
|
+
const message = typeof options === "string" ? options : options.message;
|
|
1983
|
+
const title = typeof options === "object" ? options.title : void 0;
|
|
1966
1984
|
let errorContainer = this.querySelector(".recur-sdk__error-container");
|
|
1967
1985
|
if (!errorContainer) {
|
|
1968
1986
|
errorContainer = document.createElement("div");
|
|
@@ -1972,6 +1990,9 @@ var init_payment_form = __esm({
|
|
|
1972
1990
|
}
|
|
1973
1991
|
const errorDisplay = document.createElement("recur-error-display");
|
|
1974
1992
|
errorDisplay.setAttribute("error", message);
|
|
1993
|
+
if (title) {
|
|
1994
|
+
errorDisplay.setAttribute("error-title", title);
|
|
1995
|
+
}
|
|
1975
1996
|
errorDisplay.setAttribute("dismissible", "true");
|
|
1976
1997
|
errorContainer.innerHTML = "";
|
|
1977
1998
|
errorContainer.appendChild(errorDisplay);
|
|
@@ -2647,7 +2668,7 @@ function toCamelCase(obj) {
|
|
|
2647
2668
|
|
|
2648
2669
|
// package.json
|
|
2649
2670
|
var package_default = {
|
|
2650
|
-
version: "0.10.
|
|
2671
|
+
version: "0.10.8"};
|
|
2651
2672
|
var SDK_VERSION = package_default.version;
|
|
2652
2673
|
var SDK_TYPE = "react";
|
|
2653
2674
|
var RecurContext = createContext(null);
|
|
@@ -3045,6 +3066,46 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
3045
3066
|
const rawPaymentResult = await paymentResponse.json();
|
|
3046
3067
|
const paymentResult = toCamelCase(rawPaymentResult);
|
|
3047
3068
|
console.log("[Recur SDK] Payment executed:", paymentResult);
|
|
3069
|
+
if (paymentResult.success === false && paymentResult.failure) {
|
|
3070
|
+
console.log("[Recur SDK] Payment failed:", paymentResult.failure);
|
|
3071
|
+
const failureError = {
|
|
3072
|
+
code: "PAYMENT_FAILED",
|
|
3073
|
+
message: paymentResult.failure.message || "\u4ED8\u6B3E\u5931\u6557",
|
|
3074
|
+
details: {
|
|
3075
|
+
failure_code: paymentResult.failure.code,
|
|
3076
|
+
failure_message: paymentResult.failure.message,
|
|
3077
|
+
can_retry: paymentResult.failure.canRetry
|
|
3078
|
+
}
|
|
3079
|
+
};
|
|
3080
|
+
let action;
|
|
3081
|
+
if (options.onPaymentFailed) {
|
|
3082
|
+
action = options.onPaymentFailed(failureError);
|
|
3083
|
+
}
|
|
3084
|
+
if (!action) {
|
|
3085
|
+
action = paymentResult.failure.canRetry ? { action: "retry" } : { action: "retry" };
|
|
3086
|
+
}
|
|
3087
|
+
if (action.action === "close") {
|
|
3088
|
+
options.onError?.(failureError);
|
|
3089
|
+
paymentForm.resetButton?.();
|
|
3090
|
+
closeDialog();
|
|
3091
|
+
setIsCheckingOut(false);
|
|
3092
|
+
return;
|
|
3093
|
+
} else if (action.action === "custom") {
|
|
3094
|
+
paymentForm.showError?.({
|
|
3095
|
+
title: action.customTitle || "\u4ED8\u6B3E\u5931\u6557",
|
|
3096
|
+
message: action.customMessage || failureError.message
|
|
3097
|
+
});
|
|
3098
|
+
paymentForm.resetButton?.();
|
|
3099
|
+
return;
|
|
3100
|
+
} else {
|
|
3101
|
+
paymentForm.showError?.({
|
|
3102
|
+
title: "\u4ED8\u6B3E\u5931\u6557",
|
|
3103
|
+
message: failureError.details?.failure_message || failureError.message
|
|
3104
|
+
});
|
|
3105
|
+
paymentForm.resetButton?.();
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3048
3109
|
if (paymentResult.requires3D && paymentResult.redirectUrl) {
|
|
3049
3110
|
console.log("[Recur SDK] 3D verification required");
|
|
3050
3111
|
const isMobileOrWebView = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || // Detect common WebView user agents
|
|
@@ -3395,7 +3456,7 @@ function useProducts(options = {}) {
|
|
|
3395
3456
|
};
|
|
3396
3457
|
}
|
|
3397
3458
|
function useSubscribe(options = {}) {
|
|
3398
|
-
const { onSuccess, onError, onPaymentComplete, onPaymentCancel } = options;
|
|
3459
|
+
const { onSuccess, onError, onPaymentComplete, onPaymentCancel, onPaymentFailed } = options;
|
|
3399
3460
|
const { checkout, isCheckingOut } = useRecur();
|
|
3400
3461
|
const [error, setError] = useState(null);
|
|
3401
3462
|
const mutate = useCallback(
|
|
@@ -3414,6 +3475,9 @@ function useSubscribe(options = {}) {
|
|
|
3414
3475
|
setError(err);
|
|
3415
3476
|
onError?.(err);
|
|
3416
3477
|
},
|
|
3478
|
+
onPaymentFailed: (err) => {
|
|
3479
|
+
return onPaymentFailed?.(err);
|
|
3480
|
+
},
|
|
3417
3481
|
onPaymentCancel: () => {
|
|
3418
3482
|
onPaymentCancel?.();
|
|
3419
3483
|
}
|
|
@@ -3427,7 +3491,7 @@ function useSubscribe(options = {}) {
|
|
|
3427
3491
|
onError?.(checkoutError);
|
|
3428
3492
|
}
|
|
3429
3493
|
},
|
|
3430
|
-
[checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel]
|
|
3494
|
+
[checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel, onPaymentFailed]
|
|
3431
3495
|
);
|
|
3432
3496
|
const reset = useCallback(() => {
|
|
3433
3497
|
setError(null);
|
package/dist/recur.umd.js
CHANGED
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
<p class="recur-sdk__success-message">${this.successMessage}</p>
|
|
122
122
|
<slot></slot>
|
|
123
123
|
</div>
|
|
124
|
-
`}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",oe)});var Le={};C(Le,{RecurErrorDisplay:()=>ie});var ie,Ue=k(()=>{"use strict";ie=class extends HTMLElement{static get observedAttributes(){return["error","dismissible"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get error(){return this.getAttribute("error")||""}get isDismissible(){return this.getAttribute("dismissible")==="true"}handleDismiss(){this.dispatchEvent(new CustomEvent("dismiss",{bubbles:!0,composed:!0})),this.remove()}render(){if(!this.error){this.shadowRoot.innerHTML="";return}this.shadowRoot.innerHTML=`
|
|
124
|
+
`}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",oe)});var Le={};C(Le,{RecurErrorDisplay:()=>ie});var ie,Ue=k(()=>{"use strict";ie=class extends HTMLElement{static get observedAttributes(){return["error","error-title","dismissible"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get error(){return this.getAttribute("error")||""}get errorTitle(){return this.getAttribute("error-title")||""}get isDismissible(){return this.getAttribute("dismissible")==="true"}handleDismiss(){this.dispatchEvent(new CustomEvent("dismiss",{bubbles:!0,composed:!0})),this.remove()}render(){if(!this.error){this.shadowRoot.innerHTML="";return}this.shadowRoot.innerHTML=`
|
|
125
125
|
<style>
|
|
126
126
|
:host {
|
|
127
127
|
display: block;
|
|
@@ -150,6 +150,14 @@
|
|
|
150
150
|
flex: 1;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
.recur-sdk__error-title {
|
|
154
|
+
margin: 0 0 4px 0;
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
font-weight: 600;
|
|
157
|
+
color: var(--recur-error-text, #991b1b);
|
|
158
|
+
line-height: 1.4;
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
.recur-sdk__error-message {
|
|
154
162
|
margin: 0;
|
|
155
163
|
font-size: 14px;
|
|
@@ -201,6 +209,7 @@
|
|
|
201
209
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
202
210
|
</svg>
|
|
203
211
|
<div class="recur-sdk__error-content">
|
|
212
|
+
${this.errorTitle?`<p class="recur-sdk__error-title">${this.errorTitle}</p>`:""}
|
|
204
213
|
<p class="recur-sdk__error-message">${this.error}</p>
|
|
205
214
|
</div>
|
|
206
215
|
${this.isDismissible?`
|
|
@@ -1722,7 +1731,7 @@
|
|
|
1722
1731
|
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}setVerifying(t){let r=document.getElementById(`${this.containerId}-submit-btn`);r&&(t?(r.classList.add("loading"),r.disabled=!0,r.innerHTML=`
|
|
1723
1732
|
<span class="recur-loading-spinner"></span>
|
|
1724
1733
|
<span>3D \u9A57\u8B49\u4E2D...</span>
|
|
1725
|
-
`):this.setButtonLoading(!1))}showError(t){let r=this.querySelector(".recur-sdk__error-container");
|
|
1734
|
+
`):this.setButtonLoading(!1))}showError(t){let r=typeof t=="string"?t:t.message,o=typeof t=="object"?t.title:void 0,i=this.querySelector(".recur-sdk__error-container");i||(i=document.createElement("div"),i.className="recur-sdk__error-container",i.style.cssText="margin-bottom: 16px;",this.insertBefore(i,this.firstChild));let s=document.createElement("recur-error-display");s.setAttribute("error",r),o&&s.setAttribute("error-title",o),s.setAttribute("dismissible","true"),i.innerHTML="",i.appendChild(s),s.scrollIntoView({behavior:"smooth",block:"center"})}clearError(){let t=this.querySelector(".recur-sdk__error-container");t&&(t.innerHTML="")}updateComplete(){return new Promise(t=>{if(this.isConnected)setTimeout(t,0);else{let r=new MutationObserver(()=>{this.isConnected&&(r.disconnect(),setTimeout(t,0))});r.observe(document.body,{childList:!0,subtree:!0})}})}getFormData(){return{email:document.getElementById(`${this.containerId}-email`)?.value,name:document.getElementById(`${this.containerId}-name`)?.value}}setFormData(t){if(t.email){let r=document.getElementById(`${this.containerId}-email`);r&&(r.value=t.email)}if(t.name){let r=document.getElementById(`${this.containerId}-name`);r&&(r.value=t.name)}}};customElements.get("recur-payment-form")||customElements.define("recur-payment-form",le)});var at={};C(at,{RecurCheckoutButton:()=>de});var de,ct=k(()=>{"use strict";de=class extends HTMLElement{constructor(){super();l(this,"_isLoading",!1);l(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("publishable-key"),o=this.getAttribute("product-id"),i=this.getAttribute("success-url"),s=this.getAttribute("cancel-url");if(!r){this.dispatchError("Missing required attribute: publishable-key");return}if(!o){this.dispatchError("Missing required attribute: product-id");return}if(!i){this.dispatchError("Missing required attribute: success-url");return}if(!s){this.dispatchError("Missing required attribute: cancel-url");return}this._isLoading=!0,this.render(),this.setupEventListeners();try{let a=await this.createCheckoutSession({publishableKey:r,productId:o,successUrl:this.resolveUrl(i),cancelUrl:this.resolveUrl(s),customerEmail:this.getAttribute("customer-email")||void 0,mode:this.getAttribute("mode")});this.dispatchEvent(new CustomEvent("checkout-started",{detail:{sessionId:a.id,url:a.url},bubbles:!0,composed:!0})),window.location.href=a.url}catch(a){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(a.message||"Failed to create checkout session")}});this.attachShadow({mode:"open"})}static get observedAttributes(){return["publishable-key","product-id","success-url","cancel-url","customer-email","mode","button-text","button-style","disabled"]}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){let t=this.shadowRoot?.querySelector("button");t&&t.removeEventListener("click",this.handleClick)}attributeChangedCallback(t,r,o){r!==o&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",o=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
|
|
1726
1735
|
<style>
|
|
1727
1736
|
:host {
|
|
1728
1737
|
display: inline-block;
|
|
@@ -1948,7 +1957,7 @@
|
|
|
1948
1957
|
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
|
|
1949
1958
|
<circle cx="12" cy="7" r="4"/>
|
|
1950
1959
|
</svg>
|
|
1951
|
-
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}redirectToPortal(t){let r=this.getAttribute("target");this.dispatchEvent(new CustomEvent("portal-redirect",{detail:{url:t},bubbles:!0,composed:!0})),r==="_blank"?window.open(t,"_blank","noopener,noreferrer"):window.location.href=t}async fetchAndRedirect(t){let r=this.getAttribute("customer-id"),o=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let i={};r&&(i.customerId=r),o&&(i.returnUrl=o);let s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!s.ok){let d=await s.json().catch(()=>({}));throw new Error(d.error?.message||d.message||`HTTP ${s.status}: Failed to create portal session`)}let a=await s.json(),c=a.url||a.portalUrl;if(!c)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(c)}catch(i){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(i.message||"Failed to create portal session")}}dispatchError(t){console.error("[recur-portal]",t),this.dispatchEvent(new CustomEvent("portal-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-portal")&&customElements.define("recur-portal",ue)});var zt={};C(zt,{RecurCheckout:()=>Z,RecurElements:()=>N,createElements:()=>Se,default:()=>Nt,init:()=>mt});async function Dt(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(De(),Re)),Promise.resolve().then(()=>(Me(),Pe)),Promise.resolve().then(()=>(Ue(),Le)),Promise.resolve().then(()=>(Be(),He)),Promise.resolve().then(()=>(Ze(),Ge)),Promise.resolve().then(()=>(et(),Qe)),Promise.resolve().then(()=>(nt(),st)),Promise.resolve().then(()=>(ct(),at)),Promise.resolve().then(()=>(dt(),lt))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout","recur-portal"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&Dt();function Pt(n){return n.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function w(n){if(n==null)return n;if(Array.isArray(n))return n.map(e=>w(e));if(n instanceof Date)return n;if(typeof n=="object"){let e={};for(let[t,r]of Object.entries(n)){let o=Pt(t);e[o]=w(r)}return e}return n}var ut={name:"recur-tw",version:"0.10.
|
|
1960
|
+
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}redirectToPortal(t){let r=this.getAttribute("target");this.dispatchEvent(new CustomEvent("portal-redirect",{detail:{url:t},bubbles:!0,composed:!0})),r==="_blank"?window.open(t,"_blank","noopener,noreferrer"):window.location.href=t}async fetchAndRedirect(t){let r=this.getAttribute("customer-id"),o=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let i={};r&&(i.customerId=r),o&&(i.returnUrl=o);let s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!s.ok){let d=await s.json().catch(()=>({}));throw new Error(d.error?.message||d.message||`HTTP ${s.status}: Failed to create portal session`)}let a=await s.json(),c=a.url||a.portalUrl;if(!c)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(c)}catch(i){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(i.message||"Failed to create portal session")}}dispatchError(t){console.error("[recur-portal]",t),this.dispatchEvent(new CustomEvent("portal-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-portal")&&customElements.define("recur-portal",ue)});var zt={};C(zt,{RecurCheckout:()=>Z,RecurElements:()=>N,createElements:()=>Se,default:()=>Nt,init:()=>mt});async function Dt(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(De(),Re)),Promise.resolve().then(()=>(Me(),Pe)),Promise.resolve().then(()=>(Ue(),Le)),Promise.resolve().then(()=>(Be(),He)),Promise.resolve().then(()=>(Ze(),Ge)),Promise.resolve().then(()=>(et(),Qe)),Promise.resolve().then(()=>(nt(),st)),Promise.resolve().then(()=>(ct(),at)),Promise.resolve().then(()=>(dt(),lt))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout","recur-portal"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&Dt();function Pt(n){return n.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function w(n){if(n==null)return n;if(Array.isArray(n))return n.map(e=>w(e));if(n instanceof Date)return n;if(typeof n=="object"){let e={};for(let[t,r]of Object.entries(n)){let o=Pt(t);e[o]=w(r)}return e}return n}var ut={name:"recur-tw",version:"0.10.8",description:"React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",type:"module",private:!1,author:"Recur",license:"Elastic-2.0",keywords:["react","vanilla-js","subscription","checkout","payment","payuni","recurring-billing","taiwan","\u7E41\u9AD4\u4E2D\u6587","sdk","embedded-checkout"],homepage:"https://recur.tw/",bugs:{email:"support@recur.tw"},scripts:{build:"tsup",dev:"tsup --watch",lint:"eslint .","lint:fix":"eslint . --fix","type-check":"tsc --noEmit",examples:"npx http-server . -p 8080 -o /examples/"},main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"},"./server":{types:"./dist/server.d.ts",import:"./dist/server.js",require:"./dist/server.cjs"},"./vanilla":"./dist/recur.umd.js","./checkout":"./dist/checkout.js","./widget":"./dist/widget.js","./package.json":"./package.json"},unpkg:"./dist/recur.umd.js",jsdelivr:"./dist/recur.umd.js",files:["dist","README.md","AGENTS.md","LICENSE"],sideEffects:["src/components/**/*.ts","src/components/**/*.js"],peerDependencies:{react:">=18.0.0","react-dom":">=18.0.0"},peerDependenciesMeta:{react:{optional:!0},"react-dom":{optional:!0}},devDependencies:{"@eslint/js":"^9.39.1","@types/node":"^20.19.9","@types/react":"^19.1.9","@types/react-dom":"^19.1.7","@workspace/typescript-config":"workspace:*",autoprefixer:"^10.4.22",eslint:"^9","eslint-plugin-react":"^7.37.5","eslint-plugin-react-hooks":"^7.0.1",postcss:"^8.5.6",react:"^19.2.1","react-dom":"^19.2.1",tailwindcss:"^4.1.11",tsup:"^8.3.5",typescript:"^5.9.2","typescript-eslint":"^8.47.0"},dependencies:{"lit-html":"^3.3.1"}};var Lt=ut.version,Ut="vanilla",pe=class{constructor(e){l(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}getHeaders(){return{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey,"X-Recur-SDK-Type":Ut,"X-Recur-SDK-Version":Lt,"X-Recur-Source":typeof window<"u"?window.location.origin:"server"}}async createSubscription(e){let{customerName:t,customerEmail:r}=e,o=e.productId||e.planId,i=e.productSlug;if(!o&&!i)throw new Error("Either productId or productSlug is required");let s={customerName:t,customerEmail:r};o&&(s.productId=o),i&&(s.productSlug=i);let a=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(s)});if(!a.ok){let d=await a.json().catch(()=>({}));throw{code:d.error||"CHECKOUT_FAILED",message:d.message||"Failed to initiate checkout",details:d}}let c=await a.json();return w(c)}async fetchProducts(e){let t=new URL(`${this.config.baseUrl}/v1/products`);e?.type&&t.searchParams.set("type",e.type);let r=await fetch(t.toString(),{method:"GET",headers:this.getHeaders()});if(!r.ok){let i=await r.json().catch(()=>({}));throw{code:i.error||"FETCH_PRODUCTS_FAILED",message:i.message||"Failed to fetch products",details:i}}let o=await r.json();return w(o)}async fetchPlans(){return{plans:(await this.fetchProducts({type:"SUBSCRIPTION"})).products}}getConfig(){return{...this.config}}};var he=class{constructor(e,t){l(this,"config");l(this,"options");l(this,"container");l(this,"checkoutId",null);l(this,"sdkToken",null);l(this,"sdkTimestamp",null);l(this,"creditToken",null);l(this,"sdkEnv","S");l(this,"payuniSDK",null);l(this,"isFormValid",!1);if(this.config=e,this.options=t,typeof t.container=="string"){let r=document.querySelector(t.container);if(!r)throw new Error(`Container not found: ${t.container}`);this.container=r}else this.container=t.container}async render(){try{await this.initCheckout(),this.renderHTML(),await this.loadPayUniSDK(),await this.initPayUniSDK(),this.setupFormSubmit()}catch(e){this.handleError(e)}}getBaseUrl(){if(this.config.baseUrl)return this.config.baseUrl;if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}async initCheckout(){let e=this.getBaseUrl(),t=await fetch(`${e}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({productId:this.options.productId||this.options.planId,customerEmail:this.options.customerEmail,customerName:this.options.customerName})});if(!t.ok){let i=await t.json().catch(()=>({}));throw new Error(i.error||"Failed to initialize checkout")}let r=await t.json(),o=w(r);this.checkoutId=o.checkout.id,this.sdkToken=o.sdkToken,this.sdkTimestamp=o.sdkTimestamp||null,this.creditToken=o.creditToken||null,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
|
|
1952
1961
|
<div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
|
|
1953
1962
|
<div class="recur-checkout-header" style="margin-bottom: 24px;">
|
|
1954
1963
|
<h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
|
package/dist/server.cjs
CHANGED
package/dist/server.js
CHANGED