recur-tw 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +529 -19
- package/dist/index.d.cts +129 -5
- package/dist/index.d.ts +129 -5
- package/dist/index.js +523 -19
- package/dist/recur.umd.js +199 -60
- package/dist/server.cjs +5 -5
- package/dist/server.js +5 -5
- package/package.json +30 -11
package/dist/index.cjs
CHANGED
|
@@ -757,7 +757,7 @@ var init_payment_form_skeleton = __esmMin((() => {
|
|
|
757
757
|
}));
|
|
758
758
|
|
|
759
759
|
//#endregion
|
|
760
|
-
//#region \0@oxc-project+runtime@0.
|
|
760
|
+
//#region \0@oxc-project+runtime@0.140.0/helpers/esm/typeof.js
|
|
761
761
|
function _typeof(o) {
|
|
762
762
|
"@babel/helpers - typeof";
|
|
763
763
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
@@ -769,7 +769,7 @@ function _typeof(o) {
|
|
|
769
769
|
var init_typeof = __esmMin((() => {}));
|
|
770
770
|
|
|
771
771
|
//#endregion
|
|
772
|
-
//#region \0@oxc-project+runtime@0.
|
|
772
|
+
//#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPrimitive.js
|
|
773
773
|
function toPrimitive(t, r) {
|
|
774
774
|
if ("object" != _typeof(t) || !t) return t;
|
|
775
775
|
var e = t[Symbol.toPrimitive];
|
|
@@ -785,7 +785,7 @@ var init_toPrimitive = __esmMin((() => {
|
|
|
785
785
|
}));
|
|
786
786
|
|
|
787
787
|
//#endregion
|
|
788
|
-
//#region \0@oxc-project+runtime@0.
|
|
788
|
+
//#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPropertyKey.js
|
|
789
789
|
function toPropertyKey(t) {
|
|
790
790
|
var i = toPrimitive(t, "string");
|
|
791
791
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
@@ -796,7 +796,7 @@ var init_toPropertyKey = __esmMin((() => {
|
|
|
796
796
|
}));
|
|
797
797
|
|
|
798
798
|
//#endregion
|
|
799
|
-
//#region \0@oxc-project+runtime@0.
|
|
799
|
+
//#region \0@oxc-project+runtime@0.140.0/helpers/esm/defineProperty.js
|
|
800
800
|
function _defineProperty(e, r, t) {
|
|
801
801
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
802
802
|
value: t,
|
|
@@ -1067,6 +1067,410 @@ var init_toast = __esmMin((() => {
|
|
|
1067
1067
|
if (typeof window !== "undefined" && !customElements.get("recur-toast-container")) customElements.define("recur-toast-container", RecurToastContainer);
|
|
1068
1068
|
}));
|
|
1069
1069
|
|
|
1070
|
+
//#endregion
|
|
1071
|
+
//#region src/einvoice.ts
|
|
1072
|
+
/**
|
|
1073
|
+
* 統一編號 checksum (post-2023 rule: weighted digit-sum divisible by 5, with
|
|
1074
|
+
* the 7-in-7th-digit alternate). Providers reject checksum-invalid UBNs at
|
|
1075
|
+
* issue time as a non-retryable failure, so the SDK catches this at entry.
|
|
1076
|
+
*/
|
|
1077
|
+
function isValidTaiwanUbn(ubn) {
|
|
1078
|
+
if (!UBN_RE.test(ubn)) return false;
|
|
1079
|
+
const weights = [
|
|
1080
|
+
1,
|
|
1081
|
+
2,
|
|
1082
|
+
1,
|
|
1083
|
+
2,
|
|
1084
|
+
1,
|
|
1085
|
+
2,
|
|
1086
|
+
4,
|
|
1087
|
+
1
|
|
1088
|
+
];
|
|
1089
|
+
let sum = 0;
|
|
1090
|
+
for (let i = 0; i < 8; i++) {
|
|
1091
|
+
const product = Number(ubn.charAt(i)) * (weights[i] ?? 0);
|
|
1092
|
+
sum += Math.floor(product / 10) + product % 10;
|
|
1093
|
+
}
|
|
1094
|
+
if (sum % 5 === 0) return true;
|
|
1095
|
+
return ubn.charAt(6) === "7" && (sum + 1) % 5 === 0;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Validate buyer invoice preferences.
|
|
1099
|
+
*
|
|
1100
|
+
* @returns an error message, or `null` when the prefs are valid
|
|
1101
|
+
*/
|
|
1102
|
+
function validateEinvoicePrefs(prefs) {
|
|
1103
|
+
if (typeof prefs !== "object" || prefs === null) return "Invalid einvoice prefs: expected an object like { type: \"personal\" }";
|
|
1104
|
+
switch (prefs.type) {
|
|
1105
|
+
case "personal": return null;
|
|
1106
|
+
case "mobile_barcode": return MOBILE_BARCODE_RE.test(prefs.carrierCode) ? null : "Invalid einvoice mobile barcode: must be \"/\" followed by 7 characters (0-9, A-Z, ., +, -)";
|
|
1107
|
+
case "ubn":
|
|
1108
|
+
if (!isValidTaiwanUbn(prefs.ubn)) return "Invalid einvoice UBN (統一編號): must be 8 digits with a valid checksum";
|
|
1109
|
+
if (!prefs.buyerName || !prefs.buyerName.trim() || prefs.buyerName.trim().length > 60) return "Invalid einvoice buyerName (公司抬頭): required, at most 60 characters";
|
|
1110
|
+
return null;
|
|
1111
|
+
case "donation": return DONATION_CODE_RE.test(prefs.donationCode) ? null : "Invalid einvoice donation code (愛心碼): must be 3-7 digits";
|
|
1112
|
+
default: return `Invalid einvoice type: ${String(prefs.type)}`;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
/** Throw when buyer invoice preferences are invalid (fail fast, before the API call). */
|
|
1116
|
+
function assertValidEinvoicePrefs(prefs) {
|
|
1117
|
+
const error = validateEinvoicePrefs(prefs);
|
|
1118
|
+
if (error) throw new Error(error);
|
|
1119
|
+
}
|
|
1120
|
+
var MOBILE_BARCODE_RE, UBN_RE, DONATION_CODE_RE;
|
|
1121
|
+
var init_einvoice = __esmMin((() => {
|
|
1122
|
+
MOBILE_BARCODE_RE = /^\/[0-9A-Z.+-]{7}$/;
|
|
1123
|
+
UBN_RE = /^\d{8}$/;
|
|
1124
|
+
DONATION_CODE_RE = /^\d{3,7}$/;
|
|
1125
|
+
}));
|
|
1126
|
+
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region src/components/einvoice-section.ts
|
|
1129
|
+
/**
|
|
1130
|
+
* <recur-einvoice-section> — Taiwan e-invoice (電子發票) buyer preferences
|
|
1131
|
+
*
|
|
1132
|
+
* Rendered inside <recur-payment-form> when the organization has an active
|
|
1133
|
+
* e-invoice integration (einvoice-enabled attribute), mirroring the hosted
|
|
1134
|
+
* checkout's 發票資訊 section: four choices (個人 / 手機條碼 / 統編 / 捐贈)
|
|
1135
|
+
* with per-type fields, hints, and blur-triggered validation.
|
|
1136
|
+
*
|
|
1137
|
+
* Attributes:
|
|
1138
|
+
* - defaults: JSON-encoded EinvoicePrefs used as the initial selection
|
|
1139
|
+
*
|
|
1140
|
+
* API:
|
|
1141
|
+
* - getValidatedPrefs(): EinvoicePrefs | null — validates (surfacing inline
|
|
1142
|
+
* errors) and returns the prefs, or null when invalid
|
|
1143
|
+
* - einvoice-change CustomEvent (composed) fired on every state change with
|
|
1144
|
+
* detail.prefs (null while the current input is invalid)
|
|
1145
|
+
*
|
|
1146
|
+
* Self-contained Shadow DOM: no third-party SDK needs to reach these inputs
|
|
1147
|
+
* (unlike the PAYUNi card containers, which must live in Light DOM).
|
|
1148
|
+
*/
|
|
1149
|
+
var einvoice_section_exports = /* @__PURE__ */ __exportAll({ RecurEinvoiceSection: () => RecurEinvoiceSection });
|
|
1150
|
+
var OPTIONS, styles, RecurEinvoiceSection;
|
|
1151
|
+
var init_einvoice_section = __esmMin((() => {
|
|
1152
|
+
init_einvoice();
|
|
1153
|
+
init_defineProperty();
|
|
1154
|
+
OPTIONS = [
|
|
1155
|
+
{
|
|
1156
|
+
value: "personal",
|
|
1157
|
+
label: "個人電子發票",
|
|
1158
|
+
caption: "發票將寄送至您的 Email"
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
value: "mobile_barcode",
|
|
1162
|
+
label: "手機條碼載具",
|
|
1163
|
+
caption: "發票存入您的手機條碼"
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
value: "ubn",
|
|
1167
|
+
label: "公司統編(三聯式)",
|
|
1168
|
+
caption: "開立含統編的三聯式發票"
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
value: "donation",
|
|
1172
|
+
label: "捐贈發票",
|
|
1173
|
+
caption: "將發票捐贈給社福機構"
|
|
1174
|
+
}
|
|
1175
|
+
];
|
|
1176
|
+
styles = `
|
|
1177
|
+
:host {
|
|
1178
|
+
display: block;
|
|
1179
|
+
font-family: inherit;
|
|
1180
|
+
color: var(--recur-text-color, #1f2937);
|
|
1181
|
+
}
|
|
1182
|
+
.einvoice-options { display: flex; flex-direction: column; gap: 8px; }
|
|
1183
|
+
.einvoice-option {
|
|
1184
|
+
display: flex; align-items: center; gap: 10px;
|
|
1185
|
+
padding: 10px 12px;
|
|
1186
|
+
border: 1px solid var(--recur-border-color, #e5e7eb);
|
|
1187
|
+
border-radius: var(--recur-border-radius, 8px);
|
|
1188
|
+
cursor: pointer;
|
|
1189
|
+
background: var(--recur-bg-color, #fff);
|
|
1190
|
+
transition: border-color 0.15s;
|
|
1191
|
+
text-align: left;
|
|
1192
|
+
width: 100%;
|
|
1193
|
+
font: inherit;
|
|
1194
|
+
}
|
|
1195
|
+
.einvoice-option[aria-checked="true"] {
|
|
1196
|
+
border-color: var(--recur-primary-color, #2563eb);
|
|
1197
|
+
box-shadow: 0 0 0 1px var(--recur-primary-color, #2563eb);
|
|
1198
|
+
}
|
|
1199
|
+
.einvoice-option .radio-dot {
|
|
1200
|
+
width: 16px; height: 16px; border-radius: 50%;
|
|
1201
|
+
border: 1.5px solid var(--recur-border-color, #d1d5db);
|
|
1202
|
+
flex-shrink: 0; position: relative;
|
|
1203
|
+
}
|
|
1204
|
+
.einvoice-option[aria-checked="true"] .radio-dot {
|
|
1205
|
+
border-color: var(--recur-primary-color, #2563eb);
|
|
1206
|
+
}
|
|
1207
|
+
.einvoice-option[aria-checked="true"] .radio-dot::after {
|
|
1208
|
+
content: ''; position: absolute; inset: 3px;
|
|
1209
|
+
border-radius: 50%; background: var(--recur-primary-color, #2563eb);
|
|
1210
|
+
}
|
|
1211
|
+
.option-label { font-size: 14px; font-weight: 500; }
|
|
1212
|
+
.option-caption { font-size: 12px; color: var(--recur-muted-color, #6b7280); }
|
|
1213
|
+
.einvoice-fields { margin-top: 10px; display: flex; flex-direction: column; gap: 10px; }
|
|
1214
|
+
.field label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 4px; }
|
|
1215
|
+
.field input {
|
|
1216
|
+
width: 100%; box-sizing: border-box;
|
|
1217
|
+
padding: 8px 10px; font-size: 14px; font-family: inherit;
|
|
1218
|
+
border: 1px solid var(--recur-border-color, #e5e7eb);
|
|
1219
|
+
border-radius: var(--recur-border-radius, 8px);
|
|
1220
|
+
background: var(--recur-bg-color, #fff);
|
|
1221
|
+
color: inherit;
|
|
1222
|
+
}
|
|
1223
|
+
.field input:focus {
|
|
1224
|
+
outline: none;
|
|
1225
|
+
border-color: var(--recur-primary-color, #2563eb);
|
|
1226
|
+
box-shadow: 0 0 0 1px var(--recur-primary-color, #2563eb);
|
|
1227
|
+
}
|
|
1228
|
+
.field input[aria-invalid="true"] { border-color: var(--recur-error-color, #dc2626); }
|
|
1229
|
+
.field .hint { font-size: 12px; color: var(--recur-muted-color, #6b7280); margin-top: 4px; }
|
|
1230
|
+
.field .error { font-size: 12px; color: var(--recur-error-color, #dc2626); margin-top: 4px; }
|
|
1231
|
+
`;
|
|
1232
|
+
RecurEinvoiceSection = class extends HTMLElement {
|
|
1233
|
+
constructor() {
|
|
1234
|
+
super();
|
|
1235
|
+
_defineProperty(this, "selectedType", "personal");
|
|
1236
|
+
_defineProperty(this, "values", {
|
|
1237
|
+
carrierCode: "",
|
|
1238
|
+
ubn: "",
|
|
1239
|
+
buyerName: "",
|
|
1240
|
+
donationCode: ""
|
|
1241
|
+
});
|
|
1242
|
+
_defineProperty(this, "touched", {
|
|
1243
|
+
carrierCode: false,
|
|
1244
|
+
ubn: false,
|
|
1245
|
+
buyerName: false,
|
|
1246
|
+
donationCode: false
|
|
1247
|
+
});
|
|
1248
|
+
this.attachShadow({ mode: "open" });
|
|
1249
|
+
}
|
|
1250
|
+
static get observedAttributes() {
|
|
1251
|
+
return ["defaults"];
|
|
1252
|
+
}
|
|
1253
|
+
connectedCallback() {
|
|
1254
|
+
this.applyDefaults(this.getAttribute("defaults"));
|
|
1255
|
+
this.render();
|
|
1256
|
+
}
|
|
1257
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1258
|
+
if (name === "defaults" && oldValue !== newValue) {
|
|
1259
|
+
this.applyDefaults(newValue);
|
|
1260
|
+
if (this.isConnected) this.render();
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
applyDefaults(raw) {
|
|
1264
|
+
if (!raw) return;
|
|
1265
|
+
try {
|
|
1266
|
+
const prefs = JSON.parse(raw);
|
|
1267
|
+
this.selectedType = "personal";
|
|
1268
|
+
switch (prefs.type) {
|
|
1269
|
+
case "mobile_barcode":
|
|
1270
|
+
this.selectedType = "mobile_barcode";
|
|
1271
|
+
this.values.carrierCode = "carrierCode" in prefs && prefs.carrierCode || "";
|
|
1272
|
+
break;
|
|
1273
|
+
case "ubn":
|
|
1274
|
+
this.selectedType = "ubn";
|
|
1275
|
+
this.values.ubn = "ubn" in prefs && prefs.ubn || "";
|
|
1276
|
+
this.values.buyerName = "buyerName" in prefs && prefs.buyerName || "";
|
|
1277
|
+
break;
|
|
1278
|
+
case "donation":
|
|
1279
|
+
this.selectedType = "donation";
|
|
1280
|
+
this.values.donationCode = "donationCode" in prefs && prefs.donationCode || "";
|
|
1281
|
+
break;
|
|
1282
|
+
case "personal":
|
|
1283
|
+
this.selectedType = "personal";
|
|
1284
|
+
break;
|
|
1285
|
+
}
|
|
1286
|
+
} catch {}
|
|
1287
|
+
}
|
|
1288
|
+
buildPrefs() {
|
|
1289
|
+
switch (this.selectedType) {
|
|
1290
|
+
case "mobile_barcode": return {
|
|
1291
|
+
type: "mobile_barcode",
|
|
1292
|
+
carrierCode: this.values.carrierCode
|
|
1293
|
+
};
|
|
1294
|
+
case "ubn": return {
|
|
1295
|
+
type: "ubn",
|
|
1296
|
+
ubn: this.values.ubn,
|
|
1297
|
+
buyerName: this.values.buyerName.trim()
|
|
1298
|
+
};
|
|
1299
|
+
case "donation": return {
|
|
1300
|
+
type: "donation",
|
|
1301
|
+
donationCode: this.values.donationCode
|
|
1302
|
+
};
|
|
1303
|
+
default: return { type: "personal" };
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
fieldError(field) {
|
|
1307
|
+
switch (field) {
|
|
1308
|
+
case "carrierCode":
|
|
1309
|
+
if (this.selectedType !== "mobile_barcode") return null;
|
|
1310
|
+
return MOBILE_BARCODE_RE.test(this.values.carrierCode) ? null : "手機條碼格式錯誤(/ 開頭共 8 碼)";
|
|
1311
|
+
case "ubn":
|
|
1312
|
+
if (this.selectedType !== "ubn") return null;
|
|
1313
|
+
if (!UBN_RE.test(this.values.ubn)) return "統一編號須為 8 位數字";
|
|
1314
|
+
return isValidTaiwanUbn(this.values.ubn) ? null : "統一編號檢查碼錯誤,請確認輸入是否正確";
|
|
1315
|
+
case "buyerName":
|
|
1316
|
+
if (this.selectedType !== "ubn") return null;
|
|
1317
|
+
return this.values.buyerName.trim() ? null : "請填寫公司抬頭";
|
|
1318
|
+
case "donationCode":
|
|
1319
|
+
if (this.selectedType !== "donation") return null;
|
|
1320
|
+
return DONATION_CODE_RE.test(this.values.donationCode) ? null : "愛心碼須為 3-7 位數字";
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
isValid() {
|
|
1324
|
+
return [
|
|
1325
|
+
"carrierCode",
|
|
1326
|
+
"ubn",
|
|
1327
|
+
"buyerName",
|
|
1328
|
+
"donationCode"
|
|
1329
|
+
].every((field) => this.fieldError(field) === null);
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Validate the current selection, surfacing inline errors.
|
|
1333
|
+
* @returns the prefs when valid, null otherwise
|
|
1334
|
+
*/
|
|
1335
|
+
getValidatedPrefs() {
|
|
1336
|
+
this.touched = {
|
|
1337
|
+
carrierCode: true,
|
|
1338
|
+
ubn: true,
|
|
1339
|
+
buyerName: true,
|
|
1340
|
+
donationCode: true
|
|
1341
|
+
};
|
|
1342
|
+
this.render();
|
|
1343
|
+
return this.isValid() ? this.buildPrefs() : null;
|
|
1344
|
+
}
|
|
1345
|
+
emitChange() {
|
|
1346
|
+
this.dispatchEvent(new CustomEvent("einvoice-change", {
|
|
1347
|
+
detail: { prefs: this.isValid() ? this.buildPrefs() : null },
|
|
1348
|
+
bubbles: true,
|
|
1349
|
+
composed: true
|
|
1350
|
+
}));
|
|
1351
|
+
}
|
|
1352
|
+
selectType(type) {
|
|
1353
|
+
if (this.selectedType === type) return;
|
|
1354
|
+
this.selectedType = type;
|
|
1355
|
+
this.render();
|
|
1356
|
+
this.emitChange();
|
|
1357
|
+
}
|
|
1358
|
+
/** ARIA radio keyboard pattern: arrow keys move selection (with wrap). */
|
|
1359
|
+
onOptionKeydown(event, index) {
|
|
1360
|
+
const delta = event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : event.key === "ArrowUp" || event.key === "ArrowLeft" ? -1 : 0;
|
|
1361
|
+
if (delta === 0) return;
|
|
1362
|
+
event.preventDefault();
|
|
1363
|
+
const next = (index + delta + OPTIONS.length) % OPTIONS.length;
|
|
1364
|
+
this.selectType(OPTIONS[next].value);
|
|
1365
|
+
this.shadowRoot.querySelectorAll("[role=\"radio\"]")[next]?.focus();
|
|
1366
|
+
}
|
|
1367
|
+
onInput(field, event) {
|
|
1368
|
+
const input = event.target;
|
|
1369
|
+
let value = input.value;
|
|
1370
|
+
if (field === "carrierCode") {
|
|
1371
|
+
value = value.toUpperCase();
|
|
1372
|
+
input.value = value;
|
|
1373
|
+
}
|
|
1374
|
+
if (field === "ubn" || field === "donationCode") {
|
|
1375
|
+
value = value.replace(/\D/g, "");
|
|
1376
|
+
input.value = value;
|
|
1377
|
+
}
|
|
1378
|
+
this.values[field] = value;
|
|
1379
|
+
this.emitChange();
|
|
1380
|
+
}
|
|
1381
|
+
onBlur(field) {
|
|
1382
|
+
this.touched[field] = true;
|
|
1383
|
+
this.render();
|
|
1384
|
+
}
|
|
1385
|
+
renderField(opts) {
|
|
1386
|
+
const { field, label, placeholder, hint, maxLength, inputMode } = opts;
|
|
1387
|
+
const error = this.touched[field] ? this.fieldError(field) : null;
|
|
1388
|
+
return lit_html.html`
|
|
1389
|
+
<div class="field">
|
|
1390
|
+
<label for="einvoice-${field}">${label}</label>
|
|
1391
|
+
<input
|
|
1392
|
+
id="einvoice-${field}"
|
|
1393
|
+
type="text"
|
|
1394
|
+
.value=${this.values[field]}
|
|
1395
|
+
placeholder=${placeholder}
|
|
1396
|
+
maxlength=${maxLength ?? lit_html.nothing}
|
|
1397
|
+
inputmode=${inputMode ?? lit_html.nothing}
|
|
1398
|
+
aria-invalid=${error ? "true" : "false"}
|
|
1399
|
+
@input=${(e) => this.onInput(field, e)}
|
|
1400
|
+
@blur=${() => this.onBlur(field)}
|
|
1401
|
+
/>
|
|
1402
|
+
${error ? lit_html.html`<p class="error" role="alert">${error}</p>` : hint ? lit_html.html`<p class="hint">${hint}</p>` : lit_html.nothing}
|
|
1403
|
+
</div>
|
|
1404
|
+
`;
|
|
1405
|
+
}
|
|
1406
|
+
renderFields() {
|
|
1407
|
+
switch (this.selectedType) {
|
|
1408
|
+
case "mobile_barcode": return lit_html.html`<div class="einvoice-fields">
|
|
1409
|
+
${this.renderField({
|
|
1410
|
+
field: "carrierCode",
|
|
1411
|
+
label: "手機條碼",
|
|
1412
|
+
placeholder: "/ABC1234",
|
|
1413
|
+
hint: "手機條碼共 8 碼,以 / 開頭",
|
|
1414
|
+
maxLength: 8
|
|
1415
|
+
})}
|
|
1416
|
+
</div>`;
|
|
1417
|
+
case "ubn": return lit_html.html`<div class="einvoice-fields">
|
|
1418
|
+
${this.renderField({
|
|
1419
|
+
field: "ubn",
|
|
1420
|
+
label: "統一編號",
|
|
1421
|
+
placeholder: "12345675",
|
|
1422
|
+
maxLength: 8,
|
|
1423
|
+
inputMode: "numeric"
|
|
1424
|
+
})}
|
|
1425
|
+
${this.renderField({
|
|
1426
|
+
field: "buyerName",
|
|
1427
|
+
label: "公司抬頭",
|
|
1428
|
+
placeholder: "公司名稱",
|
|
1429
|
+
maxLength: 60
|
|
1430
|
+
})}
|
|
1431
|
+
</div>`;
|
|
1432
|
+
case "donation": return lit_html.html`<div class="einvoice-fields">
|
|
1433
|
+
${this.renderField({
|
|
1434
|
+
field: "donationCode",
|
|
1435
|
+
label: "愛心碼",
|
|
1436
|
+
placeholder: "25885",
|
|
1437
|
+
hint: "例如:25885(家扶基金會)、8957(創世基金會)",
|
|
1438
|
+
maxLength: 7,
|
|
1439
|
+
inputMode: "numeric"
|
|
1440
|
+
})}
|
|
1441
|
+
</div>`;
|
|
1442
|
+
default: return lit_html.nothing;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
render() {
|
|
1446
|
+
(0, lit_html.render)(lit_html.html`
|
|
1447
|
+
<style>${styles}</style>
|
|
1448
|
+
<div class="einvoice-options" role="radiogroup" aria-label="發票類型">
|
|
1449
|
+
${OPTIONS.map((option, index) => lit_html.html`
|
|
1450
|
+
<button
|
|
1451
|
+
type="button"
|
|
1452
|
+
class="einvoice-option"
|
|
1453
|
+
role="radio"
|
|
1454
|
+
aria-checked=${this.selectedType === option.value ? "true" : "false"}
|
|
1455
|
+
tabindex=${this.selectedType === option.value ? "0" : "-1"}
|
|
1456
|
+
@click=${() => this.selectType(option.value)}
|
|
1457
|
+
@keydown=${(e) => this.onOptionKeydown(e, index)}
|
|
1458
|
+
>
|
|
1459
|
+
<span class="radio-dot"></span>
|
|
1460
|
+
<span>
|
|
1461
|
+
<span class="option-label">${option.label}</span><br />
|
|
1462
|
+
<span class="option-caption">${option.caption}</span>
|
|
1463
|
+
</span>
|
|
1464
|
+
</button>
|
|
1465
|
+
`)}
|
|
1466
|
+
</div>
|
|
1467
|
+
${this.renderFields()}
|
|
1468
|
+
`, this.shadowRoot);
|
|
1469
|
+
}
|
|
1470
|
+
};
|
|
1471
|
+
if (typeof window !== "undefined" && !customElements.get("recur-einvoice-section")) customElements.define("recur-einvoice-section", RecurEinvoiceSection);
|
|
1472
|
+
}));
|
|
1473
|
+
|
|
1070
1474
|
//#endregion
|
|
1071
1475
|
//#region src/components/styles/payment-form-shadow.css?css-text
|
|
1072
1476
|
var payment_form_shadow_default;
|
|
@@ -1078,7 +1482,7 @@ var init_payment_form_shadow = __esmMin((() => {
|
|
|
1078
1482
|
//#region src/components/styles/payment-form-light.css?css-text
|
|
1079
1483
|
var payment_form_light_default;
|
|
1080
1484
|
var init_payment_form_light = __esmMin((() => {
|
|
1081
|
-
payment_form_light_default = "/**\n * Payment Form - Light DOM Styles\n * These styles are applied to slotted content in the Light DOM\n * Using BEM naming convention with 'recur-' prefix for isolation\n */\n\n/* ============================================\n * Order Summary Styles\n * ============================================ */\n.recur-order-summary {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n}\n\n.recur-order-summary__row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.recur-order-summary__row--total {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n}\n\n.recur-order-summary__label {\n font-size: 14px;\n color: #4a5568;\n}\n\n.recur-order-summary__value {\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: #2d3748;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================\n * Coupon Section Styles\n * ============================================ */\n.recur-coupon {\n padding: 12px 0;\n border-top: 1px solid #e2e8f0;\n margin-top: 12px;\n min-height: 36px;\n display: flex;\n align-items: center;\n}\n\n.recur-coupon__trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n background: none;\n border: none;\n padding: 0;\n font-size: 14px;\n line-height: 1;\n color: #718096;\n cursor: pointer;\n transition: color 0.2s;\n}\n\n.recur-coupon__trigger:hover {\n color: #4a5568;\n}\n\n.recur-coupon__trigger-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__input-wrapper {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.recur-coupon__input {\n width: 100%;\n height: 36px;\n padding: 0 32px 0 12px;\n font-size: 14px;\n text-transform: uppercase;\n background-color: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.recur-coupon__input::placeholder {\n text-transform: none;\n}\n\n.recur-coupon__input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n.recur-coupon__input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-coupon__clear {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s;\n}\n\n.recur-coupon__clear:hover {\n color: #6b7280;\n}\n\n.recur-coupon__clear-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__btn {\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s, opacity 0.2s;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\n min-width: 52px;\n}\n\n.recur-coupon__btn--apply .recur-coupon__spinner {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__btn--apply:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-coupon__btn--apply:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.recur-coupon__error {\n width: 100%;\n margin-top: 8px;\n font-size: 13px;\n color: #dc2626;\n}\n\n.recur-coupon__applied-wrapper {\n width: 100%;\n}\n\n.recur-coupon__description {\n font-size: 12px;\n color: #6b7280;\n margin: 4px 0 0 22px;\n}\n\n.recur-coupon__bonus {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #dcfce7;\n border-radius: 6px;\n padding: 8px 12px;\n margin-top: 8px;\n font-size: 12px;\n color: #15803d;\n}\n\n.recur-coupon__applied {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n flex: 1;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #dc2626;\n flex-shrink: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 14px;\n color: #9ca3af;\n white-space: nowrap;\n margin-right: 2px;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.recur-coupon__remove {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n transition: color 0.2s, background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recur-coupon__remove:hover {\n color: #6b7280;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.recur-coupon__remove-icon {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__spinner {\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n/* Discount Row */\n.recur-order-summary__row--discount {\n color: #16a34a;\n}\n\n.recur-order-summary__row--discount .recur-order-summary__value {\n color: #16a34a;\n font-weight: 500;\n}\n\n/* ============================================\n * Customer Info Styles\n * ============================================ */\n.recur-info-display {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n margin-bottom: 8px;\n}\n\n.recur-info-row {\n margin-bottom: 12px;\n}\n\n.recur-info-row:last-child {\n margin-bottom: 0;\n}\n\n.recur-info-label {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: #718096;\n margin-bottom: 2px;\n}\n\n.recur-info-value {\n display: block;\n font-size: 14px;\n line-height: 20px;\n min-height: 20px;\n color: #2d3748;\n font-weight: 500;\n word-break: break-all;\n}\n\n.recur-form-group {\n margin-bottom: 16px;\n}\n\n.recur-form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-form-input {\n width: 100%;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n transition: border-color 0.2s;\n box-sizing: border-box;\n}\n\n.recur-form-input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n/* ============================================\n * Card Fields Styles\n * ============================================ */\n.recur-card-field {\n margin-bottom: 16px;\n}\n\n.recur-card-field-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-payuni-iframe {\n height: 36px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n touch-action: manipulation;\n}\n\n.recur-card-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n column-gap: 16px;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.recur-card-field-container {\n position: relative;\n touch-action: manipulation;\n}\n\n.recur-card-skeleton {\n position: absolute;\n top: 0;\n left: 0;\n height: 36px;\n width: 100%;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n z-index: 1;\n pointer-events: none;\n}\n\n.recur-card-skeleton.hidden {\n display: none;\n}\n\n@keyframes recur-skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* ============================================\n * Submit Button Styles\n * ============================================ */\n.recur-submit-button {\n width: 100%;\n padding: 14px 20px;\n font-size: 16px;\n font-weight: 500;\n color: #ffffff;\n background: #18181b;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n box-sizing: border-box;\n}\n\n.recur-submit-button:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.recur-submit-button.loading {\n cursor: wait;\n}\n\n.recur-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n@keyframes recur-spin {\n to { transform: rotate(360deg); }\n}\n\n/* ============================================\n * Skeleton Loading Styles\n * ============================================ */\n.recur-skeleton {\n display: inline-block;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n}\n\n/* ============================================\n * Test Mode Banner Styles\n * ============================================ */\n.recur-test-mode-banner {\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\n border: 1px solid #f59e0b;\n border-radius: 8px;\n margin-bottom: 16px;\n overflow: hidden;\n}\n\n.recur-test-mode-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n}\n\n.recur-test-mode-header:hover {\n background: rgba(245, 158, 11, 0.1);\n}\n\n.recur-test-mode-badge {\n font-size: 13px;\n font-weight: 600;\n color: #92400e;\n}\n\n.recur-test-mode-toggle {\n font-size: 12px;\n color: #b45309;\n}\n\n.recur-test-cards {\n background: rgba(255, 255, 255, 0.7);\n padding: 12px 14px;\n border-top: 1px solid rgba(245, 158, 11, 0.3);\n}\n\n.recur-test-cards-info {\n font-size: 12px;\n color: #78350f;\n margin: 0 0 10px 0;\n}\n\n.recur-test-card {\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n padding: 10px 12px;\n margin-bottom: 8px;\n}\n\n.recur-test-card:last-child {\n margin-bottom: 0;\n}\n\n.recur-test-card-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 4px;\n}\n\n.recur-test-card-number {\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 14px;\n font-weight: 600;\n color: #1f2937;\n letter-spacing: 0.5px;\n}\n\n.recur-test-card-copy {\n font-size: 11px;\n font-weight: 500;\n color: #3b82f6;\n background: #eff6ff;\n border: 1px solid #bfdbfe;\n border-radius: 4px;\n padding: 3px 8px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.recur-test-card-copy:hover {\n background: #dbeafe;\n border-color: #93c5fd;\n}\n\n.recur-test-card-copy.copied {\n background: #dcfce7;\n border-color: #86efac;\n color: #16a34a;\n}\n\n.recur-test-card-details {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 11px;\n color: #6b7280;\n}\n\n.recur-test-card-desc {\n color: #374151;\n font-weight: 500;\n}\n\n.recur-test-card-meta {\n color: #9ca3af;\n}\n";
|
|
1485
|
+
payment_form_light_default = "/**\n * Payment Form - Light DOM Styles\n * These styles are applied to slotted content in the Light DOM\n * Using BEM naming convention with 'recur-' prefix for isolation\n */\n\n/* ============================================\n * Order Summary Styles\n * ============================================ */\n.recur-order-summary {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n}\n\n.recur-order-summary__row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.recur-order-summary__row--total {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n}\n\n.recur-order-summary__label {\n font-size: 14px;\n color: #4a5568;\n}\n\n.recur-order-summary__value {\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: #2d3748;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================\n * Coupon Section Styles\n * ============================================ */\n.recur-coupon {\n padding: 12px 0;\n border-top: 1px solid #e2e8f0;\n margin-top: 12px;\n min-height: 36px;\n display: flex;\n align-items: center;\n}\n\n.recur-coupon__trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n background: none;\n border: none;\n padding: 0;\n font-size: 14px;\n line-height: 1;\n color: #718096;\n cursor: pointer;\n transition: color 0.2s;\n}\n\n.recur-coupon__trigger:hover {\n color: #4a5568;\n}\n\n.recur-coupon__trigger-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__input-wrapper {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.recur-coupon__input {\n width: 100%;\n height: 36px;\n padding: 0 32px 0 12px;\n font-size: 14px;\n text-transform: uppercase;\n background-color: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.recur-coupon__input::placeholder {\n text-transform: none;\n}\n\n.recur-coupon__input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n.recur-coupon__input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-coupon__clear {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s;\n}\n\n.recur-coupon__clear:hover {\n color: #6b7280;\n}\n\n.recur-coupon__clear-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__btn {\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s, opacity 0.2s;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\n min-width: 52px;\n}\n\n.recur-coupon__btn--apply .recur-coupon__spinner {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__btn--apply:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-coupon__btn--apply:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.recur-coupon__error {\n width: 100%;\n margin-top: 8px;\n font-size: 13px;\n color: #dc2626;\n}\n\n.recur-coupon__applied-wrapper {\n width: 100%;\n}\n\n.recur-coupon__description {\n font-size: 12px;\n color: #6b7280;\n margin: 4px 0 0 22px;\n}\n\n.recur-coupon__bonus {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #dcfce7;\n border-radius: 6px;\n padding: 8px 12px;\n margin-top: 8px;\n font-size: 12px;\n color: #15803d;\n}\n\n.recur-coupon__applied {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n flex: 1;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #dc2626;\n flex-shrink: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 14px;\n color: #9ca3af;\n white-space: nowrap;\n margin-right: 2px;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.recur-coupon__remove {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n transition: color 0.2s, background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recur-coupon__remove:hover {\n color: #6b7280;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.recur-coupon__remove-icon {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__spinner {\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n/* Discount Row */\n.recur-order-summary__row--discount {\n color: #16a34a;\n}\n\n.recur-order-summary__row--discount .recur-order-summary__value {\n color: #16a34a;\n font-weight: 500;\n}\n\n/* ============================================\n * Customer Info Styles\n * ============================================ */\n.recur-info-display {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n margin-bottom: 8px;\n}\n\n.recur-info-row {\n margin-bottom: 12px;\n}\n\n.recur-info-row:last-child {\n margin-bottom: 0;\n}\n\n.recur-info-label {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: #718096;\n margin-bottom: 2px;\n}\n\n.recur-info-value {\n display: block;\n font-size: 14px;\n line-height: 20px;\n min-height: 20px;\n color: #2d3748;\n font-weight: 500;\n word-break: break-all;\n}\n\n.recur-form-group {\n margin-bottom: 16px;\n}\n\n.recur-form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-form-input {\n width: 100%;\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n transition: border-color 0.2s;\n box-sizing: border-box;\n}\n\n.recur-form-input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n/* ============================================\n * Card Fields Styles\n * ============================================ */\n.recur-card-field {\n margin-bottom: 16px;\n}\n\n.recur-card-field-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n}\n\n.recur-payuni-iframe {\n height: 36px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n touch-action: manipulation;\n}\n\n.recur-card-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n column-gap: 16px;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.recur-card-field-container {\n position: relative;\n touch-action: manipulation;\n}\n\n.recur-card-skeleton {\n position: absolute;\n top: 0;\n left: 0;\n height: 36px;\n width: 100%;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n z-index: 1;\n pointer-events: none;\n}\n\n.recur-card-skeleton.hidden {\n display: none;\n}\n\n@keyframes recur-skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Card verification notice ($0 total, card still bound for future charges) */\n.recur-verification-notice {\n border: 1px solid #bfdbfe;\n background-color: #eff6ff;\n color: #1e40af;\n border-radius: 8px;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 500;\n margin-bottom: 16px;\n}\n\n.recur-verification-notice.hidden {\n display: none;\n}\n\n/* ============================================\n * Submit Button Styles\n * ============================================ */\n.recur-submit-button {\n width: 100%;\n padding: 14px 20px;\n font-size: 16px;\n font-weight: 500;\n color: #ffffff;\n background: #18181b;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n box-sizing: border-box;\n}\n\n.recur-submit-button:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.recur-submit-button.loading {\n cursor: wait;\n}\n\n.recur-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n}\n\n@keyframes recur-spin {\n to { transform: rotate(360deg); }\n}\n\n/* ============================================\n * Skeleton Loading Styles\n * ============================================ */\n.recur-skeleton {\n display: inline-block;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n}\n\n/* ============================================\n * Test Mode Banner Styles\n * ============================================ */\n.recur-test-mode-banner {\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\n border: 1px solid #f59e0b;\n border-radius: 8px;\n margin-bottom: 16px;\n overflow: hidden;\n}\n\n.recur-test-mode-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n}\n\n.recur-test-mode-header:hover {\n background: rgba(245, 158, 11, 0.1);\n}\n\n.recur-test-mode-badge {\n font-size: 13px;\n font-weight: 600;\n color: #92400e;\n}\n\n.recur-test-mode-toggle {\n font-size: 12px;\n color: #b45309;\n}\n\n.recur-test-cards {\n background: rgba(255, 255, 255, 0.7);\n padding: 12px 14px;\n border-top: 1px solid rgba(245, 158, 11, 0.3);\n}\n\n.recur-test-cards-info {\n font-size: 12px;\n color: #78350f;\n margin: 0 0 10px 0;\n}\n\n.recur-test-card {\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n padding: 10px 12px;\n margin-bottom: 8px;\n}\n\n.recur-test-card:last-child {\n margin-bottom: 0;\n}\n\n.recur-test-card-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 4px;\n}\n\n.recur-test-card-number {\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 14px;\n font-weight: 600;\n color: #1f2937;\n letter-spacing: 0.5px;\n}\n\n.recur-test-card-copy {\n font-size: 11px;\n font-weight: 500;\n color: #3b82f6;\n background: #eff6ff;\n border: 1px solid #bfdbfe;\n border-radius: 4px;\n padding: 3px 8px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.recur-test-card-copy:hover {\n background: #dbeafe;\n border-color: #93c5fd;\n}\n\n.recur-test-card-copy.copied {\n background: #dcfce7;\n border-color: #86efac;\n color: #16a34a;\n}\n\n.recur-test-card-details {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 11px;\n color: #6b7280;\n}\n\n.recur-test-card-desc {\n color: #374151;\n font-weight: 500;\n}\n\n.recur-test-card-meta {\n color: #9ca3af;\n}\n";
|
|
1082
1486
|
}));
|
|
1083
1487
|
|
|
1084
1488
|
//#endregion
|
|
@@ -1107,6 +1511,7 @@ function formatPrice(amount) {
|
|
|
1107
1511
|
function getCheckoutDiscountDisplay(input) {
|
|
1108
1512
|
const { discountType, couponDiscountValue, discountAmount, duration, durationMonths, productPrice, productInterval, productIntervalCount, bonusMonths, bonusTrialDays } = input;
|
|
1109
1513
|
const intervalText = getIntervalText(productInterval, productIntervalCount);
|
|
1514
|
+
const isOneTime = !intervalText;
|
|
1110
1515
|
const originalPriceText = intervalText ? `${formatPrice(productPrice)}/${intervalText}` : formatPrice(productPrice);
|
|
1111
1516
|
const bonusSuffix = buildBonusSuffix(bonusMonths, bonusTrialDays);
|
|
1112
1517
|
if (discountAmount === 0 && (bonusMonths || bonusTrialDays)) return {
|
|
@@ -1121,9 +1526,10 @@ function getCheckoutDiscountDisplay(input) {
|
|
|
1121
1526
|
if (discountType === "FIRST_PERIOD_PRICE") {
|
|
1122
1527
|
const firstPeriodPrice = couponDiscountValue;
|
|
1123
1528
|
const priceText = firstPeriodPrice === 0 ? "免費" : `特價 ${formatPrice(firstPeriodPrice)}`;
|
|
1529
|
+
const base = isOneTime ? priceText : `首期${priceText}`;
|
|
1124
1530
|
return {
|
|
1125
|
-
line1: bonusSuffix ?
|
|
1126
|
-
line2: afterLine
|
|
1531
|
+
line1: bonusSuffix ? `${base},${bonusSuffix}` : base,
|
|
1532
|
+
line2: isOneTime ? null : afterLine
|
|
1127
1533
|
};
|
|
1128
1534
|
}
|
|
1129
1535
|
if (discountType === "FIXED_AMOUNT") return buildStandardDiscount({
|
|
@@ -1131,14 +1537,16 @@ function getCheckoutDiscountDisplay(input) {
|
|
|
1131
1537
|
duration,
|
|
1132
1538
|
durationMonths,
|
|
1133
1539
|
bonusSuffix,
|
|
1134
|
-
afterLine
|
|
1540
|
+
afterLine,
|
|
1541
|
+
isOneTime
|
|
1135
1542
|
});
|
|
1136
1543
|
if (discountType === "PERCENTAGE") return buildStandardDiscount({
|
|
1137
|
-
discountText: `享 ${formatZhe(couponDiscountValue)}`,
|
|
1544
|
+
discountText: couponDiscountValue >= 1e4 ? "免費" : `享 ${formatZhe(couponDiscountValue)}`,
|
|
1138
1545
|
duration,
|
|
1139
1546
|
durationMonths,
|
|
1140
1547
|
bonusSuffix,
|
|
1141
|
-
afterLine
|
|
1548
|
+
afterLine,
|
|
1549
|
+
isOneTime
|
|
1142
1550
|
});
|
|
1143
1551
|
return {
|
|
1144
1552
|
line1: "",
|
|
@@ -1146,7 +1554,11 @@ function getCheckoutDiscountDisplay(input) {
|
|
|
1146
1554
|
};
|
|
1147
1555
|
}
|
|
1148
1556
|
function buildStandardDiscount(opts) {
|
|
1149
|
-
const { discountText, duration, durationMonths, bonusSuffix, afterLine } = opts;
|
|
1557
|
+
const { discountText, duration, durationMonths, bonusSuffix, afterLine, isOneTime } = opts;
|
|
1558
|
+
if (isOneTime) return {
|
|
1559
|
+
line1: bonusSuffix ? `${discountText},${bonusSuffix}` : discountText,
|
|
1560
|
+
line2: null
|
|
1561
|
+
};
|
|
1150
1562
|
switch (duration) {
|
|
1151
1563
|
case "ONCE": {
|
|
1152
1564
|
const base = `首期${discountText}`;
|
|
@@ -1268,7 +1680,9 @@ var init_payment_form = __esmMin((() => {
|
|
|
1268
1680
|
"interval",
|
|
1269
1681
|
"checkout-id",
|
|
1270
1682
|
"publishable-key",
|
|
1271
|
-
"api-base-url"
|
|
1683
|
+
"api-base-url",
|
|
1684
|
+
"einvoice-enabled",
|
|
1685
|
+
"einvoice-defaults"
|
|
1272
1686
|
];
|
|
1273
1687
|
}
|
|
1274
1688
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -1276,8 +1690,13 @@ var init_payment_form = __esmMin((() => {
|
|
|
1276
1690
|
if (name === "custom-styles") {
|
|
1277
1691
|
this.customStyles = newValue || "";
|
|
1278
1692
|
this.updateCustomStyles();
|
|
1693
|
+
} else if (name === "einvoice-enabled" || name === "einvoice-defaults") {
|
|
1694
|
+
if (this.isConnected) this.render();
|
|
1279
1695
|
} else this.updateOrderSummaryDOM();
|
|
1280
1696
|
}
|
|
1697
|
+
isEinvoiceEnabled() {
|
|
1698
|
+
return this.getAttribute("einvoice-enabled") === "true";
|
|
1699
|
+
}
|
|
1281
1700
|
getShadowDOMStyles() {
|
|
1282
1701
|
return payment_form_shadow_default;
|
|
1283
1702
|
}
|
|
@@ -1309,6 +1728,13 @@ var init_payment_form = __esmMin((() => {
|
|
|
1309
1728
|
<slot name="customer-info"></slot>
|
|
1310
1729
|
</div>
|
|
1311
1730
|
|
|
1731
|
+
${this.isEinvoiceEnabled() ? lit_html.html`
|
|
1732
|
+
<div class="form-section">
|
|
1733
|
+
<h3 class="section-title">發票資訊</h3>
|
|
1734
|
+
<slot name="einvoice"></slot>
|
|
1735
|
+
</div>
|
|
1736
|
+
` : lit_html.nothing}
|
|
1737
|
+
|
|
1312
1738
|
<div class="form-section">
|
|
1313
1739
|
<h3 class="section-title">信用卡資訊</h3>
|
|
1314
1740
|
<slot name="card-fields"></slot>
|
|
@@ -1342,6 +1768,15 @@ var init_payment_form = __esmMin((() => {
|
|
|
1342
1768
|
${this.renderCustomerInfo()}
|
|
1343
1769
|
</div>
|
|
1344
1770
|
|
|
1771
|
+
${this.isEinvoiceEnabled() ? lit_html.html`
|
|
1772
|
+
<div slot="einvoice">
|
|
1773
|
+
<recur-einvoice-section
|
|
1774
|
+
id="${this.containerId}-einvoice"
|
|
1775
|
+
defaults=${this.getAttribute("einvoice-defaults") ?? lit_html.nothing}
|
|
1776
|
+
></recur-einvoice-section>
|
|
1777
|
+
</div>
|
|
1778
|
+
` : lit_html.nothing}
|
|
1779
|
+
|
|
1345
1780
|
<div slot="card-fields">
|
|
1346
1781
|
${this.renderCardFields()}
|
|
1347
1782
|
</div>
|
|
@@ -1737,6 +2172,7 @@ var init_payment_form = __esmMin((() => {
|
|
|
1737
2172
|
const totalValue = summaryContainer.querySelector(".recur-order-summary__row--total")?.querySelector(".recur-order-summary__total");
|
|
1738
2173
|
if (totalValue) totalValue.textContent = `NT$ ${finalAmount.toLocaleString()}`;
|
|
1739
2174
|
this.updateCouponSectionDOM();
|
|
2175
|
+
this.updateVerificationUI();
|
|
1740
2176
|
}
|
|
1741
2177
|
async applyCoupon() {
|
|
1742
2178
|
const code = this._couponCode.trim();
|
|
@@ -1875,8 +2311,41 @@ var init_payment_form = __esmMin((() => {
|
|
|
1875
2311
|
</div>
|
|
1876
2312
|
`;
|
|
1877
2313
|
}
|
|
2314
|
+
/** Final payable amount after any applied coupon (never negative). */
|
|
2315
|
+
getFinalAmount() {
|
|
2316
|
+
const amount = Number(this.getAttribute("amount") || 0);
|
|
2317
|
+
const discountAmount = this._appliedCoupon?.discountAmount || 0;
|
|
2318
|
+
return Math.max(0, amount - discountAmount);
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* $0 total but the card form is still shown: the backend runs a NT$2
|
|
2322
|
+
* verification charge (voided afterwards) to bind the card for future
|
|
2323
|
+
* charges. The UI must say 驗證 instead of 付款.
|
|
2324
|
+
*/
|
|
2325
|
+
isCardVerificationMode() {
|
|
2326
|
+
const amountAttr = this.getAttribute("amount");
|
|
2327
|
+
if (amountAttr === null || amountAttr === "") return false;
|
|
2328
|
+
return this.getFinalAmount() === 0;
|
|
2329
|
+
}
|
|
2330
|
+
getSubmitLabel() {
|
|
2331
|
+
return this.isCardVerificationMode() ? "驗證卡片" : "確認付款";
|
|
2332
|
+
}
|
|
2333
|
+
/** Sync the verification notice + submit label with the current amount. */
|
|
2334
|
+
updateVerificationUI() {
|
|
2335
|
+
const notice = document.getElementById(`${this.containerId}-verification-notice`);
|
|
2336
|
+
if (notice) notice.classList.toggle("hidden", !this.isCardVerificationMode());
|
|
2337
|
+
const submitText = document.getElementById(`${this.containerId}-submit-text`);
|
|
2338
|
+
if (submitText) submitText.textContent = this.getSubmitLabel();
|
|
2339
|
+
}
|
|
1878
2340
|
renderCardFields() {
|
|
1879
2341
|
return lit_html.html`
|
|
2342
|
+
<div
|
|
2343
|
+
id="${this.containerId}-verification-notice"
|
|
2344
|
+
class="recur-verification-notice${this.isCardVerificationMode() ? "" : " hidden"}"
|
|
2345
|
+
>
|
|
2346
|
+
您的優惠碼已抵扣全額,仍需驗證信用卡以供日後扣款使用
|
|
2347
|
+
</div>
|
|
2348
|
+
|
|
1880
2349
|
<div class="recur-card-field">
|
|
1881
2350
|
<label class="recur-card-field-label">卡號</label>
|
|
1882
2351
|
<div class="recur-card-field-container" id="${this.containerId}-card-no-wrapper">
|
|
@@ -1922,7 +2391,7 @@ var init_payment_form = __esmMin((() => {
|
|
|
1922
2391
|
class="recur-submit-button"
|
|
1923
2392
|
disabled
|
|
1924
2393
|
>
|
|
1925
|
-
<span id="${this.containerId}-submit-text"
|
|
2394
|
+
<span id="${this.containerId}-submit-text">${this.getSubmitLabel()}</span>
|
|
1926
2395
|
</button>
|
|
1927
2396
|
`;
|
|
1928
2397
|
}
|
|
@@ -2040,6 +2509,18 @@ var init_payment_form = __esmMin((() => {
|
|
|
2040
2509
|
return;
|
|
2041
2510
|
}
|
|
2042
2511
|
}
|
|
2512
|
+
let einvoice;
|
|
2513
|
+
if (this.isEinvoiceEnabled()) {
|
|
2514
|
+
const einvoiceSection = this.querySelector("recur-einvoice-section");
|
|
2515
|
+
if (einvoiceSection) {
|
|
2516
|
+
const prefs = einvoiceSection.getValidatedPrefs();
|
|
2517
|
+
if (!prefs) {
|
|
2518
|
+
this.showError("請確認發票資訊");
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
einvoice = prefs;
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2043
2524
|
this.clearError();
|
|
2044
2525
|
this.setButtonLoading(true);
|
|
2045
2526
|
this.dispatchEvent(new CustomEvent("submit", {
|
|
@@ -2047,7 +2528,8 @@ var init_payment_form = __esmMin((() => {
|
|
|
2047
2528
|
customerEmail: email,
|
|
2048
2529
|
customerName: name,
|
|
2049
2530
|
paymentSession: this._paymentSession,
|
|
2050
|
-
appliedCoupon: this._appliedCoupon
|
|
2531
|
+
appliedCoupon: this._appliedCoupon,
|
|
2532
|
+
einvoice
|
|
2051
2533
|
},
|
|
2052
2534
|
bubbles: true,
|
|
2053
2535
|
composed: true
|
|
@@ -2067,7 +2549,7 @@ var init_payment_form = __esmMin((() => {
|
|
|
2067
2549
|
} else {
|
|
2068
2550
|
submitBtn.classList.remove("loading");
|
|
2069
2551
|
submitBtn.disabled = false;
|
|
2070
|
-
submitBtn.innerHTML = "<span id=\"" + this.containerId + "-submit-text\"
|
|
2552
|
+
submitBtn.innerHTML = "<span id=\"" + this.containerId + "-submit-text\">" + this.getSubmitLabel() + "</span>";
|
|
2071
2553
|
}
|
|
2072
2554
|
}
|
|
2073
2555
|
resetButton() {
|
|
@@ -2669,6 +3151,7 @@ async function registerComponents() {
|
|
|
2669
3151
|
Promise.resolve().then(() => (init_skeleton_loader(), skeleton_loader_exports)),
|
|
2670
3152
|
Promise.resolve().then(() => (init_payment_form_skeleton(), payment_form_skeleton_exports)),
|
|
2671
3153
|
Promise.resolve().then(() => (init_toast(), toast_exports)),
|
|
3154
|
+
Promise.resolve().then(() => (init_einvoice_section(), einvoice_section_exports)),
|
|
2672
3155
|
Promise.resolve().then(() => (init_payment_form(), payment_form_exports)),
|
|
2673
3156
|
Promise.resolve().then(() => (init_checkout_button(), checkout_button_exports)),
|
|
2674
3157
|
Promise.resolve().then(() => (init_portal_button(), portal_button_exports))
|
|
@@ -2681,6 +3164,7 @@ async function registerComponents() {
|
|
|
2681
3164
|
"recur-payment-form-skeleton",
|
|
2682
3165
|
"recur-toast",
|
|
2683
3166
|
"recur-toast-container",
|
|
3167
|
+
"recur-einvoice-section",
|
|
2684
3168
|
"recur-payment-form",
|
|
2685
3169
|
"recur-checkout",
|
|
2686
3170
|
"recur-portal"
|
|
@@ -2765,10 +3249,11 @@ function toCamelCase(obj) {
|
|
|
2765
3249
|
|
|
2766
3250
|
//#endregion
|
|
2767
3251
|
//#region package.json
|
|
2768
|
-
var version = "0.
|
|
3252
|
+
var version = "0.19.0";
|
|
2769
3253
|
|
|
2770
3254
|
//#endregion
|
|
2771
3255
|
//#region src/context.tsx
|
|
3256
|
+
init_einvoice();
|
|
2772
3257
|
const SDK_VERSION$1 = version;
|
|
2773
3258
|
const SDK_TYPE$1 = "react";
|
|
2774
3259
|
const RecurContext = (0, react.createContext)(null);
|
|
@@ -3108,6 +3593,10 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3108
3593
|
if (options.externalCustomerId) checkoutRequestBody.external_customer_id = options.externalCustomerId;
|
|
3109
3594
|
if (options.successUrl) checkoutRequestBody.success_url = options.successUrl;
|
|
3110
3595
|
if (options.cancelUrl) checkoutRequestBody.cancel_url = options.cancelUrl;
|
|
3596
|
+
if (options.einvoice) {
|
|
3597
|
+
assertValidEinvoicePrefs(options.einvoice);
|
|
3598
|
+
checkoutRequestBody.einvoice = options.einvoice;
|
|
3599
|
+
}
|
|
3111
3600
|
const checkoutResponse = await fetch(`${baseUrl}/v1/checkouts`, {
|
|
3112
3601
|
method: "POST",
|
|
3113
3602
|
headers,
|
|
@@ -3191,6 +3680,11 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3191
3680
|
if (checkoutResult.product?.name) paymentForm.setAttribute("product-name", checkoutResult.product.name);
|
|
3192
3681
|
if (checkoutResult.checkout?.amount) paymentForm.setAttribute("amount", checkoutResult.checkout.amount.toString());
|
|
3193
3682
|
if (checkoutResult.product?.interval) paymentForm.setAttribute("interval", checkoutResult.product.interval);
|
|
3683
|
+
if (checkoutResult.einvoiceEnabled) {
|
|
3684
|
+
paymentForm.setAttribute("einvoice-enabled", "true");
|
|
3685
|
+
const einvoiceDefaults = options.einvoice ?? checkoutResult.einvoiceDefaults;
|
|
3686
|
+
if (einvoiceDefaults) paymentForm.setAttribute("einvoice-defaults", JSON.stringify(einvoiceDefaults));
|
|
3687
|
+
}
|
|
3194
3688
|
paymentForm.setAttribute("custom-styles", `
|
|
3195
3689
|
.form-input-focus {
|
|
3196
3690
|
border-color: var(--ring, hsl(215 16% 47%)) !important;
|
|
@@ -3210,7 +3704,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3210
3704
|
console.log("[Recur SDK] Step 7: Setting up submit handler...");
|
|
3211
3705
|
paymentForm.addEventListener("submit", (async (event) => {
|
|
3212
3706
|
console.log("[Recur SDK] Form submitted via Web Component");
|
|
3213
|
-
const { paymentSession } = event.detail;
|
|
3707
|
+
const { paymentSession, einvoice } = event.detail;
|
|
3214
3708
|
try {
|
|
3215
3709
|
console.log("[Recur SDK] Getting trade result from PAYUNi...");
|
|
3216
3710
|
const tradeResult = await paymentSession.getTradeResult();
|
|
@@ -3252,6 +3746,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3252
3746
|
console.log("[Recur SDK] Using creditToken from checkout:", subscriptionCreditToken.substring(0, 30) + "...");
|
|
3253
3747
|
console.log("[Recur SDK] Using timestamp:", subscriptionTimestamp ? "from checkout (sdkTimestamp)" : "from tradeResult");
|
|
3254
3748
|
}
|
|
3749
|
+
if (einvoice) paymentBody.einvoice = einvoice;
|
|
3255
3750
|
const paymentResponse = await fetch(`${baseUrl}/v1/checkouts/${checkoutResult.checkout.id}/pay`, {
|
|
3256
3751
|
method: "POST",
|
|
3257
3752
|
headers,
|
|
@@ -3559,6 +4054,10 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3559
4054
|
if (options.customerEmail) requestBody.customerEmail = options.customerEmail;
|
|
3560
4055
|
if (options.customerName) requestBody.customerName = options.customerName;
|
|
3561
4056
|
if (options.externalCustomerId) requestBody.externalCustomerId = options.externalCustomerId;
|
|
4057
|
+
if (options.einvoice) {
|
|
4058
|
+
assertValidEinvoicePrefs(options.einvoice);
|
|
4059
|
+
requestBody.einvoice = options.einvoice;
|
|
4060
|
+
}
|
|
3562
4061
|
const response = await fetch(`${baseUrl}/v1/checkout/sessions`, {
|
|
3563
4062
|
method: "POST",
|
|
3564
4063
|
headers: {
|
|
@@ -3579,7 +4078,8 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3579
4078
|
id: result.id,
|
|
3580
4079
|
url: result.url,
|
|
3581
4080
|
expiresAt: result.expiresAt,
|
|
3582
|
-
clientSecret: result.clientSecret
|
|
4081
|
+
clientSecret: result.clientSecret,
|
|
4082
|
+
einvoiceEnabled: result.einvoiceEnabled
|
|
3583
4083
|
};
|
|
3584
4084
|
}, [config]);
|
|
3585
4085
|
const redirectToCheckout = (0, react.useCallback)(async (options) => {
|
|
@@ -4331,11 +4831,21 @@ function useCustomer() {
|
|
|
4331
4831
|
}
|
|
4332
4832
|
|
|
4333
4833
|
//#endregion
|
|
4834
|
+
//#region src/index.ts
|
|
4835
|
+
init_einvoice();
|
|
4836
|
+
|
|
4837
|
+
//#endregion
|
|
4838
|
+
exports.DONATION_CODE_RE = DONATION_CODE_RE;
|
|
4839
|
+
exports.MOBILE_BARCODE_RE = MOBILE_BARCODE_RE;
|
|
4334
4840
|
exports.PromoCodeInput = PromoCodeInput;
|
|
4335
4841
|
exports.RecurProvider = RecurProvider;
|
|
4842
|
+
exports.UBN_RE = UBN_RE;
|
|
4843
|
+
exports.assertValidEinvoicePrefs = assertValidEinvoicePrefs;
|
|
4844
|
+
exports.isValidTaiwanUbn = isValidTaiwanUbn;
|
|
4336
4845
|
exports.useCustomer = useCustomer;
|
|
4337
4846
|
exports.usePlans = useProducts;
|
|
4338
4847
|
exports.useProducts = useProducts;
|
|
4339
4848
|
exports.usePromoCode = usePromoCode;
|
|
4340
4849
|
exports.useRecur = useRecur;
|
|
4341
|
-
exports.useSubscribe = useSubscribe;
|
|
4850
|
+
exports.useSubscribe = useSubscribe;
|
|
4851
|
+
exports.validateEinvoicePrefs = validateEinvoicePrefs;
|