@swirepay-developer/swirepay-card-sdk 2.0.0 → 2.0.1
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 +950 -256
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,12 +154,59 @@ 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
|
+
|
|
282
184
|
async connectedCallback() {
|
|
283
185
|
this.amount = parseInt(this.getAttribute("amount")) || 0;
|
|
284
186
|
this.test = this.getAttribute("mode") === "test";
|
|
285
|
-
this.amount = parseInt(this.getAttribute("amount")) || 0;
|
|
286
187
|
this.currencyCode = this.getAttribute("currencyCode");
|
|
287
188
|
this.apiKey = this.getAttribute("api-key");
|
|
189
|
+
this.isAddressRequired = this.getAttribute("isAddressRequired") === "true";
|
|
190
|
+
this.inventory = this.getAttribute("inventory") === "true";
|
|
191
|
+
this.planGid = this.getAttribute("planGid");
|
|
192
|
+
this.productName = this.getAttribute("productName");
|
|
193
|
+
this.frequency = this.getAttribute("frequency");
|
|
194
|
+
this.description = this.getAttribute("description");
|
|
195
|
+
this.totalPayments = this.getAttribute('totalPayments');
|
|
196
|
+
try {
|
|
197
|
+
const customerAttr = this.getAttribute("customer");
|
|
198
|
+
this.customer = customerAttr ? JSON.parse(customerAttr) : {};
|
|
199
|
+
const inventoryOrders = this.getAttribute("inventoryOrders");
|
|
200
|
+
this.inventoryOrder = inventoryOrders ? JSON.parse(inventoryOrders) : {};
|
|
201
|
+
if (this.inventory) {
|
|
202
|
+
const splitUpInfo = await this.getSplitUpDetails(this.inventoryOrder);
|
|
203
|
+
this.splitUp = splitUpInfo?.entity;
|
|
204
|
+
}
|
|
205
|
+
} catch (e) {
|
|
206
|
+
console.error("Invalid JSON", e);
|
|
207
|
+
this.customer = {};
|
|
208
|
+
this.inventoryOrder = {};
|
|
209
|
+
}
|
|
288
210
|
await Promise.all([
|
|
289
211
|
loadScript(SDK_CONFIG.elliptic),
|
|
290
212
|
loadScript(SDK_CONFIG.crypto)
|
|
@@ -293,6 +215,112 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
293
215
|
this.render();
|
|
294
216
|
}
|
|
295
217
|
|
|
218
|
+
static get observedAttributes() {
|
|
219
|
+
return [
|
|
220
|
+
"amount",
|
|
221
|
+
"mode",
|
|
222
|
+
"currencycode",
|
|
223
|
+
"api-key",
|
|
224
|
+
"isaddressrequired",
|
|
225
|
+
"inventory",
|
|
226
|
+
"plangid",
|
|
227
|
+
"productname",
|
|
228
|
+
"frequency",
|
|
229
|
+
"description",
|
|
230
|
+
"totalpayments",
|
|
231
|
+
"customer",
|
|
232
|
+
"inventoryorders"
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
237
|
+
if (oldValue === newValue) return;
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
switch (name) {
|
|
241
|
+
case "amount":
|
|
242
|
+
this.amount = parseInt(newValue) || 0;
|
|
243
|
+
break;
|
|
244
|
+
|
|
245
|
+
case "mode":
|
|
246
|
+
this.test = newValue === "test";
|
|
247
|
+
break;
|
|
248
|
+
|
|
249
|
+
case "currencycode":
|
|
250
|
+
this.currencyCode = newValue;
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
case "api-key":
|
|
254
|
+
this.apiKey = newValue;
|
|
255
|
+
break;
|
|
256
|
+
|
|
257
|
+
case "isaddressrequired":
|
|
258
|
+
this.isAddressRequired = newValue === "true";
|
|
259
|
+
break;
|
|
260
|
+
|
|
261
|
+
case "inventory":
|
|
262
|
+
this.inventory = newValue === "true";
|
|
263
|
+
break;
|
|
264
|
+
|
|
265
|
+
case "plangid":
|
|
266
|
+
this.planGid = newValue;
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
case "productname":
|
|
270
|
+
this.productName = newValue;
|
|
271
|
+
break;
|
|
272
|
+
|
|
273
|
+
case "frequency":
|
|
274
|
+
this.frequency = newValue;
|
|
275
|
+
break;
|
|
276
|
+
|
|
277
|
+
case "description":
|
|
278
|
+
this.description = newValue;
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
case "totalpayments":
|
|
282
|
+
this.totalPayments = newValue;
|
|
283
|
+
break;
|
|
284
|
+
|
|
285
|
+
case "customer":
|
|
286
|
+
this.customer = newValue ? JSON.parse(newValue) : {};
|
|
287
|
+
break;
|
|
288
|
+
|
|
289
|
+
case "inventoryorders":
|
|
290
|
+
this.inventoryOrder = newValue ? JSON.parse(newValue) : {};
|
|
291
|
+
break;
|
|
292
|
+
|
|
293
|
+
default:
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if (this.shadowRoot) {
|
|
297
|
+
this.render();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
} catch (e) {
|
|
301
|
+
console.error(`Invalid value for ${name}`, e);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
setError(id, message) {
|
|
306
|
+
const input = this.shadow.getElementById(id);
|
|
307
|
+
const errorEl = this.shadow.getElementById(`error-${id}`);
|
|
308
|
+
|
|
309
|
+
if (errorEl) errorEl.textContent = message || "";
|
|
310
|
+
|
|
311
|
+
if (input) {
|
|
312
|
+
if (message) input.classList.add("input-error");
|
|
313
|
+
else input.classList.remove("input-error");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
clearErrors() {
|
|
318
|
+
this.shadow.querySelectorAll(".error").forEach(e => e.textContent = "");
|
|
319
|
+
this.shadow.querySelectorAll("input, select").forEach(el => {
|
|
320
|
+
el.classList.remove("input-error");
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
296
324
|
async encryptData(data, serverPublicKey, clientPrivateKey, keyId) {
|
|
297
325
|
try {
|
|
298
326
|
const EC = window.elliptic.ec;
|
|
@@ -388,6 +416,108 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
388
416
|
return await response.json();
|
|
389
417
|
}
|
|
390
418
|
|
|
419
|
+
async createOrder() {
|
|
420
|
+
const gateway = this.getEndpoints().gateway;
|
|
421
|
+
let url = `${gateway}/v1/inventory/order`;
|
|
422
|
+
const response = await fetch(url, {
|
|
423
|
+
method: 'POST',
|
|
424
|
+
headers: {
|
|
425
|
+
'Content-Type': 'application/json',
|
|
426
|
+
'x-api-key': this.apiKey,
|
|
427
|
+
},
|
|
428
|
+
body: JSON.stringify(this.inventoryOrder)
|
|
429
|
+
})
|
|
430
|
+
return await response.json();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async getSubScription(payload) {
|
|
434
|
+
const gateway = this.getEndpoints().gateway;
|
|
435
|
+
let url = `${gateway}/v2/subscription`;
|
|
436
|
+
const response = await fetch(url, {
|
|
437
|
+
method: 'POST',
|
|
438
|
+
headers: {
|
|
439
|
+
'Content-Type': 'application/json',
|
|
440
|
+
'x-api-key': this.apiKey,
|
|
441
|
+
},
|
|
442
|
+
body: JSON.stringify(payload)
|
|
443
|
+
})
|
|
444
|
+
return await response.json();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async getPlan() {
|
|
448
|
+
const gateway = this.getEndpoints().gateway;
|
|
449
|
+
let url = `${gateway}/v2/plan?isInternal.EQ=false&name.EQ=Recurring%20Plan`;
|
|
450
|
+
const response = await fetch(url, {
|
|
451
|
+
method: 'GET',
|
|
452
|
+
headers: {
|
|
453
|
+
'Content-Type': 'application/json',
|
|
454
|
+
'x-api-key': this.apiKey,
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
return await response.json();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async addedPlanInfo() {
|
|
461
|
+
const payload = {
|
|
462
|
+
"currencyCode": this.currencyCode,
|
|
463
|
+
"name": "Recurring Plan",
|
|
464
|
+
"description": "Recurring Plan",
|
|
465
|
+
"note": "Recurring Plan",
|
|
466
|
+
"billingFrequency": this.frequency,
|
|
467
|
+
"billingPeriod": 1
|
|
468
|
+
}
|
|
469
|
+
const gateway = this.getEndpoints().gateway;
|
|
470
|
+
let url = `${gateway}/v2/plan`;
|
|
471
|
+
const response = await fetch(url, {
|
|
472
|
+
method: 'POST',
|
|
473
|
+
headers: {
|
|
474
|
+
'Content-Type': 'application/json',
|
|
475
|
+
'x-api-key': this.apiKey,
|
|
476
|
+
},
|
|
477
|
+
body: JSON.stringify(payload)
|
|
478
|
+
})
|
|
479
|
+
return await response.json();
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async getItemInfo() {
|
|
483
|
+
const gateway = this.getEndpoints().gateway;
|
|
484
|
+
let url = `${gateway}/v1/inventory/item?name.EQ=Recurring%20item&priceType.EQ=VARIABLE&isRecurring.EQ=true`;
|
|
485
|
+
const response = await fetch(url, {
|
|
486
|
+
method: 'GET',
|
|
487
|
+
headers: {
|
|
488
|
+
'Content-Type': 'application/json',
|
|
489
|
+
'x-api-key': this.apiKey,
|
|
490
|
+
},
|
|
491
|
+
})
|
|
492
|
+
return await response.json();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async addedItemInfo() {
|
|
496
|
+
const payload = {
|
|
497
|
+
"name": "Recurring item",
|
|
498
|
+
"alternateName": "Recurring item",
|
|
499
|
+
"onlineName": "Recurring item",
|
|
500
|
+
"onlineEnabled": true,
|
|
501
|
+
"price": 100,
|
|
502
|
+
"priceType": "VARIABLE",
|
|
503
|
+
"includeTax": true,
|
|
504
|
+
"available": true,
|
|
505
|
+
"recurring": true,
|
|
506
|
+
"onKiosk": true
|
|
507
|
+
}
|
|
508
|
+
const gateway = this.getEndpoints().gateway;
|
|
509
|
+
let url = `${gateway}/v1/inventory/item`;
|
|
510
|
+
const response = await fetch(url, {
|
|
511
|
+
method: 'POST',
|
|
512
|
+
headers: {
|
|
513
|
+
'Content-Type': 'application/json',
|
|
514
|
+
'x-api-key': this.apiKey,
|
|
515
|
+
},
|
|
516
|
+
body: JSON.stringify(payload)
|
|
517
|
+
})
|
|
518
|
+
return await response.json();
|
|
519
|
+
}
|
|
520
|
+
|
|
391
521
|
async getServerEncription() {
|
|
392
522
|
try {
|
|
393
523
|
const gateway = this.getEndpoints().gateway;
|
|
@@ -443,9 +573,11 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
443
573
|
return v.replace(/\D/g, "").slice(0, 10);
|
|
444
574
|
}
|
|
445
575
|
handleCountryChange() {
|
|
446
|
-
const
|
|
576
|
+
const countryEl = this.shadow.getElementById("country");
|
|
447
577
|
const phoneCodeEl = this.shadow.getElementById("phone-code");
|
|
448
578
|
const stateEl = this.shadow.getElementById("state");
|
|
579
|
+
if (!countryEl || !phoneCodeEl || !stateEl) return;
|
|
580
|
+
const country = countryEl.value;
|
|
449
581
|
phoneCodeEl.value = COUNTRY_PHONE_MAP[country] || "";
|
|
450
582
|
const states = getStates(country);
|
|
451
583
|
stateEl.innerHTML = states.map(
|
|
@@ -453,77 +585,173 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
453
585
|
).join("");
|
|
454
586
|
}
|
|
455
587
|
|
|
588
|
+
detectCardType(number) {
|
|
589
|
+
const n = number.replace(/\s/g, "");
|
|
590
|
+
|
|
591
|
+
if (/^4/.test(n)) return "VISA";
|
|
592
|
+
if (/^5[1-5]/.test(n)) return "MASTERCARD";
|
|
593
|
+
if (/^3[47]/.test(n)) return "AMEX";
|
|
594
|
+
if (/^6(?:011|5)/.test(n)) return "DISCOVER";
|
|
595
|
+
|
|
596
|
+
return "";
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
getAddressFields() {
|
|
600
|
+
if (!this.isAddressRequired) return "";
|
|
601
|
+
|
|
602
|
+
return `
|
|
603
|
+
<div class="section">
|
|
604
|
+
<div class="section-title">Address</div>
|
|
605
|
+
|
|
606
|
+
<div class="field">
|
|
607
|
+
<input id="street" required placeholder=" " />
|
|
608
|
+
<label>Street *</label>
|
|
609
|
+
</div>
|
|
610
|
+
<div class="error" id="error-street"></div>
|
|
611
|
+
|
|
612
|
+
<div class="field">
|
|
613
|
+
<input id="city" required placeholder=" " />
|
|
614
|
+
<label>City *</label>
|
|
615
|
+
</div>
|
|
616
|
+
<div class="error" id="error-city"></div>
|
|
617
|
+
|
|
618
|
+
<div class="field">
|
|
619
|
+
<select id="state"></select>
|
|
620
|
+
<label class="floating">State *</label>
|
|
621
|
+
</div>
|
|
622
|
+
<div class="error" id="error-state"></div>
|
|
623
|
+
|
|
624
|
+
<div class="field">
|
|
625
|
+
<select id="country" required>
|
|
626
|
+
${COUNTRIES_LIST.map(c =>
|
|
627
|
+
`<option value="${c.code}" ${c.code === "US" ? "selected" : ""}>${c.name}</option>`
|
|
628
|
+
).join("")}
|
|
629
|
+
</select>
|
|
630
|
+
<label class="floating">Country *</label>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="error" id="error-country"></div>
|
|
633
|
+
|
|
634
|
+
<div class="field">
|
|
635
|
+
<input id="zip" required placeholder=" " />
|
|
636
|
+
<label>ZIP Code *</label>
|
|
637
|
+
</div>
|
|
638
|
+
<div class="error" id="error-zip"></div>
|
|
639
|
+
|
|
640
|
+
</div>
|
|
641
|
+
`;
|
|
642
|
+
}
|
|
643
|
+
|
|
456
644
|
validate() {
|
|
457
|
-
|
|
645
|
+
this.clearErrors();
|
|
646
|
+
let isValid = true;
|
|
458
647
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
648
|
+
const get = id => this.shadow.getElementById(id)?.value?.trim();
|
|
649
|
+
|
|
650
|
+
if (!get("name")) {
|
|
651
|
+
this.setError("name", "Name required");
|
|
652
|
+
isValid = false;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const email = get("email");
|
|
656
|
+
if (!email) {
|
|
657
|
+
this.setError("email", "Email required");
|
|
658
|
+
isValid = false;
|
|
659
|
+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
660
|
+
this.setError("email", "Invalid email");
|
|
661
|
+
isValid = false;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (!get("card-name")) {
|
|
665
|
+
this.setError("card-name", "Card holder name required");
|
|
666
|
+
isValid = false;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const cardNumber = get("card")?.replace(/\s/g, "");
|
|
670
|
+
if (!cardNumber || !/^\d{12,19}$/.test(cardNumber)) {
|
|
671
|
+
this.setError("card", "Invalid card number");
|
|
672
|
+
isValid = false;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const expiry = get("expiry");
|
|
676
|
+
if (!expiry || !/^\d{2}\/\d{2}$/.test(expiry)) {
|
|
677
|
+
this.setError("expiry", "Invalid expiry");
|
|
678
|
+
isValid = false;
|
|
679
|
+
} else {
|
|
680
|
+
const [mm, yy] = expiry.split("/").map(Number);
|
|
681
|
+
const now = new Date();
|
|
682
|
+
const currentYear = now.getFullYear() % 100;
|
|
683
|
+
const currentMonth = now.getMonth() + 1;
|
|
684
|
+
|
|
685
|
+
if (mm < 1 || mm > 12) {
|
|
686
|
+
this.setError("expiry", "Invalid month");
|
|
687
|
+
isValid = false;
|
|
688
|
+
} else if (yy < currentYear || (yy === currentYear && mm < currentMonth)) {
|
|
689
|
+
this.setError("expiry", "Card expired");
|
|
690
|
+
isValid = false;
|
|
477
691
|
}
|
|
478
|
-
|
|
479
|
-
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const cvv = get("cvv");
|
|
695
|
+
if (!cvv || !/^\d{3,4}$/.test(cvv)) {
|
|
696
|
+
this.setError("cvv", "Invalid CVV");
|
|
697
|
+
isValid = false;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const phone = get("phone")?.replace(/\D/g, "");
|
|
701
|
+
const code = get("phone-code");
|
|
702
|
+
if (!phone || !code) {
|
|
703
|
+
this.setError("phone", "Phone number required");
|
|
704
|
+
isValid = false;
|
|
480
705
|
} else {
|
|
481
|
-
const
|
|
482
|
-
if (
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const currentMonth = now.getMonth() + 1;
|
|
486
|
-
|
|
487
|
-
if (yy < currentYear || (yy === currentYear && mm < currentMonth)) {
|
|
488
|
-
return "Card expired";
|
|
706
|
+
const fullNumber = `${code}${phone}`;
|
|
707
|
+
if (!isValidPhoneNumber(fullNumber)) {
|
|
708
|
+
this.setError("phone", "Invalid phone number");
|
|
709
|
+
isValid = false;
|
|
489
710
|
}
|
|
490
711
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return "Invalid phone";
|
|
712
|
+
|
|
713
|
+
if (this.isAddressRequired) {
|
|
714
|
+
if (!get("street")) {
|
|
715
|
+
this.setError("street", "Street required");
|
|
716
|
+
isValid = false;
|
|
497
717
|
}
|
|
498
|
-
|
|
499
|
-
|
|
718
|
+
|
|
719
|
+
if (!get("city")) {
|
|
720
|
+
this.setError("city", "City required");
|
|
721
|
+
isValid = false;
|
|
500
722
|
}
|
|
501
723
|
|
|
502
|
-
|
|
503
|
-
|
|
724
|
+
if (!get("state")) {
|
|
725
|
+
this.setError("state", "State required");
|
|
726
|
+
isValid = false;
|
|
727
|
+
}
|
|
504
728
|
|
|
505
|
-
|
|
506
|
-
|
|
729
|
+
if (!get("country")) {
|
|
730
|
+
this.setError("country", "Country required");
|
|
731
|
+
isValid = false;
|
|
732
|
+
}
|
|
507
733
|
|
|
508
|
-
const
|
|
509
|
-
payButton.disabled = true;
|
|
510
|
-
payButton.textContent = "Processing...";
|
|
734
|
+
const zip = get("zip");
|
|
511
735
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
736
|
+
if (!zip) {
|
|
737
|
+
this.setError("zip", "ZIP required");
|
|
738
|
+
isValid = false;
|
|
739
|
+
} else if (postalCodes.validate(get("country"), zip) !== true) {
|
|
740
|
+
this.setError("zip", "Invalid ZIP code");
|
|
741
|
+
isValid = false;
|
|
518
742
|
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return isValid;
|
|
746
|
+
}
|
|
519
747
|
|
|
748
|
+
async oneTimePayment() {
|
|
520
749
|
try {
|
|
521
750
|
const values = await this.getServerEncription();
|
|
522
751
|
if (values?.public_key) {
|
|
523
752
|
const keys = await this.generateClientKeyPair();
|
|
524
753
|
if (keys?.privateKey) {
|
|
525
754
|
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
526
|
-
|
|
527
755
|
const phoneCode = getVal("phone-code") || "";
|
|
528
756
|
const phone = getVal("phone");
|
|
529
757
|
|
|
@@ -538,13 +766,17 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
538
766
|
phone: phone ? `${phoneCode}${phone}` : null
|
|
539
767
|
};
|
|
540
768
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
769
|
+
let address = null;
|
|
770
|
+
|
|
771
|
+
if (this.isAddressRequired) {
|
|
772
|
+
address = {
|
|
773
|
+
street: getVal("street"),
|
|
774
|
+
city: getVal("city"),
|
|
775
|
+
state: getVal("state"),
|
|
776
|
+
countryCode: getVal("country"),
|
|
777
|
+
postalCode: getVal("zip")
|
|
778
|
+
};
|
|
779
|
+
}
|
|
548
780
|
|
|
549
781
|
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
550
782
|
const payload = {
|
|
@@ -565,7 +797,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
565
797
|
cvv: cvv,
|
|
566
798
|
name: cardHolderName
|
|
567
799
|
},
|
|
568
|
-
postalCode: address?.
|
|
800
|
+
postalCode: address?.postalCode,
|
|
569
801
|
paymentMethodBillingAddress: address,
|
|
570
802
|
customerGid: customerGid
|
|
571
803
|
};
|
|
@@ -611,20 +843,282 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
611
843
|
body: JSON.stringify({ data: payload2?.data })
|
|
612
844
|
});
|
|
613
845
|
const resData = await res.json();
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
846
|
+
return resData;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
} catch (error) {
|
|
850
|
+
console.error("Payment Error:", error);
|
|
851
|
+
this.events.emit("error", error);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async recurringPayment () {
|
|
856
|
+
try {
|
|
857
|
+
const values = await this.getServerEncription();
|
|
858
|
+
if (values?.public_key) {
|
|
859
|
+
const keys = await this.generateClientKeyPair();
|
|
860
|
+
if (keys?.privateKey) {
|
|
861
|
+
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
862
|
+
const phoneCode = getVal("phone-code") || "";
|
|
863
|
+
const phone = getVal("phone");
|
|
864
|
+
|
|
865
|
+
const cardNumber = getVal("card")?.replace(/\s/g, "");
|
|
866
|
+
const expiry = getVal("expiry")?.split("/") || [];
|
|
867
|
+
const cvv = getVal("cvv");
|
|
868
|
+
const cardHolderName = getVal("card-name");
|
|
869
|
+
|
|
870
|
+
const customer = {
|
|
871
|
+
name: getVal("name"),
|
|
872
|
+
email: getVal("email"),
|
|
873
|
+
phone: phone ? `${phoneCode}${phone}` : null
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
let address = null;
|
|
877
|
+
|
|
878
|
+
if (this.isAddressRequired) {
|
|
879
|
+
address = {
|
|
880
|
+
street: getVal("street"),
|
|
881
|
+
city: getVal("city"),
|
|
882
|
+
state: getVal("state"),
|
|
883
|
+
countryCode: getVal("country"),
|
|
884
|
+
postalCode: getVal("zip")
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
889
|
+
const payload = {
|
|
890
|
+
publicKey: keys?.publicKey,
|
|
891
|
+
keyId: encriptedData?.keyId,
|
|
892
|
+
iv: encriptedData?.iv,
|
|
893
|
+
data: encriptedData?.encryptedData,
|
|
894
|
+
}
|
|
895
|
+
const newCustomer = await this.createCustomer(payload);
|
|
896
|
+
const customerGid = newCustomer.entity?.gid;
|
|
897
|
+
let product;
|
|
898
|
+
const itemInfo = await this.getItemInfo();
|
|
899
|
+
if (itemInfo?.entity?.content[0]?.gid) {
|
|
900
|
+
product = itemInfo?.entity?.content[0];
|
|
901
|
+
} else {
|
|
902
|
+
const newItemInfo = await this.addedItemInfo();
|
|
903
|
+
product = newItemInfo?.entity;
|
|
904
|
+
}
|
|
905
|
+
let plan;
|
|
906
|
+
const planInfo = await this.getPlan();
|
|
907
|
+
if (planInfo?.entity?.content[0]?.gid) {
|
|
908
|
+
plan = planInfo?.entity?.content[0];
|
|
909
|
+
} else {
|
|
910
|
+
const newPlanInfo = await this.addedPlanInfo();
|
|
911
|
+
plan = newPlanInfo?.entity;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const subscriptionData = {
|
|
915
|
+
currencyCode: this.currencyCode,
|
|
916
|
+
customerGid: customerGid,
|
|
917
|
+
plan2Gid: plan?.gid,
|
|
918
|
+
plan2BillingPeriod: 1,
|
|
919
|
+
plan2BillingFrequency: this.frequency,
|
|
920
|
+
plan2StartDate: new Date().toISOString(),
|
|
921
|
+
plan2TotalPayments: this.totalPayments,
|
|
922
|
+
description: this.description,
|
|
923
|
+
notificationType: "ALL",
|
|
924
|
+
emailRecipientList: [
|
|
925
|
+
customer.email
|
|
926
|
+
],
|
|
927
|
+
canCustomerCancelOrPause: true,
|
|
928
|
+
subscription2LineItems: [
|
|
929
|
+
{
|
|
930
|
+
itemGid: product?.gid,
|
|
931
|
+
quantity: 1,
|
|
932
|
+
upfront: false,
|
|
933
|
+
currencyCode: this.currencyCode,
|
|
934
|
+
amount: this.amount
|
|
935
|
+
}
|
|
936
|
+
]
|
|
937
|
+
}
|
|
938
|
+
const subscriptionDetails = await this.getSubScription(subscriptionData);
|
|
939
|
+
const subscription = subscriptionDetails?.entity;
|
|
940
|
+
const sessionData = {
|
|
941
|
+
link: subscription?.subscription2Links[0]?.link,
|
|
942
|
+
customer: subscription?.customer,
|
|
943
|
+
type: 'CARD',
|
|
944
|
+
card: {
|
|
945
|
+
number: cardNumber,
|
|
946
|
+
expiryMonth: parseInt(expiry[0]),
|
|
947
|
+
expiryYear: 2000 + parseInt(expiry[1]),
|
|
948
|
+
cvv: cvv,
|
|
949
|
+
name: cardHolderName
|
|
950
|
+
},
|
|
951
|
+
postalCode: address?.postalCode,
|
|
952
|
+
paymentMethodBillingAddress: address
|
|
953
|
+
}
|
|
954
|
+
const encriptedData4 = await this.encryptData(JSON.stringify(sessionData), values.public_key, keys.privateKey, values.key_id);
|
|
955
|
+
const payload2 = {
|
|
956
|
+
publicKey: keys?.publicKey,
|
|
957
|
+
keyId: encriptedData4?.keyId,
|
|
958
|
+
iv: encriptedData4?.iv,
|
|
959
|
+
data: encriptedData4?.encryptedData,
|
|
960
|
+
}
|
|
961
|
+
const gateway = this.getEndpoints().gateway;
|
|
962
|
+
const url = `${gateway}/v2/subscription-link/${subscription?.subscription2Links[0]?.gid}/payment-method`;
|
|
963
|
+
const res = await fetch(url, {
|
|
964
|
+
method: 'POST',
|
|
965
|
+
headers: {
|
|
966
|
+
'x-enc-public-key': payload2?.publicKey,
|
|
967
|
+
'x-enc-key-id': payload2?.keyId,
|
|
968
|
+
'x-enc-iv': payload2?.iv,
|
|
969
|
+
'Content-Type': 'application/json',
|
|
970
|
+
'x-api-key': this.apiKey,
|
|
971
|
+
},
|
|
972
|
+
body: JSON.stringify({ data: payload2?.data })
|
|
973
|
+
});
|
|
974
|
+
const resData = await res.json();
|
|
975
|
+
return resData;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
} catch (error) {
|
|
979
|
+
console.error("Payment Error:", error);
|
|
980
|
+
this.events.emit("error", error);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
async inventoryCharge() {
|
|
985
|
+
try {
|
|
986
|
+
const values = await this.getServerEncription();
|
|
987
|
+
if (values?.public_key) {
|
|
988
|
+
const keys = await this.generateClientKeyPair();
|
|
989
|
+
if (keys?.privateKey) {
|
|
990
|
+
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
991
|
+
const phoneCode = getVal("phone-code") || "";
|
|
992
|
+
const phone = getVal("phone");
|
|
993
|
+
|
|
994
|
+
const cardNumber = getVal("card")?.replace(/\s/g, "");
|
|
995
|
+
const expiry = getVal("expiry")?.split("/") || [];
|
|
996
|
+
const cvv = getVal("cvv");
|
|
997
|
+
const cardHolderName = getVal("card-name");
|
|
998
|
+
|
|
999
|
+
const customer = {
|
|
1000
|
+
name: getVal("name"),
|
|
1001
|
+
email: getVal("email"),
|
|
1002
|
+
phone: phone ? `${phoneCode}${phone}` : null
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
let address = null;
|
|
1006
|
+
|
|
1007
|
+
if (this.isAddressRequired) {
|
|
1008
|
+
address = {
|
|
1009
|
+
street: getVal("street"),
|
|
1010
|
+
city: getVal("city"),
|
|
1011
|
+
state: getVal("state"),
|
|
1012
|
+
countryCode: getVal("country"),
|
|
1013
|
+
postalCode: getVal("zip")
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
1018
|
+
const payload = {
|
|
1019
|
+
publicKey: keys?.publicKey,
|
|
1020
|
+
keyId: encriptedData?.keyId,
|
|
1021
|
+
iv: encriptedData?.iv,
|
|
1022
|
+
data: encriptedData?.encryptedData,
|
|
1023
|
+
}
|
|
1024
|
+
const newCustomer = await this.createCustomer(payload);
|
|
1025
|
+
const customerGid = newCustomer.entity?.gid;
|
|
1026
|
+
const orderDetails = await this.createOrder();
|
|
1027
|
+
const order = orderDetails?.entity;
|
|
1028
|
+
const sessionData = {
|
|
1029
|
+
customer: customer,
|
|
1030
|
+
customerGid: customerGid,
|
|
1031
|
+
paymentMethod: {
|
|
1032
|
+
card: {
|
|
1033
|
+
number: cardNumber,
|
|
1034
|
+
expiryMonth: parseInt(expiry[0]),
|
|
1035
|
+
expiryYear: 2000 + parseInt(expiry[1]),
|
|
1036
|
+
cvv: cvv,
|
|
1037
|
+
name: cardHolderName
|
|
1038
|
+
},
|
|
1039
|
+
postalCode: address?.postalCode,
|
|
1040
|
+
type: "CARD",
|
|
1041
|
+
paymentMethodBillingAddress: address,
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
const encriptedData1 = await this.encryptData(JSON.stringify(sessionData), values.public_key, keys.privateKey, values.key_id);
|
|
1045
|
+
const payload2 = {
|
|
1046
|
+
publicKey: keys?.publicKey,
|
|
1047
|
+
keyId: encriptedData1?.keyId,
|
|
1048
|
+
iv: encriptedData1?.iv,
|
|
1049
|
+
data: encriptedData1?.encryptedData,
|
|
1050
|
+
}
|
|
1051
|
+
const gateway = this.getEndpoints().gateway;
|
|
1052
|
+
const url = `${gateway}/v1/inventory/order-link/${order?.inventoryOrderLink?.gid}/charge`;
|
|
1053
|
+
const res = await fetch(url, {
|
|
1054
|
+
method: 'POST',
|
|
1055
|
+
headers: {
|
|
1056
|
+
'x-enc-public-key': payload2?.publicKey,
|
|
1057
|
+
'x-enc-key-id': payload2?.keyId,
|
|
1058
|
+
'x-enc-iv': payload2?.iv,
|
|
1059
|
+
'Content-Type': 'application/json',
|
|
1060
|
+
'x-api-key': this.apiKey,
|
|
1061
|
+
},
|
|
1062
|
+
body: JSON.stringify({ data: payload2?.data })
|
|
1063
|
+
});
|
|
1064
|
+
const resData = await res.json();
|
|
1065
|
+
return resData;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
console.error("Payment Error:", error);
|
|
1071
|
+
this.events.emit("error", error);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
async handleSubmit(e) {
|
|
1076
|
+
e.preventDefault();
|
|
1077
|
+
|
|
1078
|
+
const payButton = this.shadow.getElementById("form").querySelector(".btn");
|
|
1079
|
+
payButton.disabled = true;
|
|
1080
|
+
payButton.textContent = "Processing...";
|
|
1081
|
+
|
|
1082
|
+
const isValid = this.validate();
|
|
1083
|
+
if (!isValid) {
|
|
1084
|
+
payButton.disabled = false;
|
|
1085
|
+
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
if (this.frequency === "ONE-TIME" && !this.inventory) {
|
|
1090
|
+
const resData = await this.oneTimePayment();
|
|
1091
|
+
if (resData?.entity?.errorCode === null) {
|
|
1092
|
+
this.events.emit("success", resData);
|
|
1093
|
+
this.close();
|
|
1094
|
+
} else {
|
|
1095
|
+
const errorMsg = resData?.entity?.errorDescription || "Payment failed";
|
|
1096
|
+
console.error("Payment Error:", errorMsg);
|
|
1097
|
+
this.events.emit("error", errorMsg);
|
|
1098
|
+
}
|
|
1099
|
+
} else if (this.frequency !== "ONE-TIME" && !this.inventory) {
|
|
1100
|
+
const resData = await this.recurringPayment();
|
|
1101
|
+
if (resData?.entity?.setupSession?.errorCode === null) {
|
|
1102
|
+
this.events.emit("success", resData);
|
|
1103
|
+
this.close();
|
|
1104
|
+
} else {
|
|
1105
|
+
const errorMsg = resData?.entity?.setupSession?.errorDescription || "Payment failed";
|
|
1106
|
+
console.error("Payment Error:", errorMsg);
|
|
1107
|
+
this.events.emit("error", errorMsg);
|
|
1108
|
+
}
|
|
1109
|
+
} else if (this.inventory) {
|
|
1110
|
+
const resData = await this.inventoryCharge();
|
|
1111
|
+
if (resData?.responseCode === 200) {
|
|
1112
|
+
this.events.emit("success", resData);
|
|
1113
|
+
this.close();
|
|
1114
|
+
} else {
|
|
1115
|
+
console.error("Payment Error:", resData?.message || 'Payment failed');
|
|
1116
|
+
this.events.emit("error", resData?.message);
|
|
622
1117
|
}
|
|
623
1118
|
}
|
|
624
1119
|
} catch (error) {
|
|
625
1120
|
console.error("Payment Error:", error);
|
|
626
1121
|
this.events.emit("error", error);
|
|
627
|
-
alert(error.message || "Payment failed");
|
|
628
1122
|
} finally {
|
|
629
1123
|
payButton.disabled = false;
|
|
630
1124
|
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
@@ -633,6 +1127,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
633
1127
|
render() {
|
|
634
1128
|
const t = this.theme;
|
|
635
1129
|
const amountText = (this.amount / 100).toFixed(2);
|
|
1130
|
+
const inventoryAmountText = ((this.splitUp?.netPayable) / 100).toFixed(2);
|
|
636
1131
|
|
|
637
1132
|
this.shadow.innerHTML = `
|
|
638
1133
|
<style>
|
|
@@ -654,26 +1149,45 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
654
1149
|
background:rgba(0,0,0,0.6);
|
|
655
1150
|
justify-content:center;
|
|
656
1151
|
align-items:center;
|
|
1152
|
+
z-index:9999;
|
|
657
1153
|
}
|
|
658
1154
|
|
|
659
1155
|
.modal {
|
|
660
|
-
width:
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
1156
|
+
width: 520px;
|
|
1157
|
+
height: auto;
|
|
1158
|
+
max-height: 650px;
|
|
1159
|
+
background: var(--bg);
|
|
1160
|
+
color: var(--text);
|
|
1161
|
+
border-radius: 16px;
|
|
1162
|
+
padding: 28px;
|
|
1163
|
+
font-family: sans-serif;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
.modal-body {
|
|
1167
|
+
overflow-y: auto;
|
|
1168
|
+
flex: 1;
|
|
1169
|
+
height: auto;
|
|
1170
|
+
max-height: 530px;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
form {
|
|
1174
|
+
display: flex;
|
|
1175
|
+
flex-direction: column;
|
|
1176
|
+
gap: 16px;
|
|
666
1177
|
}
|
|
667
1178
|
|
|
668
1179
|
input, select {
|
|
669
|
-
width:100%;
|
|
670
|
-
padding:
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1180
|
+
width: 100%;
|
|
1181
|
+
padding: 12px;
|
|
1182
|
+
border: 1px solid var(--border);
|
|
1183
|
+
background: #ffffff;
|
|
1184
|
+
color: #000000;
|
|
1185
|
+
border-radius: 8px;
|
|
1186
|
+
outline: none;
|
|
1187
|
+
height: 44px;
|
|
1188
|
+
font-size: 14px;
|
|
1189
|
+
box-sizing: border-box;
|
|
1190
|
+
margin: 0 !important;
|
|
677
1191
|
}
|
|
678
1192
|
|
|
679
1193
|
input::placeholder {
|
|
@@ -696,7 +1210,7 @@ input::placeholder {
|
|
|
696
1210
|
select {
|
|
697
1211
|
width:100%;
|
|
698
1212
|
padding:10px;
|
|
699
|
-
margin:
|
|
1213
|
+
margin: 0;
|
|
700
1214
|
border:1px solid var(--border);
|
|
701
1215
|
border-radius:8px;
|
|
702
1216
|
|
|
@@ -712,67 +1226,199 @@ select option {
|
|
|
712
1226
|
color: #000000;
|
|
713
1227
|
}
|
|
714
1228
|
|
|
715
|
-
.row {
|
|
1229
|
+
.row {
|
|
1230
|
+
display: flex;
|
|
1231
|
+
gap: 16px;
|
|
1232
|
+
align-items: center;
|
|
1233
|
+
}
|
|
1234
|
+
.phone-code {
|
|
1235
|
+
flex: 0 0 70px !important;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.row input,
|
|
1239
|
+
.row select {
|
|
1240
|
+
flex: 1;
|
|
1241
|
+
}
|
|
716
1242
|
|
|
717
|
-
|
|
1243
|
+
input, select {
|
|
1244
|
+
box-sizing: border-box;
|
|
1245
|
+
height: 40px;
|
|
1246
|
+
padding: 10px;
|
|
1247
|
+
font-size: 14px;
|
|
1248
|
+
}
|
|
718
1249
|
|
|
719
1250
|
.btn {
|
|
720
|
-
background:var(--primary);
|
|
721
|
-
color
|
|
722
|
-
padding:
|
|
723
|
-
border:none;
|
|
724
|
-
border-radius:10px;
|
|
725
|
-
cursor:pointer;
|
|
726
|
-
width:100%;
|
|
1251
|
+
background: var(--primary);
|
|
1252
|
+
color: #fff;
|
|
1253
|
+
padding: 14px;
|
|
1254
|
+
border: none;
|
|
1255
|
+
border-radius: 10px;
|
|
1256
|
+
cursor: pointer;
|
|
1257
|
+
width: 100%;
|
|
1258
|
+
height: 48px;
|
|
1259
|
+
font-size: 15px;
|
|
727
1260
|
}
|
|
728
1261
|
|
|
729
1262
|
.close {
|
|
730
1263
|
float:right;
|
|
731
1264
|
cursor:pointer;
|
|
732
1265
|
}
|
|
733
|
-
</style>
|
|
734
1266
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1267
|
+
h3 {
|
|
1268
|
+
margin-bottom: 16px;
|
|
1269
|
+
}
|
|
738
1270
|
|
|
739
|
-
|
|
1271
|
+
.section {
|
|
1272
|
+
display: flex;
|
|
1273
|
+
flex-direction: column;
|
|
1274
|
+
gap: 16px;
|
|
1275
|
+
}
|
|
740
1276
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
1277
|
+
.section-title {
|
|
1278
|
+
font-size: 14px;
|
|
1279
|
+
font-weight: 600;
|
|
1280
|
+
margin: 0;
|
|
1281
|
+
}
|
|
744
1282
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
</div>
|
|
1283
|
+
.card-wrapper {
|
|
1284
|
+
position: relative;
|
|
1285
|
+
}
|
|
749
1286
|
|
|
750
|
-
|
|
751
|
-
|
|
1287
|
+
#card-icon {
|
|
1288
|
+
position: absolute;
|
|
1289
|
+
right: 10px;
|
|
1290
|
+
top: 50%;
|
|
1291
|
+
transform: translateY(-50%);
|
|
1292
|
+
height: 24px;
|
|
1293
|
+
}
|
|
1294
|
+
.error {
|
|
1295
|
+
font-size: 12px;
|
|
1296
|
+
color: #dc2626;
|
|
1297
|
+
margin-top: 0px;
|
|
1298
|
+
margin-bottom: 0px;
|
|
1299
|
+
}
|
|
752
1300
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1301
|
+
.field {
|
|
1302
|
+
position: relative;
|
|
1303
|
+
width: 100%;
|
|
1304
|
+
}
|
|
757
1305
|
|
|
758
|
-
|
|
759
|
-
|
|
1306
|
+
.field input,
|
|
1307
|
+
.field select {
|
|
1308
|
+
width: 100%;
|
|
1309
|
+
padding: 14px 12px;
|
|
1310
|
+
border: 1px solid var(--border);
|
|
1311
|
+
border-radius: 8px;
|
|
1312
|
+
font-size: 14px;
|
|
1313
|
+
background: #fff;
|
|
1314
|
+
outline: none;
|
|
1315
|
+
}
|
|
1316
|
+
.field label {
|
|
1317
|
+
position: absolute;
|
|
1318
|
+
left: 0px;
|
|
1319
|
+
top: 50%;
|
|
1320
|
+
transform: translateY(-50%);
|
|
1321
|
+
padding: 0 4px;
|
|
1322
|
+
color: #6b7280;
|
|
1323
|
+
font-size: 14px;
|
|
1324
|
+
pointer-events: none;
|
|
1325
|
+
transition: 0.2s ease;
|
|
1326
|
+
}
|
|
1327
|
+
.field input:focus + label,
|
|
1328
|
+
.field input:not(:placeholder-shown) + label {
|
|
1329
|
+
top: -8px;
|
|
1330
|
+
font-size: 12px;
|
|
1331
|
+
color: var(--primary);
|
|
1332
|
+
}
|
|
1333
|
+
.field select {
|
|
1334
|
+
width: 100%;
|
|
1335
|
+
height: 44px;
|
|
1336
|
+
padding: 0 12px;
|
|
1337
|
+
border: 1px solid var(--border);
|
|
1338
|
+
border-radius: 8px;
|
|
1339
|
+
background: #fff;
|
|
1340
|
+
color: #000;
|
|
1341
|
+
font-size: 14px;
|
|
1342
|
+
outline: none;
|
|
1343
|
+
appearance: none;
|
|
1344
|
+
}
|
|
1345
|
+
.input-error {
|
|
1346
|
+
border-color: #dc2626 !important;
|
|
1347
|
+
}
|
|
760
1348
|
|
|
761
|
-
|
|
762
|
-
|
|
1349
|
+
.field input.input-error + label {
|
|
1350
|
+
color: #dc2626;
|
|
1351
|
+
}
|
|
763
1352
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
).join("")}
|
|
771
|
-
</select>
|
|
1353
|
+
.field label.floating {
|
|
1354
|
+
top: -8px;
|
|
1355
|
+
font-size: 12px;
|
|
1356
|
+
color: var(--primary);
|
|
1357
|
+
}
|
|
1358
|
+
</style>
|
|
772
1359
|
|
|
773
|
-
|
|
1360
|
+
<div class="overlay" id="overlay">
|
|
1361
|
+
<div class="modal">
|
|
1362
|
+
<div class="modal-header">
|
|
1363
|
+
<span class="close" id="close">✕</span>
|
|
1364
|
+
<h3>Pay with Card</h3>
|
|
1365
|
+
</div>
|
|
1366
|
+
<div class="modal-body">
|
|
1367
|
+
<form id="form">
|
|
1368
|
+
<div class="section">
|
|
1369
|
+
<div class="section-title">Customer Details</div>
|
|
1370
|
+
<div class="field">
|
|
1371
|
+
<input id="name" required placeholder=" " />
|
|
1372
|
+
<label>Name *</label>
|
|
1373
|
+
</div>
|
|
1374
|
+
<div class="error" id="error-name"></div>
|
|
1375
|
+
<div class="field">
|
|
1376
|
+
<input id="email" required placeholder=" " />
|
|
1377
|
+
<label>Email *</label>
|
|
1378
|
+
</div>
|
|
1379
|
+
<div class="error" id="error-email"></div>
|
|
1380
|
+
<div class="row">
|
|
1381
|
+
<div class="field phone-code">
|
|
1382
|
+
<input id="phone-code" placeholder=" " />
|
|
1383
|
+
<label>Code</label>
|
|
1384
|
+
</div>
|
|
1385
|
+
<div class="field">
|
|
1386
|
+
<input id="phone" required placeholder=" " />
|
|
1387
|
+
<label>Phone *</label>
|
|
1388
|
+
</div>
|
|
1389
|
+
</div>
|
|
1390
|
+
<div class="error" id="error-phone"></div>
|
|
1391
|
+
</div>
|
|
1392
|
+
<div class="section">
|
|
1393
|
+
<div class="section-title">Card Details</div>
|
|
1394
|
+
<div class="field">
|
|
1395
|
+
<input id="card" required placeholder=" " />
|
|
1396
|
+
<label>Card Number *</label>
|
|
1397
|
+
</div>
|
|
1398
|
+
<div class="error" id="error-card"></div>
|
|
774
1399
|
|
|
775
|
-
<
|
|
1400
|
+
<div class="row">
|
|
1401
|
+
<div class="field">
|
|
1402
|
+
<input id="expiry" required placeholder=" " />
|
|
1403
|
+
<label>MM/YY *</label>
|
|
1404
|
+
</div>
|
|
1405
|
+
|
|
1406
|
+
<div class="field">
|
|
1407
|
+
<input id="cvv" type="password" required placeholder=" " />
|
|
1408
|
+
<label>CVV *</label>
|
|
1409
|
+
</div>
|
|
1410
|
+
</div>
|
|
1411
|
+
<div class="error" id="error-expiry"></div>
|
|
1412
|
+
<div class="error" id="error-cvv"></div>
|
|
1413
|
+
<div class="field">
|
|
1414
|
+
<input id="card-name" required placeholder=" " />
|
|
1415
|
+
<label>Card Holder Name *</label>
|
|
1416
|
+
</div>
|
|
1417
|
+
<div class="error" id="error-card-name"></div>
|
|
1418
|
+
</div>
|
|
1419
|
+
${this.getAddressFields()}
|
|
1420
|
+
<button class="btn">Pay $${this.inventory ? inventoryAmountText : amountText}</button>
|
|
1421
|
+
</div>
|
|
776
1422
|
</form>
|
|
777
1423
|
</div>
|
|
778
1424
|
</div>
|
|
@@ -780,16 +1426,64 @@ select option {
|
|
|
780
1426
|
|
|
781
1427
|
const $ = id => this.shadow.getElementById(id);
|
|
782
1428
|
|
|
1429
|
+
if (this.customer) {
|
|
1430
|
+
if (this.customer.name) $("name").value = this.customer.name;
|
|
1431
|
+
if (this.customer.email) $("email").value = this.customer.email;
|
|
1432
|
+
|
|
1433
|
+
if (this.customer.phone) {
|
|
1434
|
+
let phone = this.customer.phone;
|
|
1435
|
+
let code = "";
|
|
1436
|
+
let number = phone;
|
|
1437
|
+
|
|
1438
|
+
if (phone.startsWith("+")) {
|
|
1439
|
+
if (phone.startsWith("+1")) {
|
|
1440
|
+
code = "+1";
|
|
1441
|
+
number = phone.slice(2);
|
|
1442
|
+
} else if (phone.startsWith("+91")) {
|
|
1443
|
+
code = "+91";
|
|
1444
|
+
number = phone.slice(3);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
if ($("phone-code")) $("phone-code").value = code;
|
|
1449
|
+
if ($("phone")) $("phone").value = number;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
783
1453
|
$("close").onclick = () => this.close();
|
|
784
1454
|
$("form").onsubmit = e => this.handleSubmit(e);
|
|
785
1455
|
|
|
786
|
-
$("country").onchange = () => this.handleCountryChange();
|
|
787
|
-
|
|
788
1456
|
const countryEl = $("country");
|
|
789
|
-
countryEl
|
|
790
|
-
|
|
1457
|
+
if (countryEl) {
|
|
1458
|
+
countryEl.onchange = () => this.handleCountryChange();
|
|
1459
|
+
if (!this.customer?.countryCode) {
|
|
1460
|
+
countryEl.value = "US";
|
|
1461
|
+
}
|
|
1462
|
+
this.handleCountryChange();
|
|
1463
|
+
}
|
|
1464
|
+
const phoneCodeEl = $("phone-code");
|
|
1465
|
+
if (phoneCodeEl) {
|
|
1466
|
+
phoneCodeEl.dataset.auto = "true";
|
|
1467
|
+
}
|
|
791
1468
|
|
|
792
|
-
|
|
1469
|
+
const icons = {
|
|
1470
|
+
VISA: `<svg width="40" viewBox="0 0 48 16"><text x="0" y="14">VISA</text></svg>`,
|
|
1471
|
+
MASTERCARD: `<svg width="40"><text x="0" y="14">MC</text></svg>`,
|
|
1472
|
+
AMEX: `<svg width="40"><text x="0" y="14">AMEX</text></svg>`,
|
|
1473
|
+
DISCOVER: `<svg width="40"><text x="0" y="14">DISC</text></svg>`
|
|
1474
|
+
};
|
|
1475
|
+
$("card").oninput = e => {
|
|
1476
|
+
const formatted = this.formatCard(e.target.value);
|
|
1477
|
+
e.target.value = formatted;
|
|
1478
|
+
|
|
1479
|
+
const type = this.detectCardType(formatted);
|
|
1480
|
+
|
|
1481
|
+
const iconEl = $("card-icon");
|
|
1482
|
+
if (iconEl) {
|
|
1483
|
+
iconEl.src = icons[type] || "";
|
|
1484
|
+
iconEl.style.display = type ? "block" : "none";
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
793
1487
|
$("expiry").oninput = e => e.target.value = this.formatExpiry(e.target.value);
|
|
794
1488
|
$("phone").oninput = e => e.target.value = this.formatPhone(e.target.value);
|
|
795
1489
|
}
|