recur-tw 0.9.9 → 0.9.11

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 CHANGED
@@ -1035,7 +1035,7 @@ var init_payment_form_shadow = __esm({
1035
1035
  var payment_form_light_default;
1036
1036
  var init_payment_form_light = __esm({
1037
1037
  "src/components/styles/payment-form-light.css"() {
1038
- 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}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\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: inline-flex;\n align-items: center;\n gap: 6px;\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 gap: 8px;\n width: 100%;\n}\n\n.recur-coupon__input {\n flex: 1;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n text-transform: uppercase;\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:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\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__btn--cancel {\n background: transparent;\n color: #718096;\n border: 1px solid #d1d5db;\n}\n\n.recur-coupon__btn--cancel:hover {\n background: #f7fafc;\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 {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #16a34a;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.recur-coupon__applied-details {\n min-width: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #16a34a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 12px;\n color: #718096;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #16a34a;\n white-space: nowrap;\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}\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 padding: 10px 12px;\n font-size: 16px;\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 outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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";
1038
+ 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 {\n flex: 1;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n text-transform: uppercase;\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:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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__btn--cancel {\n background: transparent;\n color: #718096;\n border: 1px solid #d1d5db;\n}\n\n.recur-coupon__btn--cancel:hover {\n background: #f7fafc;\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 {\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 padding: 10px 12px;\n font-size: 16px;\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 outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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";
1039
1039
  }
1040
1040
  });
1041
1041
 
@@ -1224,7 +1224,32 @@ var init_payment_form = __esm({
1224
1224
  const amount = this.getAttribute("amount");
1225
1225
  const interval = this.getAttribute("interval");
1226
1226
  if (!productName || !amount) {
1227
- return litHtml.html`<div class="recur-order-summary" style="display: none;"></div>`;
1227
+ const isSubscription2 = !!interval;
1228
+ return litHtml.html`
1229
+ <div class="recur-order-summary" id="${this.containerId}-order-summary">
1230
+ <div class="recur-order-summary__row">
1231
+ <span class="recur-order-summary__label">${isSubscription2 ? "\u8A02\u95B1\u65B9\u6848" : "\u5546\u54C1"}</span>
1232
+ <span class="recur-order-summary__value recur-skeleton" style="width: 100px; height: 20px;"></span>
1233
+ </div>
1234
+ ${isSubscription2 ? litHtml.html`
1235
+ <div class="recur-order-summary__row">
1236
+ <span class="recur-order-summary__label">計費週期</span>
1237
+ <span class="recur-order-summary__value recur-skeleton" style="width: 40px; height: 20px;"></span>
1238
+ </div>
1239
+ ` : litHtml.nothing}
1240
+ <div class="recur-order-summary__row">
1241
+ <span class="recur-order-summary__label">小計</span>
1242
+ <span class="recur-order-summary__value recur-skeleton" style="width: 80px; height: 20px;"></span>
1243
+ </div>
1244
+ <div class="recur-coupon">
1245
+ <span class="recur-skeleton" style="width: 80px; height: 20px;"></span>
1246
+ </div>
1247
+ <div class="recur-order-summary__row recur-order-summary__row--total">
1248
+ <span class="recur-order-summary__label">總計</span>
1249
+ <span class="recur-order-summary__total recur-skeleton" style="width: 100px; height: 26px;"></span>
1250
+ </div>
1251
+ </div>
1252
+ `;
1228
1253
  }
1229
1254
  const intervalMap = {
1230
1255
  "day": "\u65E5",
@@ -1236,35 +1261,33 @@ var init_payment_form = __esm({
1236
1261
  const originalAmount = Number(amount);
1237
1262
  const discountAmount = this._appliedCoupon?.discountAmount || 0;
1238
1263
  const finalAmount = originalAmount - discountAmount;
1264
+ const isSubscription = !!periodText;
1239
1265
  return litHtml.html`
1240
1266
  <div class="recur-order-summary" id="${this.containerId}-order-summary">
1241
1267
  <div class="recur-order-summary__row">
1242
- <span class="recur-order-summary__label">訂閱方案</span>
1268
+ <span class="recur-order-summary__label">${isSubscription ? "\u8A02\u95B1\u65B9\u6848" : "\u5546\u54C1"}</span>
1243
1269
  <span class="recur-order-summary__value">${productName}</span>
1244
1270
  </div>
1245
1271
 
1246
- ${periodText ? litHtml.html`
1272
+ ${isSubscription ? litHtml.html`
1247
1273
  <div class="recur-order-summary__row">
1248
1274
  <span class="recur-order-summary__label">計費週期</span>
1249
1275
  <span class="recur-order-summary__value">每${periodText}</span>
1250
1276
  </div>
1251
1277
  ` : litHtml.nothing}
1252
1278
 
1279
+ <!-- 小計 (Subtotal) - always show -->
1280
+ <div class="recur-order-summary__row">
1281
+ <span class="recur-order-summary__label">小計</span>
1282
+ <span class="recur-order-summary__value">NT$ ${originalAmount.toLocaleString()}</span>
1283
+ </div>
1284
+
1285
+ <!-- 優惠碼區塊 (Coupon section with discount amount) -->
1253
1286
  <div class="recur-coupon" id="${this.containerId}-coupon">
1254
1287
  ${this.renderCoupon()}
1255
1288
  </div>
1256
1289
 
1257
- ${discountAmount > 0 ? litHtml.html`
1258
- <div class="recur-order-summary__row">
1259
- <span class="recur-order-summary__label">小計</span>
1260
- <span class="recur-order-summary__value">NT$ ${originalAmount.toLocaleString()}</span>
1261
- </div>
1262
- <div class="recur-order-summary__row recur-order-summary__row--discount">
1263
- <span class="recur-order-summary__label">折扣</span>
1264
- <span class="recur-order-summary__value">-NT$ ${discountAmount.toLocaleString()}</span>
1265
- </div>
1266
- ` : litHtml.nothing}
1267
-
1290
+ <!-- 總計 (Total) -->
1268
1291
  <div class="recur-order-summary__row recur-order-summary__row--total">
1269
1292
  <span class="recur-order-summary__label">總計</span>
1270
1293
  <span class="recur-order-summary__total">NT$ ${finalAmount.toLocaleString()}</span>
@@ -1278,26 +1301,24 @@ var init_payment_form = __esm({
1278
1301
  <div class="recur-coupon__applied">
1279
1302
  <div class="recur-coupon__applied-info">
1280
1303
  ${icons.tagApplied}
1281
- <div class="recur-coupon__applied-details">
1282
- <div class="recur-coupon__applied-name">
1283
- ${this._appliedCoupon.couponName || this._appliedCoupon.code}
1284
- </div>
1285
- ${this._appliedCoupon.couponName ? litHtml.html`
1286
- <div class="recur-coupon__applied-code">代碼:${this._appliedCoupon.code}</div>
1287
- ` : litHtml.nothing}
1288
- </div>
1304
+ ${this._appliedCoupon.couponName ? litHtml.html`
1305
+ <span class="recur-coupon__applied-name">${this._appliedCoupon.couponName}</span>
1306
+ <span class="recur-coupon__applied-code">(${this._appliedCoupon.code})</span>
1307
+ ` : litHtml.html`
1308
+ <span class="recur-coupon__applied-name">${this._appliedCoupon.code}</span>
1309
+ `}
1310
+ <button
1311
+ type="button"
1312
+ class="recur-coupon__remove"
1313
+ ?disabled=${this._couponLoading}
1314
+ @click=${() => this.removeCoupon()}
1315
+ >
1316
+ ${this._couponLoading ? icons.spinner : icons.close}
1317
+ </button>
1289
1318
  </div>
1290
1319
  <span class="recur-coupon__applied-amount">
1291
1320
  -NT$ ${this._appliedCoupon.discountAmount.toLocaleString()}
1292
1321
  </span>
1293
- <button
1294
- type="button"
1295
- class="recur-coupon__remove"
1296
- ?disabled=${this._couponLoading}
1297
- @click=${() => this.removeCoupon()}
1298
- >
1299
- ${this._couponLoading ? icons.spinner : icons.close}
1300
- </button>
1301
1322
  </div>
1302
1323
  `;
1303
1324
  }
@@ -1386,7 +1407,8 @@ var init_payment_form = __esm({
1386
1407
  }
1387
1408
  // Direct DOM manipulation for coupon section updates (avoids lit-html tracking issues)
1388
1409
  updateCouponSectionDOM() {
1389
- const container = document.getElementById(`${this.containerId}-coupon`);
1410
+ const containerId = `${this.containerId}-coupon`;
1411
+ const container = document.getElementById(containerId);
1390
1412
  if (!container) return;
1391
1413
  if (this._appliedCoupon) {
1392
1414
  container.innerHTML = this.getCouponAppliedHTML();
@@ -1443,22 +1465,19 @@ var init_payment_form = __esm({
1443
1465
  }
1444
1466
  getCouponAppliedHTML() {
1445
1467
  const coupon = this._appliedCoupon;
1446
- const codeHTML = coupon.couponName ? `<div class="recur-coupon__applied-code">\u4EE3\u78BC\uFF1A${coupon.code}</div>` : "";
1468
+ const nameHTML = coupon.couponName ? `<span class="recur-coupon__applied-name">${coupon.couponName}</span><span class="recur-coupon__applied-code">(${coupon.code})</span>` : `<span class="recur-coupon__applied-name">${coupon.code}</span>`;
1447
1469
  return `
1448
1470
  <div class="recur-coupon__applied">
1449
1471
  <div class="recur-coupon__applied-info">
1450
1472
  <svg class="recur-coupon__applied-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1451
1473
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
1452
1474
  </svg>
1453
- <div class="recur-coupon__applied-details">
1454
- <div class="recur-coupon__applied-name">${coupon.couponName || coupon.code}</div>
1455
- ${codeHTML}
1456
- </div>
1475
+ ${nameHTML}
1476
+ <button type="button" class="recur-coupon__remove" ${this._couponLoading ? "disabled" : ""}>
1477
+ ${this._couponLoading ? '<span class="recur-coupon__spinner"></span>' : '<svg class="recur-coupon__remove-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'}
1478
+ </button>
1457
1479
  </div>
1458
1480
  <span class="recur-coupon__applied-amount">-NT$ ${coupon.discountAmount.toLocaleString()}</span>
1459
- <button type="button" class="recur-coupon__remove" ${this._couponLoading ? "disabled" : ""}>
1460
- ${this._couponLoading ? '<span class="recur-coupon__spinner"></span>' : '<svg class="recur-coupon__remove-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'}
1461
- </button>
1462
1481
  </div>
1463
1482
  `;
1464
1483
  }
@@ -1485,31 +1504,7 @@ var init_payment_form = __esm({
1485
1504
  const amount = Number(this.getAttribute("amount") || 0);
1486
1505
  const discountAmount = this._appliedCoupon?.discountAmount || 0;
1487
1506
  const finalAmount = amount - discountAmount;
1488
- let discountRow = summaryContainer.querySelector(".recur-order-summary__row--discount");
1489
1507
  const totalRow = summaryContainer.querySelector(".recur-order-summary__row--total");
1490
- if (discountAmount > 0) {
1491
- if (!discountRow && totalRow) {
1492
- const subtotalHTML = `
1493
- <div class="recur-order-summary__row">
1494
- <span class="recur-order-summary__label">\u5C0F\u8A08</span>
1495
- <span class="recur-order-summary__value">NT$ ${amount.toLocaleString()}</span>
1496
- </div>
1497
- `;
1498
- const discountHTML = `
1499
- <div class="recur-order-summary__row recur-order-summary__row--discount">
1500
- <span class="recur-order-summary__label">\u6298\u6263</span>
1501
- <span class="recur-order-summary__value">-NT$ ${discountAmount.toLocaleString()}</span>
1502
- </div>
1503
- `;
1504
- totalRow.insertAdjacentHTML("beforebegin", subtotalHTML + discountHTML);
1505
- }
1506
- } else {
1507
- const subtotalRow = summaryContainer.querySelector(".recur-order-summary__row:not(.recur-order-summary__row--total):last-of-type");
1508
- discountRow?.remove();
1509
- if (subtotalRow?.querySelector(".recur-order-summary__label")?.textContent === "\u5C0F\u8A08") {
1510
- subtotalRow.remove();
1511
- }
1512
- }
1513
1508
  const totalValue = totalRow?.querySelector(".recur-order-summary__total");
1514
1509
  if (totalValue) {
1515
1510
  totalValue.textContent = `NT$ ${finalAmount.toLocaleString()}`;
@@ -1537,7 +1532,7 @@ var init_payment_form = __esm({
1537
1532
  this._couponError = null;
1538
1533
  this.updateCouponSectionDOM();
1539
1534
  try {
1540
- const response = await fetch(`${apiBaseUrl}/api/checkout/${checkoutId}/coupon`, {
1535
+ const response = await fetch(`${apiBaseUrl}/v1/checkouts/${checkoutId}/coupon`, {
1541
1536
  method: "POST",
1542
1537
  headers: { "Content-Type": "application/json" },
1543
1538
  body: JSON.stringify({ code })
@@ -1583,7 +1578,7 @@ var init_payment_form = __esm({
1583
1578
  this._couponLoading = true;
1584
1579
  this.updateCouponSectionDOM();
1585
1580
  try {
1586
- const response = await fetch(`${apiBaseUrl}/api/checkout/${checkoutId}/coupon`, {
1581
+ const response = await fetch(`${apiBaseUrl}/v1/checkouts/${checkoutId}/coupon`, {
1587
1582
  method: "DELETE"
1588
1583
  });
1589
1584
  if (!response.ok) {
@@ -2565,7 +2560,7 @@ function toCamelCase(obj) {
2565
2560
 
2566
2561
  // package.json
2567
2562
  var package_default = {
2568
- version: "0.9.8"};
2563
+ version: "0.9.11"};
2569
2564
  var SDK_VERSION = package_default.version;
2570
2565
  var SDK_TYPE = "react";
2571
2566
  var RecurContext = React.createContext(null);
@@ -2881,6 +2876,10 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2881
2876
  container.innerHTML = "";
2882
2877
  const paymentForm = document.createElement("recur-payment-form");
2883
2878
  paymentForm.setAttribute("container-id", containerElementId);
2879
+ if (checkoutResult.checkout?.id) {
2880
+ paymentForm.setAttribute("checkout-id", checkoutResult.checkout.id);
2881
+ }
2882
+ paymentForm.setAttribute("api-base-url", baseUrl);
2884
2883
  if (options.customerName) {
2885
2884
  paymentForm.setAttribute("customer-name", options.customerName);
2886
2885
  }
package/dist/index.js CHANGED
@@ -1029,7 +1029,7 @@ var init_payment_form_shadow = __esm({
1029
1029
  var payment_form_light_default;
1030
1030
  var init_payment_form_light = __esm({
1031
1031
  "src/components/styles/payment-form-light.css"() {
1032
- 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}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\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: inline-flex;\n align-items: center;\n gap: 6px;\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 gap: 8px;\n width: 100%;\n}\n\n.recur-coupon__input {\n flex: 1;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n text-transform: uppercase;\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:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\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__btn--cancel {\n background: transparent;\n color: #718096;\n border: 1px solid #d1d5db;\n}\n\n.recur-coupon__btn--cancel:hover {\n background: #f7fafc;\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 {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #16a34a;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.recur-coupon__applied-details {\n min-width: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #16a34a;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 12px;\n color: #718096;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #16a34a;\n white-space: nowrap;\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}\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 padding: 10px 12px;\n font-size: 16px;\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 outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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";
1032
+ 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 {\n flex: 1;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n text-transform: uppercase;\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:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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__btn--cancel {\n background: transparent;\n color: #718096;\n border: 1px solid #d1d5db;\n}\n\n.recur-coupon__btn--cancel:hover {\n background: #f7fafc;\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 {\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 padding: 10px 12px;\n font-size: 16px;\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 outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\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";
1033
1033
  }
1034
1034
  });
1035
1035
 
@@ -1218,7 +1218,32 @@ var init_payment_form = __esm({
1218
1218
  const amount = this.getAttribute("amount");
1219
1219
  const interval = this.getAttribute("interval");
1220
1220
  if (!productName || !amount) {
1221
- return html`<div class="recur-order-summary" style="display: none;"></div>`;
1221
+ const isSubscription2 = !!interval;
1222
+ return html`
1223
+ <div class="recur-order-summary" id="${this.containerId}-order-summary">
1224
+ <div class="recur-order-summary__row">
1225
+ <span class="recur-order-summary__label">${isSubscription2 ? "\u8A02\u95B1\u65B9\u6848" : "\u5546\u54C1"}</span>
1226
+ <span class="recur-order-summary__value recur-skeleton" style="width: 100px; height: 20px;"></span>
1227
+ </div>
1228
+ ${isSubscription2 ? html`
1229
+ <div class="recur-order-summary__row">
1230
+ <span class="recur-order-summary__label">計費週期</span>
1231
+ <span class="recur-order-summary__value recur-skeleton" style="width: 40px; height: 20px;"></span>
1232
+ </div>
1233
+ ` : nothing}
1234
+ <div class="recur-order-summary__row">
1235
+ <span class="recur-order-summary__label">小計</span>
1236
+ <span class="recur-order-summary__value recur-skeleton" style="width: 80px; height: 20px;"></span>
1237
+ </div>
1238
+ <div class="recur-coupon">
1239
+ <span class="recur-skeleton" style="width: 80px; height: 20px;"></span>
1240
+ </div>
1241
+ <div class="recur-order-summary__row recur-order-summary__row--total">
1242
+ <span class="recur-order-summary__label">總計</span>
1243
+ <span class="recur-order-summary__total recur-skeleton" style="width: 100px; height: 26px;"></span>
1244
+ </div>
1245
+ </div>
1246
+ `;
1222
1247
  }
1223
1248
  const intervalMap = {
1224
1249
  "day": "\u65E5",
@@ -1230,35 +1255,33 @@ var init_payment_form = __esm({
1230
1255
  const originalAmount = Number(amount);
1231
1256
  const discountAmount = this._appliedCoupon?.discountAmount || 0;
1232
1257
  const finalAmount = originalAmount - discountAmount;
1258
+ const isSubscription = !!periodText;
1233
1259
  return html`
1234
1260
  <div class="recur-order-summary" id="${this.containerId}-order-summary">
1235
1261
  <div class="recur-order-summary__row">
1236
- <span class="recur-order-summary__label">訂閱方案</span>
1262
+ <span class="recur-order-summary__label">${isSubscription ? "\u8A02\u95B1\u65B9\u6848" : "\u5546\u54C1"}</span>
1237
1263
  <span class="recur-order-summary__value">${productName}</span>
1238
1264
  </div>
1239
1265
 
1240
- ${periodText ? html`
1266
+ ${isSubscription ? html`
1241
1267
  <div class="recur-order-summary__row">
1242
1268
  <span class="recur-order-summary__label">計費週期</span>
1243
1269
  <span class="recur-order-summary__value">每${periodText}</span>
1244
1270
  </div>
1245
1271
  ` : nothing}
1246
1272
 
1273
+ <!-- 小計 (Subtotal) - always show -->
1274
+ <div class="recur-order-summary__row">
1275
+ <span class="recur-order-summary__label">小計</span>
1276
+ <span class="recur-order-summary__value">NT$ ${originalAmount.toLocaleString()}</span>
1277
+ </div>
1278
+
1279
+ <!-- 優惠碼區塊 (Coupon section with discount amount) -->
1247
1280
  <div class="recur-coupon" id="${this.containerId}-coupon">
1248
1281
  ${this.renderCoupon()}
1249
1282
  </div>
1250
1283
 
1251
- ${discountAmount > 0 ? html`
1252
- <div class="recur-order-summary__row">
1253
- <span class="recur-order-summary__label">小計</span>
1254
- <span class="recur-order-summary__value">NT$ ${originalAmount.toLocaleString()}</span>
1255
- </div>
1256
- <div class="recur-order-summary__row recur-order-summary__row--discount">
1257
- <span class="recur-order-summary__label">折扣</span>
1258
- <span class="recur-order-summary__value">-NT$ ${discountAmount.toLocaleString()}</span>
1259
- </div>
1260
- ` : nothing}
1261
-
1284
+ <!-- 總計 (Total) -->
1262
1285
  <div class="recur-order-summary__row recur-order-summary__row--total">
1263
1286
  <span class="recur-order-summary__label">總計</span>
1264
1287
  <span class="recur-order-summary__total">NT$ ${finalAmount.toLocaleString()}</span>
@@ -1272,26 +1295,24 @@ var init_payment_form = __esm({
1272
1295
  <div class="recur-coupon__applied">
1273
1296
  <div class="recur-coupon__applied-info">
1274
1297
  ${icons.tagApplied}
1275
- <div class="recur-coupon__applied-details">
1276
- <div class="recur-coupon__applied-name">
1277
- ${this._appliedCoupon.couponName || this._appliedCoupon.code}
1278
- </div>
1279
- ${this._appliedCoupon.couponName ? html`
1280
- <div class="recur-coupon__applied-code">代碼:${this._appliedCoupon.code}</div>
1281
- ` : nothing}
1282
- </div>
1298
+ ${this._appliedCoupon.couponName ? html`
1299
+ <span class="recur-coupon__applied-name">${this._appliedCoupon.couponName}</span>
1300
+ <span class="recur-coupon__applied-code">(${this._appliedCoupon.code})</span>
1301
+ ` : html`
1302
+ <span class="recur-coupon__applied-name">${this._appliedCoupon.code}</span>
1303
+ `}
1304
+ <button
1305
+ type="button"
1306
+ class="recur-coupon__remove"
1307
+ ?disabled=${this._couponLoading}
1308
+ @click=${() => this.removeCoupon()}
1309
+ >
1310
+ ${this._couponLoading ? icons.spinner : icons.close}
1311
+ </button>
1283
1312
  </div>
1284
1313
  <span class="recur-coupon__applied-amount">
1285
1314
  -NT$ ${this._appliedCoupon.discountAmount.toLocaleString()}
1286
1315
  </span>
1287
- <button
1288
- type="button"
1289
- class="recur-coupon__remove"
1290
- ?disabled=${this._couponLoading}
1291
- @click=${() => this.removeCoupon()}
1292
- >
1293
- ${this._couponLoading ? icons.spinner : icons.close}
1294
- </button>
1295
1316
  </div>
1296
1317
  `;
1297
1318
  }
@@ -1380,7 +1401,8 @@ var init_payment_form = __esm({
1380
1401
  }
1381
1402
  // Direct DOM manipulation for coupon section updates (avoids lit-html tracking issues)
1382
1403
  updateCouponSectionDOM() {
1383
- const container = document.getElementById(`${this.containerId}-coupon`);
1404
+ const containerId = `${this.containerId}-coupon`;
1405
+ const container = document.getElementById(containerId);
1384
1406
  if (!container) return;
1385
1407
  if (this._appliedCoupon) {
1386
1408
  container.innerHTML = this.getCouponAppliedHTML();
@@ -1437,22 +1459,19 @@ var init_payment_form = __esm({
1437
1459
  }
1438
1460
  getCouponAppliedHTML() {
1439
1461
  const coupon = this._appliedCoupon;
1440
- const codeHTML = coupon.couponName ? `<div class="recur-coupon__applied-code">\u4EE3\u78BC\uFF1A${coupon.code}</div>` : "";
1462
+ const nameHTML = coupon.couponName ? `<span class="recur-coupon__applied-name">${coupon.couponName}</span><span class="recur-coupon__applied-code">(${coupon.code})</span>` : `<span class="recur-coupon__applied-name">${coupon.code}</span>`;
1441
1463
  return `
1442
1464
  <div class="recur-coupon__applied">
1443
1465
  <div class="recur-coupon__applied-info">
1444
1466
  <svg class="recur-coupon__applied-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1445
1467
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
1446
1468
  </svg>
1447
- <div class="recur-coupon__applied-details">
1448
- <div class="recur-coupon__applied-name">${coupon.couponName || coupon.code}</div>
1449
- ${codeHTML}
1450
- </div>
1469
+ ${nameHTML}
1470
+ <button type="button" class="recur-coupon__remove" ${this._couponLoading ? "disabled" : ""}>
1471
+ ${this._couponLoading ? '<span class="recur-coupon__spinner"></span>' : '<svg class="recur-coupon__remove-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'}
1472
+ </button>
1451
1473
  </div>
1452
1474
  <span class="recur-coupon__applied-amount">-NT$ ${coupon.discountAmount.toLocaleString()}</span>
1453
- <button type="button" class="recur-coupon__remove" ${this._couponLoading ? "disabled" : ""}>
1454
- ${this._couponLoading ? '<span class="recur-coupon__spinner"></span>' : '<svg class="recur-coupon__remove-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'}
1455
- </button>
1456
1475
  </div>
1457
1476
  `;
1458
1477
  }
@@ -1479,31 +1498,7 @@ var init_payment_form = __esm({
1479
1498
  const amount = Number(this.getAttribute("amount") || 0);
1480
1499
  const discountAmount = this._appliedCoupon?.discountAmount || 0;
1481
1500
  const finalAmount = amount - discountAmount;
1482
- let discountRow = summaryContainer.querySelector(".recur-order-summary__row--discount");
1483
1501
  const totalRow = summaryContainer.querySelector(".recur-order-summary__row--total");
1484
- if (discountAmount > 0) {
1485
- if (!discountRow && totalRow) {
1486
- const subtotalHTML = `
1487
- <div class="recur-order-summary__row">
1488
- <span class="recur-order-summary__label">\u5C0F\u8A08</span>
1489
- <span class="recur-order-summary__value">NT$ ${amount.toLocaleString()}</span>
1490
- </div>
1491
- `;
1492
- const discountHTML = `
1493
- <div class="recur-order-summary__row recur-order-summary__row--discount">
1494
- <span class="recur-order-summary__label">\u6298\u6263</span>
1495
- <span class="recur-order-summary__value">-NT$ ${discountAmount.toLocaleString()}</span>
1496
- </div>
1497
- `;
1498
- totalRow.insertAdjacentHTML("beforebegin", subtotalHTML + discountHTML);
1499
- }
1500
- } else {
1501
- const subtotalRow = summaryContainer.querySelector(".recur-order-summary__row:not(.recur-order-summary__row--total):last-of-type");
1502
- discountRow?.remove();
1503
- if (subtotalRow?.querySelector(".recur-order-summary__label")?.textContent === "\u5C0F\u8A08") {
1504
- subtotalRow.remove();
1505
- }
1506
- }
1507
1502
  const totalValue = totalRow?.querySelector(".recur-order-summary__total");
1508
1503
  if (totalValue) {
1509
1504
  totalValue.textContent = `NT$ ${finalAmount.toLocaleString()}`;
@@ -1531,7 +1526,7 @@ var init_payment_form = __esm({
1531
1526
  this._couponError = null;
1532
1527
  this.updateCouponSectionDOM();
1533
1528
  try {
1534
- const response = await fetch(`${apiBaseUrl}/api/checkout/${checkoutId}/coupon`, {
1529
+ const response = await fetch(`${apiBaseUrl}/v1/checkouts/${checkoutId}/coupon`, {
1535
1530
  method: "POST",
1536
1531
  headers: { "Content-Type": "application/json" },
1537
1532
  body: JSON.stringify({ code })
@@ -1577,7 +1572,7 @@ var init_payment_form = __esm({
1577
1572
  this._couponLoading = true;
1578
1573
  this.updateCouponSectionDOM();
1579
1574
  try {
1580
- const response = await fetch(`${apiBaseUrl}/api/checkout/${checkoutId}/coupon`, {
1575
+ const response = await fetch(`${apiBaseUrl}/v1/checkouts/${checkoutId}/coupon`, {
1581
1576
  method: "DELETE"
1582
1577
  });
1583
1578
  if (!response.ok) {
@@ -2559,7 +2554,7 @@ function toCamelCase(obj) {
2559
2554
 
2560
2555
  // package.json
2561
2556
  var package_default = {
2562
- version: "0.9.8"};
2557
+ version: "0.9.11"};
2563
2558
  var SDK_VERSION = package_default.version;
2564
2559
  var SDK_TYPE = "react";
2565
2560
  var RecurContext = createContext(null);
@@ -2875,6 +2870,10 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2875
2870
  container.innerHTML = "";
2876
2871
  const paymentForm = document.createElement("recur-payment-form");
2877
2872
  paymentForm.setAttribute("container-id", containerElementId);
2873
+ if (checkoutResult.checkout?.id) {
2874
+ paymentForm.setAttribute("checkout-id", checkoutResult.checkout.id);
2875
+ }
2876
+ paymentForm.setAttribute("api-base-url", baseUrl);
2878
2877
  if (options.customerName) {
2879
2878
  paymentForm.setAttribute("customer-name", options.customerName);
2880
2879
  }
package/dist/recur.umd.js CHANGED
@@ -211,7 +211,7 @@
211
211
  </button>
212
212
  `:""}
213
213
  </div>
214
- `,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",ie)});var He={};C(He,{RecurSkeletonLoader:()=>se});var se,Ne=k(()=>{"use strict";se=class extends HTMLElement{static get observedAttributes(){return["type"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get type(){let e=this.getAttribute("type");return e==="list"||e==="card"?e:"payment-form"}renderPaymentFormSkeleton(){return`
214
+ `,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",ie)});var He={};C(He,{RecurSkeletonLoader:()=>se});var se,Be=k(()=>{"use strict";se=class extends HTMLElement{static get observedAttributes(){return["type"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get type(){let e=this.getAttribute("type");return e==="list"||e==="card"?e:"payment-form"}renderPaymentFormSkeleton(){return`
215
215
  <div class="skeleton-field">
216
216
  <div class="skeleton-label"></div>
217
217
  <div class="skeleton-input"></div>
@@ -381,9 +381,9 @@
381
381
  </style>
382
382
 
383
383
  ${e}
384
- `}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",se)});function We(n,e){if(!_e(n)||!n.hasOwnProperty("raw"))throw Error("invalid template strings array");return Be!==void 0?Be.createHTML(e):e}function U(n,e,t=n,r){if(e===V)return e;let o=r!==void 0?t._$Co?.[r]:t._$Cl,i=q(e)?void 0:e._$litDirective$;return o?.constructor!==i&&(o?._$AO?.(!1),i===void 0?o=void 0:(o=new i(n),o._$AT(n,t,r)),r!==void 0?(t._$Co??(t._$Co=[]))[r]=o:t._$Cl=o),o!==void 0&&(e=U(n,o._$AS(n,e.values),o,r)),e}var L,ne,Be,qe,I,Ve,Et,M,F,q,_e,Ct,be,j,ze,Oe,D,Ke,je,Ye,Ee,m,Ft,qt,V,f,Fe,P,St,Y,ye,W,H,ve,ke,we,xe,Tt,X,Ce=k(()=>{"use strict";L=globalThis,ne=L.trustedTypes,Be=ne?ne.createPolicy("lit-html",{createHTML:n=>n}):void 0,qe="$lit$",I=`lit$${Math.random().toFixed(9).slice(2)}$`,Ve="?"+I,Et=`<${Ve}>`,M=L.document===void 0?{createTreeWalker:()=>({})}:document,F=()=>M.createComment(""),q=n=>n===null||typeof n!="object"&&typeof n!="function",_e=Array.isArray,Ct=n=>_e(n)||typeof n?.[Symbol.iterator]=="function",be=`[
384
+ `}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",se)});function We(n,e){if(!_e(n)||!n.hasOwnProperty("raw"))throw Error("invalid template strings array");return Ne!==void 0?Ne.createHTML(e):e}function U(n,e,t=n,r){if(e===V)return e;let o=r!==void 0?t._$Co?.[r]:t._$Cl,i=q(e)?void 0:e._$litDirective$;return o?.constructor!==i&&(o?._$AO?.(!1),i===void 0?o=void 0:(o=new i(n),o._$AT(n,t,r)),r!==void 0?(t._$Co??(t._$Co=[]))[r]=o:t._$Cl=o),o!==void 0&&(e=U(n,o._$AS(n,e.values),o,r)),e}var L,ne,Ne,qe,I,Ve,Et,M,F,q,_e,Ct,be,j,ze,Oe,D,Ke,je,Ye,Ee,m,Ft,qt,V,b,Fe,P,St,Y,ye,W,H,ve,ke,we,xe,Tt,X,Ce=k(()=>{"use strict";L=globalThis,ne=L.trustedTypes,Ne=ne?ne.createPolicy("lit-html",{createHTML:n=>n}):void 0,qe="$lit$",I=`lit$${Math.random().toFixed(9).slice(2)}$`,Ve="?"+I,Et=`<${Ve}>`,M=L.document===void 0?{createTreeWalker:()=>({})}:document,F=()=>M.createComment(""),q=n=>n===null||typeof n!="object"&&typeof n!="function",_e=Array.isArray,Ct=n=>_e(n)||typeof n?.[Symbol.iterator]=="function",be=`[
385
385
  \f\r]`,j=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,ze=/-->/g,Oe=/>/g,D=RegExp(`>|${be}(?:([^\\s"'>=/]+)(${be}*=${be}*(?:[^
386
- \f\r"'\`<>=]|("|')|))|$)`,"g"),Ke=/'/g,je=/"/g,Ye=/^(?:script|style|textarea|title)$/i,Ee=n=>(e,...t)=>({_$litType$:n,strings:e,values:t}),m=Ee(1),Ft=Ee(2),qt=Ee(3),V=Symbol.for("lit-noChange"),f=Symbol.for("lit-nothing"),Fe=new WeakMap,P=M.createTreeWalker(M,129);St=(n,e)=>{let t=n.length-1,r=[],o,i=e===2?"<svg>":e===3?"<math>":"",s=j;for(let a=0;a<t;a++){let c=n[a],d,p,h=-1,x=0;for(;x<c.length&&(s.lastIndex=x,p=s.exec(c),p!==null);)x=s.lastIndex,s===j?p[1]==="!--"?s=ze:p[1]!==void 0?s=Oe:p[2]!==void 0?(Ye.test(p[2])&&(o=RegExp("</"+p[2],"g")),s=D):p[3]!==void 0&&(s=D):s===D?p[0]===">"?(s=o??j,h=-1):p[1]===void 0?h=-2:(h=s.lastIndex-p[2].length,d=p[1],s=p[3]===void 0?D:p[3]==='"'?je:Ke):s===je||s===Ke?s=D:s===ze||s===Oe?s=j:(s=D,o=void 0);let u=s===D&&n[a+1].startsWith("/>")?" ":"";i+=s===j?c+Et:h>=0?(r.push(d),c.slice(0,h)+qe+c.slice(h)+I+u):c+I+(h===-2?a:u)}return[We(n,i+(n[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),r]},Y=class n{constructor({strings:e,_$litType$:t},r){let o;this.parts=[];let i=0,s=0,a=e.length-1,c=this.parts,[d,p]=St(e,t);if(this.el=n.createElement(d,r),P.currentNode=this.el.content,t===2||t===3){let h=this.el.content.firstChild;h.replaceWith(...h.childNodes)}for(;(o=P.nextNode())!==null&&c.length<a;){if(o.nodeType===1){if(o.hasAttributes())for(let h of o.getAttributeNames())if(h.endsWith(qe)){let x=p[s++],u=o.getAttribute(h).split(I),T=/([.?@])?(.*)/.exec(x);c.push({type:1,index:i,name:T[2],strings:u,ctor:T[1]==="."?ve:T[1]==="?"?ke:T[1]==="@"?we:H}),o.removeAttribute(h)}else h.startsWith(I)&&(c.push({type:6,index:i}),o.removeAttribute(h));if(Ye.test(o.tagName)){let h=o.textContent.split(I),x=h.length-1;if(x>0){o.textContent=ne?ne.emptyScript:"";for(let u=0;u<x;u++)o.append(h[u],F()),P.nextNode(),c.push({type:2,index:++i});o.append(h[x],F())}}}else if(o.nodeType===8)if(o.data===Ve)c.push({type:2,index:i});else{let h=-1;for(;(h=o.data.indexOf(I,h+1))!==-1;)c.push({type:7,index:i}),h+=I.length-1}i++}}static createElement(e,t){let r=M.createElement("template");return r.innerHTML=e,r}};ye=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:r}=this._$AD,o=(e?.creationScope??M).importNode(t,!0);P.currentNode=o;let i=P.nextNode(),s=0,a=0,c=r[0];for(;c!==void 0;){if(s===c.index){let d;c.type===2?d=new W(i,i.nextSibling,this,e):c.type===1?d=new c.ctor(i,c.name,c.strings,this,e):c.type===6&&(d=new xe(i,this,e)),this._$AV.push(d),c=r[++a]}s!==c?.index&&(i=P.nextNode(),s++)}return P.currentNode=M,o}p(e){let t=0;for(let r of this._$AV)r!==void 0&&(r.strings!==void 0?(r._$AI(e,r,t),t+=r.strings.length-2):r._$AI(e[t])),t++}},W=class n{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,r,o){this.type=2,this._$AH=f,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=r,this.options=o,this._$Cv=o?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=U(this,e,t),q(e)?e===f||e==null||e===""?(this._$AH!==f&&this._$AR(),this._$AH=f):e!==this._$AH&&e!==V&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):Ct(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==f&&q(this._$AH)?this._$AA.nextSibling.data=e:this.T(M.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:r}=e,o=typeof r=="number"?this._$AC(e):(r.el===void 0&&(r.el=Y.createElement(We(r.h,r.h[0]),this.options)),r);if(this._$AH?._$AD===o)this._$AH.p(t);else{let i=new ye(o,this),s=i.u(this.options);i.p(t),this.T(s),this._$AH=i}}_$AC(e){let t=Fe.get(e.strings);return t===void 0&&Fe.set(e.strings,t=new Y(e)),t}k(e){_e(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,r,o=0;for(let i of e)o===t.length?t.push(r=new n(this.O(F()),this.O(F()),this,this.options)):r=t[o],r._$AI(i),o++;o<t.length&&(this._$AR(r&&r._$AB.nextSibling,o),t.length=o)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let r=e.nextSibling;e.remove(),e=r}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},H=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,r,o,i){this.type=1,this._$AH=f,this._$AN=void 0,this.element=e,this.name=t,this._$AM=o,this.options=i,r.length>2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=f}_$AI(e,t=this,r,o){let i=this.strings,s=!1;if(i===void 0)e=U(this,e,t,0),s=!q(e)||e!==this._$AH&&e!==V,s&&(this._$AH=e);else{let a=e,c,d;for(e=i[0],c=0;c<i.length-1;c++)d=U(this,a[r+c],t,c),d===V&&(d=this._$AH[c]),s||(s=!q(d)||d!==this._$AH[c]),d===f?e=f:e!==f&&(e+=(d??"")+i[c+1]),this._$AH[c]=d}s&&!o&&this.j(e)}j(e){e===f?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"")}},ve=class extends H{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===f?void 0:e}},ke=class extends H{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==f)}},we=class extends H{constructor(e,t,r,o,i){super(e,t,r,o,i),this.type=5}_$AI(e,t=this){if((e=U(this,e,t,0)??f)===V)return;let r=this._$AH,o=e===f&&r!==f||e.capture!==r.capture||e.once!==r.once||e.passive!==r.passive,i=e!==f&&(r===f||o);o&&this.element.removeEventListener(this.name,this,r),i&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}},xe=class{constructor(e,t,r){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=r}get _$AU(){return this._$AM._$AU}_$AI(e){U(this,e)}},Tt=L.litHtmlPolyfillSupport;Tt?.(Y,W),(L.litHtmlVersions??(L.litHtmlVersions=[])).push("3.3.1");X=(n,e,t)=>{let r=t?.renderBefore??e,o=r._$litPart$;if(o===void 0){let i=t?.renderBefore??null;r._$litPart$=o=new W(e.insertBefore(F(),i),i,void 0,t??{})}return o._$AI(n),o}});var Je,Xe=k(()=>{Je=`/**
386
+ \f\r"'\`<>=]|("|')|))|$)`,"g"),Ke=/'/g,je=/"/g,Ye=/^(?:script|style|textarea|title)$/i,Ee=n=>(e,...t)=>({_$litType$:n,strings:e,values:t}),m=Ee(1),Ft=Ee(2),qt=Ee(3),V=Symbol.for("lit-noChange"),b=Symbol.for("lit-nothing"),Fe=new WeakMap,P=M.createTreeWalker(M,129);St=(n,e)=>{let t=n.length-1,r=[],o,i=e===2?"<svg>":e===3?"<math>":"",s=j;for(let a=0;a<t;a++){let c=n[a],d,p,h=-1,x=0;for(;x<c.length&&(s.lastIndex=x,p=s.exec(c),p!==null);)x=s.lastIndex,s===j?p[1]==="!--"?s=ze:p[1]!==void 0?s=Oe:p[2]!==void 0?(Ye.test(p[2])&&(o=RegExp("</"+p[2],"g")),s=D):p[3]!==void 0&&(s=D):s===D?p[0]===">"?(s=o??j,h=-1):p[1]===void 0?h=-2:(h=s.lastIndex-p[2].length,d=p[1],s=p[3]===void 0?D:p[3]==='"'?je:Ke):s===je||s===Ke?s=D:s===ze||s===Oe?s=j:(s=D,o=void 0);let u=s===D&&n[a+1].startsWith("/>")?" ":"";i+=s===j?c+Et:h>=0?(r.push(d),c.slice(0,h)+qe+c.slice(h)+I+u):c+I+(h===-2?a:u)}return[We(n,i+(n[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),r]},Y=class n{constructor({strings:e,_$litType$:t},r){let o;this.parts=[];let i=0,s=0,a=e.length-1,c=this.parts,[d,p]=St(e,t);if(this.el=n.createElement(d,r),P.currentNode=this.el.content,t===2||t===3){let h=this.el.content.firstChild;h.replaceWith(...h.childNodes)}for(;(o=P.nextNode())!==null&&c.length<a;){if(o.nodeType===1){if(o.hasAttributes())for(let h of o.getAttributeNames())if(h.endsWith(qe)){let x=p[s++],u=o.getAttribute(h).split(I),T=/([.?@])?(.*)/.exec(x);c.push({type:1,index:i,name:T[2],strings:u,ctor:T[1]==="."?ve:T[1]==="?"?ke:T[1]==="@"?we:H}),o.removeAttribute(h)}else h.startsWith(I)&&(c.push({type:6,index:i}),o.removeAttribute(h));if(Ye.test(o.tagName)){let h=o.textContent.split(I),x=h.length-1;if(x>0){o.textContent=ne?ne.emptyScript:"";for(let u=0;u<x;u++)o.append(h[u],F()),P.nextNode(),c.push({type:2,index:++i});o.append(h[x],F())}}}else if(o.nodeType===8)if(o.data===Ve)c.push({type:2,index:i});else{let h=-1;for(;(h=o.data.indexOf(I,h+1))!==-1;)c.push({type:7,index:i}),h+=I.length-1}i++}}static createElement(e,t){let r=M.createElement("template");return r.innerHTML=e,r}};ye=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:r}=this._$AD,o=(e?.creationScope??M).importNode(t,!0);P.currentNode=o;let i=P.nextNode(),s=0,a=0,c=r[0];for(;c!==void 0;){if(s===c.index){let d;c.type===2?d=new W(i,i.nextSibling,this,e):c.type===1?d=new c.ctor(i,c.name,c.strings,this,e):c.type===6&&(d=new xe(i,this,e)),this._$AV.push(d),c=r[++a]}s!==c?.index&&(i=P.nextNode(),s++)}return P.currentNode=M,o}p(e){let t=0;for(let r of this._$AV)r!==void 0&&(r.strings!==void 0?(r._$AI(e,r,t),t+=r.strings.length-2):r._$AI(e[t])),t++}},W=class n{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,r,o){this.type=2,this._$AH=b,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=r,this.options=o,this._$Cv=o?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=U(this,e,t),q(e)?e===b||e==null||e===""?(this._$AH!==b&&this._$AR(),this._$AH=b):e!==this._$AH&&e!==V&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):Ct(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==b&&q(this._$AH)?this._$AA.nextSibling.data=e:this.T(M.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:r}=e,o=typeof r=="number"?this._$AC(e):(r.el===void 0&&(r.el=Y.createElement(We(r.h,r.h[0]),this.options)),r);if(this._$AH?._$AD===o)this._$AH.p(t);else{let i=new ye(o,this),s=i.u(this.options);i.p(t),this.T(s),this._$AH=i}}_$AC(e){let t=Fe.get(e.strings);return t===void 0&&Fe.set(e.strings,t=new Y(e)),t}k(e){_e(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,r,o=0;for(let i of e)o===t.length?t.push(r=new n(this.O(F()),this.O(F()),this,this.options)):r=t[o],r._$AI(i),o++;o<t.length&&(this._$AR(r&&r._$AB.nextSibling,o),t.length=o)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let r=e.nextSibling;e.remove(),e=r}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},H=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,r,o,i){this.type=1,this._$AH=b,this._$AN=void 0,this.element=e,this.name=t,this._$AM=o,this.options=i,r.length>2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=b}_$AI(e,t=this,r,o){let i=this.strings,s=!1;if(i===void 0)e=U(this,e,t,0),s=!q(e)||e!==this._$AH&&e!==V,s&&(this._$AH=e);else{let a=e,c,d;for(e=i[0],c=0;c<i.length-1;c++)d=U(this,a[r+c],t,c),d===V&&(d=this._$AH[c]),s||(s=!q(d)||d!==this._$AH[c]),d===b?e=b:e!==b&&(e+=(d??"")+i[c+1]),this._$AH[c]=d}s&&!o&&this.j(e)}j(e){e===b?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"")}},ve=class extends H{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===b?void 0:e}},ke=class extends H{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==b)}},we=class extends H{constructor(e,t,r,o,i){super(e,t,r,o,i),this.type=5}_$AI(e,t=this){if((e=U(this,e,t,0)??b)===V)return;let r=this._$AH,o=e===b&&r!==b||e.capture!==r.capture||e.once!==r.once||e.passive!==r.passive,i=e!==b&&(r===b||o);o&&this.element.removeEventListener(this.name,this,r),i&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}},xe=class{constructor(e,t,r){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=r}get _$AU(){return this._$AM._$AU}_$AI(e){U(this,e)}},Tt=L.litHtmlPolyfillSupport;Tt?.(Y,W),(L.litHtmlVersions??(L.litHtmlVersions=[])).push("3.3.1");X=(n,e,t)=>{let r=t?.renderBefore??e,o=r._$litPart$;if(o===void 0){let i=t?.renderBefore??null;r._$litPart$=o=new W(e.insertBefore(F(),i),i,void 0,t??{})}return o._$AI(n),o}});var Je,Xe=k(()=>{Je=`/**
387
387
  * Payment Form Skeleton - Shadow DOM Styles
388
388
  * Loading skeleton that matches the structure of RecurPaymentForm
389
389
  */
@@ -929,6 +929,8 @@
929
929
  line-height: 20px;
930
930
  font-weight: 500;
931
931
  color: #2d3748;
932
+ text-align: right;
933
+ font-variant-numeric: tabular-nums;
932
934
  }
933
935
 
934
936
  .recur-order-summary__total {
@@ -936,6 +938,8 @@
936
938
  line-height: 26px;
937
939
  font-weight: 700;
938
940
  color: #1a202c;
941
+ text-align: right;
942
+ font-variant-numeric: tabular-nums;
939
943
  }
940
944
 
941
945
  /* ============================================
@@ -951,9 +955,10 @@
951
955
  }
952
956
 
953
957
  .recur-coupon__trigger {
954
- display: inline-flex;
958
+ display: flex;
955
959
  align-items: center;
956
960
  gap: 6px;
961
+ width: 100%;
957
962
  height: 36px;
958
963
  background: none;
959
964
  border: none;
@@ -976,8 +981,10 @@
976
981
 
977
982
  .recur-coupon__input-row {
978
983
  display: flex;
984
+ align-items: center;
979
985
  gap: 8px;
980
986
  width: 100%;
987
+ height: 36px;
981
988
  }
982
989
 
983
990
  .recur-coupon__input {
@@ -1010,12 +1017,21 @@
1010
1017
  cursor: pointer;
1011
1018
  transition: background-color 0.2s, opacity 0.2s;
1012
1019
  white-space: nowrap;
1020
+ display: inline-flex;
1021
+ align-items: center;
1022
+ justify-content: center;
1013
1023
  }
1014
1024
 
1015
1025
  .recur-coupon__btn--apply {
1016
1026
  background: #18181b;
1017
1027
  color: white;
1018
1028
  border: none;
1029
+ min-width: 52px;
1030
+ }
1031
+
1032
+ .recur-coupon__btn--apply .recur-coupon__spinner {
1033
+ width: 14px;
1034
+ height: 14px;
1019
1035
  }
1020
1036
 
1021
1037
  .recur-coupon__btn--apply:hover:not(:disabled) {
@@ -1046,50 +1062,49 @@
1046
1062
 
1047
1063
  .recur-coupon__applied {
1048
1064
  display: flex;
1049
- align-items: flex-start;
1050
- justify-content: space-between;
1065
+ align-items: center;
1051
1066
  gap: 8px;
1052
1067
  width: 100%;
1068
+ height: 36px;
1053
1069
  }
1054
1070
 
1055
1071
  .recur-coupon__applied-info {
1056
1072
  display: flex;
1057
- align-items: flex-start;
1058
- gap: 8px;
1073
+ align-items: center;
1074
+ gap: 6px;
1059
1075
  min-width: 0;
1076
+ flex: 1;
1060
1077
  }
1061
1078
 
1062
1079
  .recur-coupon__applied-icon {
1063
1080
  width: 16px;
1064
1081
  height: 16px;
1065
- color: #16a34a;
1082
+ color: #dc2626;
1066
1083
  flex-shrink: 0;
1067
- margin-top: 2px;
1068
- }
1069
-
1070
- .recur-coupon__applied-details {
1071
- min-width: 0;
1072
1084
  }
1073
1085
 
1074
1086
  .recur-coupon__applied-name {
1075
1087
  font-size: 14px;
1076
1088
  font-weight: 500;
1077
- color: #16a34a;
1078
- overflow: hidden;
1079
- text-overflow: ellipsis;
1089
+ color: #dc2626;
1080
1090
  white-space: nowrap;
1081
1091
  }
1082
1092
 
1083
1093
  .recur-coupon__applied-code {
1084
- font-size: 12px;
1085
- color: #718096;
1094
+ font-size: 14px;
1095
+ color: #9ca3af;
1096
+ white-space: nowrap;
1097
+ margin-right: 2px;
1086
1098
  }
1087
1099
 
1088
1100
  .recur-coupon__applied-amount {
1089
1101
  font-size: 14px;
1090
1102
  font-weight: 500;
1091
- color: #16a34a;
1103
+ color: #dc2626;
1092
1104
  white-space: nowrap;
1105
+ font-variant-numeric: tabular-nums;
1106
+ flex-shrink: 0;
1107
+ margin-left: auto;
1093
1108
  }
1094
1109
 
1095
1110
  .recur-coupon__remove {
@@ -1103,6 +1118,7 @@
1103
1118
  display: flex;
1104
1119
  align-items: center;
1105
1120
  justify-content: center;
1121
+ flex-shrink: 0;
1106
1122
  }
1107
1123
 
1108
1124
  .recur-coupon__remove:hover {
@@ -1308,7 +1324,18 @@
1308
1324
  @keyframes recur-spin {
1309
1325
  to { transform: rotate(360deg); }
1310
1326
  }
1311
- `});var st={};C(st,{RecurPaymentForm:()=>le});var N,le,nt=k(()=>{"use strict";Ce();tt();ot();N={lock:m`
1327
+
1328
+ /* ============================================
1329
+ * Skeleton Loading Styles
1330
+ * ============================================ */
1331
+ .recur-skeleton {
1332
+ display: inline-block;
1333
+ background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);
1334
+ background-size: 200% 100%;
1335
+ animation: recur-skeleton-loading 1.5s ease-in-out infinite;
1336
+ border-radius: 4px;
1337
+ }
1338
+ `});var st={};C(st,{RecurPaymentForm:()=>le});var B,le,nt=k(()=>{"use strict";Ce();tt();ot();B={lock:m`
1312
1339
  <svg class="security-icon" width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1313
1340
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
1314
1341
  </svg>
@@ -1350,7 +1377,7 @@
1350
1377
  <slot name="actions"></slot>
1351
1378
 
1352
1379
  <div class="security-note">
1353
- ${N.lock}
1380
+ ${B.lock}
1354
1381
  <p class="security-text">您的付款資訊經過加密保護</p>
1355
1382
  </div>
1356
1383
  </div>
@@ -1383,35 +1410,56 @@
1383
1410
  height: 36px !important;
1384
1411
  }
1385
1412
  ${this.customStyles}
1386
- `}updateCustomStyles(){let t=this.querySelector(`#${this.containerId}-styles`);t&&(t.textContent=this.getLightDOMStyles()+this.getCustomStyles())}renderOrderSummary(){let t=this.getAttribute("product-name"),r=this.getAttribute("amount"),o=this.getAttribute("interval");if(!t||!r)return m`<div class="recur-order-summary" style="display: none;"></div>`;let s=o?{day:"\u65E5",week:"\u9031",month:"\u6708",year:"\u5E74"}[o]||o:"",a=Number(r),c=this._appliedCoupon?.discountAmount||0,d=a-c;return m`
1413
+ `}updateCustomStyles(){let t=this.querySelector(`#${this.containerId}-styles`);t&&(t.textContent=this.getLightDOMStyles()+this.getCustomStyles())}renderOrderSummary(){let t=this.getAttribute("product-name"),r=this.getAttribute("amount"),o=this.getAttribute("interval");if(!t||!r){let h=!!o;return m`
1414
+ <div class="recur-order-summary" id="${this.containerId}-order-summary">
1415
+ <div class="recur-order-summary__row">
1416
+ <span class="recur-order-summary__label">${h?"\u8A02\u95B1\u65B9\u6848":"\u5546\u54C1"}</span>
1417
+ <span class="recur-order-summary__value recur-skeleton" style="width: 100px; height: 20px;"></span>
1418
+ </div>
1419
+ ${h?m`
1420
+ <div class="recur-order-summary__row">
1421
+ <span class="recur-order-summary__label">計費週期</span>
1422
+ <span class="recur-order-summary__value recur-skeleton" style="width: 40px; height: 20px;"></span>
1423
+ </div>
1424
+ `:b}
1425
+ <div class="recur-order-summary__row">
1426
+ <span class="recur-order-summary__label">小計</span>
1427
+ <span class="recur-order-summary__value recur-skeleton" style="width: 80px; height: 20px;"></span>
1428
+ </div>
1429
+ <div class="recur-coupon">
1430
+ <span class="recur-skeleton" style="width: 80px; height: 20px;"></span>
1431
+ </div>
1432
+ <div class="recur-order-summary__row recur-order-summary__row--total">
1433
+ <span class="recur-order-summary__label">總計</span>
1434
+ <span class="recur-order-summary__total recur-skeleton" style="width: 100px; height: 26px;"></span>
1435
+ </div>
1436
+ </div>
1437
+ `}let s=o?{day:"\u65E5",week:"\u9031",month:"\u6708",year:"\u5E74"}[o]||o:"",a=Number(r),c=this._appliedCoupon?.discountAmount||0,d=a-c,p=!!s;return m`
1387
1438
  <div class="recur-order-summary" id="${this.containerId}-order-summary">
1388
1439
  <div class="recur-order-summary__row">
1389
- <span class="recur-order-summary__label">訂閱方案</span>
1440
+ <span class="recur-order-summary__label">${p?"\u8A02\u95B1\u65B9\u6848":"\u5546\u54C1"}</span>
1390
1441
  <span class="recur-order-summary__value">${t}</span>
1391
1442
  </div>
1392
1443
 
1393
- ${s?m`
1444
+ ${p?m`
1394
1445
  <div class="recur-order-summary__row">
1395
1446
  <span class="recur-order-summary__label">計費週期</span>
1396
1447
  <span class="recur-order-summary__value">每${s}</span>
1397
1448
  </div>
1398
- `:f}
1449
+ `:b}
1399
1450
 
1451
+ <!-- 小計 (Subtotal) - always show -->
1452
+ <div class="recur-order-summary__row">
1453
+ <span class="recur-order-summary__label">小計</span>
1454
+ <span class="recur-order-summary__value">NT$ ${a.toLocaleString()}</span>
1455
+ </div>
1456
+
1457
+ <!-- 優惠碼區塊 (Coupon section with discount amount) -->
1400
1458
  <div class="recur-coupon" id="${this.containerId}-coupon">
1401
1459
  ${this.renderCoupon()}
1402
1460
  </div>
1403
1461
 
1404
- ${c>0?m`
1405
- <div class="recur-order-summary__row">
1406
- <span class="recur-order-summary__label">小計</span>
1407
- <span class="recur-order-summary__value">NT$ ${a.toLocaleString()}</span>
1408
- </div>
1409
- <div class="recur-order-summary__row recur-order-summary__row--discount">
1410
- <span class="recur-order-summary__label">折扣</span>
1411
- <span class="recur-order-summary__value">-NT$ ${c.toLocaleString()}</span>
1412
- </div>
1413
- `:f}
1414
-
1462
+ <!-- 總計 (Total) -->
1415
1463
  <div class="recur-order-summary__row recur-order-summary__row--total">
1416
1464
  <span class="recur-order-summary__label">總計</span>
1417
1465
  <span class="recur-order-summary__total">NT$ ${d.toLocaleString()}</span>
@@ -1420,27 +1468,25 @@
1420
1468
  `}renderCoupon(){return this._appliedCoupon?m`
1421
1469
  <div class="recur-coupon__applied">
1422
1470
  <div class="recur-coupon__applied-info">
1423
- ${N.tagApplied}
1424
- <div class="recur-coupon__applied-details">
1425
- <div class="recur-coupon__applied-name">
1426
- ${this._appliedCoupon.couponName||this._appliedCoupon.code}
1427
- </div>
1428
- ${this._appliedCoupon.couponName?m`
1429
- <div class="recur-coupon__applied-code">代碼:${this._appliedCoupon.code}</div>
1430
- `:f}
1431
- </div>
1471
+ ${B.tagApplied}
1472
+ ${this._appliedCoupon.couponName?m`
1473
+ <span class="recur-coupon__applied-name">${this._appliedCoupon.couponName}</span>
1474
+ <span class="recur-coupon__applied-code">(${this._appliedCoupon.code})</span>
1475
+ `:m`
1476
+ <span class="recur-coupon__applied-name">${this._appliedCoupon.code}</span>
1477
+ `}
1478
+ <button
1479
+ type="button"
1480
+ class="recur-coupon__remove"
1481
+ ?disabled=${this._couponLoading}
1482
+ @click=${()=>this.removeCoupon()}
1483
+ >
1484
+ ${this._couponLoading?B.spinner:B.close}
1485
+ </button>
1432
1486
  </div>
1433
1487
  <span class="recur-coupon__applied-amount">
1434
1488
  -NT$ ${this._appliedCoupon.discountAmount.toLocaleString()}
1435
1489
  </span>
1436
- <button
1437
- type="button"
1438
- class="recur-coupon__remove"
1439
- ?disabled=${this._couponLoading}
1440
- @click=${()=>this.removeCoupon()}
1441
- >
1442
- ${this._couponLoading?N.spinner:N.close}
1443
- </button>
1444
1490
  </div>
1445
1491
  `:this._showCouponInput?m`
1446
1492
  <div style="width: 100%;">
@@ -1461,7 +1507,7 @@
1461
1507
  ?disabled=${this._couponLoading||!this._couponCode.trim()}
1462
1508
  @click=${()=>this.applyCoupon()}
1463
1509
  >
1464
- ${this._couponLoading?N.spinner:"\u5957\u7528"}
1510
+ ${this._couponLoading?B.spinner:"\u5957\u7528"}
1465
1511
  </button>
1466
1512
  <button
1467
1513
  type="button"
@@ -1474,7 +1520,7 @@
1474
1520
  </div>
1475
1521
  ${this._couponError?m`
1476
1522
  <div class="recur-coupon__error">${this._couponError}</div>
1477
- `:f}
1523
+ `:b}
1478
1524
  </div>
1479
1525
  `:m`
1480
1526
  <button
@@ -1482,10 +1528,10 @@
1482
1528
  class="recur-coupon__trigger"
1483
1529
  @click=${()=>this.showCouponInput()}
1484
1530
  >
1485
- ${N.tag}
1531
+ ${B.tag}
1486
1532
  有優惠碼?
1487
1533
  </button>
1488
- `}showCouponInput(){this._showCouponInput=!0,this._couponError=null,this.updateCouponSectionDOM(),requestAnimationFrame(()=>{document.getElementById(`${this.containerId}-coupon-input`)?.focus()})}cancelCouponInput(){this._showCouponInput=!1,this._couponCode="",this._couponError=null,this.updateCouponSectionDOM()}handleCouponInputChange(t){this._couponCode=t.target.value.toUpperCase(),this._couponError=null,this.updateApplyButtonState()}handleCouponKeydown(t){t.key==="Enter"?(t.preventDefault(),this.applyCoupon()):t.key==="Escape"&&this.cancelCouponInput()}updateApplyButtonState(){let t=this.querySelector(".recur-coupon__btn--apply");t&&(t.disabled=this._couponLoading||!this._couponCode.trim())}updateCouponSectionDOM(){let t=document.getElementById(`${this.containerId}-coupon`);t&&(this._appliedCoupon?(t.innerHTML=this.getCouponAppliedHTML(),this.bindCouponAppliedEvents(t)):this._showCouponInput?(t.innerHTML=this.getCouponInputHTML(),this.bindCouponInputEvents(t)):(t.innerHTML=this.getCouponTriggerHTML(),this.bindCouponTriggerEvents(t)))}getCouponTriggerHTML(){return`
1534
+ `}showCouponInput(){this._showCouponInput=!0,this._couponError=null,this.updateCouponSectionDOM(),requestAnimationFrame(()=>{document.getElementById(`${this.containerId}-coupon-input`)?.focus()})}cancelCouponInput(){this._showCouponInput=!1,this._couponCode="",this._couponError=null,this.updateCouponSectionDOM()}handleCouponInputChange(t){this._couponCode=t.target.value.toUpperCase(),this._couponError=null,this.updateApplyButtonState()}handleCouponKeydown(t){t.key==="Enter"?(t.preventDefault(),this.applyCoupon()):t.key==="Escape"&&this.cancelCouponInput()}updateApplyButtonState(){let t=this.querySelector(".recur-coupon__btn--apply");t&&(t.disabled=this._couponLoading||!this._couponCode.trim())}updateCouponSectionDOM(){let t=`${this.containerId}-coupon`,r=document.getElementById(t);r&&(this._appliedCoupon?(r.innerHTML=this.getCouponAppliedHTML(),this.bindCouponAppliedEvents(r)):this._showCouponInput?(r.innerHTML=this.getCouponInputHTML(),this.bindCouponInputEvents(r)):(r.innerHTML=this.getCouponTriggerHTML(),this.bindCouponTriggerEvents(r)))}getCouponTriggerHTML(){return`
1489
1535
  <button type="button" class="recur-coupon__trigger">
1490
1536
  <svg class="recur-coupon__trigger-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1491
1537
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
@@ -1520,40 +1566,27 @@
1520
1566
  </div>
1521
1567
  ${t}
1522
1568
  </div>
1523
- `}getCouponAppliedHTML(){let t=this._appliedCoupon,r=t.couponName?`<div class="recur-coupon__applied-code">\u4EE3\u78BC\uFF1A${t.code}</div>`:"";return`
1569
+ `}getCouponAppliedHTML(){let t=this._appliedCoupon;return`
1524
1570
  <div class="recur-coupon__applied">
1525
1571
  <div class="recur-coupon__applied-info">
1526
1572
  <svg class="recur-coupon__applied-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1527
1573
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
1528
1574
  </svg>
1529
- <div class="recur-coupon__applied-details">
1530
- <div class="recur-coupon__applied-name">${t.couponName||t.code}</div>
1531
- ${r}
1532
- </div>
1575
+ ${t.couponName?`<span class="recur-coupon__applied-name">${t.couponName}</span><span class="recur-coupon__applied-code">(${t.code})</span>`:`<span class="recur-coupon__applied-name">${t.code}</span>`}
1576
+ <button type="button" class="recur-coupon__remove" ${this._couponLoading?"disabled":""}>
1577
+ ${this._couponLoading?'<span class="recur-coupon__spinner"></span>':'<svg class="recur-coupon__remove-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'}
1578
+ </button>
1533
1579
  </div>
1534
1580
  <span class="recur-coupon__applied-amount">-NT$ ${t.discountAmount.toLocaleString()}</span>
1535
- <button type="button" class="recur-coupon__remove" ${this._couponLoading?"disabled":""}>
1536
- ${this._couponLoading?'<span class="recur-coupon__spinner"></span>':'<svg class="recur-coupon__remove-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'}
1537
- </button>
1538
1581
  </div>
1539
- `}bindCouponTriggerEvents(t){t.querySelector(".recur-coupon__trigger")?.addEventListener("click",()=>this.showCouponInput())}bindCouponInputEvents(t){let r=t.querySelector(".recur-coupon__input"),o=t.querySelector(".recur-coupon__btn--apply"),i=t.querySelector(".recur-coupon__btn--cancel");r?.addEventListener("input",s=>this.handleCouponInputChange(s)),r?.addEventListener("keydown",s=>this.handleCouponKeydown(s)),o?.addEventListener("click",()=>this.applyCoupon()),i?.addEventListener("click",()=>this.cancelCouponInput())}bindCouponAppliedEvents(t){t.querySelector(".recur-coupon__remove")?.addEventListener("click",()=>this.removeCoupon())}updateOrderSummaryDOM(){let t=document.getElementById(`${this.containerId}-order-summary`);if(!t)return;let r=Number(this.getAttribute("amount")||0),o=this._appliedCoupon?.discountAmount||0,i=r-o,s=t.querySelector(".recur-order-summary__row--discount"),a=t.querySelector(".recur-order-summary__row--total");if(o>0){if(!s&&a){let d=`
1540
- <div class="recur-order-summary__row">
1541
- <span class="recur-order-summary__label">\u5C0F\u8A08</span>
1542
- <span class="recur-order-summary__value">NT$ ${r.toLocaleString()}</span>
1543
- </div>
1544
- `,p=`
1545
- <div class="recur-order-summary__row recur-order-summary__row--discount">
1546
- <span class="recur-order-summary__label">\u6298\u6263</span>
1547
- <span class="recur-order-summary__value">-NT$ ${o.toLocaleString()}</span>
1548
- </div>
1549
- `;a.insertAdjacentHTML("beforebegin",d+p)}}else{let d=t.querySelector(".recur-order-summary__row:not(.recur-order-summary__row--total):last-of-type");s?.remove(),d?.querySelector(".recur-order-summary__label")?.textContent==="\u5C0F\u8A08"&&d.remove()}let c=a?.querySelector(".recur-order-summary__total");c&&(c.textContent=`NT$ ${i.toLocaleString()}`),this.updateCouponSectionDOM()}async applyCoupon(){let t=this._couponCode.trim();if(!t){this._couponError="\u8ACB\u8F38\u5165\u512A\u60E0\u78BC",this.updateCouponSectionDOM();return}let r=this.getAttribute("checkout-id"),o=this.getAttribute("api-base-url")||"";if(!r){this._couponError="\u7121\u6CD5\u5957\u7528\u512A\u60E0\u78BC",this.updateCouponSectionDOM();return}this._couponLoading=!0,this._couponError=null,this.updateCouponSectionDOM();try{let i=await fetch(`${o}/api/checkout/${r}/coupon`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:t})}),s=await i.json();if(!i.ok){this._couponError=s.error?.message||"\u512A\u60E0\u78BC\u7121\u6548",this._couponLoading=!1,this.updateCouponSectionDOM();return}this._appliedCoupon={code:s.discount.code,couponName:s.discount.couponName,discountType:s.discount.discountType,discountAmount:s.discount.discountAmount,duration:s.discount.duration,durationMonths:s.discount.durationMonths,bonusTrialDays:s.discount.bonusTrialDays,bonusMonths:s.discount.bonusMonths},this._showCouponInput=!1,this._couponCode="",this._couponLoading=!1,this.updateOrderSummaryDOM(),this.dispatchEvent(new CustomEvent("coupon-applied",{detail:{coupon:this._appliedCoupon},bubbles:!0,composed:!0}))}catch{this._couponError="\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66",this._couponLoading=!1,this.updateCouponSectionDOM()}}async removeCoupon(){let t=this.getAttribute("checkout-id"),r=this.getAttribute("api-base-url")||"";if(t){this._couponLoading=!0,this.updateCouponSectionDOM();try{let o=await fetch(`${r}/api/checkout/${t}/coupon`,{method:"DELETE"});if(!o.ok){let s=await o.json();this._couponError=s.error?.message||"\u79FB\u9664\u5931\u6557",this._couponLoading=!1,this.updateCouponSectionDOM();return}let i=this._appliedCoupon;this._appliedCoupon=null,this._couponLoading=!1,this.updateOrderSummaryDOM(),this.dispatchEvent(new CustomEvent("coupon-removed",{detail:{coupon:i},bubbles:!0,composed:!0}))}catch{this._couponError="\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66",this._couponLoading=!1,this.updateCouponSectionDOM()}}}getAppliedCoupon(){return this._appliedCoupon}renderCustomerInfo(){let t=this.getAttribute("customer-name"),r=this.getAttribute("customer-email");return r?m`
1582
+ `}bindCouponTriggerEvents(t){t.querySelector(".recur-coupon__trigger")?.addEventListener("click",()=>this.showCouponInput())}bindCouponInputEvents(t){let r=t.querySelector(".recur-coupon__input"),o=t.querySelector(".recur-coupon__btn--apply"),i=t.querySelector(".recur-coupon__btn--cancel");r?.addEventListener("input",s=>this.handleCouponInputChange(s)),r?.addEventListener("keydown",s=>this.handleCouponKeydown(s)),o?.addEventListener("click",()=>this.applyCoupon()),i?.addEventListener("click",()=>this.cancelCouponInput())}bindCouponAppliedEvents(t){t.querySelector(".recur-coupon__remove")?.addEventListener("click",()=>this.removeCoupon())}updateOrderSummaryDOM(){let t=document.getElementById(`${this.containerId}-order-summary`);if(!t)return;let r=Number(this.getAttribute("amount")||0),o=this._appliedCoupon?.discountAmount||0,i=r-o,a=t.querySelector(".recur-order-summary__row--total")?.querySelector(".recur-order-summary__total");a&&(a.textContent=`NT$ ${i.toLocaleString()}`),this.updateCouponSectionDOM()}async applyCoupon(){let t=this._couponCode.trim();if(!t){this._couponError="\u8ACB\u8F38\u5165\u512A\u60E0\u78BC",this.updateCouponSectionDOM();return}let r=this.getAttribute("checkout-id"),o=this.getAttribute("api-base-url")||"";if(!r){this._couponError="\u7121\u6CD5\u5957\u7528\u512A\u60E0\u78BC",this.updateCouponSectionDOM();return}this._couponLoading=!0,this._couponError=null,this.updateCouponSectionDOM();try{let i=await fetch(`${o}/v1/checkouts/${r}/coupon`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:t})}),s=await i.json();if(!i.ok){this._couponError=s.error?.message||"\u512A\u60E0\u78BC\u7121\u6548",this._couponLoading=!1,this.updateCouponSectionDOM();return}this._appliedCoupon={code:s.discount.code,couponName:s.discount.couponName,discountType:s.discount.discountType,discountAmount:s.discount.discountAmount,duration:s.discount.duration,durationMonths:s.discount.durationMonths,bonusTrialDays:s.discount.bonusTrialDays,bonusMonths:s.discount.bonusMonths},this._showCouponInput=!1,this._couponCode="",this._couponLoading=!1,this.updateOrderSummaryDOM(),this.dispatchEvent(new CustomEvent("coupon-applied",{detail:{coupon:this._appliedCoupon},bubbles:!0,composed:!0}))}catch{this._couponError="\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66",this._couponLoading=!1,this.updateCouponSectionDOM()}}async removeCoupon(){let t=this.getAttribute("checkout-id"),r=this.getAttribute("api-base-url")||"";if(t){this._couponLoading=!0,this.updateCouponSectionDOM();try{let o=await fetch(`${r}/v1/checkouts/${t}/coupon`,{method:"DELETE"});if(!o.ok){let s=await o.json();this._couponError=s.error?.message||"\u79FB\u9664\u5931\u6557",this._couponLoading=!1,this.updateCouponSectionDOM();return}let i=this._appliedCoupon;this._appliedCoupon=null,this._couponLoading=!1,this.updateOrderSummaryDOM(),this.dispatchEvent(new CustomEvent("coupon-removed",{detail:{coupon:i},bubbles:!0,composed:!0}))}catch{this._couponError="\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66",this._couponLoading=!1,this.updateCouponSectionDOM()}}}getAppliedCoupon(){return this._appliedCoupon}renderCustomerInfo(){let t=this.getAttribute("customer-name"),r=this.getAttribute("customer-email");return r?m`
1550
1583
  <div class="recur-info-display">
1551
1584
  ${t?m`
1552
1585
  <div class="recur-info-row">
1553
1586
  <span class="recur-info-label">姓名</span>
1554
1587
  <span class="recur-info-value">${t}</span>
1555
1588
  </div>
1556
- `:f}
1589
+ `:b}
1557
1590
  <div class="recur-info-row">
1558
1591
  <span class="recur-info-label">電子郵件</span>
1559
1592
  <span class="recur-info-value">${r}</span>
@@ -1849,7 +1882,7 @@
1849
1882
  <path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
1850
1883
  <circle cx="12" cy="7" r="4"/>
1851
1884
  </svg>
1852
- `}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:()=>G,RecurElements:()=>B,createElements:()=>Se,default:()=>Bt,init:()=>ht});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(()=>(Ne(),He)),Promise.resolve().then(()=>(Ge(),Ze)),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.9.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 examples -p 8080 -o"},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","./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 me=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=`
1885
+ `}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:()=>G,RecurElements:()=>N,createElements:()=>Se,default:()=>Nt,init:()=>ht});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(()=>(Ge(),Ze)),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.9.11",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 examples -p 8080 -o"},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","./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 me=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=`
1853
1886
  <div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
1854
1887
  <div class="recur-checkout-header" style="margin-bottom: 24px;">
1855
1888
  <h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
@@ -1919,21 +1952,21 @@
1919
1952
  </p>
1920
1953
  </form>
1921
1954
  </div>
1922
- `}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let o=r?.status;o&&(this.isFormValid=o.CardNo===!0&&o.CardExp===!0&&o.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let r=await this.payuniSDK.getTradeResult();if(r.Status!=="SUCCESS")throw new Error(r.Message||"Card validation failed");let o=r.EncryptInfo||r.creditToken;if(!o)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let i=this.getBaseUrl(),s=await fetch(`${i}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:o,timestamp:this.sdkTimestamp||r.HashTimestamp||r.timestamp})});if(!s.ok){let p=await s.json().catch(()=>({}));throw new Error(p.error||"Failed to process payment")}let a=await s.json(),c=this.options.productId||this.options.planId||"",d={subscription:{id:a.subscription?.id||a.charge?.id||"",status:a.success?"active":"failed",productId:c,productName:"",amount:a.charge?.amount||0,billingPeriod:a.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(d),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var B=class{constructor(e){l(this,"publishableKey");l(this,"baseUrl");l(this,"embedUrl");l(this,"iframe",null);l(this,"container",null);l(this,"sessionId",null);l(this,"timestamp",null);l(this,"creditToken",null);l(this,"sdkToken",null);l(this,"cardToken",null);l(this,"cardTimestamp",null);l(this,"eventHandlers",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
1955
+ `}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let o=r?.status;o&&(this.isFormValid=o.CardNo===!0&&o.CardExp===!0&&o.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let r=await this.payuniSDK.getTradeResult();if(r.Status!=="SUCCESS")throw new Error(r.Message||"Card validation failed");let o=r.EncryptInfo||r.creditToken;if(!o)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let i=this.getBaseUrl(),s=await fetch(`${i}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:o,timestamp:this.sdkTimestamp||r.HashTimestamp||r.timestamp})});if(!s.ok){let p=await s.json().catch(()=>({}));throw new Error(p.error||"Failed to process payment")}let a=await s.json(),c=this.options.productId||this.options.planId||"",d={subscription:{id:a.subscription?.id||a.charge?.id||"",status:a.success?"active":"failed",productId:c,productName:"",amount:a.charge?.amount||0,billingPeriod:a.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(d),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var N=class{constructor(e){l(this,"publishableKey");l(this,"baseUrl");l(this,"embedUrl");l(this,"iframe",null);l(this,"container",null);l(this,"sessionId",null);l(this,"timestamp",null);l(this,"creditToken",null);l(this,"sdkToken",null);l(this,"cardToken",null);l(this,"cardTimestamp",null);l(this,"eventHandlers",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
1923
1956
  width: 100%;
1924
1957
  border: none;
1925
1958
  min-height: 200px;
1926
1959
  display: block;
1927
1960
  user-select: none;
1928
1961
  transition: height 0.35s ease, opacity 0.4s ease 0.1s;
1929
- `.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,o)=>{let i=setTimeout(()=>{o(new Error("Elements initialization timeout"))},3e4),s=()=>{clearTimeout(i),this.off("ready",s),r()},a=c=>{clearTimeout(i),this.off("error",a),o(new Error(c.message||"Elements initialization failed"))};this.on("ready",s),this.on("error",a)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(o=>o(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.sdkToken=r.sdkToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let o=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),i=a=>{clearTimeout(o),this.off("tokenized",i),t(a)},s=a=>{clearTimeout(o),this.off("error",s),r(new Error(a.message||"Tokenization failed"))};this.on("tokenized",i),this.on("error",s)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,productId:e.productId,email:e.email,name:e.name,phone:e.phone,externalCustomerId:e.externalCustomerId,metadata:e.metadata,successUrl:e.successUrl,cancelUrl:e.cancelUrl})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.sdkToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function Se(n){return new B(n)}var Ht="https://vendor.payuni.com.tw/sdk/uni-payment.js",Nt="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",pt=!1,he=!1,Z=null;async function mt(n=!1){return pt&&window.UniPayment?Promise.resolve():(he&&Z||(he=!0,Z=new Promise((e,t)=>{let r=document.createElement("script");r.src=n?Nt:Ht,r.async=!0,r.onload=()=>{pt=!0,he=!1,e()},r.onerror=()=>{he=!1,Z=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),Z)}var G=class{constructor(e){l(this,"core");l(this,"currentDialog",null);l(this,"currentIframe",null);l(this,"currentDialogElement",null);this.core=new pe(e)}async fetchProducts(e){return await this.core.fetchProducts(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new me(t,e).render()}async redirectToCheckout(e){let t=await this.createCheckoutSession(e);window.location.href=t.url}async createCheckoutSession(e){let t=this.getBaseUrl();if(!e.productId&&!e.productSlug)throw new Error("Either productId or productSlug is required");if(!e.successUrl)throw new Error("successUrl is required for hosted checkout");if(!e.cancelUrl)throw new Error("cancelUrl is required for hosted checkout");let r={successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.productId&&(r.productId=e.productId),e.productSlug&&(r.productSlug=e.productSlug),e.customerEmail&&(r.customerEmail=e.customerEmail),e.customerName&&(r.customerName=e.customerName),e.externalCustomerId&&(r.externalCustomerId=e.externalCustomerId);let o=this.core.getConfig(),i=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":o.publishableKey},body:JSON.stringify(r)});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error?.message||c.error||"Failed to create checkout session")}let s=await i.json(),a=w(s);return{id:a.id,url:a.url,expiresAt:a.expiresAt,clientSecret:a.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),o=this.core.getConfig(),i=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":o.publishableKey}});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error||"Failed to get checkout status")}let s=await i.json(),a=w(s);return{id:a.checkout.id,status:a.checkout.status,amount:a.checkout.amount,currency:a.checkout.currency,lastCharge:a.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,o=e.productId||e.planId,i=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:o,productSlug:i,mode:e.mode}),!o&&!i)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let s=this.getBaseUrl();console.log("[Recur SDK] Base URL:",s);let a={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},c=e.mode||"modal";if(c==="redirect"){if(!e.successUrl)throw new Error("successUrl is required for redirect mode");if(!e.cancelUrl)throw new Error("cancelUrl is required for redirect mode");console.log("[Recur SDK] Creating hosted checkout session...");let y={successUrl:e.successUrl,cancelUrl:e.cancelUrl};o&&(y.productId=o),i&&(y.productSlug=i),e.customerEmail&&(y.customerEmail=e.customerEmail),e.customerName&&(y.customerName=e.customerName),e.externalCustomerId&&(y.externalCustomerId=e.externalCustomerId);let A=await fetch(`${s}/v1/checkout/sessions`,{method:"POST",headers:a,body:JSON.stringify(y)});if(!A.ok){let R=await A.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",R),new Error(R.error?.message||R.error||"Failed to create checkout session")}let ge=await A.json(),E=w(ge);console.log("[Recur SDK] Checkout session created:",E),console.log("[Recur SDK] Redirecting to hosted checkout:",E.url),window.location.href=E.url;return}let d=null;if(c==="modal"){let y=this.createDialogWithSkeleton(e.onClose);r=y.dialog,d=y.container}else if(c==="iframe"){if(d=this.getEmbeddedContainer(e.container),!d)throw new Error("Container is required for iframe mode");d.innerHTML="";let y=document.createElement("recur-payment-form-skeleton");d.appendChild(y)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let p={customerName:e.customerName,customerEmail:e.customerEmail};o&&(p.productId=o),i&&(p.productSlug=i),e.externalCustomerId&&(p.externalCustomerId=e.externalCustomerId),e.successUrl&&(p.successUrl=e.successUrl),e.cancelUrl&&(p.cancelUrl=e.cancelUrl);let h=await fetch(`${s}/v1/checkouts`,{method:"POST",headers:a,body:JSON.stringify(p)});if(!h.ok){let y=await h.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",y);let A=y.details||y.error||"Failed to create checkout";throw new Error(A)}let x=await h.json(),u=w(x);if(console.log("[Recur SDK] Checkout created successfully:",u),e.onSuccess?.(u),console.log("[Recur SDK] Step 2: Extracting SDK token..."),!u.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let T=!u.livemode;if(console.log("[Recur SDK] Environment:",T?"SANDBOX":"PRODUCTION"),await mt(T),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!d)throw new Error("Payment container not available");d.innerHTML="";let b=document.createElement("recur-payment-form");if(b.setAttribute("container-id",d.id||"recur-payment-container"),e.customerName&&b.setAttribute("customer-name",e.customerName),e.customerEmail&&b.setAttribute("customer-email",e.customerEmail),u.product?.name&&b.setAttribute("product-name",u.product.name),u.checkout?.amount&&b.setAttribute("amount",u.checkout.amount.toString()),u.product?.interval&&b.setAttribute("interval",u.product.interval),b.setAttribute("custom-styles",`
1962
+ `.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,o)=>{let i=setTimeout(()=>{o(new Error("Elements initialization timeout"))},3e4),s=()=>{clearTimeout(i),this.off("ready",s),r()},a=c=>{clearTimeout(i),this.off("error",a),o(new Error(c.message||"Elements initialization failed"))};this.on("ready",s),this.on("error",a)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(o=>o(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.sdkToken=r.sdkToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let o=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),i=a=>{clearTimeout(o),this.off("tokenized",i),t(a)},s=a=>{clearTimeout(o),this.off("error",s),r(new Error(a.message||"Tokenization failed"))};this.on("tokenized",i),this.on("error",s)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,productId:e.productId,email:e.email,name:e.name,phone:e.phone,externalCustomerId:e.externalCustomerId,metadata:e.metadata,successUrl:e.successUrl,cancelUrl:e.cancelUrl})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.sdkToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function Se(n){return new N(n)}var Ht="https://vendor.payuni.com.tw/sdk/uni-payment.js",Bt="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",pt=!1,he=!1,Z=null;async function mt(n=!1){return pt&&window.UniPayment?Promise.resolve():(he&&Z||(he=!0,Z=new Promise((e,t)=>{let r=document.createElement("script");r.src=n?Bt:Ht,r.async=!0,r.onload=()=>{pt=!0,he=!1,e()},r.onerror=()=>{he=!1,Z=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),Z)}var G=class{constructor(e){l(this,"core");l(this,"currentDialog",null);l(this,"currentIframe",null);l(this,"currentDialogElement",null);this.core=new pe(e)}async fetchProducts(e){return await this.core.fetchProducts(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new me(t,e).render()}async redirectToCheckout(e){let t=await this.createCheckoutSession(e);window.location.href=t.url}async createCheckoutSession(e){let t=this.getBaseUrl();if(!e.productId&&!e.productSlug)throw new Error("Either productId or productSlug is required");if(!e.successUrl)throw new Error("successUrl is required for hosted checkout");if(!e.cancelUrl)throw new Error("cancelUrl is required for hosted checkout");let r={successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.productId&&(r.productId=e.productId),e.productSlug&&(r.productSlug=e.productSlug),e.customerEmail&&(r.customerEmail=e.customerEmail),e.customerName&&(r.customerName=e.customerName),e.externalCustomerId&&(r.externalCustomerId=e.externalCustomerId);let o=this.core.getConfig(),i=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":o.publishableKey},body:JSON.stringify(r)});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error?.message||c.error||"Failed to create checkout session")}let s=await i.json(),a=w(s);return{id:a.id,url:a.url,expiresAt:a.expiresAt,clientSecret:a.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),o=this.core.getConfig(),i=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":o.publishableKey}});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error||"Failed to get checkout status")}let s=await i.json(),a=w(s);return{id:a.checkout.id,status:a.checkout.status,amount:a.checkout.amount,currency:a.checkout.currency,lastCharge:a.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,o=e.productId||e.planId,i=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:o,productSlug:i,mode:e.mode}),!o&&!i)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let s=this.getBaseUrl();console.log("[Recur SDK] Base URL:",s);let a={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},c=e.mode||"modal";if(c==="redirect"){if(!e.successUrl)throw new Error("successUrl is required for redirect mode");if(!e.cancelUrl)throw new Error("cancelUrl is required for redirect mode");console.log("[Recur SDK] Creating hosted checkout session...");let y={successUrl:e.successUrl,cancelUrl:e.cancelUrl};o&&(y.productId=o),i&&(y.productSlug=i),e.customerEmail&&(y.customerEmail=e.customerEmail),e.customerName&&(y.customerName=e.customerName),e.externalCustomerId&&(y.externalCustomerId=e.externalCustomerId);let A=await fetch(`${s}/v1/checkout/sessions`,{method:"POST",headers:a,body:JSON.stringify(y)});if(!A.ok){let R=await A.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",R),new Error(R.error?.message||R.error||"Failed to create checkout session")}let ge=await A.json(),E=w(ge);console.log("[Recur SDK] Checkout session created:",E),console.log("[Recur SDK] Redirecting to hosted checkout:",E.url),window.location.href=E.url;return}let d=null;if(c==="modal"){let y=this.createDialogWithSkeleton(e.onClose);r=y.dialog,d=y.container}else if(c==="iframe"){if(d=this.getEmbeddedContainer(e.container),!d)throw new Error("Container is required for iframe mode");d.innerHTML="";let y=document.createElement("recur-payment-form-skeleton");d.appendChild(y)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let p={customerName:e.customerName,customerEmail:e.customerEmail};o&&(p.productId=o),i&&(p.productSlug=i),e.externalCustomerId&&(p.externalCustomerId=e.externalCustomerId),e.successUrl&&(p.successUrl=e.successUrl),e.cancelUrl&&(p.cancelUrl=e.cancelUrl);let h=await fetch(`${s}/v1/checkouts`,{method:"POST",headers:a,body:JSON.stringify(p)});if(!h.ok){let y=await h.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",y);let A=y.details||y.error||"Failed to create checkout";throw new Error(A)}let x=await h.json(),u=w(x);if(console.log("[Recur SDK] Checkout created successfully:",u),e.onSuccess?.(u),console.log("[Recur SDK] Step 2: Extracting SDK token..."),!u.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let T=!u.livemode;if(console.log("[Recur SDK] Environment:",T?"SANDBOX":"PRODUCTION"),await mt(T),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!d)throw new Error("Payment container not available");d.innerHTML="";let f=document.createElement("recur-payment-form");if(f.setAttribute("container-id",d.id||"recur-payment-container"),u.checkout?.id&&f.setAttribute("checkout-id",u.checkout.id),f.setAttribute("api-base-url",s),e.customerName&&f.setAttribute("customer-name",e.customerName),e.customerEmail&&f.setAttribute("customer-email",e.customerEmail),u.product?.name&&f.setAttribute("product-name",u.product.name),u.checkout?.amount&&f.setAttribute("amount",u.checkout.amount.toString()),u.product?.interval&&f.setAttribute("interval",u.product.interval),f.setAttribute("custom-styles",`
1930
1963
  .form-input-focus {
1931
1964
  border-color: var(--ring, hsl(215 16% 47%)) !important;
1932
1965
  outline: 0 !important;
1933
1966
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent) !important;
1934
1967
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
1935
1968
  }
1936
- `),d.appendChild(b),await new Promise(y=>setTimeout(y,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await b.initializePayment(u.sdkToken,T?"SANDBOX":"PRODUCTION")===null){console.log("[Recur SDK] Initialization aborted");return}console.log("[Recur SDK] PAYUNi SDK initialized successfully"),console.log("[Recur SDK] Step 6: Setting up submit handler..."),b.addEventListener("submit",(async y=>{console.log("[Recur SDK] Form submitted");let A=y,{paymentSession:ge}=A.detail;try{let E=await ge.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let R=E.HashTimestamp||E.timestamp,Te={};if(u.checkout.productType==="SUBSCRIPTION"){let $=u.creditToken,_=u.sdkTimestamp;if(!$)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");Te={creditToken:$,timestamp:_||R},console.log("[Recur SDK] Using creditToken from checkout:",$.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",_?"from checkout (sdkTimestamp)":"from tradeResult")}let fe=await fetch(`${s}/v1/checkouts/${u.checkout.id}/pay`,{method:"POST",headers:a,body:JSON.stringify(Te)});if(!fe.ok){let $=await fe.json().catch(()=>({}));throw new Error($.error||"Failed to execute payment")}let gt=await fe.json(),g=w(gt);if(console.log("[Recur SDK] Payment executed:",g),g.requires3D&&g.redirectUrl){if(console.log("[Recur SDK] 3D verification required"),/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||/wv|WebView|FBAN|FBAV|Instagram|Line|MicroMessenger|QQ/i.test(navigator.userAgent)||/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent)||"standalone"in navigator&&navigator.standalone){console.log("[Recur SDK] Mobile/WebView detected, using redirect for 3D verification"),window.location.href=g.redirectUrl;return}console.log("[Recur SDK] Using popup for 3D verification");{let _=window.open(g.redirectUrl,"recur_3d_verification","width=500,height=700,scrollbars=yes,resizable=yes");if(!_){console.log("[Recur SDK] Popup was blocked (null), falling back to redirect"),window.location.href=g.redirectUrl;return}if(await new Promise(O=>setTimeout(O,100)),_.closed){console.log("[Recur SDK] Popup was closed immediately, falling back to redirect"),window.location.href=g.redirectUrl;return}b.setVerifying?.(!0);let Q=u.checkout.id,$e=u.checkout.clientSecret,ft=90,bt=2e3,z=!0,Ie=!1,Ae;console.log("[Recur SDK] Starting 3D verification polling...");for(let O=0;O<ft&&z;O++){if(_.closed){console.log("[Recur SDK] Popup was closed");try{let v=await fetch(`${s}/v1/checkouts/${Q}/status?client_secret=${encodeURIComponent($e)}`,{headers:a});if(v.ok){let K=await v.json();if(K.checkout?.status==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded after popup close"),z=!1,Ie=!0,Ae=K.checkout?.orderId;break}}}catch(v){console.error("[Recur SDK] Final status check failed:",v)}throw console.log("[Recur SDK] Payment not confirmed, user closed popup"),b.setVerifying?.(!1),b.resetButton?.(),new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88")}try{let v=await fetch(`${s}/v1/checkouts/${Q}/status?client_secret=${encodeURIComponent($e)}`,{headers:a});if(v.ok){let K=await v.json(),ee=K.checkout?.status;if(console.log(`[Recur SDK] Poll ${O+1}: status = ${ee}`),ee==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded"),z=!1;try{_.close()}catch{}b.setVerifying?.(!1),e.onPaymentComplete&&(g.subscription?e.onPaymentComplete({id:g.subscription.id,status:"ACTIVE",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd}):e.onPaymentComplete({id:K.checkout?.orderId||Q,status:"SUCCEEDED",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})),r&&(r.close(),r.remove());return}if(ee==="CANCELED"||ee==="FAILED"){console.log("[Recur SDK] Payment failed or canceled"),z=!1;try{_.close()}catch{}throw b.setVerifying?.(!1),b.resetButton?.(),new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")}}}catch(v){if(v.message==="3D \u9A57\u8B49\u5DF2\u53D6\u6D88"||v.message==="\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")throw v;console.error("[Recur SDK] Poll error:",v)}await new Promise(v=>setTimeout(v,bt))}if(z){console.log("[Recur SDK] 3D verification polling timeout");try{_.close()}catch{}throw b.setVerifying?.(!1),b.resetButton?.(),new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66")}Ie&&(console.log("[Recur SDK] Handling success after popup close"),b.setVerifying?.(!1),e.onPaymentComplete&&(g.subscription?e.onPaymentComplete({id:g.subscription.id,status:"ACTIVE",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd}):e.onPaymentComplete({id:Ae||Q,status:"SUCCEEDED",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})),r&&(r.close(),r.remove()));return}}if(e.onPaymentComplete)if(g.subscription)e.onPaymentComplete({id:g.subscription.id,status:g.subscription.status,productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd});else{let $=g.charge?.id||g.paymentIntent?.id,_=g.charge?.status||"SUCCEEDED";e.onPaymentComplete({id:$,status:_,productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})}console.log("[Recur SDK] Checkout flow completed successfully!"),b.resetButton?.(),r&&(r.close(),r.remove())}catch(E){console.error("[Recur SDK] Payment error:",E);let R={code:"PAYMENT_FAILED",message:E instanceof Error?E.message:"Payment failed"};e.onError?.(R),b.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(s){console.error("[Recur SDK] Checkout error:",s),r&&(r.close(),r.remove());let a={code:"CHECKOUT_ERROR",message:s instanceof Error?s.message:"An unknown error occurred"};throw e.onError?.(a),s}}getBaseUrl(){let e=this.core.getConfig();if(e.baseUrl)return e.baseUrl;if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t.includes(".test")||t.includes(".local")||t==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}injectDialogStyles(){if(document.getElementById("recur-dialog-styles"))return;let e=document.createElement("style");e.id="recur-dialog-styles",e.textContent=`
1969
+ `),d.appendChild(f),await new Promise(y=>setTimeout(y,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await f.initializePayment(u.sdkToken,T?"SANDBOX":"PRODUCTION")===null){console.log("[Recur SDK] Initialization aborted");return}console.log("[Recur SDK] PAYUNi SDK initialized successfully"),console.log("[Recur SDK] Step 6: Setting up submit handler..."),f.addEventListener("submit",(async y=>{console.log("[Recur SDK] Form submitted");let A=y,{paymentSession:ge}=A.detail;try{let E=await ge.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let R=E.HashTimestamp||E.timestamp,Te={};if(u.checkout.productType==="SUBSCRIPTION"){let $=u.creditToken,_=u.sdkTimestamp;if(!$)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");Te={creditToken:$,timestamp:_||R},console.log("[Recur SDK] Using creditToken from checkout:",$.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",_?"from checkout (sdkTimestamp)":"from tradeResult")}let fe=await fetch(`${s}/v1/checkouts/${u.checkout.id}/pay`,{method:"POST",headers:a,body:JSON.stringify(Te)});if(!fe.ok){let $=await fe.json().catch(()=>({}));throw new Error($.error||"Failed to execute payment")}let gt=await fe.json(),g=w(gt);if(console.log("[Recur SDK] Payment executed:",g),g.requires3D&&g.redirectUrl){if(console.log("[Recur SDK] 3D verification required"),/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||/wv|WebView|FBAN|FBAV|Instagram|Line|MicroMessenger|QQ/i.test(navigator.userAgent)||/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent)||"standalone"in navigator&&navigator.standalone){console.log("[Recur SDK] Mobile/WebView detected, using redirect for 3D verification"),window.location.href=g.redirectUrl;return}console.log("[Recur SDK] Using popup for 3D verification");{let _=window.open(g.redirectUrl,"recur_3d_verification","width=500,height=700,scrollbars=yes,resizable=yes");if(!_){console.log("[Recur SDK] Popup was blocked (null), falling back to redirect"),window.location.href=g.redirectUrl;return}if(await new Promise(O=>setTimeout(O,100)),_.closed){console.log("[Recur SDK] Popup was closed immediately, falling back to redirect"),window.location.href=g.redirectUrl;return}f.setVerifying?.(!0);let Q=u.checkout.id,$e=u.checkout.clientSecret,ft=90,bt=2e3,z=!0,Ie=!1,Ae;console.log("[Recur SDK] Starting 3D verification polling...");for(let O=0;O<ft&&z;O++){if(_.closed){console.log("[Recur SDK] Popup was closed");try{let v=await fetch(`${s}/v1/checkouts/${Q}/status?client_secret=${encodeURIComponent($e)}`,{headers:a});if(v.ok){let K=await v.json();if(K.checkout?.status==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded after popup close"),z=!1,Ie=!0,Ae=K.checkout?.orderId;break}}}catch(v){console.error("[Recur SDK] Final status check failed:",v)}throw console.log("[Recur SDK] Payment not confirmed, user closed popup"),f.setVerifying?.(!1),f.resetButton?.(),new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88")}try{let v=await fetch(`${s}/v1/checkouts/${Q}/status?client_secret=${encodeURIComponent($e)}`,{headers:a});if(v.ok){let K=await v.json(),ee=K.checkout?.status;if(console.log(`[Recur SDK] Poll ${O+1}: status = ${ee}`),ee==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded"),z=!1;try{_.close()}catch{}f.setVerifying?.(!1),e.onPaymentComplete&&(g.subscription?e.onPaymentComplete({id:g.subscription.id,status:"ACTIVE",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd}):e.onPaymentComplete({id:K.checkout?.orderId||Q,status:"SUCCEEDED",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})),r&&(r.close(),r.remove());return}if(ee==="CANCELED"||ee==="FAILED"){console.log("[Recur SDK] Payment failed or canceled"),z=!1;try{_.close()}catch{}throw f.setVerifying?.(!1),f.resetButton?.(),new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")}}}catch(v){if(v.message==="3D \u9A57\u8B49\u5DF2\u53D6\u6D88"||v.message==="\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")throw v;console.error("[Recur SDK] Poll error:",v)}await new Promise(v=>setTimeout(v,bt))}if(z){console.log("[Recur SDK] 3D verification polling timeout");try{_.close()}catch{}throw f.setVerifying?.(!1),f.resetButton?.(),new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66")}Ie&&(console.log("[Recur SDK] Handling success after popup close"),f.setVerifying?.(!1),e.onPaymentComplete&&(g.subscription?e.onPaymentComplete({id:g.subscription.id,status:"ACTIVE",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd}):e.onPaymentComplete({id:Ae||Q,status:"SUCCEEDED",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})),r&&(r.close(),r.remove()));return}}if(e.onPaymentComplete)if(g.subscription)e.onPaymentComplete({id:g.subscription.id,status:g.subscription.status,productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd});else{let $=g.charge?.id||g.paymentIntent?.id,_=g.charge?.status||"SUCCEEDED";e.onPaymentComplete({id:$,status:_,productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})}console.log("[Recur SDK] Checkout flow completed successfully!"),f.resetButton?.(),r&&(r.close(),r.remove())}catch(E){console.error("[Recur SDK] Payment error:",E);let R={code:"PAYMENT_FAILED",message:E instanceof Error?E.message:"Payment failed"};e.onError?.(R),f.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(s){console.error("[Recur SDK] Checkout error:",s),r&&(r.close(),r.remove());let a={code:"CHECKOUT_ERROR",message:s instanceof Error?s.message:"An unknown error occurred"};throw e.onError?.(a),s}}getBaseUrl(){let e=this.core.getConfig();if(e.baseUrl)return e.baseUrl;if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t.includes(".test")||t.includes(".local")||t==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}injectDialogStyles(){if(document.getElementById("recur-dialog-styles"))return;let e=document.createElement("style");e.id="recur-dialog-styles",e.textContent=`
1937
1970
  .recur-dialog {
1938
1971
  border: none;
1939
1972
  border-radius: 12px;
@@ -1989,7 +2022,7 @@
1989
2022
  .recur-dialog__close:hover {
1990
2023
  background: rgba(0, 0, 0, 0.1);
1991
2024
  }
1992
- `,document.head.appendChild(e)}createDialogWithSkeleton(e){this.closeDialog(),this.injectDialogStyles();let t=document.createElement("dialog");t.id="recur-dialog",t.className="recur-dialog";let r=document.createElement("div");r.className="recur-dialog__content";let o=document.createElement("button");o.className="recur-dialog__close",o.innerHTML="\u2715",o.type="button",o.onclick=()=>{this.closeDialog(),e?.()};let i=document.createElement("div");i.id="recur-dialog-payment-container";let s=document.createElement("recur-payment-form-skeleton");return i.appendChild(s),r.appendChild(o),r.appendChild(i),t.appendChild(r),document.body.appendChild(t),t.showModal(),t.addEventListener("cancel",a=>{a.preventDefault(),this.closeDialog(),e?.()}),this.currentDialogElement=t,{dialog:t,container:i}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeDialog(){this.currentDialog&&(this.currentDialog.close(),this.currentDialog=null),this.currentDialogElement&&(this.currentDialogElement.close(),this.currentDialogElement.remove(),this.currentDialogElement=null)}closeModal(){this.closeDialog()}removeIframe(){this.currentIframe&&(this.currentIframe.remove(),this.currentIframe=null)}close(){this.closeModal(),this.removeIframe()}async createPortalSession(e,t){let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerId:t.customerId,returnUrl:t.returnUrl})});if(!r.ok){let i=await r.json().catch(()=>({}));throw new Error(i.error?.message||i.message||"Failed to create portal session")}let o=await r.json();return{id:o.id,url:o.url||o.portalUrl,expiresAt:o.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function ht(n){return new G(n)}var Bt={init:ht,RecurCheckout:G,RecurElements:B,createElements:Se};return _t(zt);})();
2025
+ `,document.head.appendChild(e)}createDialogWithSkeleton(e){this.closeDialog(),this.injectDialogStyles();let t=document.createElement("dialog");t.id="recur-dialog",t.className="recur-dialog";let r=document.createElement("div");r.className="recur-dialog__content";let o=document.createElement("button");o.className="recur-dialog__close",o.innerHTML="\u2715",o.type="button",o.onclick=()=>{this.closeDialog(),e?.()};let i=document.createElement("div");i.id="recur-dialog-payment-container";let s=document.createElement("recur-payment-form-skeleton");return i.appendChild(s),r.appendChild(o),r.appendChild(i),t.appendChild(r),document.body.appendChild(t),t.showModal(),t.addEventListener("cancel",a=>{a.preventDefault(),this.closeDialog(),e?.()}),this.currentDialogElement=t,{dialog:t,container:i}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeDialog(){this.currentDialog&&(this.currentDialog.close(),this.currentDialog=null),this.currentDialogElement&&(this.currentDialogElement.close(),this.currentDialogElement.remove(),this.currentDialogElement=null)}closeModal(){this.closeDialog()}removeIframe(){this.currentIframe&&(this.currentIframe.remove(),this.currentIframe=null)}close(){this.closeModal(),this.removeIframe()}async createPortalSession(e,t){let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerId:t.customerId,returnUrl:t.returnUrl})});if(!r.ok){let i=await r.json().catch(()=>({}));throw new Error(i.error?.message||i.message||"Failed to create portal session")}let o=await r.json();return{id:o.id,url:o.url||o.portalUrl,expiresAt:o.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function ht(n){return new G(n)}var Nt={init:ht,RecurCheckout:G,RecurElements:N,createElements:Se};return _t(zt);})();
1993
2026
  /*! Bundled license information:
1994
2027
 
1995
2028
  lit-html/node/lit-html.js:
package/dist/server.cjs CHANGED
@@ -20,7 +20,7 @@ var RecurAPIError = class extends Error {
20
20
 
21
21
  // package.json
22
22
  var package_default = {
23
- version: "0.9.8"};
23
+ version: "0.9.11"};
24
24
 
25
25
  // src/server/resources/portal.ts
26
26
  var SDK_VERSION = package_default.version;
package/dist/server.js CHANGED
@@ -18,7 +18,7 @@ var RecurAPIError = class extends Error {
18
18
 
19
19
  // package.json
20
20
  var package_default = {
21
- version: "0.9.8"};
21
+ version: "0.9.11"};
22
22
 
23
23
  // src/server/resources/portal.ts
24
24
  var SDK_VERSION = package_default.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recur-tw",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "description": "React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",
5
5
  "type": "module",
6
6
  "private": false,