@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swirepay-developer/swirepay-ach-sdk",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Swirepay Ach Payment SDK (Web Component with Modal UI)",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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
- this.amount = parseInt(this.getAttribute("amount")) || 0;
284
- this.test = this.getAttribute("mode") === "test";
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 country = this.shadow.getElementById("country").value;
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
- const phone = get("phone");
462
- const code = get("phone-code");
463
- const zip = get("zip");
464
- const country = get("country");
465
-
466
- const accountName = get("account-name");
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
- if (!routingNumber || !/^\d{9}$/.test(routingNumber)) {
483
- return "Invalid routing number (must be 9 digits)";
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
- if (!accountType) return "Select account type";
657
+ if (!get("city")) {
658
+ this.setError("city", "City required");
659
+ isValid = false;
660
+ }
487
661
 
488
- if (!bankName) return "Bank name required";
662
+ if (!get("state")) {
663
+ this.setError("state", "State required");
664
+ isValid = false;
665
+ }
489
666
 
490
- if (!businessType) return "Select business type";
491
- if (!street) return "Street address required";
492
- if (phone && !isValidPhoneNumber(code + phone)) {
493
- return "Invalid phone";
494
- }
495
- if (zip && postalCodes.validate(country, zip) !== true) {
496
- return "Invalid ZIP";
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 null;
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 handleSubmit(e) {
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
- name: getVal("account-name"),
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
- const address = {
551
- street: getVal("street"),
552
- city: getVal("city"),
553
- state: getVal("state"),
554
- countryCode: getVal("country"),
555
- postalCode: getVal("zip")
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?.zip,
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
- if (resData?.entity?.errorCode === null){
618
- this.events.emit("success", resData);
619
- this.close();
620
- } else if (resData?.entity?.errorCode !== null){
621
- console.error("Payment Error:", resData?.entity?.errorDescription);
622
- this.events.emit("error", resData?.entity?.errorDescription);
623
- alert(resData?.entity?.errorDescription || "Payment failed");
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:420px;
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:20px;
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:10px;
673
- margin:6px 0;
674
- border:1px solid var(--border);
675
- background: white;
676
- color: black;
677
- border-radius:8px;
678
- outline:none;
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:6px 0;
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 { display:flex; gap:10px; }
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
- .phone-code { width:70px; }
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:#fff;
724
- padding:12px;
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
- <h3>Pay by Bank</h3>
742
-
1293
+ <h3>Pay By Bank</h3>
1294
+ </div>
1295
+ <div class="modal-body">
743
1296
  <form id="form">
744
- <input id="name" placeholder="Name"/>
745
- <input id="email" placeholder="Email"/>
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
- <input class="phone-code" id="phone-code" value="+1"/>
749
- <input id="phone" placeholder="Phone"/>
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
- <input id="account-name" placeholder="Account Holder Name"/>
753
- <input id="account-number" placeholder="Account Number"/>
754
- <input id="routing-number" placeholder="Routing Number"/>
755
-
756
- <select id="account-type">
757
- <option value="">Account Type</option>
758
- <option value="CURRENT">Current</option>
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
- <input id="street" placeholder="Street Address"/>
771
- <input id="city" placeholder="City"/>
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.value = "US";
802
- this.handleCountryChange();
803
- $("account-number").oninput = e => e.target.value = this.formatAccountNumber(e.target.value);
804
- $("routing-number").oninput = e => e.target.value = this.formatRouting(e.target.value);
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
  }