recur-tw 0.11.0 → 0.12.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 +129 -27
- package/dist/index.d.cts +175 -15
- package/dist/index.d.ts +175 -15
- package/dist/index.js +129 -27
- package/dist/recur.umd.js +178 -33
- package/dist/server.cjs +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1048,7 +1048,7 @@ var init_payment_form_shadow = __esm({
|
|
|
1048
1048
|
var payment_form_light_default;
|
|
1049
1049
|
var init_payment_form_light = __esm({
|
|
1050
1050
|
"src/components/styles/payment-form-light.css"() {
|
|
1051
|
-
payment_form_light_default = "/**\n * Payment Form - Light DOM Styles\n * These styles are applied to slotted content in the Light DOM\n * Using BEM naming convention with 'recur-' prefix for isolation\n */\n\n/* ============================================\n * Order Summary Styles\n * ============================================ */\n.recur-order-summary {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n}\n\n.recur-order-summary__row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.recur-order-summary__row--total {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n}\n\n.recur-order-summary__label {\n font-size: 14px;\n color: #4a5568;\n}\n\n.recur-order-summary__value {\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: #2d3748;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================\n * Coupon Section Styles\n * ============================================ */\n.recur-coupon {\n padding: 12px 0;\n border-top: 1px solid #e2e8f0;\n margin-top: 12px;\n min-height: 36px;\n display: flex;\n align-items: center;\n}\n\n.recur-coupon__trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n background: none;\n border: none;\n padding: 0;\n font-size: 14px;\n line-height: 1;\n color: #718096;\n cursor: pointer;\n transition: color 0.2s;\n}\n\n.recur-coupon__trigger:hover {\n color: #4a5568;\n}\n\n.recur-coupon__trigger-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__input-wrapper {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.recur-coupon__input {\n width: 100%;\n height: 36px;\n padding: 0 32px 0 12px;\n font-size: 14px;\n text-transform: uppercase;\n background-color: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.recur-coupon__input::placeholder {\n text-transform: none;\n}\n\n.recur-coupon__input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n.recur-coupon__input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-coupon__clear {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s;\n}\n\n.recur-coupon__clear:hover {\n color: #6b7280;\n}\n\n.recur-coupon__clear-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__btn {\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s, opacity 0.2s;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\n min-width: 52px;\n}\n\n.recur-coupon__btn--apply .recur-coupon__spinner {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__btn--apply:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-coupon__btn--apply:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.recur-coupon__error {\n width: 100%;\n margin-top: 8px;\n font-size: 13px;\n color: #dc2626;\n}\n\n.recur-coupon__applied-wrapper {\n width: 100%;\n}\n\n.recur-coupon__description {\n font-size: 12px;\n color: #6b7280;\n margin: 4px 0 0 22px;\n}\n\n.recur-coupon__bonus {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #dcfce7;\n border-radius: 6px;\n padding: 8px 12px;\n margin-top: 8px;\n font-size: 12px;\n color: #15803d;\n}\n\n.recur-coupon__applied {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n flex: 1;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #dc2626;\n flex-shrink: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 14px;\n color: #9ca3af;\n white-space: nowrap;\n margin-right: 2px;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.recur-coupon__remove {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n transition: color 0.2s, background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recur-coupon__remove:hover {\n color: #6b7280;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.recur-coupon__remove-icon {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__spinner {\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n/* Discount Row */\n.recur-order-summary__row--discount {\n color: #16a34a;\n}\n\n.recur-order-summary__row--discount .recur-order-summary__value {\n color: #16a34a;\n font-weight: 500;\n}\n\n/* ============================================\n * Customer Info Styles\n * ============================================ */\n.recur-info-display {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n margin-bottom: 8px;\n}\n\n.recur-info-row {\n margin-bottom: 12px;\n}\n\n.recur-info-row:last-child {\n margin-bottom: 0;\n}\n\n.recur-info-label {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: #718096;\n margin-bottom: 2px;\n}\n\n.recur-info-value {\n display: block;\n font-size: 14px;\n line-height: 20px;\n min-height: 20px;\n color: #2d3748;\n font-weight: 500;\n word-break: break-all;\n}\n\n.recur-form-group {\n margin-bottom: 16px;\n}\n\n.recur-form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-form-input {\n width: 100%;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n transition: border-color 0.2s;\n box-sizing: border-box;\n}\n\n.recur-form-input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n/* ============================================\n * Card Fields Styles\n * ============================================ */\n.recur-card-field {\n margin-bottom: 16px;\n}\n\n.recur-card-field-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-payuni-iframe {\n height: 36px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n touch-action: manipulation;\n}\n\n.recur-card-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n column-gap: 16px;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.recur-card-field-container {\n position: relative;\n touch-action: manipulation;\n}\n\n.recur-card-skeleton {\n position: absolute;\n top: 0;\n left: 0;\n height: 36px;\n width: 100%;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n z-index: 1;\n pointer-events: none;\n}\n\n.recur-card-skeleton.hidden {\n display: none;\n}\n\n@keyframes recur-skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* ============================================\n * Submit Button Styles\n * ============================================ */\n.recur-submit-button {\n width: 100%;\n padding: 14px 20px;\n font-size: 16px;\n font-weight: 500;\n color: #ffffff;\n background: #18181b;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n box-sizing: border-box;\n}\n\n.recur-submit-button:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.recur-submit-button.loading {\n cursor: wait;\n}\n\n.recur-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n@keyframes recur-spin {\n to { transform: rotate(360deg); }\n}\n\n/* ============================================\n * Skeleton Loading Styles\n * ============================================ */\n.recur-skeleton {\n display: inline-block;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n}\n";
|
|
1051
|
+
payment_form_light_default = "/**\n * Payment Form - Light DOM Styles\n * These styles are applied to slotted content in the Light DOM\n * Using BEM naming convention with 'recur-' prefix for isolation\n */\n\n/* ============================================\n * Order Summary Styles\n * ============================================ */\n.recur-order-summary {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n}\n\n.recur-order-summary__row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.recur-order-summary__row--total {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n}\n\n.recur-order-summary__label {\n font-size: 14px;\n color: #4a5568;\n}\n\n.recur-order-summary__value {\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: #2d3748;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================\n * Coupon Section Styles\n * ============================================ */\n.recur-coupon {\n padding: 12px 0;\n border-top: 1px solid #e2e8f0;\n margin-top: 12px;\n min-height: 36px;\n display: flex;\n align-items: center;\n}\n\n.recur-coupon__trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n background: none;\n border: none;\n padding: 0;\n font-size: 14px;\n line-height: 1;\n color: #718096;\n cursor: pointer;\n transition: color 0.2s;\n}\n\n.recur-coupon__trigger:hover {\n color: #4a5568;\n}\n\n.recur-coupon__trigger-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__input-wrapper {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.recur-coupon__input {\n width: 100%;\n height: 36px;\n padding: 0 32px 0 12px;\n font-size: 14px;\n text-transform: uppercase;\n background-color: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.recur-coupon__input::placeholder {\n text-transform: none;\n}\n\n.recur-coupon__input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n.recur-coupon__input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-coupon__clear {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s;\n}\n\n.recur-coupon__clear:hover {\n color: #6b7280;\n}\n\n.recur-coupon__clear-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__btn {\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s, opacity 0.2s;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\n min-width: 52px;\n}\n\n.recur-coupon__btn--apply .recur-coupon__spinner {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__btn--apply:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-coupon__btn--apply:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.recur-coupon__error {\n width: 100%;\n margin-top: 8px;\n font-size: 13px;\n color: #dc2626;\n}\n\n.recur-coupon__applied-wrapper {\n width: 100%;\n}\n\n.recur-coupon__description {\n font-size: 12px;\n color: #6b7280;\n margin: 4px 0 0 22px;\n}\n\n.recur-coupon__bonus {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #dcfce7;\n border-radius: 6px;\n padding: 8px 12px;\n margin-top: 8px;\n font-size: 12px;\n color: #15803d;\n}\n\n.recur-coupon__applied {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n flex: 1;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #dc2626;\n flex-shrink: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 14px;\n color: #9ca3af;\n white-space: nowrap;\n margin-right: 2px;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.recur-coupon__remove {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n transition: color 0.2s, background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recur-coupon__remove:hover {\n color: #6b7280;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.recur-coupon__remove-icon {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__spinner {\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n/* Discount Row */\n.recur-order-summary__row--discount {\n color: #16a34a;\n}\n\n.recur-order-summary__row--discount .recur-order-summary__value {\n color: #16a34a;\n font-weight: 500;\n}\n\n/* ============================================\n * Customer Info Styles\n * ============================================ */\n.recur-info-display {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n margin-bottom: 8px;\n}\n\n.recur-info-row {\n margin-bottom: 12px;\n}\n\n.recur-info-row:last-child {\n margin-bottom: 0;\n}\n\n.recur-info-label {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: #718096;\n margin-bottom: 2px;\n}\n\n.recur-info-value {\n display: block;\n font-size: 14px;\n line-height: 20px;\n min-height: 20px;\n color: #2d3748;\n font-weight: 500;\n word-break: break-all;\n}\n\n.recur-form-group {\n margin-bottom: 16px;\n}\n\n.recur-form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-form-input {\n width: 100%;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n transition: border-color 0.2s;\n box-sizing: border-box;\n}\n\n.recur-form-input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n/* ============================================\n * Card Fields Styles\n * ============================================ */\n.recur-card-field {\n margin-bottom: 16px;\n}\n\n.recur-card-field-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-payuni-iframe {\n height: 36px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n touch-action: manipulation;\n}\n\n.recur-card-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n column-gap: 16px;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.recur-card-field-container {\n position: relative;\n touch-action: manipulation;\n}\n\n.recur-card-skeleton {\n position: absolute;\n top: 0;\n left: 0;\n height: 36px;\n width: 100%;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n z-index: 1;\n pointer-events: none;\n}\n\n.recur-card-skeleton.hidden {\n display: none;\n}\n\n@keyframes recur-skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* ============================================\n * Submit Button Styles\n * ============================================ */\n.recur-submit-button {\n width: 100%;\n padding: 14px 20px;\n font-size: 16px;\n font-weight: 500;\n color: #ffffff;\n background: #18181b;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n box-sizing: border-box;\n}\n\n.recur-submit-button:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.recur-submit-button.loading {\n cursor: wait;\n}\n\n.recur-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n@keyframes recur-spin {\n to { transform: rotate(360deg); }\n}\n\n/* ============================================\n * Skeleton Loading Styles\n * ============================================ */\n.recur-skeleton {\n display: inline-block;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n}\n\n/* ============================================\n * Test Mode Banner Styles\n * ============================================ */\n.recur-test-mode-banner {\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\n border: 1px solid #f59e0b;\n border-radius: 8px;\n margin-bottom: 16px;\n overflow: hidden;\n}\n\n.recur-test-mode-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n}\n\n.recur-test-mode-header:hover {\n background: rgba(245, 158, 11, 0.1);\n}\n\n.recur-test-mode-badge {\n font-size: 13px;\n font-weight: 600;\n color: #92400e;\n}\n\n.recur-test-mode-toggle {\n font-size: 12px;\n color: #b45309;\n}\n\n.recur-test-cards {\n background: rgba(255, 255, 255, 0.7);\n padding: 12px 14px;\n border-top: 1px solid rgba(245, 158, 11, 0.3);\n}\n\n.recur-test-cards-info {\n font-size: 12px;\n color: #78350f;\n margin: 0 0 10px 0;\n}\n\n.recur-test-card {\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n padding: 10px 12px;\n margin-bottom: 8px;\n}\n\n.recur-test-card:last-child {\n margin-bottom: 0;\n}\n\n.recur-test-card-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 4px;\n}\n\n.recur-test-card-number {\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 14px;\n font-weight: 600;\n color: #1f2937;\n letter-spacing: 0.5px;\n}\n\n.recur-test-card-copy {\n font-size: 11px;\n font-weight: 500;\n color: #3b82f6;\n background: #eff6ff;\n border: 1px solid #bfdbfe;\n border-radius: 4px;\n padding: 3px 8px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.recur-test-card-copy:hover {\n background: #dbeafe;\n border-color: #93c5fd;\n}\n\n.recur-test-card-copy.copied {\n background: #dcfce7;\n border-color: #86efac;\n color: #16a34a;\n}\n\n.recur-test-card-details {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 11px;\n color: #6b7280;\n}\n\n.recur-test-card-desc {\n color: #374151;\n font-weight: 500;\n}\n\n.recur-test-card-meta {\n color: #9ca3af;\n}\n";
|
|
1052
1052
|
}
|
|
1053
1053
|
});
|
|
1054
1054
|
|
|
@@ -1097,6 +1097,8 @@ var init_payment_form = __esm({
|
|
|
1097
1097
|
__publicField(this, "customStyles");
|
|
1098
1098
|
__publicField(this, "_isInitializing", false);
|
|
1099
1099
|
__publicField(this, "_initializationAborted", false);
|
|
1100
|
+
// Test mode state
|
|
1101
|
+
__publicField(this, "_showTestCardHints", false);
|
|
1100
1102
|
// Coupon state
|
|
1101
1103
|
__publicField(this, "_showCouponInput", false);
|
|
1102
1104
|
__publicField(this, "_couponCode", "");
|
|
@@ -1163,6 +1165,8 @@ var init_payment_form = __esm({
|
|
|
1163
1165
|
<style>${this.getShadowDOMStyles()}</style>
|
|
1164
1166
|
|
|
1165
1167
|
<div class="payment-form-wrapper">
|
|
1168
|
+
<slot name="test-mode-banner"></slot>
|
|
1169
|
+
|
|
1166
1170
|
<div class="form-header">
|
|
1167
1171
|
<h2 class="form-title">訂閱付款</h2>
|
|
1168
1172
|
</div>
|
|
@@ -1199,6 +1203,10 @@ var init_payment_form = __esm({
|
|
|
1199
1203
|
${this.getCustomStyles()}
|
|
1200
1204
|
</style>
|
|
1201
1205
|
|
|
1206
|
+
<div slot="test-mode-banner">
|
|
1207
|
+
${this.renderTestCardHints()}
|
|
1208
|
+
</div>
|
|
1209
|
+
|
|
1202
1210
|
<div slot="order-summary">
|
|
1203
1211
|
${this.renderOrderSummary()}
|
|
1204
1212
|
</div>
|
|
@@ -1235,6 +1243,63 @@ var init_payment_form = __esm({
|
|
|
1235
1243
|
}
|
|
1236
1244
|
}
|
|
1237
1245
|
// ============================================
|
|
1246
|
+
// Test Mode Helpers
|
|
1247
|
+
// ============================================
|
|
1248
|
+
isTestMode() {
|
|
1249
|
+
const publishableKey = this.getAttribute("publishable-key") || "";
|
|
1250
|
+
return publishableKey.startsWith("pk_test_");
|
|
1251
|
+
}
|
|
1252
|
+
renderTestCardHints() {
|
|
1253
|
+
if (!this.isTestMode()) {
|
|
1254
|
+
return litHtml.html``;
|
|
1255
|
+
}
|
|
1256
|
+
const testCards = [
|
|
1257
|
+
{ number: "4147 6310 0000 0001", desc: "VISA \u6E2C\u8A66\u5361\uFF08\u6388\u6B0A\u6210\u529F\uFF09", expiry: "\u4EFB\u610F\u672A\u4F86\u65E5\u671F", cvc: "\u4EFB\u610F3\u78BC" },
|
|
1258
|
+
{ number: "3560 5110 0000 0001", desc: "JCB \u6E2C\u8A66\u5361\uFF08\u6388\u6B0A\u6210\u529F\uFF09", expiry: "\u4EFB\u610F\u672A\u4F86\u65E5\u671F", cvc: "\u4EFB\u610F3\u78BC" },
|
|
1259
|
+
{ number: "4147 6310 0000 0002", desc: "VISA \u6E2C\u8A66\u5361\uFF083D\u9A57\u8B49\u5931\u6557\uFF09", expiry: "\u4EFB\u610F\u672A\u4F86\u65E5\u671F", cvc: "\u4EFB\u610F3\u78BC" }
|
|
1260
|
+
];
|
|
1261
|
+
const copyToClipboard = (text, event) => {
|
|
1262
|
+
const btn = event.currentTarget;
|
|
1263
|
+
navigator.clipboard.writeText(text.replace(/\s/g, "")).then(() => {
|
|
1264
|
+
const originalText = btn.textContent;
|
|
1265
|
+
btn.textContent = "\u5DF2\u8907\u88FD!";
|
|
1266
|
+
btn.classList.add("copied");
|
|
1267
|
+
setTimeout(() => {
|
|
1268
|
+
btn.textContent = originalText;
|
|
1269
|
+
btn.classList.remove("copied");
|
|
1270
|
+
}, 1500);
|
|
1271
|
+
});
|
|
1272
|
+
};
|
|
1273
|
+
return litHtml.html`
|
|
1274
|
+
<div class="recur-test-mode-banner">
|
|
1275
|
+
<div class="recur-test-mode-header" @click=${() => {
|
|
1276
|
+
this._showTestCardHints = !this._showTestCardHints;
|
|
1277
|
+
this.renderLightDOM();
|
|
1278
|
+
}}>
|
|
1279
|
+
<span class="recur-test-mode-badge">🧪 測試模式</span>
|
|
1280
|
+
<span class="recur-test-mode-toggle">${this._showTestCardHints ? "\u6536\u5408 \u25B2" : "\u5C55\u958B\u6E2C\u8A66\u5361\u865F \u25BC"}</span>
|
|
1281
|
+
</div>
|
|
1282
|
+
${this._showTestCardHints ? litHtml.html`
|
|
1283
|
+
<div class="recur-test-cards">
|
|
1284
|
+
<p class="recur-test-cards-info">使用以下測試卡號進行測試付款:</p>
|
|
1285
|
+
${testCards.map((card) => litHtml.html`
|
|
1286
|
+
<div class="recur-test-card">
|
|
1287
|
+
<div class="recur-test-card-row">
|
|
1288
|
+
<span class="recur-test-card-number">${card.number}</span>
|
|
1289
|
+
<button type="button" class="recur-test-card-copy" @click=${(e) => copyToClipboard(card.number, e)}>複製</button>
|
|
1290
|
+
</div>
|
|
1291
|
+
<div class="recur-test-card-details">
|
|
1292
|
+
<span class="recur-test-card-desc">${card.desc}</span>
|
|
1293
|
+
<span class="recur-test-card-meta">到期日: ${card.expiry} / CVC: ${card.cvc}</span>
|
|
1294
|
+
</div>
|
|
1295
|
+
</div>
|
|
1296
|
+
`)}
|
|
1297
|
+
</div>
|
|
1298
|
+
` : litHtml.nothing}
|
|
1299
|
+
</div>
|
|
1300
|
+
`;
|
|
1301
|
+
}
|
|
1302
|
+
// ============================================
|
|
1238
1303
|
// Section Renderers (lit-html)
|
|
1239
1304
|
// ============================================
|
|
1240
1305
|
renderOrderSummary() {
|
|
@@ -1648,16 +1713,16 @@ var init_payment_form = __esm({
|
|
|
1648
1713
|
}
|
|
1649
1714
|
this._appliedCoupon = {
|
|
1650
1715
|
code: data.discount.code,
|
|
1651
|
-
couponName: data.discount.
|
|
1652
|
-
discountType: data.discount.
|
|
1653
|
-
discountAmount: data.discount.
|
|
1716
|
+
couponName: data.discount.coupon_name,
|
|
1717
|
+
discountType: data.discount.discount_type,
|
|
1718
|
+
discountAmount: data.discount.discount_amount,
|
|
1654
1719
|
duration: data.discount.duration,
|
|
1655
|
-
durationMonths: data.discount.
|
|
1656
|
-
bonusTrialDays: data.discount.
|
|
1657
|
-
bonusMonths: data.discount.
|
|
1658
|
-
productInterval: data.discount.
|
|
1659
|
-
productIntervalCount: data.discount.
|
|
1660
|
-
productPrice: data.discount.
|
|
1720
|
+
durationMonths: data.discount.duration_months,
|
|
1721
|
+
bonusTrialDays: data.discount.bonus_trial_days,
|
|
1722
|
+
bonusMonths: data.discount.bonus_months,
|
|
1723
|
+
productInterval: data.discount.product_interval,
|
|
1724
|
+
productIntervalCount: data.discount.product_interval_count,
|
|
1725
|
+
productPrice: data.discount.product_price
|
|
1661
1726
|
};
|
|
1662
1727
|
this._showCouponInput = false;
|
|
1663
1728
|
this._couponCode = "";
|
|
@@ -2674,7 +2739,7 @@ function toCamelCase(obj) {
|
|
|
2674
2739
|
|
|
2675
2740
|
// package.json
|
|
2676
2741
|
var package_default = {
|
|
2677
|
-
version: "0.
|
|
2742
|
+
version: "0.12.0"};
|
|
2678
2743
|
var SDK_VERSION = package_default.version;
|
|
2679
2744
|
var SDK_TYPE = "react";
|
|
2680
2745
|
var RecurContext = React.createContext(null);
|
|
@@ -2988,14 +3053,14 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
2988
3053
|
}
|
|
2989
3054
|
console.log("[Recur SDK] Step 1: Creating checkout session...");
|
|
2990
3055
|
const checkoutRequestBody = {
|
|
2991
|
-
|
|
2992
|
-
|
|
3056
|
+
customer_name: options.customerName,
|
|
3057
|
+
customer_email: options.customerEmail
|
|
2993
3058
|
};
|
|
2994
|
-
if (productId) checkoutRequestBody.
|
|
2995
|
-
if (productSlug) checkoutRequestBody.
|
|
2996
|
-
if (options.externalCustomerId) checkoutRequestBody.
|
|
2997
|
-
if (options.successUrl) checkoutRequestBody.
|
|
2998
|
-
if (options.cancelUrl) checkoutRequestBody.
|
|
3059
|
+
if (productId) checkoutRequestBody.product_id = productId;
|
|
3060
|
+
if (productSlug) checkoutRequestBody.product_slug = productSlug;
|
|
3061
|
+
if (options.externalCustomerId) checkoutRequestBody.external_customer_id = options.externalCustomerId;
|
|
3062
|
+
if (options.successUrl) checkoutRequestBody.success_url = options.successUrl;
|
|
3063
|
+
if (options.cancelUrl) checkoutRequestBody.cancel_url = options.cancelUrl;
|
|
2999
3064
|
const checkoutResponse = await fetch(`${baseUrl}/v1/checkouts`, {
|
|
3000
3065
|
method: "POST",
|
|
3001
3066
|
headers,
|
|
@@ -3610,28 +3675,65 @@ function useCustomer() {
|
|
|
3610
3675
|
throw new Error("useCustomer must be used within a RecurProvider");
|
|
3611
3676
|
}
|
|
3612
3677
|
const { customer, subscription, entitlements, isLoading: isLoading2, error, refetch } = context;
|
|
3613
|
-
const
|
|
3614
|
-
(
|
|
3678
|
+
const resolveProduct = (target) => {
|
|
3679
|
+
if (typeof target === "string") {
|
|
3680
|
+
return target;
|
|
3681
|
+
}
|
|
3682
|
+
if ("product" in target) {
|
|
3683
|
+
return target.product;
|
|
3684
|
+
}
|
|
3685
|
+
if ("feature" in target) {
|
|
3686
|
+
throw new Error("Feature-based checks are not yet supported. Coming in Phase 2.");
|
|
3687
|
+
}
|
|
3688
|
+
if ("benefit" in target) {
|
|
3689
|
+
throw new Error("Benefit-based checks are not yet supported. Coming in Phase 2.");
|
|
3690
|
+
}
|
|
3691
|
+
throw new Error("Invalid check target");
|
|
3692
|
+
};
|
|
3693
|
+
const checkSync = React.useCallback(
|
|
3694
|
+
(target) => {
|
|
3695
|
+
const product = resolveProduct(target);
|
|
3615
3696
|
if (!customer) {
|
|
3616
3697
|
return { allowed: false, reason: "no_customer" };
|
|
3617
3698
|
}
|
|
3618
3699
|
if (entitlements.length === 0) {
|
|
3619
|
-
return { allowed: false, reason: "
|
|
3700
|
+
return { allowed: false, reason: "no_entitlement" };
|
|
3620
3701
|
}
|
|
3621
|
-
const
|
|
3622
|
-
(e) => e.product ===
|
|
3702
|
+
const matchedEntitlement = entitlements.find(
|
|
3703
|
+
(e) => e.product === product || e.productId === product
|
|
3623
3704
|
);
|
|
3624
|
-
if (!
|
|
3625
|
-
return { allowed: false, reason: "
|
|
3705
|
+
if (!matchedEntitlement) {
|
|
3706
|
+
return { allowed: false, reason: "not_found" };
|
|
3626
3707
|
}
|
|
3627
3708
|
let matchingSubscription;
|
|
3628
|
-
if (subscription && (subscription.product.slug ===
|
|
3709
|
+
if (subscription && (subscription.product.slug === product || subscription.product.id === product)) {
|
|
3629
3710
|
matchingSubscription = subscription;
|
|
3630
3711
|
}
|
|
3631
|
-
return {
|
|
3712
|
+
return {
|
|
3713
|
+
allowed: true,
|
|
3714
|
+
entitlement: matchedEntitlement,
|
|
3715
|
+
// Backward compatibility
|
|
3716
|
+
subscription: matchingSubscription
|
|
3717
|
+
};
|
|
3632
3718
|
},
|
|
3633
3719
|
[customer, entitlements, subscription]
|
|
3634
3720
|
);
|
|
3721
|
+
const checkLive = React.useCallback(
|
|
3722
|
+
async (target) => {
|
|
3723
|
+
await refetch();
|
|
3724
|
+
return checkSync(target);
|
|
3725
|
+
},
|
|
3726
|
+
[refetch, checkSync]
|
|
3727
|
+
);
|
|
3728
|
+
const check = React.useCallback(
|
|
3729
|
+
(target, options) => {
|
|
3730
|
+
if (options?.live) {
|
|
3731
|
+
return checkLive(target);
|
|
3732
|
+
}
|
|
3733
|
+
return checkSync(target);
|
|
3734
|
+
},
|
|
3735
|
+
[checkSync, checkLive]
|
|
3736
|
+
);
|
|
3635
3737
|
return {
|
|
3636
3738
|
customer,
|
|
3637
3739
|
subscription,
|
package/dist/index.d.cts
CHANGED
|
@@ -570,12 +570,27 @@ interface CustomerIdentifier {
|
|
|
570
570
|
id?: string;
|
|
571
571
|
}
|
|
572
572
|
/**
|
|
573
|
-
* Entitlement status
|
|
573
|
+
* Entitlement status
|
|
574
|
+
*
|
|
575
|
+
* - active: Subscription is active
|
|
576
|
+
* - trialing: Subscription is in trial period
|
|
577
|
+
* - past_due: Subscription payment is past due but still has access
|
|
578
|
+
* - canceled: Subscription canceled but not yet expired
|
|
579
|
+
* - purchased: ONE_TIME product has been paid for
|
|
580
|
+
*/
|
|
581
|
+
type EntitlementStatus = 'active' | 'trialing' | 'past_due' | 'canceled' | 'purchased';
|
|
582
|
+
/**
|
|
583
|
+
* Source of the entitlement
|
|
584
|
+
*
|
|
585
|
+
* - subscription: From an active subscription
|
|
586
|
+
* - order: From a paid ONE_TIME order
|
|
574
587
|
*/
|
|
575
|
-
type
|
|
588
|
+
type EntitlementSource = 'subscription' | 'order';
|
|
576
589
|
/**
|
|
577
590
|
* An entitlement represents a customer's access to a product
|
|
578
|
-
*
|
|
591
|
+
*
|
|
592
|
+
* Computed from active subscriptions and paid ONE_TIME orders.
|
|
593
|
+
* Use the `source` field to determine if access comes from a subscription or purchase.
|
|
579
594
|
*/
|
|
580
595
|
interface Entitlement {
|
|
581
596
|
/**
|
|
@@ -593,13 +608,62 @@ interface Entitlement {
|
|
|
593
608
|
*/
|
|
594
609
|
status: EntitlementStatus;
|
|
595
610
|
/**
|
|
596
|
-
*
|
|
597
|
-
|
|
611
|
+
* Source of the entitlement (subscription or order)
|
|
612
|
+
*/
|
|
613
|
+
source: EntitlementSource;
|
|
614
|
+
/**
|
|
615
|
+
* ID of the source (subscriptionId or orderId)
|
|
616
|
+
* @example 'sub_xyz789' or 'ord_abc123'
|
|
617
|
+
*/
|
|
618
|
+
sourceId: string;
|
|
619
|
+
/**
|
|
620
|
+
* When access was granted (ISO 8601)
|
|
621
|
+
* @example '2024-01-15T10:30:00.000Z'
|
|
622
|
+
*/
|
|
623
|
+
grantedAt: string;
|
|
624
|
+
/**
|
|
625
|
+
* When access expires (ISO 8601), null means permanent access
|
|
626
|
+
* For subscriptions: currentPeriodEnd
|
|
627
|
+
* For ONE_TIME purchases: null (permanent)
|
|
628
|
+
* @example '2024-02-15T10:30:00.000Z' or null
|
|
629
|
+
*/
|
|
630
|
+
expiresAt: string | null;
|
|
631
|
+
/**
|
|
632
|
+
* @deprecated Use sourceId instead. Kept for backward compatibility.
|
|
633
|
+
* Associated subscription ID (only present for subscription entitlements)
|
|
598
634
|
*/
|
|
599
|
-
subscriptionId
|
|
635
|
+
subscriptionId?: string;
|
|
600
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Target for the check() method
|
|
639
|
+
*
|
|
640
|
+
* Can be a string (product slug) or an object with product/feature/benefit
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* // String shorthand
|
|
644
|
+
* check("pro-plan")
|
|
645
|
+
*
|
|
646
|
+
* // Object form
|
|
647
|
+
* check({ product: "pro-plan" })
|
|
648
|
+
*
|
|
649
|
+
* // Future: Feature-based check (Phase 2)
|
|
650
|
+
* check({ feature: "ai-chat", required: 10 })
|
|
651
|
+
*
|
|
652
|
+
* // Future: Benefit check (Phase 2)
|
|
653
|
+
* check({ benefit: "priority-support" })
|
|
654
|
+
*/
|
|
655
|
+
type CheckTarget = string | {
|
|
656
|
+
product: string;
|
|
657
|
+
} | {
|
|
658
|
+
feature: string;
|
|
659
|
+
required?: number;
|
|
660
|
+
} | {
|
|
661
|
+
benefit: string;
|
|
662
|
+
};
|
|
601
663
|
/**
|
|
602
664
|
* Options for the check() method
|
|
665
|
+
*
|
|
666
|
+
* @deprecated Use CheckTarget and CheckLiveOptions instead
|
|
603
667
|
*/
|
|
604
668
|
interface CheckOptions {
|
|
605
669
|
/**
|
|
@@ -608,10 +672,22 @@ interface CheckOptions {
|
|
|
608
672
|
*/
|
|
609
673
|
product: string;
|
|
610
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Options for check() second parameter
|
|
677
|
+
*/
|
|
678
|
+
interface CheckLiveOptions {
|
|
679
|
+
/**
|
|
680
|
+
* If true, fetches fresh data from the API instead of using cache
|
|
681
|
+
* Use this when you need real-time accuracy (e.g., after checkout)
|
|
682
|
+
*
|
|
683
|
+
* @default false
|
|
684
|
+
*/
|
|
685
|
+
live?: boolean;
|
|
686
|
+
}
|
|
611
687
|
/**
|
|
612
688
|
* Reason why access was denied
|
|
613
689
|
*/
|
|
614
|
-
type CheckDeniedReason = 'no_customer' | '
|
|
690
|
+
type CheckDeniedReason = 'no_customer' | 'no_entitlement' | 'not_found' | 'expired' | 'insufficient_balance';
|
|
615
691
|
/**
|
|
616
692
|
* Result of the check() method
|
|
617
693
|
*/
|
|
@@ -625,7 +701,23 @@ interface CheckResult {
|
|
|
625
701
|
*/
|
|
626
702
|
reason?: CheckDeniedReason;
|
|
627
703
|
/**
|
|
628
|
-
*
|
|
704
|
+
* The entitlement granting access (only present when allowed is true)
|
|
705
|
+
* Contains source info (subscription or order) and expiration details
|
|
706
|
+
*/
|
|
707
|
+
entitlement?: Entitlement;
|
|
708
|
+
/**
|
|
709
|
+
* Current balance (for credit/usage-based entitlements)
|
|
710
|
+
* @example 42 (remaining credits)
|
|
711
|
+
*/
|
|
712
|
+
balance?: number;
|
|
713
|
+
/**
|
|
714
|
+
* Whether the entitlement has unlimited access
|
|
715
|
+
* @example true (for unlimited plans)
|
|
716
|
+
*/
|
|
717
|
+
unlimited?: boolean;
|
|
718
|
+
/**
|
|
719
|
+
* @deprecated Use entitlement instead. Kept for backward compatibility.
|
|
720
|
+
* Associated subscription (only present when allowed is true and source is subscription)
|
|
629
721
|
*/
|
|
630
722
|
subscription?: EntitlementSubscription;
|
|
631
723
|
}
|
|
@@ -693,13 +785,38 @@ interface UseCustomerResult {
|
|
|
693
785
|
entitlements: Entitlement[];
|
|
694
786
|
/**
|
|
695
787
|
* Check if customer has access to a specific product
|
|
696
|
-
*
|
|
788
|
+
*
|
|
789
|
+
* Synchronous by default - reads from local cache (zero latency).
|
|
790
|
+
* Use `{ live: true }` option for real-time API check.
|
|
791
|
+
*
|
|
792
|
+
* @param target - Product slug string or check target object
|
|
793
|
+
* @param options - Optional settings like `{ live: true }`
|
|
794
|
+
* @returns CheckResult with allowed status and entitlement if granted
|
|
697
795
|
*
|
|
698
796
|
* @example
|
|
699
|
-
*
|
|
700
|
-
*
|
|
797
|
+
* // String shorthand (recommended)
|
|
798
|
+
* const { allowed } = check("pro-plan");
|
|
799
|
+
*
|
|
800
|
+
* // Object form
|
|
801
|
+
* const { allowed } = check({ product: "pro-plan" });
|
|
802
|
+
*
|
|
803
|
+
* // Real-time check (async)
|
|
804
|
+
* const { allowed } = await check("pro-plan", { live: true });
|
|
805
|
+
*
|
|
806
|
+
* // Access entitlement details
|
|
807
|
+
* const { allowed, entitlement } = check("pro-plan");
|
|
808
|
+
* if (allowed && entitlement?.expiresAt) {
|
|
809
|
+
* console.log(`Access until ${entitlement.expiresAt}`);
|
|
810
|
+
* }
|
|
701
811
|
*/
|
|
702
|
-
check:
|
|
812
|
+
check: {
|
|
813
|
+
(target: CheckTarget, options?: CheckLiveOptions & {
|
|
814
|
+
live?: false;
|
|
815
|
+
}): CheckResult;
|
|
816
|
+
(target: CheckTarget, options: CheckLiveOptions & {
|
|
817
|
+
live: true;
|
|
818
|
+
}): Promise<CheckResult>;
|
|
819
|
+
};
|
|
703
820
|
/**
|
|
704
821
|
* Manually refresh entitlements from the API
|
|
705
822
|
* Call this after checkout to update permissions
|
|
@@ -730,6 +847,7 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
730
847
|
private customStyles;
|
|
731
848
|
private _isInitializing;
|
|
732
849
|
private _initializationAborted;
|
|
850
|
+
private _showTestCardHints;
|
|
733
851
|
private _showCouponInput;
|
|
734
852
|
private _couponCode;
|
|
735
853
|
private _couponLoading;
|
|
@@ -747,6 +865,8 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
747
865
|
private renderLightDOM;
|
|
748
866
|
private getCustomStyles;
|
|
749
867
|
private updateCustomStyles;
|
|
868
|
+
private isTestMode;
|
|
869
|
+
private renderTestCardHints;
|
|
750
870
|
private renderOrderSummary;
|
|
751
871
|
private getIntervalText;
|
|
752
872
|
private getDiscountDescription;
|
|
@@ -1135,6 +1255,8 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1135
1255
|
* your own database or webhook integration. The hook fetches entitlements
|
|
1136
1256
|
* from Recur on mount and provides synchronous access via the check() method.
|
|
1137
1257
|
*
|
|
1258
|
+
* Supports both SUBSCRIPTION and ONE_TIME product types.
|
|
1259
|
+
*
|
|
1138
1260
|
* Must be used within a RecurProvider with a customer identifier:
|
|
1139
1261
|
* ```tsx
|
|
1140
1262
|
* <RecurProvider
|
|
@@ -1153,7 +1275,8 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1153
1275
|
*
|
|
1154
1276
|
* if (isLoading) return <Spinner />;
|
|
1155
1277
|
*
|
|
1156
|
-
*
|
|
1278
|
+
* // String shorthand (recommended)
|
|
1279
|
+
* const { allowed } = check("pro-plan");
|
|
1157
1280
|
*
|
|
1158
1281
|
* if (!allowed) {
|
|
1159
1282
|
* return <UpgradePrompt />;
|
|
@@ -1163,6 +1286,43 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1163
1286
|
* }
|
|
1164
1287
|
* ```
|
|
1165
1288
|
*
|
|
1289
|
+
* ## Check with Entitlement Details
|
|
1290
|
+
* @example
|
|
1291
|
+
* ```tsx
|
|
1292
|
+
* function SubscriptionInfo() {
|
|
1293
|
+
* const { check } = useCustomer();
|
|
1294
|
+
*
|
|
1295
|
+
* const { allowed, entitlement } = check("pro-plan");
|
|
1296
|
+
*
|
|
1297
|
+
* if (!allowed) return <UpgradePrompt />;
|
|
1298
|
+
*
|
|
1299
|
+
* return (
|
|
1300
|
+
* <div>
|
|
1301
|
+
* <p>Status: {entitlement?.status}</p>
|
|
1302
|
+
* <p>Source: {entitlement?.source}</p>
|
|
1303
|
+
* {entitlement?.expiresAt && (
|
|
1304
|
+
* <p>Expires: {new Date(entitlement.expiresAt).toLocaleDateString()}</p>
|
|
1305
|
+
* )}
|
|
1306
|
+
* </div>
|
|
1307
|
+
* );
|
|
1308
|
+
* }
|
|
1309
|
+
* ```
|
|
1310
|
+
*
|
|
1311
|
+
* ## Real-time Check (After Payment)
|
|
1312
|
+
* @example
|
|
1313
|
+
* ```tsx
|
|
1314
|
+
* async function verifyAccess() {
|
|
1315
|
+
* const { check } = useCustomer();
|
|
1316
|
+
*
|
|
1317
|
+
* // Force fresh check from API
|
|
1318
|
+
* const { allowed } = await check("pro-plan", { live: true });
|
|
1319
|
+
*
|
|
1320
|
+
* if (allowed) {
|
|
1321
|
+
* console.log("Access confirmed!");
|
|
1322
|
+
* }
|
|
1323
|
+
* }
|
|
1324
|
+
* ```
|
|
1325
|
+
*
|
|
1166
1326
|
* ## Display Subscription Status
|
|
1167
1327
|
* @example
|
|
1168
1328
|
* ```tsx
|
|
@@ -1225,8 +1385,8 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1225
1385
|
* @returns {UseCustomerResult} Object containing:
|
|
1226
1386
|
* - `customer` - Customer info (null if not found)
|
|
1227
1387
|
* - `subscription` - Most recent active subscription (null if none)
|
|
1228
|
-
* - `entitlements` - Array of all active entitlements
|
|
1229
|
-
* - `check(options)` -
|
|
1388
|
+
* - `entitlements` - Array of all active entitlements (from subscriptions and purchases)
|
|
1389
|
+
* - `check(target, options?)` - Permission check (sync or async with `{ live: true }`)
|
|
1230
1390
|
* - `refetch()` - Manual refresh (debounced)
|
|
1231
1391
|
* - `isLoading` - Whether data is being fetched
|
|
1232
1392
|
* - `error` - Error from last fetch attempt
|