@swirepay-developer/swirepay-card-sdk 2.0.0 → 2.0.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/package.json +1 -1
- package/web-component/swirepay-checkout.js +890 -249
package/package.json
CHANGED
|
@@ -108,133 +108,8 @@ const USA_STATES = [
|
|
|
108
108
|
];
|
|
109
109
|
|
|
110
110
|
const COUNTRIES_LIST = [
|
|
111
|
-
{ "name": "Afghanistan", "code": "AF" },
|
|
112
|
-
{ "name": "Albania", "code": "AL" },
|
|
113
|
-
{ "name": "Algeria", "code": "DZ" },
|
|
114
|
-
{ "name": "Andorra", "code": "AD" },
|
|
115
|
-
{ "name": "Angola", "code": "AO" },
|
|
116
|
-
{ "name": "Antigua and Barbuda", "code": "AG" },
|
|
117
|
-
{ "name": "Argentina", "code": "AR" },
|
|
118
|
-
{ "name": "Armenia", "code": "AM" },
|
|
119
|
-
{ "name": "Australia", "code": "AU" },
|
|
120
|
-
{ "name": "Austria", "code": "AT" },
|
|
121
|
-
{ "name": "Azerbaijan", "code": "AZ" },
|
|
122
|
-
{ "name": "Bahamas", "code": "BS" },
|
|
123
|
-
{ "name": "Bahrain", "code": "BH" },
|
|
124
|
-
{ "name": "Bangladesh", "code": "BD" },
|
|
125
|
-
{ "name": "Barbados", "code": "BB" },
|
|
126
|
-
{ "name": "Belarus", "code": "BY" },
|
|
127
|
-
{ "name": "Belgium", "code": "BE" },
|
|
128
|
-
{ "name": "Belize", "code": "BZ" },
|
|
129
|
-
{ "name": "Benin", "code": "BJ" },
|
|
130
|
-
{ "name": "Bhutan", "code": "BT" },
|
|
131
|
-
{ "name": "Bolivia", "code": "BO" },
|
|
132
|
-
{ "name": "Bosnia and Herzegovina", "code": "BA" },
|
|
133
|
-
{ "name": "Botswana", "code": "BW" },
|
|
134
|
-
{ "name": "Brazil", "code": "BR" },
|
|
135
|
-
{ "name": "Brunei", "code": "BN" },
|
|
136
|
-
{ "name": "Bulgaria", "code": "BG" },
|
|
137
|
-
{ "name": "Burkina Faso", "code": "BF" },
|
|
138
|
-
{ "name": "Burundi", "code": "BI" },
|
|
139
|
-
{ "name": "Cambodia", "code": "KH" },
|
|
140
|
-
{ "name": "Cameroon", "code": "CM" },
|
|
141
|
-
{ "name": "Canada", "code": "CA" },
|
|
142
|
-
{ "name": "Cape Verde", "code": "CV" },
|
|
143
|
-
{ "name": "Central African Republic", "code": "CF" },
|
|
144
|
-
{ "name": "Chad", "code": "TD" },
|
|
145
|
-
{ "name": "Chile", "code": "CL" },
|
|
146
|
-
{ "name": "China", "code": "CN" },
|
|
147
|
-
{ "name": "Colombia", "code": "CO" },
|
|
148
|
-
{ "name": "Comoros", "code": "KM" },
|
|
149
|
-
{ "name": "Congo", "code": "CG" },
|
|
150
|
-
{ "name": "Costa Rica", "code": "CR" },
|
|
151
|
-
{ "name": "Croatia", "code": "HR" },
|
|
152
|
-
{ "name": "Cuba", "code": "CU" },
|
|
153
|
-
{ "name": "Cyprus", "code": "CY" },
|
|
154
|
-
{ "name": "Czech Republic", "code": "CZ" },
|
|
155
|
-
{ "name": "Denmark", "code": "DK" },
|
|
156
|
-
{ "name": "Djibouti", "code": "DJ" },
|
|
157
|
-
{ "name": "Dominica", "code": "DM" },
|
|
158
|
-
{ "name": "Dominican Republic", "code": "DO" },
|
|
159
|
-
{ "name": "Ecuador", "code": "EC" },
|
|
160
|
-
{ "name": "Egypt", "code": "EG" },
|
|
161
|
-
{ "name": "El Salvador", "code": "SV" },
|
|
162
|
-
{ "name": "Equatorial Guinea", "code": "GQ" },
|
|
163
|
-
{ "name": "Eritrea", "code": "ER" },
|
|
164
|
-
{ "name": "Estonia", "code": "EE" },
|
|
165
|
-
{ "name": "Ethiopia", "code": "ET" },
|
|
166
|
-
{ "name": "Fiji", "code": "FJ" },
|
|
167
|
-
{ "name": "Finland", "code": "FI" },
|
|
168
|
-
{ "name": "France", "code": "FR" },
|
|
169
|
-
{ "name": "Gabon", "code": "GA" },
|
|
170
|
-
{ "name": "Gambia", "code": "GM" },
|
|
171
|
-
{ "name": "Georgia", "code": "GE" },
|
|
172
|
-
{ "name": "Germany", "code": "DE" },
|
|
173
|
-
{ "name": "Ghana", "code": "GH" },
|
|
174
|
-
{ "name": "Greece", "code": "GR" },
|
|
175
|
-
{ "name": "Grenada", "code": "GD" },
|
|
176
|
-
{ "name": "Guatemala", "code": "GT" },
|
|
177
|
-
{ "name": "Guinea", "code": "GN" },
|
|
178
|
-
{ "name": "Guyana", "code": "GY" },
|
|
179
|
-
{ "name": "Haiti", "code": "HT" },
|
|
180
|
-
{ "name": "Honduras", "code": "HN" },
|
|
181
|
-
{ "name": "Hungary", "code": "HU" },
|
|
182
|
-
{ "name": "Iceland", "code": "IS" },
|
|
183
111
|
{ "name": "India", "code": "IN" },
|
|
184
|
-
{ "name": "Indonesia", "code": "ID" },
|
|
185
|
-
{ "name": "Iran", "code": "IR" },
|
|
186
|
-
{ "name": "Iraq", "code": "IQ" },
|
|
187
|
-
{ "name": "Ireland", "code": "IE" },
|
|
188
|
-
{ "name": "Israel", "code": "IL" },
|
|
189
|
-
{ "name": "Italy", "code": "IT" },
|
|
190
|
-
{ "name": "Jamaica", "code": "JM" },
|
|
191
|
-
{ "name": "Japan", "code": "JP" },
|
|
192
|
-
{ "name": "Jordan", "code": "JO" },
|
|
193
|
-
{ "name": "Kazakhstan", "code": "KZ" },
|
|
194
|
-
{ "name": "Kenya", "code": "KE" },
|
|
195
|
-
{ "name": "Korea, South", "code": "KR" },
|
|
196
|
-
{ "name": "Kuwait", "code": "KW" },
|
|
197
|
-
{ "name": "Laos", "code": "LA" },
|
|
198
|
-
{ "name": "Latvia", "code": "LV" },
|
|
199
|
-
{ "name": "Lebanon", "code": "LB" },
|
|
200
|
-
{ "name": "Libya", "code": "LY" },
|
|
201
|
-
{ "name": "Lithuania", "code": "LT" },
|
|
202
|
-
{ "name": "Luxembourg", "code": "LU" },
|
|
203
|
-
{ "name": "Malaysia", "code": "MY" },
|
|
204
|
-
{ "name": "Maldives", "code": "MV" },
|
|
205
|
-
{ "name": "Mexico", "code": "MX" },
|
|
206
|
-
{ "name": "Monaco", "code": "MC" },
|
|
207
|
-
{ "name": "Morocco", "code": "MA" },
|
|
208
|
-
{ "name": "Nepal", "code": "NP" },
|
|
209
|
-
{ "name": "Netherlands", "code": "NL" },
|
|
210
|
-
{ "name": "New Zealand", "code": "NZ" },
|
|
211
|
-
{ "name": "Nigeria", "code": "NG" },
|
|
212
|
-
{ "name": "Norway", "code": "NO" },
|
|
213
|
-
{ "name": "Oman", "code": "OM" },
|
|
214
|
-
{ "name": "Pakistan", "code": "PK" },
|
|
215
|
-
{ "name": "Panama", "code": "PA" },
|
|
216
|
-
{ "name": "Peru", "code": "PE" },
|
|
217
|
-
{ "name": "Philippines", "code": "PH" },
|
|
218
|
-
{ "name": "Poland", "code": "PL" },
|
|
219
|
-
{ "name": "Portugal", "code": "PT" },
|
|
220
|
-
{ "name": "Qatar", "code": "QA" },
|
|
221
|
-
{ "name": "Romania", "code": "RO" },
|
|
222
|
-
{ "name": "Russia", "code": "RU" },
|
|
223
|
-
{ "name": "Saudi Arabia", "code": "SA" },
|
|
224
|
-
{ "name": "Singapore", "code": "SG" },
|
|
225
|
-
{ "name": "South Africa", "code": "ZA" },
|
|
226
|
-
{ "name": "Spain", "code": "ES" },
|
|
227
|
-
{ "name": "Sri Lanka", "code": "LK" },
|
|
228
|
-
{ "name": "Sweden", "code": "SE" },
|
|
229
|
-
{ "name": "Switzerland", "code": "CH" },
|
|
230
|
-
{ "name": "Thailand", "code": "TH" },
|
|
231
|
-
{ "name": "Turkey", "code": "TR" },
|
|
232
|
-
{ "name": "Ukraine", "code": "UA" },
|
|
233
|
-
{ "name": "United Arab Emirates", "code": "AE" },
|
|
234
|
-
{ "name": "United Kingdom", "code": "GB" },
|
|
235
112
|
{ "name": "United States", "code": "US" },
|
|
236
|
-
{ "name": "Vietnam", "code": "VN" },
|
|
237
|
-
{ "name": "Zimbabwe", "code": "ZW" }
|
|
238
113
|
];
|
|
239
114
|
|
|
240
115
|
const DEFAULT_THEME = {
|
|
@@ -279,20 +154,120 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
279
154
|
this.theme = DEFAULT_THEME;
|
|
280
155
|
}
|
|
281
156
|
|
|
157
|
+
getEndpoints() {
|
|
158
|
+
if (this.test) {
|
|
159
|
+
return { gateway: 'https://staging-backend.swirepay.com' };
|
|
160
|
+
}
|
|
161
|
+
return { gateway: 'https://api.swirepay.com' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async getSplitUpDetails(inventoryOrder) {
|
|
165
|
+
const payload = {
|
|
166
|
+
shopGid: inventoryOrder?.shopGid,
|
|
167
|
+
items: inventoryOrder?.inventoryOrderLineItems,
|
|
168
|
+
tipType: "AMOUNT",
|
|
169
|
+
tip: inventoryOrder?.totalTip
|
|
170
|
+
}
|
|
171
|
+
const gateway = this.getEndpoints().gateway;
|
|
172
|
+
let url = `${gateway}/v1/inventory/order/inventory-order-split-up`;
|
|
173
|
+
const response = await fetch(url, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: {
|
|
176
|
+
'Content-Type': 'application/json',
|
|
177
|
+
'x-api-key': this.apiKey,
|
|
178
|
+
},
|
|
179
|
+
body: JSON.stringify(payload)
|
|
180
|
+
})
|
|
181
|
+
return await response.json();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async updateFromAttributes() {
|
|
185
|
+
try {
|
|
186
|
+
this.amount = parseInt(this.getAttribute("amount")) || 0;
|
|
187
|
+
this.test = this.getAttribute("mode") === "test";
|
|
188
|
+
this.currencyCode = this.getAttribute("currencycode");
|
|
189
|
+
this.apiKey = this.getAttribute("api-key");
|
|
190
|
+
this.isAddressRequired = this.getAttribute("isaddressrequired") === "true";
|
|
191
|
+
this.inventory = this.getAttribute("inventory") === "true";
|
|
192
|
+
this.planGid = this.getAttribute("plangid");
|
|
193
|
+
this.productName = this.getAttribute("productname");
|
|
194
|
+
this.frequency = this.getAttribute("frequency");
|
|
195
|
+
this.description = this.getAttribute("description");
|
|
196
|
+
this.totalPayments = this.getAttribute("totalpayments");
|
|
197
|
+
const customerAttr = this.getAttribute("customer");
|
|
198
|
+
this.customer = customerAttr ? JSON.parse(customerAttr) : {};
|
|
199
|
+
|
|
200
|
+
const inventoryOrders = this.getAttribute("inventoryorders");
|
|
201
|
+
this.inventoryOrder = inventoryOrders ? JSON.parse(inventoryOrders) : {};
|
|
202
|
+
if (this.inventory && this.inventoryOrder) {
|
|
203
|
+
const splitUpInfo = await this.getSplitUpDetails(this.inventoryOrder);
|
|
204
|
+
this.splitUp = splitUpInfo?.entity;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
} catch (e) {
|
|
208
|
+
console.error("Attribute parsing error:", e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
282
212
|
async connectedCallback() {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
this.amount = parseInt(this.getAttribute("amount")) || 0;
|
|
286
|
-
this.currencyCode = this.getAttribute("currencyCode");
|
|
287
|
-
this.apiKey = this.getAttribute("api-key");
|
|
213
|
+
await this.updateFromAttributes();
|
|
214
|
+
|
|
288
215
|
await Promise.all([
|
|
289
216
|
loadScript(SDK_CONFIG.elliptic),
|
|
290
|
-
loadScript(SDK_CONFIG.crypto)
|
|
217
|
+
loadScript(SDK_CONFIG.crypto),
|
|
218
|
+
loadScript(SDK_CONFIG.plaid)
|
|
291
219
|
]);
|
|
292
220
|
|
|
293
221
|
this.render();
|
|
294
222
|
}
|
|
295
223
|
|
|
224
|
+
static get observedAttributes() {
|
|
225
|
+
return [
|
|
226
|
+
"amount",
|
|
227
|
+
"mode",
|
|
228
|
+
"currencycode",
|
|
229
|
+
"api-key",
|
|
230
|
+
"isaddressrequired",
|
|
231
|
+
"inventory",
|
|
232
|
+
"plangid",
|
|
233
|
+
"productname",
|
|
234
|
+
"frequency",
|
|
235
|
+
"description",
|
|
236
|
+
"totalpayments",
|
|
237
|
+
"customer",
|
|
238
|
+
"inventoryorders"
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async attributeChangedCallback(name, oldValue, newValue) {
|
|
243
|
+
if (oldValue === newValue) return;
|
|
244
|
+
|
|
245
|
+
await this.updateFromAttributes();
|
|
246
|
+
|
|
247
|
+
if (this.shadowRoot) {
|
|
248
|
+
this.render();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setError(id, message) {
|
|
253
|
+
const input = this.shadow.getElementById(id);
|
|
254
|
+
const errorEl = this.shadow.getElementById(`error-${id}`);
|
|
255
|
+
|
|
256
|
+
if (errorEl) errorEl.textContent = message || "";
|
|
257
|
+
|
|
258
|
+
if (input) {
|
|
259
|
+
if (message) input.classList.add("input-error");
|
|
260
|
+
else input.classList.remove("input-error");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
clearErrors() {
|
|
265
|
+
this.shadow.querySelectorAll(".error").forEach(e => e.textContent = "");
|
|
266
|
+
this.shadow.querySelectorAll("input, select").forEach(el => {
|
|
267
|
+
el.classList.remove("input-error");
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
296
271
|
async encryptData(data, serverPublicKey, clientPrivateKey, keyId) {
|
|
297
272
|
try {
|
|
298
273
|
const EC = window.elliptic.ec;
|
|
@@ -388,6 +363,108 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
388
363
|
return await response.json();
|
|
389
364
|
}
|
|
390
365
|
|
|
366
|
+
async createOrder() {
|
|
367
|
+
const gateway = this.getEndpoints().gateway;
|
|
368
|
+
let url = `${gateway}/v1/inventory/order`;
|
|
369
|
+
const response = await fetch(url, {
|
|
370
|
+
method: 'POST',
|
|
371
|
+
headers: {
|
|
372
|
+
'Content-Type': 'application/json',
|
|
373
|
+
'x-api-key': this.apiKey,
|
|
374
|
+
},
|
|
375
|
+
body: JSON.stringify(this.inventoryOrder)
|
|
376
|
+
})
|
|
377
|
+
return await response.json();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async getSubScription(payload) {
|
|
381
|
+
const gateway = this.getEndpoints().gateway;
|
|
382
|
+
let url = `${gateway}/v2/subscription`;
|
|
383
|
+
const response = await fetch(url, {
|
|
384
|
+
method: 'POST',
|
|
385
|
+
headers: {
|
|
386
|
+
'Content-Type': 'application/json',
|
|
387
|
+
'x-api-key': this.apiKey,
|
|
388
|
+
},
|
|
389
|
+
body: JSON.stringify(payload)
|
|
390
|
+
})
|
|
391
|
+
return await response.json();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async getPlan() {
|
|
395
|
+
const gateway = this.getEndpoints().gateway;
|
|
396
|
+
let url = `${gateway}/v2/plan?isInternal.EQ=false&name.EQ=Recurring%20Plan`;
|
|
397
|
+
const response = await fetch(url, {
|
|
398
|
+
method: 'GET',
|
|
399
|
+
headers: {
|
|
400
|
+
'Content-Type': 'application/json',
|
|
401
|
+
'x-api-key': this.apiKey,
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
return await response.json();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async addedPlanInfo() {
|
|
408
|
+
const payload = {
|
|
409
|
+
"currencyCode": this.currencyCode,
|
|
410
|
+
"name": "Recurring Plan",
|
|
411
|
+
"description": "Recurring Plan",
|
|
412
|
+
"note": "Recurring Plan",
|
|
413
|
+
"billingFrequency": this.frequency,
|
|
414
|
+
"billingPeriod": 1
|
|
415
|
+
}
|
|
416
|
+
const gateway = this.getEndpoints().gateway;
|
|
417
|
+
let url = `${gateway}/v2/plan`;
|
|
418
|
+
const response = await fetch(url, {
|
|
419
|
+
method: 'POST',
|
|
420
|
+
headers: {
|
|
421
|
+
'Content-Type': 'application/json',
|
|
422
|
+
'x-api-key': this.apiKey,
|
|
423
|
+
},
|
|
424
|
+
body: JSON.stringify(payload)
|
|
425
|
+
})
|
|
426
|
+
return await response.json();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async getItemInfo() {
|
|
430
|
+
const gateway = this.getEndpoints().gateway;
|
|
431
|
+
let url = `${gateway}/v1/inventory/item?name.EQ=Recurring%20item&priceType.EQ=VARIABLE&isRecurring.EQ=true`;
|
|
432
|
+
const response = await fetch(url, {
|
|
433
|
+
method: 'GET',
|
|
434
|
+
headers: {
|
|
435
|
+
'Content-Type': 'application/json',
|
|
436
|
+
'x-api-key': this.apiKey,
|
|
437
|
+
},
|
|
438
|
+
})
|
|
439
|
+
return await response.json();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async addedItemInfo() {
|
|
443
|
+
const payload = {
|
|
444
|
+
"name": "Recurring item",
|
|
445
|
+
"alternateName": "Recurring item",
|
|
446
|
+
"onlineName": "Recurring item",
|
|
447
|
+
"onlineEnabled": true,
|
|
448
|
+
"price": 100,
|
|
449
|
+
"priceType": "VARIABLE",
|
|
450
|
+
"includeTax": true,
|
|
451
|
+
"available": true,
|
|
452
|
+
"recurring": true,
|
|
453
|
+
"onKiosk": true
|
|
454
|
+
}
|
|
455
|
+
const gateway = this.getEndpoints().gateway;
|
|
456
|
+
let url = `${gateway}/v1/inventory/item`;
|
|
457
|
+
const response = await fetch(url, {
|
|
458
|
+
method: 'POST',
|
|
459
|
+
headers: {
|
|
460
|
+
'Content-Type': 'application/json',
|
|
461
|
+
'x-api-key': this.apiKey,
|
|
462
|
+
},
|
|
463
|
+
body: JSON.stringify(payload)
|
|
464
|
+
})
|
|
465
|
+
return await response.json();
|
|
466
|
+
}
|
|
467
|
+
|
|
391
468
|
async getServerEncription() {
|
|
392
469
|
try {
|
|
393
470
|
const gateway = this.getEndpoints().gateway;
|
|
@@ -443,9 +520,11 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
443
520
|
return v.replace(/\D/g, "").slice(0, 10);
|
|
444
521
|
}
|
|
445
522
|
handleCountryChange() {
|
|
446
|
-
const
|
|
523
|
+
const countryEl = this.shadow.getElementById("country");
|
|
447
524
|
const phoneCodeEl = this.shadow.getElementById("phone-code");
|
|
448
525
|
const stateEl = this.shadow.getElementById("state");
|
|
526
|
+
if (!countryEl || !phoneCodeEl || !stateEl) return;
|
|
527
|
+
const country = countryEl.value;
|
|
449
528
|
phoneCodeEl.value = COUNTRY_PHONE_MAP[country] || "";
|
|
450
529
|
const states = getStates(country);
|
|
451
530
|
stateEl.innerHTML = states.map(
|
|
@@ -453,77 +532,173 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
453
532
|
).join("");
|
|
454
533
|
}
|
|
455
534
|
|
|
535
|
+
detectCardType(number) {
|
|
536
|
+
const n = number.replace(/\s/g, "");
|
|
537
|
+
|
|
538
|
+
if (/^4/.test(n)) return "VISA";
|
|
539
|
+
if (/^5[1-5]/.test(n)) return "MASTERCARD";
|
|
540
|
+
if (/^3[47]/.test(n)) return "AMEX";
|
|
541
|
+
if (/^6(?:011|5)/.test(n)) return "DISCOVER";
|
|
542
|
+
|
|
543
|
+
return "";
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
getAddressFields() {
|
|
547
|
+
if (!this.isAddressRequired) return "";
|
|
548
|
+
|
|
549
|
+
return `
|
|
550
|
+
<div class="section">
|
|
551
|
+
<div class="section-title">Address</div>
|
|
552
|
+
|
|
553
|
+
<div class="field">
|
|
554
|
+
<input id="street" required placeholder=" " />
|
|
555
|
+
<label>Street *</label>
|
|
556
|
+
</div>
|
|
557
|
+
<div class="error" id="error-street"></div>
|
|
558
|
+
|
|
559
|
+
<div class="field">
|
|
560
|
+
<input id="city" required placeholder=" " />
|
|
561
|
+
<label>City *</label>
|
|
562
|
+
</div>
|
|
563
|
+
<div class="error" id="error-city"></div>
|
|
564
|
+
|
|
565
|
+
<div class="field">
|
|
566
|
+
<select id="state"></select>
|
|
567
|
+
<label class="floating">State *</label>
|
|
568
|
+
</div>
|
|
569
|
+
<div class="error" id="error-state"></div>
|
|
570
|
+
|
|
571
|
+
<div class="field">
|
|
572
|
+
<select id="country" required>
|
|
573
|
+
${COUNTRIES_LIST.map(c =>
|
|
574
|
+
`<option value="${c.code}" ${c.code === "US" ? "selected" : ""}>${c.name}</option>`
|
|
575
|
+
).join("")}
|
|
576
|
+
</select>
|
|
577
|
+
<label class="floating">Country *</label>
|
|
578
|
+
</div>
|
|
579
|
+
<div class="error" id="error-country"></div>
|
|
580
|
+
|
|
581
|
+
<div class="field">
|
|
582
|
+
<input id="zip" required placeholder=" " />
|
|
583
|
+
<label>ZIP Code *</label>
|
|
584
|
+
</div>
|
|
585
|
+
<div class="error" id="error-zip"></div>
|
|
586
|
+
|
|
587
|
+
</div>
|
|
588
|
+
`;
|
|
589
|
+
}
|
|
590
|
+
|
|
456
591
|
validate() {
|
|
592
|
+
this.clearErrors();
|
|
593
|
+
let isValid = true;
|
|
594
|
+
|
|
457
595
|
const get = id => this.shadow.getElementById(id)?.value?.trim();
|
|
458
596
|
|
|
459
|
-
|
|
597
|
+
if (!get("name")) {
|
|
598
|
+
this.setError("name", "Name required");
|
|
599
|
+
isValid = false;
|
|
600
|
+
}
|
|
601
|
+
|
|
460
602
|
const email = get("email");
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
603
|
+
if (!email) {
|
|
604
|
+
this.setError("email", "Email required");
|
|
605
|
+
isValid = false;
|
|
606
|
+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
607
|
+
this.setError("email", "Invalid email");
|
|
608
|
+
isValid = false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (!get("card-name")) {
|
|
612
|
+
this.setError("card-name", "Card holder name required");
|
|
613
|
+
isValid = false;
|
|
614
|
+
}
|
|
465
615
|
|
|
466
|
-
const cardName = get("card-name");
|
|
467
616
|
const cardNumber = get("card")?.replace(/\s/g, "");
|
|
468
|
-
const expiry = get("expiry");
|
|
469
|
-
const cvv = get("cvv");
|
|
470
|
-
const street = get("street");
|
|
471
|
-
if (!name) return "Name required";
|
|
472
|
-
if (!email) return "Email required";
|
|
473
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return "Invalid email address";
|
|
474
|
-
if (!cardName) return "Card holder name required";
|
|
475
617
|
if (!cardNumber || !/^\d{12,19}$/.test(cardNumber)) {
|
|
476
|
-
|
|
618
|
+
this.setError("card", "Invalid card number");
|
|
619
|
+
isValid = false;
|
|
477
620
|
}
|
|
621
|
+
|
|
622
|
+
const expiry = get("expiry");
|
|
478
623
|
if (!expiry || !/^\d{2}\/\d{2}$/.test(expiry)) {
|
|
479
|
-
|
|
624
|
+
this.setError("expiry", "Invalid expiry");
|
|
625
|
+
isValid = false;
|
|
480
626
|
} else {
|
|
481
627
|
const [mm, yy] = expiry.split("/").map(Number);
|
|
482
|
-
if (mm < 1 || mm > 12) return "Invalid expiry month";
|
|
483
628
|
const now = new Date();
|
|
484
629
|
const currentYear = now.getFullYear() % 100;
|
|
485
630
|
const currentMonth = now.getMonth() + 1;
|
|
486
631
|
|
|
487
|
-
if (
|
|
488
|
-
|
|
632
|
+
if (mm < 1 || mm > 12) {
|
|
633
|
+
this.setError("expiry", "Invalid month");
|
|
634
|
+
isValid = false;
|
|
635
|
+
} else if (yy < currentYear || (yy === currentYear && mm < currentMonth)) {
|
|
636
|
+
this.setError("expiry", "Card expired");
|
|
637
|
+
isValid = false;
|
|
489
638
|
}
|
|
490
639
|
}
|
|
640
|
+
|
|
641
|
+
const cvv = get("cvv");
|
|
491
642
|
if (!cvv || !/^\d{3,4}$/.test(cvv)) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (!street) return "Street address required";
|
|
495
|
-
if (phone && !isValidPhoneNumber(code + phone)) {
|
|
496
|
-
return "Invalid phone";
|
|
643
|
+
this.setError("cvv", "Invalid CVV");
|
|
644
|
+
isValid = false;
|
|
497
645
|
}
|
|
498
|
-
|
|
499
|
-
|
|
646
|
+
|
|
647
|
+
const phone = get("phone")?.replace(/\D/g, "");
|
|
648
|
+
const code = get("phone-code");
|
|
649
|
+
if (!phone || !code) {
|
|
650
|
+
this.setError("phone", "Phone number required");
|
|
651
|
+
isValid = false;
|
|
652
|
+
} else {
|
|
653
|
+
const fullNumber = `${code}${phone}`;
|
|
654
|
+
if (!isValidPhoneNumber(fullNumber)) {
|
|
655
|
+
this.setError("phone", "Invalid phone number");
|
|
656
|
+
isValid = false;
|
|
657
|
+
}
|
|
500
658
|
}
|
|
501
659
|
|
|
502
|
-
|
|
503
|
-
|
|
660
|
+
if (this.isAddressRequired) {
|
|
661
|
+
if (!get("street")) {
|
|
662
|
+
this.setError("street", "Street required");
|
|
663
|
+
isValid = false;
|
|
664
|
+
}
|
|
504
665
|
|
|
505
|
-
|
|
506
|
-
|
|
666
|
+
if (!get("city")) {
|
|
667
|
+
this.setError("city", "City required");
|
|
668
|
+
isValid = false;
|
|
669
|
+
}
|
|
507
670
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
671
|
+
if (!get("state")) {
|
|
672
|
+
this.setError("state", "State required");
|
|
673
|
+
isValid = false;
|
|
674
|
+
}
|
|
511
675
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
676
|
+
if (!get("country")) {
|
|
677
|
+
this.setError("country", "Country required");
|
|
678
|
+
isValid = false;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const zip = get("zip");
|
|
682
|
+
|
|
683
|
+
if (!zip) {
|
|
684
|
+
this.setError("zip", "ZIP required");
|
|
685
|
+
isValid = false;
|
|
686
|
+
} else if (postalCodes.validate(get("country"), zip) !== true) {
|
|
687
|
+
this.setError("zip", "Invalid ZIP code");
|
|
688
|
+
isValid = false;
|
|
689
|
+
}
|
|
518
690
|
}
|
|
519
691
|
|
|
692
|
+
return isValid;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async oneTimePayment() {
|
|
520
696
|
try {
|
|
521
697
|
const values = await this.getServerEncription();
|
|
522
698
|
if (values?.public_key) {
|
|
523
699
|
const keys = await this.generateClientKeyPair();
|
|
524
700
|
if (keys?.privateKey) {
|
|
525
701
|
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
526
|
-
|
|
527
702
|
const phoneCode = getVal("phone-code") || "";
|
|
528
703
|
const phone = getVal("phone");
|
|
529
704
|
|
|
@@ -538,13 +713,17 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
538
713
|
phone: phone ? `${phoneCode}${phone}` : null
|
|
539
714
|
};
|
|
540
715
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
716
|
+
let address = null;
|
|
717
|
+
|
|
718
|
+
if (this.isAddressRequired) {
|
|
719
|
+
address = {
|
|
720
|
+
street: getVal("street"),
|
|
721
|
+
city: getVal("city"),
|
|
722
|
+
state: getVal("state"),
|
|
723
|
+
countryCode: getVal("country"),
|
|
724
|
+
postalCode: getVal("zip")
|
|
725
|
+
};
|
|
726
|
+
}
|
|
548
727
|
|
|
549
728
|
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
550
729
|
const payload = {
|
|
@@ -565,7 +744,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
565
744
|
cvv: cvv,
|
|
566
745
|
name: cardHolderName
|
|
567
746
|
},
|
|
568
|
-
postalCode: address?.
|
|
747
|
+
postalCode: address?.postalCode,
|
|
569
748
|
paymentMethodBillingAddress: address,
|
|
570
749
|
customerGid: customerGid
|
|
571
750
|
};
|
|
@@ -611,20 +790,282 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
611
790
|
body: JSON.stringify({ data: payload2?.data })
|
|
612
791
|
});
|
|
613
792
|
const resData = await res.json();
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
793
|
+
return resData;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
} catch (error) {
|
|
797
|
+
console.error("Payment Error:", error);
|
|
798
|
+
this.events.emit("error", error);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
async recurringPayment() {
|
|
803
|
+
try {
|
|
804
|
+
const values = await this.getServerEncription();
|
|
805
|
+
if (values?.public_key) {
|
|
806
|
+
const keys = await this.generateClientKeyPair();
|
|
807
|
+
if (keys?.privateKey) {
|
|
808
|
+
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
809
|
+
const phoneCode = getVal("phone-code") || "";
|
|
810
|
+
const phone = getVal("phone");
|
|
811
|
+
|
|
812
|
+
const cardNumber = getVal("card")?.replace(/\s/g, "");
|
|
813
|
+
const expiry = getVal("expiry")?.split("/") || [];
|
|
814
|
+
const cvv = getVal("cvv");
|
|
815
|
+
const cardHolderName = getVal("card-name");
|
|
816
|
+
|
|
817
|
+
const customer = {
|
|
818
|
+
name: getVal("name"),
|
|
819
|
+
email: getVal("email"),
|
|
820
|
+
phone: phone ? `${phoneCode}${phone}` : null
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
let address = null;
|
|
824
|
+
|
|
825
|
+
if (this.isAddressRequired) {
|
|
826
|
+
address = {
|
|
827
|
+
street: getVal("street"),
|
|
828
|
+
city: getVal("city"),
|
|
829
|
+
state: getVal("state"),
|
|
830
|
+
countryCode: getVal("country"),
|
|
831
|
+
postalCode: getVal("zip")
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
836
|
+
const payload = {
|
|
837
|
+
publicKey: keys?.publicKey,
|
|
838
|
+
keyId: encriptedData?.keyId,
|
|
839
|
+
iv: encriptedData?.iv,
|
|
840
|
+
data: encriptedData?.encryptedData,
|
|
841
|
+
}
|
|
842
|
+
const newCustomer = await this.createCustomer(payload);
|
|
843
|
+
const customerGid = newCustomer.entity?.gid;
|
|
844
|
+
let product;
|
|
845
|
+
const itemInfo = await this.getItemInfo();
|
|
846
|
+
if (itemInfo?.entity?.content[0]?.gid) {
|
|
847
|
+
product = itemInfo?.entity?.content[0];
|
|
848
|
+
} else {
|
|
849
|
+
const newItemInfo = await this.addedItemInfo();
|
|
850
|
+
product = newItemInfo?.entity;
|
|
851
|
+
}
|
|
852
|
+
let plan;
|
|
853
|
+
const planInfo = await this.getPlan();
|
|
854
|
+
if (planInfo?.entity?.content[0]?.gid) {
|
|
855
|
+
plan = planInfo?.entity?.content[0];
|
|
856
|
+
} else {
|
|
857
|
+
const newPlanInfo = await this.addedPlanInfo();
|
|
858
|
+
plan = newPlanInfo?.entity;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const subscriptionData = {
|
|
862
|
+
currencyCode: this.currencyCode,
|
|
863
|
+
customerGid: customerGid,
|
|
864
|
+
plan2Gid: plan?.gid,
|
|
865
|
+
plan2BillingPeriod: 1,
|
|
866
|
+
plan2BillingFrequency: this.frequency,
|
|
867
|
+
plan2StartDate: new Date().toISOString(),
|
|
868
|
+
plan2TotalPayments: this.totalPayments,
|
|
869
|
+
description: this.description,
|
|
870
|
+
notificationType: "ALL",
|
|
871
|
+
emailRecipientList: [
|
|
872
|
+
customer.email
|
|
873
|
+
],
|
|
874
|
+
canCustomerCancelOrPause: true,
|
|
875
|
+
subscription2LineItems: [
|
|
876
|
+
{
|
|
877
|
+
itemGid: product?.gid,
|
|
878
|
+
quantity: 1,
|
|
879
|
+
upfront: false,
|
|
880
|
+
currencyCode: this.currencyCode,
|
|
881
|
+
amount: this.amount
|
|
882
|
+
}
|
|
883
|
+
]
|
|
884
|
+
}
|
|
885
|
+
const subscriptionDetails = await this.getSubScription(subscriptionData);
|
|
886
|
+
const subscription = subscriptionDetails?.entity;
|
|
887
|
+
const sessionData = {
|
|
888
|
+
link: subscription?.subscription2Links[0]?.link,
|
|
889
|
+
customer: subscription?.customer,
|
|
890
|
+
type: 'CARD',
|
|
891
|
+
card: {
|
|
892
|
+
number: cardNumber,
|
|
893
|
+
expiryMonth: parseInt(expiry[0]),
|
|
894
|
+
expiryYear: 2000 + parseInt(expiry[1]),
|
|
895
|
+
cvv: cvv,
|
|
896
|
+
name: cardHolderName
|
|
897
|
+
},
|
|
898
|
+
postalCode: address?.postalCode,
|
|
899
|
+
paymentMethodBillingAddress: address
|
|
900
|
+
}
|
|
901
|
+
const encriptedData4 = await this.encryptData(JSON.stringify(sessionData), values.public_key, keys.privateKey, values.key_id);
|
|
902
|
+
const payload2 = {
|
|
903
|
+
publicKey: keys?.publicKey,
|
|
904
|
+
keyId: encriptedData4?.keyId,
|
|
905
|
+
iv: encriptedData4?.iv,
|
|
906
|
+
data: encriptedData4?.encryptedData,
|
|
907
|
+
}
|
|
908
|
+
const gateway = this.getEndpoints().gateway;
|
|
909
|
+
const url = `${gateway}/v2/subscription-link/${subscription?.subscription2Links[0]?.gid}/payment-method`;
|
|
910
|
+
const res = await fetch(url, {
|
|
911
|
+
method: 'POST',
|
|
912
|
+
headers: {
|
|
913
|
+
'x-enc-public-key': payload2?.publicKey,
|
|
914
|
+
'x-enc-key-id': payload2?.keyId,
|
|
915
|
+
'x-enc-iv': payload2?.iv,
|
|
916
|
+
'Content-Type': 'application/json',
|
|
917
|
+
'x-api-key': this.apiKey,
|
|
918
|
+
},
|
|
919
|
+
body: JSON.stringify({ data: payload2?.data })
|
|
920
|
+
});
|
|
921
|
+
const resData = await res.json();
|
|
922
|
+
return resData;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
} catch (error) {
|
|
926
|
+
console.error("Payment Error:", error);
|
|
927
|
+
this.events.emit("error", error);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
async inventoryCharge() {
|
|
932
|
+
try {
|
|
933
|
+
const values = await this.getServerEncription();
|
|
934
|
+
if (values?.public_key) {
|
|
935
|
+
const keys = await this.generateClientKeyPair();
|
|
936
|
+
if (keys?.privateKey) {
|
|
937
|
+
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
938
|
+
const phoneCode = getVal("phone-code") || "";
|
|
939
|
+
const phone = getVal("phone");
|
|
940
|
+
|
|
941
|
+
const cardNumber = getVal("card")?.replace(/\s/g, "");
|
|
942
|
+
const expiry = getVal("expiry")?.split("/") || [];
|
|
943
|
+
const cvv = getVal("cvv");
|
|
944
|
+
const cardHolderName = getVal("card-name");
|
|
945
|
+
|
|
946
|
+
const customer = {
|
|
947
|
+
name: getVal("name"),
|
|
948
|
+
email: getVal("email"),
|
|
949
|
+
phone: phone ? `${phoneCode}${phone}` : null
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
let address = null;
|
|
953
|
+
|
|
954
|
+
if (this.isAddressRequired) {
|
|
955
|
+
address = {
|
|
956
|
+
street: getVal("street"),
|
|
957
|
+
city: getVal("city"),
|
|
958
|
+
state: getVal("state"),
|
|
959
|
+
countryCode: getVal("country"),
|
|
960
|
+
postalCode: getVal("zip")
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
965
|
+
const payload = {
|
|
966
|
+
publicKey: keys?.publicKey,
|
|
967
|
+
keyId: encriptedData?.keyId,
|
|
968
|
+
iv: encriptedData?.iv,
|
|
969
|
+
data: encriptedData?.encryptedData,
|
|
970
|
+
}
|
|
971
|
+
const newCustomer = await this.createCustomer(payload);
|
|
972
|
+
const customerGid = newCustomer.entity?.gid;
|
|
973
|
+
const orderDetails = await this.createOrder();
|
|
974
|
+
const order = orderDetails?.entity;
|
|
975
|
+
const sessionData = {
|
|
976
|
+
customer: customer,
|
|
977
|
+
customerGid: customerGid,
|
|
978
|
+
paymentMethod: {
|
|
979
|
+
card: {
|
|
980
|
+
number: cardNumber,
|
|
981
|
+
expiryMonth: parseInt(expiry[0]),
|
|
982
|
+
expiryYear: 2000 + parseInt(expiry[1]),
|
|
983
|
+
cvv: cvv,
|
|
984
|
+
name: cardHolderName
|
|
985
|
+
},
|
|
986
|
+
postalCode: address?.postalCode,
|
|
987
|
+
type: "CARD",
|
|
988
|
+
paymentMethodBillingAddress: address,
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const encriptedData1 = await this.encryptData(JSON.stringify(sessionData), values.public_key, keys.privateKey, values.key_id);
|
|
992
|
+
const payload2 = {
|
|
993
|
+
publicKey: keys?.publicKey,
|
|
994
|
+
keyId: encriptedData1?.keyId,
|
|
995
|
+
iv: encriptedData1?.iv,
|
|
996
|
+
data: encriptedData1?.encryptedData,
|
|
997
|
+
}
|
|
998
|
+
const gateway = this.getEndpoints().gateway;
|
|
999
|
+
const url = `${gateway}/v1/inventory/order-link/${order?.inventoryOrderLink?.gid}/charge`;
|
|
1000
|
+
const res = await fetch(url, {
|
|
1001
|
+
method: 'POST',
|
|
1002
|
+
headers: {
|
|
1003
|
+
'x-enc-public-key': payload2?.publicKey,
|
|
1004
|
+
'x-enc-key-id': payload2?.keyId,
|
|
1005
|
+
'x-enc-iv': payload2?.iv,
|
|
1006
|
+
'Content-Type': 'application/json',
|
|
1007
|
+
'x-api-key': this.apiKey,
|
|
1008
|
+
},
|
|
1009
|
+
body: JSON.stringify({ data: payload2?.data })
|
|
1010
|
+
});
|
|
1011
|
+
const resData = await res.json();
|
|
1012
|
+
return resData;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
console.error("Payment Error:", error);
|
|
1018
|
+
this.events.emit("error", error);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async handleSubmit(e) {
|
|
1023
|
+
e.preventDefault();
|
|
1024
|
+
|
|
1025
|
+
const payButton = this.shadow.getElementById("form").querySelector(".btn");
|
|
1026
|
+
payButton.disabled = true;
|
|
1027
|
+
payButton.textContent = "Processing...";
|
|
1028
|
+
|
|
1029
|
+
const isValid = this.validate();
|
|
1030
|
+
if (!isValid) {
|
|
1031
|
+
payButton.disabled = false;
|
|
1032
|
+
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
try {
|
|
1036
|
+
if (this.frequency === "ONE-TIME" && !this.inventory) {
|
|
1037
|
+
const resData = await this.oneTimePayment();
|
|
1038
|
+
if (resData?.entity?.errorCode === null) {
|
|
1039
|
+
this.events.emit("success", resData);
|
|
1040
|
+
this.close();
|
|
1041
|
+
} else {
|
|
1042
|
+
const errorMsg = resData?.entity?.errorDescription || "Payment failed";
|
|
1043
|
+
console.error("Payment Error:", errorMsg);
|
|
1044
|
+
this.events.emit("error", errorMsg);
|
|
1045
|
+
}
|
|
1046
|
+
} else if (this.frequency !== "ONE-TIME" && !this.inventory) {
|
|
1047
|
+
const resData = await this.recurringPayment();
|
|
1048
|
+
if (resData?.entity?.setupSession?.errorCode === null) {
|
|
1049
|
+
this.events.emit("success", resData);
|
|
1050
|
+
this.close();
|
|
1051
|
+
} else {
|
|
1052
|
+
const errorMsg = resData?.entity?.setupSession?.errorDescription || "Payment failed";
|
|
1053
|
+
console.error("Payment Error:", errorMsg);
|
|
1054
|
+
this.events.emit("error", errorMsg);
|
|
1055
|
+
}
|
|
1056
|
+
} else if (this.inventory) {
|
|
1057
|
+
const resData = await this.inventoryCharge();
|
|
1058
|
+
if (resData?.responseCode === 200) {
|
|
1059
|
+
this.events.emit("success", resData);
|
|
1060
|
+
this.close();
|
|
1061
|
+
} else {
|
|
1062
|
+
console.error("Payment Error:", resData?.message || 'Payment failed');
|
|
1063
|
+
this.events.emit("error", resData?.message);
|
|
622
1064
|
}
|
|
623
1065
|
}
|
|
624
1066
|
} catch (error) {
|
|
625
1067
|
console.error("Payment Error:", error);
|
|
626
1068
|
this.events.emit("error", error);
|
|
627
|
-
alert(error.message || "Payment failed");
|
|
628
1069
|
} finally {
|
|
629
1070
|
payButton.disabled = false;
|
|
630
1071
|
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
@@ -633,6 +1074,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
633
1074
|
render() {
|
|
634
1075
|
const t = this.theme;
|
|
635
1076
|
const amountText = (this.amount / 100).toFixed(2);
|
|
1077
|
+
const inventoryAmountText = ((this.splitUp?.netPayable) / 100).toFixed(2);
|
|
636
1078
|
|
|
637
1079
|
this.shadow.innerHTML = `
|
|
638
1080
|
<style>
|
|
@@ -654,26 +1096,45 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
654
1096
|
background:rgba(0,0,0,0.6);
|
|
655
1097
|
justify-content:center;
|
|
656
1098
|
align-items:center;
|
|
1099
|
+
z-index:9999;
|
|
657
1100
|
}
|
|
658
1101
|
|
|
659
1102
|
.modal {
|
|
660
|
-
width:
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
1103
|
+
width: 520px;
|
|
1104
|
+
height: auto;
|
|
1105
|
+
max-height: 650px;
|
|
1106
|
+
background: var(--bg);
|
|
1107
|
+
color: var(--text);
|
|
1108
|
+
border-radius: 16px;
|
|
1109
|
+
padding: 28px;
|
|
1110
|
+
font-family: sans-serif;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.modal-body {
|
|
1114
|
+
overflow-y: auto;
|
|
1115
|
+
flex: 1;
|
|
1116
|
+
height: auto;
|
|
1117
|
+
max-height: 530px;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
form {
|
|
1121
|
+
display: flex;
|
|
1122
|
+
flex-direction: column;
|
|
1123
|
+
gap: 16px;
|
|
666
1124
|
}
|
|
667
1125
|
|
|
668
1126
|
input, select {
|
|
669
|
-
width:100%;
|
|
670
|
-
padding:
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1127
|
+
width: 100%;
|
|
1128
|
+
padding: 12px;
|
|
1129
|
+
border: 1px solid var(--border);
|
|
1130
|
+
background: #ffffff;
|
|
1131
|
+
color: #000000;
|
|
1132
|
+
border-radius: 8px;
|
|
1133
|
+
outline: none;
|
|
1134
|
+
height: 44px;
|
|
1135
|
+
font-size: 14px;
|
|
1136
|
+
box-sizing: border-box;
|
|
1137
|
+
margin: 0 !important;
|
|
677
1138
|
}
|
|
678
1139
|
|
|
679
1140
|
input::placeholder {
|
|
@@ -696,7 +1157,7 @@ input::placeholder {
|
|
|
696
1157
|
select {
|
|
697
1158
|
width:100%;
|
|
698
1159
|
padding:10px;
|
|
699
|
-
margin:
|
|
1160
|
+
margin: 0;
|
|
700
1161
|
border:1px solid var(--border);
|
|
701
1162
|
border-radius:8px;
|
|
702
1163
|
|
|
@@ -712,67 +1173,199 @@ select option {
|
|
|
712
1173
|
color: #000000;
|
|
713
1174
|
}
|
|
714
1175
|
|
|
715
|
-
.row {
|
|
1176
|
+
.row {
|
|
1177
|
+
display: flex;
|
|
1178
|
+
gap: 16px;
|
|
1179
|
+
align-items: center;
|
|
1180
|
+
}
|
|
1181
|
+
.phone-code {
|
|
1182
|
+
flex: 0 0 70px !important;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.row input,
|
|
1186
|
+
.row select {
|
|
1187
|
+
flex: 1;
|
|
1188
|
+
}
|
|
716
1189
|
|
|
717
|
-
|
|
1190
|
+
input, select {
|
|
1191
|
+
box-sizing: border-box;
|
|
1192
|
+
height: 40px;
|
|
1193
|
+
padding: 10px;
|
|
1194
|
+
font-size: 14px;
|
|
1195
|
+
}
|
|
718
1196
|
|
|
719
1197
|
.btn {
|
|
720
|
-
background:var(--primary);
|
|
721
|
-
color
|
|
722
|
-
padding:
|
|
723
|
-
border:none;
|
|
724
|
-
border-radius:10px;
|
|
725
|
-
cursor:pointer;
|
|
726
|
-
width:100%;
|
|
1198
|
+
background: var(--primary);
|
|
1199
|
+
color: #fff;
|
|
1200
|
+
padding: 14px;
|
|
1201
|
+
border: none;
|
|
1202
|
+
border-radius: 10px;
|
|
1203
|
+
cursor: pointer;
|
|
1204
|
+
width: 100%;
|
|
1205
|
+
height: 48px;
|
|
1206
|
+
font-size: 15px;
|
|
727
1207
|
}
|
|
728
1208
|
|
|
729
1209
|
.close {
|
|
730
1210
|
float:right;
|
|
731
1211
|
cursor:pointer;
|
|
732
1212
|
}
|
|
733
|
-
</style>
|
|
734
1213
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1214
|
+
h3 {
|
|
1215
|
+
margin-bottom: 16px;
|
|
1216
|
+
}
|
|
738
1217
|
|
|
739
|
-
|
|
1218
|
+
.section {
|
|
1219
|
+
display: flex;
|
|
1220
|
+
flex-direction: column;
|
|
1221
|
+
gap: 16px;
|
|
1222
|
+
}
|
|
740
1223
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
1224
|
+
.section-title {
|
|
1225
|
+
font-size: 14px;
|
|
1226
|
+
font-weight: 600;
|
|
1227
|
+
margin: 0;
|
|
1228
|
+
}
|
|
744
1229
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
</div>
|
|
1230
|
+
.card-wrapper {
|
|
1231
|
+
position: relative;
|
|
1232
|
+
}
|
|
749
1233
|
|
|
750
|
-
|
|
751
|
-
|
|
1234
|
+
#card-icon {
|
|
1235
|
+
position: absolute;
|
|
1236
|
+
right: 10px;
|
|
1237
|
+
top: 50%;
|
|
1238
|
+
transform: translateY(-50%);
|
|
1239
|
+
height: 24px;
|
|
1240
|
+
}
|
|
1241
|
+
.error {
|
|
1242
|
+
font-size: 12px;
|
|
1243
|
+
color: #dc2626;
|
|
1244
|
+
margin-top: 0px;
|
|
1245
|
+
margin-bottom: 0px;
|
|
1246
|
+
}
|
|
752
1247
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1248
|
+
.field {
|
|
1249
|
+
position: relative;
|
|
1250
|
+
width: 100%;
|
|
1251
|
+
}
|
|
757
1252
|
|
|
758
|
-
|
|
759
|
-
|
|
1253
|
+
.field input,
|
|
1254
|
+
.field select {
|
|
1255
|
+
width: 100%;
|
|
1256
|
+
padding: 14px 12px;
|
|
1257
|
+
border: 1px solid var(--border);
|
|
1258
|
+
border-radius: 8px;
|
|
1259
|
+
font-size: 14px;
|
|
1260
|
+
background: #fff;
|
|
1261
|
+
outline: none;
|
|
1262
|
+
}
|
|
1263
|
+
.field label {
|
|
1264
|
+
position: absolute;
|
|
1265
|
+
left: 0px;
|
|
1266
|
+
top: 50%;
|
|
1267
|
+
transform: translateY(-50%);
|
|
1268
|
+
padding: 0 4px;
|
|
1269
|
+
color: #6b7280;
|
|
1270
|
+
font-size: 14px;
|
|
1271
|
+
pointer-events: none;
|
|
1272
|
+
transition: 0.2s ease;
|
|
1273
|
+
}
|
|
1274
|
+
.field input:focus + label,
|
|
1275
|
+
.field input:not(:placeholder-shown) + label {
|
|
1276
|
+
top: -8px;
|
|
1277
|
+
font-size: 12px;
|
|
1278
|
+
color: var(--primary);
|
|
1279
|
+
}
|
|
1280
|
+
.field select {
|
|
1281
|
+
width: 100%;
|
|
1282
|
+
height: 44px;
|
|
1283
|
+
padding: 0 12px;
|
|
1284
|
+
border: 1px solid var(--border);
|
|
1285
|
+
border-radius: 8px;
|
|
1286
|
+
background: #fff;
|
|
1287
|
+
color: #000;
|
|
1288
|
+
font-size: 14px;
|
|
1289
|
+
outline: none;
|
|
1290
|
+
appearance: none;
|
|
1291
|
+
}
|
|
1292
|
+
.input-error {
|
|
1293
|
+
border-color: #dc2626 !important;
|
|
1294
|
+
}
|
|
760
1295
|
|
|
761
|
-
|
|
762
|
-
|
|
1296
|
+
.field input.input-error + label {
|
|
1297
|
+
color: #dc2626;
|
|
1298
|
+
}
|
|
763
1299
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
).join("")}
|
|
771
|
-
</select>
|
|
1300
|
+
.field label.floating {
|
|
1301
|
+
top: -8px;
|
|
1302
|
+
font-size: 12px;
|
|
1303
|
+
color: var(--primary);
|
|
1304
|
+
}
|
|
1305
|
+
</style>
|
|
772
1306
|
|
|
773
|
-
|
|
1307
|
+
<div class="overlay" id="overlay">
|
|
1308
|
+
<div class="modal">
|
|
1309
|
+
<div class="modal-header">
|
|
1310
|
+
<span class="close" id="close">✕</span>
|
|
1311
|
+
<h3>Pay with Card</h3>
|
|
1312
|
+
</div>
|
|
1313
|
+
<div class="modal-body">
|
|
1314
|
+
<form id="form">
|
|
1315
|
+
<div class="section">
|
|
1316
|
+
<div class="section-title">Customer Details</div>
|
|
1317
|
+
<div class="field">
|
|
1318
|
+
<input id="name" required placeholder=" " />
|
|
1319
|
+
<label>Name *</label>
|
|
1320
|
+
</div>
|
|
1321
|
+
<div class="error" id="error-name"></div>
|
|
1322
|
+
<div class="field">
|
|
1323
|
+
<input id="email" required placeholder=" " />
|
|
1324
|
+
<label>Email *</label>
|
|
1325
|
+
</div>
|
|
1326
|
+
<div class="error" id="error-email"></div>
|
|
1327
|
+
<div class="row">
|
|
1328
|
+
<div class="field phone-code">
|
|
1329
|
+
<input id="phone-code" placeholder=" " />
|
|
1330
|
+
<label>Code</label>
|
|
1331
|
+
</div>
|
|
1332
|
+
<div class="field">
|
|
1333
|
+
<input id="phone" required placeholder=" " />
|
|
1334
|
+
<label>Phone *</label>
|
|
1335
|
+
</div>
|
|
1336
|
+
</div>
|
|
1337
|
+
<div class="error" id="error-phone"></div>
|
|
1338
|
+
</div>
|
|
1339
|
+
<div class="section">
|
|
1340
|
+
<div class="section-title">Card Details</div>
|
|
1341
|
+
<div class="field">
|
|
1342
|
+
<input id="card" required placeholder=" " />
|
|
1343
|
+
<label>Card Number *</label>
|
|
1344
|
+
</div>
|
|
1345
|
+
<div class="error" id="error-card"></div>
|
|
774
1346
|
|
|
775
|
-
<
|
|
1347
|
+
<div class="row">
|
|
1348
|
+
<div class="field">
|
|
1349
|
+
<input id="expiry" required placeholder=" " />
|
|
1350
|
+
<label>MM/YY *</label>
|
|
1351
|
+
</div>
|
|
1352
|
+
|
|
1353
|
+
<div class="field">
|
|
1354
|
+
<input id="cvv" type="password" required placeholder=" " />
|
|
1355
|
+
<label>CVV *</label>
|
|
1356
|
+
</div>
|
|
1357
|
+
</div>
|
|
1358
|
+
<div class="error" id="error-expiry"></div>
|
|
1359
|
+
<div class="error" id="error-cvv"></div>
|
|
1360
|
+
<div class="field">
|
|
1361
|
+
<input id="card-name" required placeholder=" " />
|
|
1362
|
+
<label>Card Holder Name *</label>
|
|
1363
|
+
</div>
|
|
1364
|
+
<div class="error" id="error-card-name"></div>
|
|
1365
|
+
</div>
|
|
1366
|
+
${this.getAddressFields()}
|
|
1367
|
+
<button class="btn">Pay $${this.inventory ? inventoryAmountText : amountText}</button>
|
|
1368
|
+
</div>
|
|
776
1369
|
</form>
|
|
777
1370
|
</div>
|
|
778
1371
|
</div>
|
|
@@ -780,16 +1373,64 @@ select option {
|
|
|
780
1373
|
|
|
781
1374
|
const $ = id => this.shadow.getElementById(id);
|
|
782
1375
|
|
|
1376
|
+
if (this.customer) {
|
|
1377
|
+
if (this.customer.name) $("name").value = this.customer.name;
|
|
1378
|
+
if (this.customer.email) $("email").value = this.customer.email;
|
|
1379
|
+
|
|
1380
|
+
if (this.customer.phone) {
|
|
1381
|
+
let phone = this.customer.phone;
|
|
1382
|
+
let code = "";
|
|
1383
|
+
let number = phone;
|
|
1384
|
+
|
|
1385
|
+
if (phone.startsWith("+")) {
|
|
1386
|
+
if (phone.startsWith("+1")) {
|
|
1387
|
+
code = "+1";
|
|
1388
|
+
number = phone.slice(2);
|
|
1389
|
+
} else if (phone.startsWith("+91")) {
|
|
1390
|
+
code = "+91";
|
|
1391
|
+
number = phone.slice(3);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if ($("phone-code")) $("phone-code").value = code;
|
|
1396
|
+
if ($("phone")) $("phone").value = number;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
783
1400
|
$("close").onclick = () => this.close();
|
|
784
1401
|
$("form").onsubmit = e => this.handleSubmit(e);
|
|
785
1402
|
|
|
786
|
-
$("country").onchange = () => this.handleCountryChange();
|
|
787
|
-
|
|
788
1403
|
const countryEl = $("country");
|
|
789
|
-
countryEl
|
|
790
|
-
|
|
1404
|
+
if (countryEl) {
|
|
1405
|
+
countryEl.onchange = () => this.handleCountryChange();
|
|
1406
|
+
if (!this.customer?.countryCode) {
|
|
1407
|
+
countryEl.value = "US";
|
|
1408
|
+
}
|
|
1409
|
+
this.handleCountryChange();
|
|
1410
|
+
}
|
|
1411
|
+
const phoneCodeEl = $("phone-code");
|
|
1412
|
+
if (phoneCodeEl) {
|
|
1413
|
+
phoneCodeEl.dataset.auto = "true";
|
|
1414
|
+
}
|
|
791
1415
|
|
|
792
|
-
|
|
1416
|
+
const icons = {
|
|
1417
|
+
VISA: `<svg width="40" viewBox="0 0 48 16"><text x="0" y="14">VISA</text></svg>`,
|
|
1418
|
+
MASTERCARD: `<svg width="40"><text x="0" y="14">MC</text></svg>`,
|
|
1419
|
+
AMEX: `<svg width="40"><text x="0" y="14">AMEX</text></svg>`,
|
|
1420
|
+
DISCOVER: `<svg width="40"><text x="0" y="14">DISC</text></svg>`
|
|
1421
|
+
};
|
|
1422
|
+
$("card").oninput = e => {
|
|
1423
|
+
const formatted = this.formatCard(e.target.value);
|
|
1424
|
+
e.target.value = formatted;
|
|
1425
|
+
|
|
1426
|
+
const type = this.detectCardType(formatted);
|
|
1427
|
+
|
|
1428
|
+
const iconEl = $("card-icon");
|
|
1429
|
+
if (iconEl) {
|
|
1430
|
+
iconEl.src = icons[type] || "";
|
|
1431
|
+
iconEl.style.display = type ? "block" : "none";
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
793
1434
|
$("expiry").oninput = e => e.target.value = this.formatExpiry(e.target.value);
|
|
794
1435
|
$("phone").oninput = e => e.target.value = this.formatPhone(e.target.value);
|
|
795
1436
|
}
|