@swirepay-developer/swirepay-ach-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 +858 -273
package/package.json
CHANGED
|
@@ -3,7 +3,8 @@ import { isValidPhoneNumber } from "libphonenumber-js";
|
|
|
3
3
|
|
|
4
4
|
const SDK_CONFIG = {
|
|
5
5
|
elliptic: "https://cdnjs.cloudflare.com/ajax/libs/elliptic/6.3.1/elliptic.js",
|
|
6
|
-
crypto: "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"
|
|
6
|
+
crypto: "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js",
|
|
7
|
+
plaid: "https://cdn.plaid.com/link/v2/stable/link-initialize.js"
|
|
7
8
|
};
|
|
8
9
|
class EventEmitter {
|
|
9
10
|
constructor() { this.events = {}; }
|
|
@@ -108,133 +109,8 @@ const USA_STATES = [
|
|
|
108
109
|
];
|
|
109
110
|
|
|
110
111
|
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
112
|
{ "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
113
|
{ "name": "United States", "code": "US" },
|
|
236
|
-
{ "name": "Vietnam", "code": "VN" },
|
|
237
|
-
{ "name": "Zimbabwe", "code": "ZW" }
|
|
238
114
|
];
|
|
239
115
|
|
|
240
116
|
const DEFAULT_THEME = {
|
|
@@ -279,20 +155,121 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
279
155
|
this.theme = DEFAULT_THEME;
|
|
280
156
|
}
|
|
281
157
|
|
|
158
|
+
getEndpoints() {
|
|
159
|
+
if (this.test) {
|
|
160
|
+
return { gateway: 'https://staging-backend.swirepay.com' };
|
|
161
|
+
}
|
|
162
|
+
return { gateway: 'https://api.swirepay.com' };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getSplitUpDetails(inventoryOrder) {
|
|
166
|
+
const payload = {
|
|
167
|
+
shopGid: inventoryOrder?.shopGid,
|
|
168
|
+
items: inventoryOrder?.inventoryOrderLineItems,
|
|
169
|
+
tipType: "AMOUNT",
|
|
170
|
+
tip: inventoryOrder?.totalTip
|
|
171
|
+
}
|
|
172
|
+
const gateway = this.getEndpoints().gateway;
|
|
173
|
+
let url = `${gateway}/v1/inventory/order/inventory-order-split-up`;
|
|
174
|
+
const response = await fetch(url, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: {
|
|
177
|
+
'Content-Type': 'application/json',
|
|
178
|
+
'x-api-key': this.apiKey,
|
|
179
|
+
},
|
|
180
|
+
body: JSON.stringify(payload)
|
|
181
|
+
})
|
|
182
|
+
return await response.json();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async updateFromAttributes() {
|
|
186
|
+
try {
|
|
187
|
+
this.amount = parseInt(this.getAttribute("amount")) || 0;
|
|
188
|
+
this.test = this.getAttribute("mode") === "test";
|
|
189
|
+
this.currencyCode = this.getAttribute("currencycode");
|
|
190
|
+
this.apiKey = this.getAttribute("api-key");
|
|
191
|
+
this.isAddressRequired = this.getAttribute("isaddressrequired") === "true";
|
|
192
|
+
this.inventory = this.getAttribute("inventory") === "true";
|
|
193
|
+
this.planGid = this.getAttribute("plangid");
|
|
194
|
+
this.productName = this.getAttribute("productname");
|
|
195
|
+
this.frequency = this.getAttribute("frequency");
|
|
196
|
+
this.description = this.getAttribute("description");
|
|
197
|
+
this.totalPayments = this.getAttribute("totalpayments");
|
|
198
|
+
this.accountGid = this.getAttribute("accountgid");
|
|
199
|
+
const customerAttr = this.getAttribute("customer");
|
|
200
|
+
this.customer = customerAttr ? JSON.parse(customerAttr) : {};
|
|
201
|
+
|
|
202
|
+
const inventoryOrders = this.getAttribute("inventoryorders");
|
|
203
|
+
this.inventoryOrder = inventoryOrders ? JSON.parse(inventoryOrders) : {};
|
|
204
|
+
if (this.inventory && this.inventoryOrder) {
|
|
205
|
+
const splitUpInfo = await this.getSplitUpDetails(this.inventoryOrder);
|
|
206
|
+
this.splitUp = splitUpInfo?.entity;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
} catch (e) {
|
|
210
|
+
console.error("Attribute parsing error:", e);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
282
214
|
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");
|
|
215
|
+
await this.updateFromAttributes();
|
|
216
|
+
|
|
288
217
|
await Promise.all([
|
|
289
218
|
loadScript(SDK_CONFIG.elliptic),
|
|
290
|
-
loadScript(SDK_CONFIG.crypto)
|
|
219
|
+
loadScript(SDK_CONFIG.crypto),
|
|
220
|
+
loadScript(SDK_CONFIG.plaid)
|
|
291
221
|
]);
|
|
292
222
|
|
|
293
223
|
this.render();
|
|
294
224
|
}
|
|
295
225
|
|
|
226
|
+
static get observedAttributes() {
|
|
227
|
+
return [
|
|
228
|
+
"amount",
|
|
229
|
+
"mode",
|
|
230
|
+
"currencycode",
|
|
231
|
+
"api-key",
|
|
232
|
+
"isaddressrequired",
|
|
233
|
+
"inventory",
|
|
234
|
+
"plangid",
|
|
235
|
+
"productname",
|
|
236
|
+
"frequency",
|
|
237
|
+
"description",
|
|
238
|
+
"totalpayments",
|
|
239
|
+
"customer",
|
|
240
|
+
"inventoryorders"
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async attributeChangedCallback(name, oldValue, newValue) {
|
|
245
|
+
if (oldValue === newValue) return;
|
|
246
|
+
|
|
247
|
+
await this.updateFromAttributes();
|
|
248
|
+
|
|
249
|
+
if (this.shadowRoot) {
|
|
250
|
+
this.render();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
setError(id, message) {
|
|
255
|
+
const input = this.shadow.getElementById(id);
|
|
256
|
+
const errorEl = this.shadow.getElementById(`error-${id}`);
|
|
257
|
+
|
|
258
|
+
if (errorEl) errorEl.textContent = message || "";
|
|
259
|
+
|
|
260
|
+
if (input) {
|
|
261
|
+
if (message) input.classList.add("input-error");
|
|
262
|
+
else input.classList.remove("input-error");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
clearErrors() {
|
|
267
|
+
this.shadow.querySelectorAll(".error").forEach(e => e.textContent = "");
|
|
268
|
+
this.shadow.querySelectorAll("input, select").forEach(el => {
|
|
269
|
+
el.classList.remove("input-error");
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
296
273
|
async encryptData(data, serverPublicKey, clientPrivateKey, keyId) {
|
|
297
274
|
try {
|
|
298
275
|
const EC = window.elliptic.ec;
|
|
@@ -347,13 +324,6 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
347
324
|
}
|
|
348
325
|
}
|
|
349
326
|
|
|
350
|
-
getEndpoints() {
|
|
351
|
-
if (this.test) {
|
|
352
|
-
return { gateway: 'https://staging-backend.swirepay.com' };
|
|
353
|
-
}
|
|
354
|
-
return { gateway: 'https://api.swirepay.com' };
|
|
355
|
-
}
|
|
356
|
-
|
|
357
327
|
async createCustomer(payload) {
|
|
358
328
|
const gateway = this.getEndpoints().gateway;
|
|
359
329
|
let url = `${gateway}/v3/customer`;
|
|
@@ -371,6 +341,31 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
371
341
|
return await response.json();
|
|
372
342
|
}
|
|
373
343
|
|
|
344
|
+
async initPlaid() {
|
|
345
|
+
const gateway = this.getEndpoints().gateway;
|
|
346
|
+
let url = `${gateway}/v1/integration/plaid/${this.accountGid}/create-secure-link-token?isTest=${!this.test}`;
|
|
347
|
+
const res = await fetch(url, {
|
|
348
|
+
method: 'POST',
|
|
349
|
+
headers: {
|
|
350
|
+
'Content-Type': 'application/json',
|
|
351
|
+
'x-api-key': this.apiKey,
|
|
352
|
+
},
|
|
353
|
+
body: JSON.stringify({})
|
|
354
|
+
})
|
|
355
|
+
const response = await res.json();
|
|
356
|
+
|
|
357
|
+
this.plaidHandler = Plaid.create({
|
|
358
|
+
token: response?.entity?.token,
|
|
359
|
+
|
|
360
|
+
onSuccess: async (public_token, metadata) => {
|
|
361
|
+
this.processorToken = public_token;
|
|
362
|
+
|
|
363
|
+
this.shadow.getElementById("bank-status").textContent =
|
|
364
|
+
`Connected: ${metadata.institution.name}`;
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
374
369
|
async addPaymentMethod(payload) {
|
|
375
370
|
const gateway = this.getEndpoints().gateway;
|
|
376
371
|
let url = `${gateway}/v3/payment-method`;
|
|
@@ -388,6 +383,109 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
388
383
|
return await response.json();
|
|
389
384
|
}
|
|
390
385
|
|
|
386
|
+
async createOrder() {
|
|
387
|
+
const gateway = this.getEndpoints().gateway;
|
|
388
|
+
let url = `${gateway}/v1/inventory/order`;
|
|
389
|
+
const response = await fetch(url, {
|
|
390
|
+
method: 'POST',
|
|
391
|
+
headers: {
|
|
392
|
+
'Content-Type': 'application/json',
|
|
393
|
+
'x-api-key': this.apiKey,
|
|
394
|
+
},
|
|
395
|
+
body: JSON.stringify(this.inventoryOrder)
|
|
396
|
+
})
|
|
397
|
+
return await response.json();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async getSubScription(payload) {
|
|
401
|
+
const gateway = this.getEndpoints().gateway;
|
|
402
|
+
let url = `${gateway}/v2/subscription`;
|
|
403
|
+
const response = await fetch(url, {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: {
|
|
406
|
+
'Content-Type': 'application/json',
|
|
407
|
+
'x-api-key': this.apiKey,
|
|
408
|
+
},
|
|
409
|
+
body: JSON.stringify(payload)
|
|
410
|
+
})
|
|
411
|
+
return await response.json();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async getPlan() {
|
|
415
|
+
const gateway = this.getEndpoints().gateway;
|
|
416
|
+
let url = `${gateway}/v2/plan?isInternal.EQ=false&name.EQ=Recurring%20Plan`;
|
|
417
|
+
const response = await fetch(url, {
|
|
418
|
+
method: 'GET',
|
|
419
|
+
headers: {
|
|
420
|
+
'Content-Type': 'application/json',
|
|
421
|
+
'x-api-key': this.apiKey,
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
return await response.json();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async addedPlanInfo() {
|
|
428
|
+
const payload = {
|
|
429
|
+
"currencyCode": this.currencyCode,
|
|
430
|
+
"name": "Recurring Plan",
|
|
431
|
+
"description": "Recurring Plan",
|
|
432
|
+
"note": "Recurring Plan",
|
|
433
|
+
"billingFrequency": this.frequency,
|
|
434
|
+
"billingPeriod": 1
|
|
435
|
+
}
|
|
436
|
+
const gateway = this.getEndpoints().gateway;
|
|
437
|
+
let url = `${gateway}/v2/plan`;
|
|
438
|
+
const response = await fetch(url, {
|
|
439
|
+
method: 'POST',
|
|
440
|
+
headers: {
|
|
441
|
+
'Content-Type': 'application/json',
|
|
442
|
+
'x-api-key': this.apiKey,
|
|
443
|
+
},
|
|
444
|
+
body: JSON.stringify(payload)
|
|
445
|
+
})
|
|
446
|
+
return await response.json();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async getItemInfo() {
|
|
450
|
+
const gateway = this.getEndpoints().gateway;
|
|
451
|
+
let url = `${gateway}/v1/inventory/item?name.EQ=Recurring%20item&priceType.EQ=VARIABLE&isRecurring.EQ=true`;
|
|
452
|
+
const response = await fetch(url, {
|
|
453
|
+
method: 'GET',
|
|
454
|
+
headers: {
|
|
455
|
+
'Content-Type': 'application/json',
|
|
456
|
+
'x-api-key': this.apiKey,
|
|
457
|
+
},
|
|
458
|
+
})
|
|
459
|
+
return await response.json();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async addedItemInfo() {
|
|
463
|
+
const payload = {
|
|
464
|
+
"name": "Recurring item",
|
|
465
|
+
"alternateName": "Recurring item",
|
|
466
|
+
"onlineName": "Recurring item",
|
|
467
|
+
"onlineEnabled": true,
|
|
468
|
+
"price": 100,
|
|
469
|
+
"priceType": "VARIABLE",
|
|
470
|
+
"includeTax": true,
|
|
471
|
+
"available": true,
|
|
472
|
+
"recurring": true,
|
|
473
|
+
"onKiosk": true
|
|
474
|
+
}
|
|
475
|
+
const gateway = this.getEndpoints().gateway;
|
|
476
|
+
let url = `${gateway}/v1/inventory/item`;
|
|
477
|
+
const response = await fetch(url, {
|
|
478
|
+
method: 'POST',
|
|
479
|
+
headers: {
|
|
480
|
+
'Content-Type': 'application/json',
|
|
481
|
+
'x-api-key': this.apiKey,
|
|
482
|
+
},
|
|
483
|
+
body: JSON.stringify(payload)
|
|
484
|
+
})
|
|
485
|
+
return await response.json();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
|
|
391
489
|
async getServerEncription() {
|
|
392
490
|
try {
|
|
393
491
|
const gateway = this.getEndpoints().gateway;
|
|
@@ -443,9 +541,11 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
443
541
|
return v.replace(/\D/g, "").slice(0, 10);
|
|
444
542
|
}
|
|
445
543
|
handleCountryChange() {
|
|
446
|
-
const
|
|
544
|
+
const countryEl = this.shadow.getElementById("country");
|
|
447
545
|
const phoneCodeEl = this.shadow.getElementById("phone-code");
|
|
448
546
|
const stateEl = this.shadow.getElementById("state");
|
|
547
|
+
if (!countryEl || !phoneCodeEl || !stateEl) return;
|
|
548
|
+
const country = countryEl.value;
|
|
449
549
|
phoneCodeEl.value = COUNTRY_PHONE_MAP[country] || "";
|
|
450
550
|
const states = getStates(country);
|
|
451
551
|
stateEl.innerHTML = states.map(
|
|
@@ -453,50 +553,134 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
453
553
|
).join("");
|
|
454
554
|
}
|
|
455
555
|
|
|
556
|
+
detectCardType(number) {
|
|
557
|
+
const n = number.replace(/\s/g, "");
|
|
558
|
+
|
|
559
|
+
if (/^4/.test(n)) return "VISA";
|
|
560
|
+
if (/^5[1-5]/.test(n)) return "MASTERCARD";
|
|
561
|
+
if (/^3[47]/.test(n)) return "AMEX";
|
|
562
|
+
if (/^6(?:011|5)/.test(n)) return "DISCOVER";
|
|
563
|
+
|
|
564
|
+
return "";
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
getAddressFields() {
|
|
568
|
+
if (!this.isAddressRequired) return "";
|
|
569
|
+
|
|
570
|
+
return `
|
|
571
|
+
<div class="section">
|
|
572
|
+
<div class="section-title">Address</div>
|
|
573
|
+
|
|
574
|
+
<div class="field">
|
|
575
|
+
<input id="street" required placeholder=" " />
|
|
576
|
+
<label>Street *</label>
|
|
577
|
+
</div>
|
|
578
|
+
<div class="error" id="error-street"></div>
|
|
579
|
+
|
|
580
|
+
<div class="field">
|
|
581
|
+
<input id="city" required placeholder=" " />
|
|
582
|
+
<label>City *</label>
|
|
583
|
+
</div>
|
|
584
|
+
<div class="error" id="error-city"></div>
|
|
585
|
+
|
|
586
|
+
<div class="field">
|
|
587
|
+
<select id="state"></select>
|
|
588
|
+
<label class="floating">State *</label>
|
|
589
|
+
</div>
|
|
590
|
+
<div class="error" id="error-state"></div>
|
|
591
|
+
|
|
592
|
+
<div class="field">
|
|
593
|
+
<select id="country" required>
|
|
594
|
+
${COUNTRIES_LIST.map(c =>
|
|
595
|
+
`<option value="${c.code}" ${c.code === "US" ? "selected" : ""}>${c.name}</option>`
|
|
596
|
+
).join("")}
|
|
597
|
+
</select>
|
|
598
|
+
<label class="floating">Country *</label>
|
|
599
|
+
</div>
|
|
600
|
+
<div class="error" id="error-country"></div>
|
|
601
|
+
|
|
602
|
+
<div class="field">
|
|
603
|
+
<input id="zip" required placeholder=" " />
|
|
604
|
+
<label>ZIP Code *</label>
|
|
605
|
+
</div>
|
|
606
|
+
<div class="error" id="error-zip"></div>
|
|
607
|
+
|
|
608
|
+
</div>
|
|
609
|
+
`;
|
|
610
|
+
}
|
|
611
|
+
|
|
456
612
|
validate() {
|
|
613
|
+
this.clearErrors();
|
|
614
|
+
let isValid = true;
|
|
615
|
+
|
|
457
616
|
const get = id => this.shadow.getElementById(id)?.value?.trim();
|
|
617
|
+
if (!get("name")) {
|
|
618
|
+
this.setError("name", "Name required");
|
|
619
|
+
isValid = false;
|
|
620
|
+
}
|
|
458
621
|
|
|
459
|
-
const name = get("name");
|
|
460
622
|
const email = get("email");
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const accountNumber = get("account-number");
|
|
468
|
-
const routingNumber = get("routing-number");
|
|
469
|
-
const accountType = get("account-type");
|
|
470
|
-
const bankName = get("bank-name");
|
|
471
|
-
const businessType = get("business-type");
|
|
472
|
-
const street = get("street");
|
|
473
|
-
if (!name) return "Name required";
|
|
474
|
-
if (!email) return "Email required";
|
|
475
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return "Invalid email address";
|
|
476
|
-
if (!accountName) return "Account holder name required";
|
|
477
|
-
|
|
478
|
-
if (!accountNumber || !/^\d{6,18}$/.test(accountNumber)) {
|
|
479
|
-
return "Invalid account number";
|
|
623
|
+
if (!email) {
|
|
624
|
+
this.setError("email", "Email required");
|
|
625
|
+
isValid = false;
|
|
626
|
+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
627
|
+
this.setError("email", "Invalid email");
|
|
628
|
+
isValid = false;
|
|
480
629
|
}
|
|
481
630
|
|
|
482
|
-
|
|
483
|
-
|
|
631
|
+
const phone = get("phone")?.replace(/\D/g, "");
|
|
632
|
+
const code = get("phone-code");
|
|
633
|
+
if (!phone || !code) {
|
|
634
|
+
this.setError("phone", "Phone number required");
|
|
635
|
+
isValid = false;
|
|
636
|
+
} else {
|
|
637
|
+
const fullNumber = `${code}${phone}`;
|
|
638
|
+
if (!isValidPhoneNumber(fullNumber)) {
|
|
639
|
+
this.setError("phone", "Invalid phone number");
|
|
640
|
+
isValid = false;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (!this.processorToken) {
|
|
644
|
+
this.setError("bank", "Please link bank account");
|
|
645
|
+
isValid = false;
|
|
646
|
+
}
|
|
647
|
+
if (!get("business-type")) {
|
|
648
|
+
this.setError("business-type", "Select business type");
|
|
649
|
+
isValid = false;
|
|
484
650
|
}
|
|
651
|
+
if (this.isAddressRequired) {
|
|
652
|
+
if (!get("street")) {
|
|
653
|
+
this.setError("street", "Street required");
|
|
654
|
+
isValid = false;
|
|
655
|
+
}
|
|
485
656
|
|
|
486
|
-
|
|
657
|
+
if (!get("city")) {
|
|
658
|
+
this.setError("city", "City required");
|
|
659
|
+
isValid = false;
|
|
660
|
+
}
|
|
487
661
|
|
|
488
|
-
|
|
662
|
+
if (!get("state")) {
|
|
663
|
+
this.setError("state", "State required");
|
|
664
|
+
isValid = false;
|
|
665
|
+
}
|
|
489
666
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
667
|
+
if (!get("country")) {
|
|
668
|
+
this.setError("country", "Country required");
|
|
669
|
+
isValid = false;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const zip = get("zip");
|
|
673
|
+
|
|
674
|
+
if (!zip) {
|
|
675
|
+
this.setError("zip", "ZIP required");
|
|
676
|
+
isValid = false;
|
|
677
|
+
} else if (postalCodes.validate(get("country"), zip) !== true) {
|
|
678
|
+
this.setError("zip", "Invalid ZIP code");
|
|
679
|
+
isValid = false;
|
|
680
|
+
}
|
|
497
681
|
}
|
|
498
682
|
|
|
499
|
-
return
|
|
683
|
+
return isValid;
|
|
500
684
|
}
|
|
501
685
|
|
|
502
686
|
formatAccountNumber(v) {
|
|
@@ -507,21 +691,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
507
691
|
return v.replace(/\D/g, "").slice(0, 9);
|
|
508
692
|
}
|
|
509
693
|
|
|
510
|
-
async
|
|
511
|
-
e.preventDefault();
|
|
512
|
-
|
|
513
|
-
const payButton = this.shadow.getElementById("form").querySelector(".btn");
|
|
514
|
-
payButton.disabled = true;
|
|
515
|
-
payButton.textContent = "Processing...";
|
|
516
|
-
|
|
517
|
-
const err = this.validate();
|
|
518
|
-
if (err) {
|
|
519
|
-
alert(err);
|
|
520
|
-
payButton.disabled = false;
|
|
521
|
-
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
|
|
694
|
+
async oneTimePayment() {
|
|
525
695
|
try {
|
|
526
696
|
const values = await this.getServerEncription();
|
|
527
697
|
if (values?.public_key) {
|
|
@@ -533,11 +703,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
533
703
|
const phone = getVal("phone");
|
|
534
704
|
|
|
535
705
|
const bankDetails = {
|
|
536
|
-
|
|
537
|
-
accountNumber: getVal("account-number"),
|
|
538
|
-
routingNumber: getVal("routing-number"),
|
|
539
|
-
accountType: getVal("account-type"),
|
|
540
|
-
bankName: getVal("bank-name"),
|
|
706
|
+
plaidPublicToken: this.processorToken,
|
|
541
707
|
businessType: getVal("business-type"),
|
|
542
708
|
};
|
|
543
709
|
|
|
@@ -547,13 +713,17 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
547
713
|
phone: phone ? `${phoneCode}${phone}` : null
|
|
548
714
|
};
|
|
549
715
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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
|
+
}
|
|
557
727
|
|
|
558
728
|
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
559
729
|
const payload = {
|
|
@@ -568,7 +738,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
568
738
|
const pmPayload = {
|
|
569
739
|
type: 'ACH_LEGACY',
|
|
570
740
|
bank: bankDetails,
|
|
571
|
-
postalCode: address?.
|
|
741
|
+
postalCode: address?.postalCode,
|
|
572
742
|
paymentMethodBillingAddress: address,
|
|
573
743
|
customerGid: customerGid
|
|
574
744
|
};
|
|
@@ -614,20 +784,272 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
614
784
|
body: JSON.stringify({ data: payload2?.data })
|
|
615
785
|
});
|
|
616
786
|
const resData = await res.json();
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
787
|
+
return resData;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} catch (error) {
|
|
791
|
+
console.error("Payment Error:", error);
|
|
792
|
+
this.events.emit("error", error);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
async recurringPayment() {
|
|
797
|
+
try {
|
|
798
|
+
const values = await this.getServerEncription();
|
|
799
|
+
if (values?.public_key) {
|
|
800
|
+
const keys = await this.generateClientKeyPair();
|
|
801
|
+
if (keys?.privateKey) {
|
|
802
|
+
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
803
|
+
const phoneCode = getVal("phone-code") || "";
|
|
804
|
+
const phone = getVal("phone");
|
|
805
|
+
|
|
806
|
+
const bankDetails = {
|
|
807
|
+
plaidPublicToken: this.processorToken,
|
|
808
|
+
businessType: getVal("business-type"),
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
const customer = {
|
|
812
|
+
name: getVal("name"),
|
|
813
|
+
email: getVal("email"),
|
|
814
|
+
phone: phone ? `${phoneCode}${phone}` : null
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
let address = null;
|
|
818
|
+
|
|
819
|
+
if (this.isAddressRequired) {
|
|
820
|
+
address = {
|
|
821
|
+
street: getVal("street"),
|
|
822
|
+
city: getVal("city"),
|
|
823
|
+
state: getVal("state"),
|
|
824
|
+
countryCode: getVal("country"),
|
|
825
|
+
postalCode: getVal("zip")
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
830
|
+
const payload = {
|
|
831
|
+
publicKey: keys?.publicKey,
|
|
832
|
+
keyId: encriptedData?.keyId,
|
|
833
|
+
iv: encriptedData?.iv,
|
|
834
|
+
data: encriptedData?.encryptedData,
|
|
835
|
+
}
|
|
836
|
+
const newCustomer = await this.createCustomer(payload);
|
|
837
|
+
const customerGid = newCustomer.entity?.gid;
|
|
838
|
+
let product;
|
|
839
|
+
const itemInfo = await this.getItemInfo();
|
|
840
|
+
if (itemInfo?.entity?.content[0]?.gid) {
|
|
841
|
+
product = itemInfo?.entity?.content[0];
|
|
842
|
+
} else {
|
|
843
|
+
const newItemInfo = await this.addedItemInfo();
|
|
844
|
+
product = newItemInfo?.entity;
|
|
845
|
+
}
|
|
846
|
+
let plan;
|
|
847
|
+
const planInfo = await this.getPlan();
|
|
848
|
+
if (planInfo?.entity?.content[0]?.gid) {
|
|
849
|
+
plan = planInfo?.entity?.content[0];
|
|
850
|
+
} else {
|
|
851
|
+
const newPlanInfo = await this.addedPlanInfo();
|
|
852
|
+
plan = newPlanInfo?.entity;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const subscriptionData = {
|
|
856
|
+
currencyCode: this.currencyCode,
|
|
857
|
+
customerGid: customerGid,
|
|
858
|
+
plan2Gid: plan?.gid,
|
|
859
|
+
plan2BillingPeriod: 1,
|
|
860
|
+
plan2BillingFrequency: this.frequency,
|
|
861
|
+
plan2StartDate: new Date().toISOString(),
|
|
862
|
+
plan2TotalPayments: this.totalPayments,
|
|
863
|
+
description: this.description,
|
|
864
|
+
notificationType: "ALL",
|
|
865
|
+
emailRecipientList: [
|
|
866
|
+
customer.email
|
|
867
|
+
],
|
|
868
|
+
canCustomerCancelOrPause: true,
|
|
869
|
+
subscription2LineItems: [
|
|
870
|
+
{
|
|
871
|
+
itemGid: product?.gid,
|
|
872
|
+
quantity: 1,
|
|
873
|
+
upfront: false,
|
|
874
|
+
currencyCode: this.currencyCode,
|
|
875
|
+
amount: this.amount
|
|
876
|
+
}
|
|
877
|
+
]
|
|
878
|
+
}
|
|
879
|
+
const subscriptionDetails = await this.getSubScription(subscriptionData);
|
|
880
|
+
const subscription = subscriptionDetails?.entity;
|
|
881
|
+
const sessionData = {
|
|
882
|
+
link: subscription?.subscription2Links[0]?.link,
|
|
883
|
+
customer: subscription?.customer,
|
|
884
|
+
type: 'ACH_LEGACY',
|
|
885
|
+
bank: bankDetails,
|
|
886
|
+
postalCode: address?.postalCode,
|
|
887
|
+
paymentMethodBillingAddress: address
|
|
888
|
+
}
|
|
889
|
+
const encriptedData4 = await this.encryptData(JSON.stringify(sessionData), values.public_key, keys.privateKey, values.key_id);
|
|
890
|
+
const payload2 = {
|
|
891
|
+
publicKey: keys?.publicKey,
|
|
892
|
+
keyId: encriptedData4?.keyId,
|
|
893
|
+
iv: encriptedData4?.iv,
|
|
894
|
+
data: encriptedData4?.encryptedData,
|
|
895
|
+
}
|
|
896
|
+
const gateway = this.getEndpoints().gateway;
|
|
897
|
+
const url = `${gateway}/v2/subscription-link/${subscription?.subscription2Links[0]?.gid}/payment-method`;
|
|
898
|
+
const res = await fetch(url, {
|
|
899
|
+
method: 'POST',
|
|
900
|
+
headers: {
|
|
901
|
+
'x-enc-public-key': payload2?.publicKey,
|
|
902
|
+
'x-enc-key-id': payload2?.keyId,
|
|
903
|
+
'x-enc-iv': payload2?.iv,
|
|
904
|
+
'Content-Type': 'application/json',
|
|
905
|
+
'x-api-key': this.apiKey,
|
|
906
|
+
},
|
|
907
|
+
body: JSON.stringify({ data: payload2?.data })
|
|
908
|
+
});
|
|
909
|
+
const resData = await res.json();
|
|
910
|
+
return resData;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
} catch (error) {
|
|
914
|
+
console.error("Payment Error:", error);
|
|
915
|
+
this.events.emit("error", error);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
async inventoryCharge() {
|
|
921
|
+
try {
|
|
922
|
+
const values = await this.getServerEncription();
|
|
923
|
+
if (values?.public_key) {
|
|
924
|
+
const keys = await this.generateClientKeyPair();
|
|
925
|
+
if (keys?.privateKey) {
|
|
926
|
+
const getVal = id => this.shadow.getElementById(id)?.value?.trim();
|
|
927
|
+
const phoneCode = getVal("phone-code") || "";
|
|
928
|
+
const phone = getVal("phone");
|
|
929
|
+
|
|
930
|
+
const bankDetails = {
|
|
931
|
+
plaidPublicToken: this.processorToken,
|
|
932
|
+
businessType: getVal("business-type"),
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
const customer = {
|
|
936
|
+
name: getVal("name"),
|
|
937
|
+
email: getVal("email"),
|
|
938
|
+
phone: phone ? `${phoneCode}${phone}` : null
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
let address = null;
|
|
942
|
+
|
|
943
|
+
if (this.isAddressRequired) {
|
|
944
|
+
address = {
|
|
945
|
+
street: getVal("street"),
|
|
946
|
+
city: getVal("city"),
|
|
947
|
+
state: getVal("state"),
|
|
948
|
+
countryCode: getVal("country"),
|
|
949
|
+
postalCode: getVal("zip")
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const encriptedData = await this.encryptData(JSON.stringify(customer), values.public_key, keys.privateKey, values.key_id);
|
|
954
|
+
const payload = {
|
|
955
|
+
publicKey: keys?.publicKey,
|
|
956
|
+
keyId: encriptedData?.keyId,
|
|
957
|
+
iv: encriptedData?.iv,
|
|
958
|
+
data: encriptedData?.encryptedData,
|
|
959
|
+
}
|
|
960
|
+
const newCustomer = await this.createCustomer(payload);
|
|
961
|
+
const customerGid = newCustomer.entity?.gid;
|
|
962
|
+
const orderDetails = await this.createOrder();
|
|
963
|
+
const order = orderDetails?.entity;
|
|
964
|
+
const sessionData = {
|
|
965
|
+
customer: customer,
|
|
966
|
+
customerGid: customerGid,
|
|
967
|
+
paymentMethod: {
|
|
968
|
+
bank: bankDetails,
|
|
969
|
+
postalCode: address?.postalCode,
|
|
970
|
+
type: "ACH_LEGACY",
|
|
971
|
+
paymentMethodBillingAddress: address,
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
const encriptedData1 = await this.encryptData(JSON.stringify(sessionData), values.public_key, keys.privateKey, values.key_id);
|
|
975
|
+
const payload2 = {
|
|
976
|
+
publicKey: keys?.publicKey,
|
|
977
|
+
keyId: encriptedData1?.keyId,
|
|
978
|
+
iv: encriptedData1?.iv,
|
|
979
|
+
data: encriptedData1?.encryptedData,
|
|
980
|
+
}
|
|
981
|
+
const gateway = this.getEndpoints().gateway;
|
|
982
|
+
const url = `${gateway}/v1/inventory/order-link/${order?.inventoryOrderLink?.gid}/charge`;
|
|
983
|
+
const res = await fetch(url, {
|
|
984
|
+
method: 'POST',
|
|
985
|
+
headers: {
|
|
986
|
+
'x-enc-public-key': payload2?.publicKey,
|
|
987
|
+
'x-enc-key-id': payload2?.keyId,
|
|
988
|
+
'x-enc-iv': payload2?.iv,
|
|
989
|
+
'Content-Type': 'application/json',
|
|
990
|
+
'x-api-key': this.apiKey,
|
|
991
|
+
},
|
|
992
|
+
body: JSON.stringify({ data: payload2?.data })
|
|
993
|
+
});
|
|
994
|
+
const resData = await res.json();
|
|
995
|
+
return resData;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
console.error("Payment Error:", error);
|
|
1001
|
+
this.events.emit("error", error);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
async handleSubmit(e) {
|
|
1006
|
+
e.preventDefault();
|
|
1007
|
+
|
|
1008
|
+
const payButton = this.shadow.getElementById("form").querySelector(".btn");
|
|
1009
|
+
payButton.disabled = true;
|
|
1010
|
+
payButton.textContent = "Processing...";
|
|
1011
|
+
|
|
1012
|
+
const isValid = this.validate();
|
|
1013
|
+
if (!isValid) {
|
|
1014
|
+
payButton.disabled = false;
|
|
1015
|
+
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
try {
|
|
1020
|
+
if (this.frequency === "ONE-TIME" && !this.inventory) {
|
|
1021
|
+
const resData = await this.oneTimePayment();
|
|
1022
|
+
if (resData?.entity?.errorCode === null) {
|
|
1023
|
+
this.events.emit("success", resData);
|
|
1024
|
+
this.close();
|
|
1025
|
+
} else {
|
|
1026
|
+
const errorMsg = resData?.entity?.errorDescription || "Payment failed";
|
|
1027
|
+
console.error("Payment Error:", errorMsg);
|
|
1028
|
+
this.events.emit("error", errorMsg);
|
|
1029
|
+
}
|
|
1030
|
+
} else if (this.frequency !== "ONE-TIME" && !this.inventory) {
|
|
1031
|
+
const resData = await this.recurringPayment();
|
|
1032
|
+
if (resData?.entity?.setupSession?.errorCode === null) {
|
|
1033
|
+
this.events.emit("success", resData);
|
|
1034
|
+
this.close();
|
|
1035
|
+
} else {
|
|
1036
|
+
const errorMsg = resData?.entity?.setupSession?.errorDescription || "Payment failed";
|
|
1037
|
+
console.error("Payment Error:", errorMsg);
|
|
1038
|
+
this.events.emit("error", errorMsg);
|
|
1039
|
+
}
|
|
1040
|
+
} else if (this.inventory) {
|
|
1041
|
+
const resData = await this.inventoryCharge();
|
|
1042
|
+
if (resData?.responseCode === 200) {
|
|
1043
|
+
this.events.emit("success", resData);
|
|
1044
|
+
this.close();
|
|
1045
|
+
} else {
|
|
1046
|
+
console.error("Payment Error:", resData?.message || 'Payment failed');
|
|
1047
|
+
this.events.emit("error", resData?.message);
|
|
625
1048
|
}
|
|
626
1049
|
}
|
|
627
1050
|
} catch (error) {
|
|
628
1051
|
console.error("Payment Error:", error);
|
|
629
1052
|
this.events.emit("error", error);
|
|
630
|
-
alert(error.message || "Payment failed");
|
|
631
1053
|
} finally {
|
|
632
1054
|
payButton.disabled = false;
|
|
633
1055
|
payButton.textContent = `Pay $${(this.amount / 100).toFixed(2)}`;
|
|
@@ -636,6 +1058,7 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
636
1058
|
render() {
|
|
637
1059
|
const t = this.theme;
|
|
638
1060
|
const amountText = (this.amount / 100).toFixed(2);
|
|
1061
|
+
const inventoryAmountText = ((this.splitUp?.netPayable) / 100).toFixed(2);
|
|
639
1062
|
this.shadow.innerHTML = `
|
|
640
1063
|
<style>
|
|
641
1064
|
:host {
|
|
@@ -656,26 +1079,44 @@ export class SwirepayCheckout extends HTMLElement {
|
|
|
656
1079
|
background:rgba(0,0,0,0.6);
|
|
657
1080
|
justify-content:center;
|
|
658
1081
|
align-items:center;
|
|
1082
|
+
z-index:9999;
|
|
659
1083
|
}
|
|
660
1084
|
|
|
661
1085
|
.modal {
|
|
662
|
-
width:
|
|
1086
|
+
width: 520px;
|
|
1087
|
+
height: auto;
|
|
1088
|
+
max-height: 650px;
|
|
663
1089
|
background:var(--bg);
|
|
664
1090
|
color:var(--text);
|
|
665
1091
|
border-radius:16px;
|
|
666
|
-
padding:
|
|
1092
|
+
padding:28px;
|
|
667
1093
|
font-family:sans-serif;
|
|
668
1094
|
}
|
|
669
1095
|
|
|
1096
|
+
.modal-body {
|
|
1097
|
+
overflow-y: auto;
|
|
1098
|
+
flex: 1;
|
|
1099
|
+
height: 530px;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
form {
|
|
1103
|
+
display: flex;
|
|
1104
|
+
flex-direction: column;
|
|
1105
|
+
gap: 16px;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
670
1108
|
input, select {
|
|
671
|
-
width:100%;
|
|
672
|
-
padding:
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
1109
|
+
width: 100%;
|
|
1110
|
+
padding: 12px;
|
|
1111
|
+
border: 1px solid var(--border);
|
|
1112
|
+
background: #ffffff;
|
|
1113
|
+
color: #000000;
|
|
1114
|
+
border-radius: 8px;
|
|
1115
|
+
outline: none;
|
|
1116
|
+
height: 44px;
|
|
1117
|
+
font-size: 14px;
|
|
1118
|
+
box-sizing: border-box;
|
|
1119
|
+
margin: 0 !important;
|
|
679
1120
|
}
|
|
680
1121
|
|
|
681
1122
|
input::placeholder {
|
|
@@ -698,7 +1139,7 @@ input::placeholder {
|
|
|
698
1139
|
select {
|
|
699
1140
|
width:100%;
|
|
700
1141
|
padding:10px;
|
|
701
|
-
margin:
|
|
1142
|
+
margin: 0;
|
|
702
1143
|
border:1px solid var(--border);
|
|
703
1144
|
border-radius:8px;
|
|
704
1145
|
|
|
@@ -714,94 +1155,238 @@ select option {
|
|
|
714
1155
|
color: #000000;
|
|
715
1156
|
}
|
|
716
1157
|
|
|
717
|
-
.row {
|
|
1158
|
+
.row {
|
|
1159
|
+
display: flex;
|
|
1160
|
+
gap: 16px;
|
|
1161
|
+
align-items: center;
|
|
1162
|
+
}
|
|
1163
|
+
.phone-code {
|
|
1164
|
+
flex: 0 0 70px !important;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
.row input,
|
|
1168
|
+
.row select {
|
|
1169
|
+
flex: 1;
|
|
1170
|
+
}
|
|
718
1171
|
|
|
719
|
-
|
|
1172
|
+
input, select {
|
|
1173
|
+
box-sizing: border-box;
|
|
1174
|
+
height: 40px;
|
|
1175
|
+
padding: 10px;
|
|
1176
|
+
font-size: 14px;
|
|
1177
|
+
}
|
|
720
1178
|
|
|
721
1179
|
.btn {
|
|
722
|
-
background:var(--primary);
|
|
723
|
-
color
|
|
724
|
-
padding:
|
|
725
|
-
border:none;
|
|
726
|
-
border-radius:10px;
|
|
727
|
-
cursor:pointer;
|
|
728
|
-
width:100%;
|
|
1180
|
+
background: var(--primary);
|
|
1181
|
+
color: #fff;
|
|
1182
|
+
padding: 14px;
|
|
1183
|
+
border: none;
|
|
1184
|
+
border-radius: 10px;
|
|
1185
|
+
cursor: pointer;
|
|
1186
|
+
width: 100%;
|
|
1187
|
+
height: 48px;
|
|
1188
|
+
font-size: 15px;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.btn1 {
|
|
1192
|
+
background: var(--primary);
|
|
1193
|
+
color: #fff;
|
|
1194
|
+
padding: 14px;
|
|
1195
|
+
border: none;
|
|
1196
|
+
border-radius: 10px;
|
|
1197
|
+
cursor: pointer;
|
|
1198
|
+
width: 100%;
|
|
1199
|
+
height: 48px;
|
|
1200
|
+
font-size: 15px;
|
|
729
1201
|
}
|
|
730
1202
|
|
|
731
1203
|
.close {
|
|
732
1204
|
float:right;
|
|
733
1205
|
cursor:pointer;
|
|
734
1206
|
}
|
|
1207
|
+
|
|
1208
|
+
h3 {
|
|
1209
|
+
margin-bottom: 16px;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.section {
|
|
1213
|
+
display: flex;
|
|
1214
|
+
flex-direction: column;
|
|
1215
|
+
gap: 16px;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
.section-title {
|
|
1219
|
+
font-size: 14px;
|
|
1220
|
+
font-weight: 600;
|
|
1221
|
+
margin: 0;
|
|
1222
|
+
}
|
|
1223
|
+
.error {
|
|
1224
|
+
font-size: 12px;
|
|
1225
|
+
color: #dc2626;
|
|
1226
|
+
margin-top: 0px;
|
|
1227
|
+
margin-bottom: 0px;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.field {
|
|
1231
|
+
position: relative;
|
|
1232
|
+
width: 100%;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
.field input,
|
|
1236
|
+
.field select {
|
|
1237
|
+
width: 100%;
|
|
1238
|
+
padding: 14px 12px;
|
|
1239
|
+
border: 1px solid var(--border);
|
|
1240
|
+
border-radius: 8px;
|
|
1241
|
+
font-size: 14px;
|
|
1242
|
+
background: #fff;
|
|
1243
|
+
outline: none;
|
|
1244
|
+
}
|
|
1245
|
+
.field label {
|
|
1246
|
+
position: absolute;
|
|
1247
|
+
left: 0px;
|
|
1248
|
+
top: 50%;
|
|
1249
|
+
transform: translateY(-50%);
|
|
1250
|
+
padding: 0 4px;
|
|
1251
|
+
color: #6b7280;
|
|
1252
|
+
font-size: 14px;
|
|
1253
|
+
pointer-events: none;
|
|
1254
|
+
transition: 0.2s ease;
|
|
1255
|
+
}
|
|
1256
|
+
.field input:focus + label,
|
|
1257
|
+
.field input:not(:placeholder-shown) + label {
|
|
1258
|
+
top: -8px;
|
|
1259
|
+
font-size: 12px;
|
|
1260
|
+
color: var(--primary);
|
|
1261
|
+
}
|
|
1262
|
+
.field select {
|
|
1263
|
+
width: 100%;
|
|
1264
|
+
height: 44px;
|
|
1265
|
+
padding: 0 12px;
|
|
1266
|
+
border: 1px solid var(--border);
|
|
1267
|
+
border-radius: 8px;
|
|
1268
|
+
background: #fff;
|
|
1269
|
+
color: #000;
|
|
1270
|
+
font-size: 14px;
|
|
1271
|
+
outline: none;
|
|
1272
|
+
appearance: none;
|
|
1273
|
+
}
|
|
1274
|
+
.input-error {
|
|
1275
|
+
border-color: #dc2626 !important;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
.field input.input-error + label {
|
|
1279
|
+
color: #dc2626;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
.field label.floating {
|
|
1283
|
+
top: -8px;
|
|
1284
|
+
font-size: 12px;
|
|
1285
|
+
color: var(--primary);
|
|
1286
|
+
}
|
|
735
1287
|
</style>
|
|
736
1288
|
|
|
737
1289
|
<div class="overlay" id="overlay">
|
|
738
1290
|
<div class="modal">
|
|
1291
|
+
<div class="modal-header">
|
|
739
1292
|
<span class="close" id="close">✕</span>
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1293
|
+
<h3>Pay By Bank</h3>
|
|
1294
|
+
</div>
|
|
1295
|
+
<div class="modal-body">
|
|
743
1296
|
<form id="form">
|
|
744
|
-
|
|
745
|
-
|
|
1297
|
+
<div class="section">
|
|
1298
|
+
<div class="section-title">Customer Details</div>
|
|
1299
|
+
<div class="field">
|
|
1300
|
+
<input id="name" required placeholder=" " />
|
|
1301
|
+
<label>Name *</label>
|
|
1302
|
+
</div>
|
|
1303
|
+
<div class="error" id="error-name"></div>
|
|
1304
|
+
<div class="field">
|
|
1305
|
+
<input id="email" required placeholder=" " />
|
|
1306
|
+
<label>Email *</label>
|
|
1307
|
+
</div>
|
|
1308
|
+
<div class="error" id="error-email"></div>
|
|
746
1309
|
|
|
747
1310
|
<div class="row">
|
|
748
|
-
<
|
|
749
|
-
|
|
1311
|
+
<div class="field phone-code">
|
|
1312
|
+
<input id="phone-code" placeholder=" " />
|
|
1313
|
+
<label>Code</label>
|
|
1314
|
+
</div>
|
|
1315
|
+
<div class="field">
|
|
1316
|
+
<input id="phone" required placeholder=" " />
|
|
1317
|
+
<label>Phone *</label>
|
|
1318
|
+
</div>
|
|
750
1319
|
</div>
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
<
|
|
754
|
-
<
|
|
755
|
-
|
|
756
|
-
<
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
<option value="SAVINGS">Savings</option>
|
|
760
|
-
</select>
|
|
761
|
-
|
|
762
|
-
<input id="bank-name" placeholder="Bank Name"/>
|
|
763
|
-
|
|
1320
|
+
<div class="error" id="error-phone"></div>
|
|
1321
|
+
</div>
|
|
1322
|
+
<div class="section">
|
|
1323
|
+
<div class="section-title">Bank Details</div>
|
|
1324
|
+
<button id="link-bank" class="btn1">Add Bank Account</button>
|
|
1325
|
+
<div id="bank-status"></div>
|
|
1326
|
+
<div class="error" id="error-bank"></div>
|
|
1327
|
+
<div class="field">
|
|
764
1328
|
<select id="business-type">
|
|
765
1329
|
<option value="">Business Type</option>
|
|
766
1330
|
<option value="INDIVIDUAL">Individual</option>
|
|
767
1331
|
<option value="BUSINESS">Business</option>
|
|
768
1332
|
</select>
|
|
1333
|
+
<label class="floating">Business Type *</label>
|
|
1334
|
+
</div>
|
|
1335
|
+
<div class="error" id="error-business-type"></div>
|
|
1336
|
+
</div>
|
|
769
1337
|
|
|
770
|
-
|
|
771
|
-
<
|
|
772
|
-
|
|
773
|
-
<!-- STATE FIRST -->
|
|
774
|
-
<select id="state"></select>
|
|
775
|
-
|
|
776
|
-
<!-- COUNTRY AFTER -->
|
|
777
|
-
<select id="country">
|
|
778
|
-
${COUNTRIES_LIST.map(c =>
|
|
779
|
-
`<option value="${c.code}" ${c.code === "US" ? "selected" : ""}>
|
|
780
|
-
${c.name}
|
|
781
|
-
</option>`
|
|
782
|
-
).join("")}
|
|
783
|
-
</select>
|
|
784
|
-
|
|
785
|
-
<input id="zip" placeholder="ZIP Code"/>
|
|
786
|
-
|
|
787
|
-
<button class="btn">Pay $${amountText}</button>
|
|
1338
|
+
${this.getAddressFields()}
|
|
1339
|
+
<button class="btn">Pay $${this.inventory ? inventoryAmountText : amountText}</button>
|
|
1340
|
+
</div>
|
|
788
1341
|
</form>
|
|
789
1342
|
</div>
|
|
790
1343
|
</div>
|
|
791
1344
|
`;
|
|
792
1345
|
|
|
793
1346
|
const $ = id => this.shadow.getElementById(id);
|
|
1347
|
+
if (this.customer) {
|
|
1348
|
+
if (this.customer.name) $("name").value = this.customer.name;
|
|
1349
|
+
if (this.customer.email) $("email").value = this.customer.email;
|
|
1350
|
+
|
|
1351
|
+
if (this.customer.phone) {
|
|
1352
|
+
let phone = this.customer.phone;
|
|
1353
|
+
let code = "";
|
|
1354
|
+
let number = phone;
|
|
1355
|
+
|
|
1356
|
+
if (phone.startsWith("+")) {
|
|
1357
|
+
if (phone.startsWith("+1")) {
|
|
1358
|
+
code = "+1";
|
|
1359
|
+
number = phone.slice(2);
|
|
1360
|
+
} else if (phone.startsWith("+91")) {
|
|
1361
|
+
code = "+91";
|
|
1362
|
+
number = phone.slice(3);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
if ($("phone-code")) $("phone-code").value = code;
|
|
1367
|
+
if ($("phone")) $("phone").value = number;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
this.shadow.getElementById("link-bank").onclick = async () => {
|
|
1371
|
+
if (!this.plaidHandler) await this.initPlaid();
|
|
1372
|
+
this.plaidHandler.open();
|
|
1373
|
+
};
|
|
794
1374
|
|
|
795
1375
|
$("close").onclick = () => this.close();
|
|
796
1376
|
$("form").onsubmit = e => this.handleSubmit(e);
|
|
797
1377
|
|
|
798
|
-
$("country").onchange = () => this.handleCountryChange();
|
|
799
|
-
|
|
800
1378
|
const countryEl = $("country");
|
|
801
|
-
countryEl
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1379
|
+
if (countryEl) {
|
|
1380
|
+
countryEl.onchange = () => this.handleCountryChange();
|
|
1381
|
+
if (!this.customer?.countryCode) {
|
|
1382
|
+
countryEl.value = "US";
|
|
1383
|
+
}
|
|
1384
|
+
this.handleCountryChange();
|
|
1385
|
+
}
|
|
1386
|
+
const phoneCodeEl = $("phone-code");
|
|
1387
|
+
if (phoneCodeEl) {
|
|
1388
|
+
phoneCodeEl.dataset.auto = "true";
|
|
1389
|
+
}
|
|
805
1390
|
$("phone").oninput = e => e.target.value = this.formatPhone(e.target.value);
|
|
806
1391
|
}
|
|
807
1392
|
}
|