recur-tw 0.13.0 → 0.13.2

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.js CHANGED
@@ -1046,6 +1046,124 @@ var init_payment_form_light = __esm({
1046
1046
  }
1047
1047
  });
1048
1048
 
1049
+ // src/utils/checkout-display.ts
1050
+ function formatZhe(basisPoints) {
1051
+ const zhe = (1e4 - basisPoints) / 1e3;
1052
+ if (Number.isInteger(zhe)) return `${zhe} \u6298`;
1053
+ const zhe10 = zhe * 10;
1054
+ if (Number.isInteger(zhe10)) return `${zhe10} \u6298`;
1055
+ return `${(zhe * 10).toFixed(2)} \u6298`;
1056
+ }
1057
+ function getIntervalText(interval, count) {
1058
+ if (!interval) return "";
1059
+ const countText = count && count > 1 ? count.toString() : "";
1060
+ switch (interval.toUpperCase()) {
1061
+ case "MONTH":
1062
+ return `${countText}\u6708`;
1063
+ case "YEAR":
1064
+ return `${countText}\u5E74`;
1065
+ case "WEEK":
1066
+ return `${countText}\u9031`;
1067
+ case "DAY":
1068
+ return `${countText}\u5929`;
1069
+ default:
1070
+ return "";
1071
+ }
1072
+ }
1073
+ function formatPrice(amount) {
1074
+ return `NT$ ${amount.toLocaleString("zh-TW")}`;
1075
+ }
1076
+ function getCheckoutDiscountDisplay(input) {
1077
+ const {
1078
+ discountType,
1079
+ couponDiscountValue,
1080
+ discountAmount,
1081
+ duration,
1082
+ durationMonths,
1083
+ productPrice,
1084
+ productInterval,
1085
+ productIntervalCount,
1086
+ bonusMonths,
1087
+ bonusTrialDays
1088
+ } = input;
1089
+ const intervalText = getIntervalText(productInterval, productIntervalCount);
1090
+ const originalPriceText = intervalText ? `${formatPrice(productPrice)}/${intervalText}` : formatPrice(productPrice);
1091
+ const bonusSuffix = buildBonusSuffix(bonusMonths, bonusTrialDays);
1092
+ if (discountAmount === 0 && (bonusMonths || bonusTrialDays)) {
1093
+ return {
1094
+ line1: buildPureBonusLine(bonusMonths, bonusTrialDays),
1095
+ line2: null
1096
+ };
1097
+ }
1098
+ if (discountAmount === 0) {
1099
+ return { line1: "", line2: null };
1100
+ }
1101
+ const afterLine = `\u4E4B\u5F8C ${originalPriceText}`;
1102
+ if (discountType === "FIRST_PERIOD_PRICE") {
1103
+ const firstPeriodPrice = couponDiscountValue;
1104
+ const priceText = firstPeriodPrice === 0 ? "\u514D\u8CBB" : `\u7279\u50F9 ${formatPrice(firstPeriodPrice)}`;
1105
+ const line1 = bonusSuffix ? `\u9996\u671F${priceText}\uFF0C${bonusSuffix}` : `\u9996\u671F${priceText}`;
1106
+ return { line1, line2: afterLine };
1107
+ }
1108
+ if (discountType === "FIXED_AMOUNT") {
1109
+ return buildStandardDiscount({
1110
+ discountText: `\u6298 ${formatPrice(couponDiscountValue)}`,
1111
+ duration,
1112
+ durationMonths,
1113
+ bonusSuffix,
1114
+ afterLine
1115
+ });
1116
+ }
1117
+ if (discountType === "PERCENTAGE") {
1118
+ return buildStandardDiscount({
1119
+ discountText: `\u4EAB ${formatZhe(couponDiscountValue)}`,
1120
+ duration,
1121
+ durationMonths,
1122
+ bonusSuffix,
1123
+ afterLine
1124
+ });
1125
+ }
1126
+ return { line1: "", line2: null };
1127
+ }
1128
+ function buildStandardDiscount(opts) {
1129
+ const { discountText, duration, durationMonths, bonusSuffix, afterLine } = opts;
1130
+ switch (duration) {
1131
+ case "ONCE": {
1132
+ const base = `\u9996\u671F${discountText}`;
1133
+ const line1 = bonusSuffix ? `${base}\uFF0C${bonusSuffix}` : base;
1134
+ return { line1, line2: afterLine };
1135
+ }
1136
+ case "REPEATING": {
1137
+ const periods = durationMonths ?? 0;
1138
+ const line1 = `\u524D ${periods} \u671F\u6BCF\u671F${discountText}`;
1139
+ return { line1, line2: afterLine };
1140
+ }
1141
+ case "FOREVER": {
1142
+ const line1 = `\u6BCF\u671F${discountText}`;
1143
+ return { line1, line2: null };
1144
+ }
1145
+ default:
1146
+ return { line1: "", line2: null };
1147
+ }
1148
+ }
1149
+ function buildBonusSuffix(bonusMonths, bonusTrialDays) {
1150
+ const parts = [];
1151
+ if (bonusMonths) parts.push(`\u52A0\u9001 ${bonusMonths} \u500B\u6708`);
1152
+ if (bonusTrialDays) parts.push(`\u5EF6\u9577 ${bonusTrialDays} \u5929\u8A66\u7528`);
1153
+ if (parts.length === 0) return null;
1154
+ return `\u4E26${parts.join("\u53CA")}`;
1155
+ }
1156
+ function buildPureBonusLine(bonusMonths, bonusTrialDays) {
1157
+ const parts = [];
1158
+ if (bonusMonths) parts.push(`\u52A0\u9001 ${bonusMonths} \u500B\u6708`);
1159
+ if (bonusTrialDays) parts.push(`\u5EF6\u9577\u8A66\u7528 ${bonusTrialDays} \u5929`);
1160
+ return parts.join("\u53CA");
1161
+ }
1162
+ var init_checkout_display = __esm({
1163
+ "src/utils/checkout-display.ts"() {
1164
+ }
1165
+ });
1166
+
1049
1167
  // src/components/payment-form.ts
1050
1168
  var payment_form_exports = {};
1051
1169
  __export(payment_form_exports, {
@@ -1056,6 +1174,7 @@ var init_payment_form = __esm({
1056
1174
  "src/components/payment-form.ts"() {
1057
1175
  init_payment_form_shadow();
1058
1176
  init_payment_form_light();
1177
+ init_checkout_display();
1059
1178
  icons = {
1060
1179
  lock: html`
1061
1180
  <svg class="security-icon" width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1359,73 +1478,26 @@ var init_payment_form = __esm({
1359
1478
  </div>
1360
1479
  `;
1361
1480
  }
1362
- // Helper to get interval text in Chinese
1363
- getIntervalText(interval, count) {
1364
- if (!interval) return "";
1365
- const countText = count && count > 1 ? count.toString() : "";
1366
- switch (interval.toUpperCase()) {
1367
- case "MONTH":
1368
- return `${countText}\u6708`;
1369
- case "YEAR":
1370
- return `${countText}\u5E74`;
1371
- case "WEEK":
1372
- return `${countText}\u9031`;
1373
- case "DAY":
1374
- return `${countText}\u5929`;
1375
- default:
1376
- return "";
1377
- }
1378
- }
1379
- // Get discount description based on type and duration
1380
- getDiscountDescription() {
1481
+ // Get unified discount display using shared function
1482
+ getDiscountDisplay() {
1381
1483
  if (!this._appliedCoupon) return null;
1382
- const { discountType, duration, durationMonths, productPrice, productInterval, productIntervalCount } = this._appliedCoupon;
1383
- const intervalText = this.getIntervalText(productInterval, productIntervalCount);
1384
- const originalPriceText = productPrice && intervalText ? `NT$ ${productPrice.toLocaleString()}/${intervalText}` : productPrice ? `NT$ ${productPrice.toLocaleString()}` : "";
1385
1484
  const amount = parseInt(this.getAttribute("amount") || "0", 10);
1386
- const finalAmount = amount - this._appliedCoupon.discountAmount;
1387
- if (discountType === "FIRST_PERIOD_PRICE") {
1388
- if (originalPriceText) {
1389
- return `\u9996\u671F\u53EA\u9700 NT$ ${finalAmount.toLocaleString()}\uFF0C\u4E4B\u5F8C ${originalPriceText}`;
1390
- }
1391
- return `\u9996\u671F\u53EA\u9700 NT$ ${finalAmount.toLocaleString()}`;
1392
- }
1393
- if (duration) {
1394
- switch (duration) {
1395
- case "ONCE":
1396
- if (originalPriceText) {
1397
- return `\u9996\u671F\u6298\u6263\uFF0C\u4E4B\u5F8C ${originalPriceText}`;
1398
- }
1399
- return "\u50C5\u9650\u9996\u671F";
1400
- case "REPEATING":
1401
- if (durationMonths) {
1402
- return `\u524D ${durationMonths} \u671F\u4EAB\u512A\u60E0\uFF0C\u4E4B\u5F8C\u6062\u5FA9\u539F\u50F9`;
1403
- }
1404
- return null;
1405
- case "FOREVER":
1406
- return "\u7D42\u8EAB\u512A\u60E0";
1407
- default:
1408
- return null;
1409
- }
1410
- }
1411
- return null;
1412
- }
1413
- // Get bonus description
1414
- getBonusDescription() {
1415
- if (!this._appliedCoupon) return null;
1416
- const parts = [];
1417
- if (this._appliedCoupon.bonusTrialDays) {
1418
- parts.push(`\u5EF6\u9577\u8A66\u7528 ${this._appliedCoupon.bonusTrialDays} \u5929`);
1419
- }
1420
- if (this._appliedCoupon.bonusMonths) {
1421
- parts.push(`\u52A0\u9001 ${this._appliedCoupon.bonusMonths} \u500B\u6708`);
1422
- }
1423
- return parts.length > 0 ? parts.join("\u3001") : null;
1485
+ return getCheckoutDiscountDisplay({
1486
+ discountType: this._appliedCoupon.discountType,
1487
+ couponDiscountValue: this._appliedCoupon.couponDiscountValue,
1488
+ discountAmount: this._appliedCoupon.discountAmount,
1489
+ duration: this._appliedCoupon.duration || "ONCE",
1490
+ durationMonths: this._appliedCoupon.durationMonths,
1491
+ productPrice: this._appliedCoupon.productPrice ?? amount,
1492
+ productInterval: this._appliedCoupon.productInterval,
1493
+ productIntervalCount: this._appliedCoupon.productIntervalCount,
1494
+ bonusMonths: this._appliedCoupon.bonusMonths,
1495
+ bonusTrialDays: this._appliedCoupon.bonusTrialDays
1496
+ });
1424
1497
  }
1425
1498
  renderCoupon() {
1426
1499
  if (this._appliedCoupon) {
1427
- const discountDescription = this.getDiscountDescription();
1428
- const bonusDescription = this.getBonusDescription();
1500
+ const display = this.getDiscountDisplay();
1429
1501
  return html`
1430
1502
  <div class="recur-coupon__applied-wrapper">
1431
1503
  <div class="recur-coupon__applied">
@@ -1446,18 +1518,17 @@ var init_payment_form = __esm({
1446
1518
  ${this._couponLoading ? icons.spinner : icons.close}
1447
1519
  </button>
1448
1520
  </div>
1449
- <span class="recur-coupon__applied-amount">
1450
- -NT$ ${this._appliedCoupon.discountAmount.toLocaleString()}
1451
- </span>
1521
+ ${this._appliedCoupon.discountAmount > 0 ? html`
1522
+ <span class="recur-coupon__applied-amount">
1523
+ -NT$ ${this._appliedCoupon.discountAmount.toLocaleString()}
1524
+ </span>
1525
+ ` : nothing}
1452
1526
  </div>
1453
- ${discountDescription ? html`
1454
- <p class="recur-coupon__description">${discountDescription}</p>
1527
+ ${display?.line1 ? html`
1528
+ <p class="recur-coupon__description">${display.line1}</p>
1455
1529
  ` : nothing}
1456
- ${bonusDescription ? html`
1457
- <div class="recur-coupon__bonus">
1458
- <span>✨</span>
1459
- <span>${bonusDescription}</span>
1460
- </div>
1530
+ ${display?.line2 ? html`
1531
+ <p class="recur-coupon__description">${display.line2}</p>
1461
1532
  ` : nothing}
1462
1533
  </div>
1463
1534
  `;
@@ -1620,10 +1691,10 @@ var init_payment_form = __esm({
1620
1691
  getCouponAppliedHTML() {
1621
1692
  const coupon = this._appliedCoupon;
1622
1693
  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>`;
1623
- const discountDescription = this.getDiscountDescription();
1624
- const bonusDescription = this.getBonusDescription();
1625
- const descriptionHTML = discountDescription ? `<p class="recur-coupon__description">${discountDescription}</p>` : "";
1626
- const bonusHTML = bonusDescription ? `<div class="recur-coupon__bonus"><span>\u2728</span><span>${bonusDescription}</span></div>` : "";
1694
+ const display = this.getDiscountDisplay();
1695
+ const amountHTML = coupon.discountAmount > 0 ? `<span class="recur-coupon__applied-amount">-NT$ ${coupon.discountAmount.toLocaleString()}</span>` : "";
1696
+ const line1HTML = display?.line1 ? `<p class="recur-coupon__description">${display.line1}</p>` : "";
1697
+ const line2HTML = display?.line2 ? `<p class="recur-coupon__description">${display.line2}</p>` : "";
1627
1698
  return `
1628
1699
  <div class="recur-coupon__applied-wrapper">
1629
1700
  <div class="recur-coupon__applied">
@@ -1636,10 +1707,10 @@ var init_payment_form = __esm({
1636
1707
  ${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>'}
1637
1708
  </button>
1638
1709
  </div>
1639
- <span class="recur-coupon__applied-amount">-NT$ ${coupon.discountAmount.toLocaleString()}</span>
1710
+ ${amountHTML}
1640
1711
  </div>
1641
- ${descriptionHTML}
1642
- ${bonusHTML}
1712
+ ${line1HTML}
1713
+ ${line2HTML}
1643
1714
  </div>
1644
1715
  `;
1645
1716
  }
@@ -1711,6 +1782,7 @@ var init_payment_form = __esm({
1711
1782
  couponName: data.discount.coupon_name,
1712
1783
  discountType: data.discount.discount_type,
1713
1784
  discountAmount: data.discount.discount_amount,
1785
+ couponDiscountValue: data.discount.coupon_discount_value ?? data.discount.discount_amount,
1714
1786
  duration: data.discount.duration,
1715
1787
  durationMonths: data.discount.duration_months,
1716
1788
  bonusTrialDays: data.discount.bonus_trial_days,
@@ -2736,7 +2808,7 @@ function toCamelCase(obj) {
2736
2808
 
2737
2809
  // package.json
2738
2810
  var package_default = {
2739
- version: "0.13.0"};
2811
+ version: "0.13.2"};
2740
2812
  var SDK_VERSION = package_default.version;
2741
2813
  var SDK_TYPE = "react";
2742
2814
  var RecurContext = createContext(null);
@@ -2955,6 +3027,8 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
2955
3027
  dialog.className = "recur-sdk__dialog";
2956
3028
  dialogContent = document.createElement("div");
2957
3029
  dialogContent.className = "recur-sdk__dialog-content";
3030
+ dialogContent.setAttribute("tabindex", "-1");
3031
+ dialogContent.setAttribute("autofocus", "");
2958
3032
  dialogContent.style.cssText = `
2959
3033
  max-width: 500px;
2960
3034
  width: 100%;
@@ -2965,6 +3039,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
2965
3039
  border-radius: 12px;
2966
3040
  background: white;
2967
3041
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
3042
+ outline: none;
2968
3043
  `;
2969
3044
  const closeButton = document.createElement("button");
2970
3045
  closeButton.className = "recur-sdk__close-button";