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