swell-js 5.1.2 → 5.2.0

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/dist/payment.mjs CHANGED
@@ -21,6 +21,7 @@ const SCRIPT_HANDLERS = {
21
21
  'braintree-google-payment': loadBraintreeGoogle,
22
22
  'braintree-apple-payment': loadBraintreeApple,
23
23
  'amazon-checkout': loadAmazonCheckout,
24
+ 'sezzle-sdk': loadSezzleCheckout,
24
25
  };
25
26
 
26
27
  const BRAINTREE_VERSION = '3.91.0';
@@ -188,6 +189,19 @@ async function loadAmazonCheckout() {
188
189
  }
189
190
  }
190
191
 
192
+ async function loadSezzleCheckout() {
193
+ if (!window.Checkout) {
194
+ await loadScript(
195
+ 'sezzle-sdk',
196
+ 'https://checkout-sdk.sezzle.com/express_checkout.min.js',
197
+ );
198
+ }
199
+
200
+ if (!window.Checkout) {
201
+ console.error('Warning: Sezzle Checkout was not loaded');
202
+ }
203
+ }
204
+
191
205
  async function loadScripts(scripts) {
192
206
  if (!scripts) {
193
207
  return;
@@ -441,8 +455,8 @@ class Payment {
441
455
  /**
442
456
  * Calls the onSuccess handler.
443
457
  *
444
- * @param {object | undefined} data
445
- * @returns {any}
458
+ * @param {unknown} [data]
459
+ * @returns {unknown}
446
460
  */
447
461
  onSuccess(data) {
448
462
  const successHandler = get(this.params, 'onSuccess');
@@ -455,7 +469,7 @@ class Payment {
455
469
  /**
456
470
  * Calls the onCancel handler.
457
471
  *
458
- * @returns {any}
472
+ * @returns {unknown}
459
473
  */
460
474
  onCancel() {
461
475
  const cancelHandler = get(this.params, 'onCancel');
@@ -469,7 +483,7 @@ class Payment {
469
483
  * Calls the onError handler.
470
484
  *
471
485
  * @param {Error} error
472
- * @returns {any}
486
+ * @returns {unknown}
473
487
  */
474
488
  onError(error) {
475
489
  const errorHandler = get(this.params, 'onError');
@@ -1983,6 +1997,107 @@ class BraintreePaypalPayment extends Payment {
1983
1997
  }
1984
1998
  }
1985
1999
 
2000
+ /** @typedef {import('../../types').Cart} Cart */
2001
+ /** @typedef {import('../../types').Discount} Discount */
2002
+
2003
+ function adjustConfig(params) {
2004
+ if (!params.config) {
2005
+ return;
2006
+ }
2007
+
2008
+ if (params.card) {
2009
+ console.warn('Please move the "config" field to the "card.config"');
2010
+
2011
+ params.card.config = params.config;
2012
+ }
2013
+
2014
+ if (params.ideal) {
2015
+ console.warn('Please move the "config" field to the "ideal.config"');
2016
+
2017
+ params.ideal.config = params.config;
2018
+ }
2019
+
2020
+ delete params.config;
2021
+ }
2022
+
2023
+ function adjustElementId(methodParams) {
2024
+ if (methodParams.cardNumber) {
2025
+ adjustElementId(methodParams.cardNumber);
2026
+ }
2027
+
2028
+ if (methodParams.cardExpiry) {
2029
+ adjustElementId(methodParams.cardExpiry);
2030
+ }
2031
+
2032
+ if (methodParams.cardCvc) {
2033
+ adjustElementId(methodParams.cardCvc);
2034
+ }
2035
+
2036
+ if (!methodParams.elementId) {
2037
+ return;
2038
+ }
2039
+
2040
+ if (methodParams.elementId.startsWith('#')) {
2041
+ console.warn(
2042
+ `Please remove the "#" sign from the "${methodParams.elementId}" element ID`,
2043
+ );
2044
+
2045
+ methodParams.elementId = methodParams.elementId.substring(1);
2046
+ }
2047
+ }
2048
+
2049
+ function adjustParams(_params) {
2050
+ const params = { ..._params };
2051
+
2052
+ adjustConfig(params);
2053
+
2054
+ return params;
2055
+ }
2056
+
2057
+ function adjustMethodParams(_methodParams) {
2058
+ const methodParams = { ..._methodParams };
2059
+
2060
+ adjustElementId(methodParams);
2061
+
2062
+ return methodParams;
2063
+ }
2064
+
2065
+ /**
2066
+ * @param {Discount} discount
2067
+ * @param {Cart} cart
2068
+ */
2069
+ function getDiscountLabel(discount, cart) {
2070
+ switch (discount.type) {
2071
+ case 'coupon': {
2072
+ const { coupon } = cart;
2073
+
2074
+ if (coupon?.name) {
2075
+ return coupon.name;
2076
+ }
2077
+
2078
+ break;
2079
+ }
2080
+
2081
+ case 'promo': {
2082
+ const { promotions } = cart;
2083
+
2084
+ if (Array.isArray(promotions?.results)) {
2085
+ const promo = promotions.results.find(
2086
+ (promo) => promo.id === discount.source_id,
2087
+ );
2088
+
2089
+ if (promo?.name) {
2090
+ return promo?.name;
2091
+ }
2092
+ }
2093
+
2094
+ break;
2095
+ }
2096
+ }
2097
+
2098
+ return discount.id;
2099
+ }
2100
+
1986
2101
  /** @typedef {import('./payment').default} Payment */
1987
2102
  /** @typedef {import('../../types').Cart} Cart */
1988
2103
  /** @typedef {import('../../types').Address} Address */
@@ -2025,7 +2140,7 @@ async function onPaymentDataChanged(intermediatePaymentData) {
2025
2140
 
2026
2141
  // Update cart with new shipping address and force tax recalculation
2027
2142
  let cart = await this.updateCart({
2028
- shipping: convertToSwellAddress$1(
2143
+ shipping: convertToSwellAddress$2(
2029
2144
  intermediatePaymentData.shippingAddress,
2030
2145
  ),
2031
2146
  });
@@ -2118,42 +2233,6 @@ async function onPaymentDataChanged(intermediatePaymentData) {
2118
2233
  return {};
2119
2234
  }
2120
2235
 
2121
- /**
2122
- * @param {Discount} discount
2123
- * @param {Cart} cart
2124
- */
2125
- function getDiscountLabel(discount, cart) {
2126
- switch (discount.type) {
2127
- case 'coupon': {
2128
- const { coupon } = cart;
2129
-
2130
- if (coupon?.name) {
2131
- return coupon.name;
2132
- }
2133
-
2134
- break;
2135
- }
2136
-
2137
- case 'promo': {
2138
- const { promotions } = cart;
2139
-
2140
- if (Array.isArray(promotions?.results)) {
2141
- const promo = promotions.results.find(
2142
- (promo) => promo.id === discount.source_id,
2143
- );
2144
-
2145
- if (promo?.name) {
2146
- return promo?.name;
2147
- }
2148
- }
2149
-
2150
- break;
2151
- }
2152
- }
2153
-
2154
- return discount.id;
2155
- }
2156
-
2157
2236
  /**
2158
2237
  * Converts cart data to Google Pay display items for the payment sheet.
2159
2238
  * Only works when payment callbacks are properly configured.
@@ -2311,7 +2390,7 @@ function getOfferInfo(cart) {
2311
2390
  * @param {google.payments.api.Address} address
2312
2391
  * @returns {Address}
2313
2392
  */
2314
- function convertToSwellAddress$1(address) {
2393
+ function convertToSwellAddress$2(address) {
2315
2394
  return {
2316
2395
  name: address.name,
2317
2396
  address1: address.address1,
@@ -2572,10 +2651,10 @@ class BraintreeGooglePayment extends Payment {
2572
2651
  nonce,
2573
2652
  gateway: 'braintree',
2574
2653
  },
2575
- ...convertToSwellAddress$1(billingAddress),
2654
+ ...convertToSwellAddress$2(billingAddress),
2576
2655
  },
2577
2656
  ...(requireShipping && {
2578
- shipping: convertToSwellAddress$1(shippingAddress),
2657
+ shipping: convertToSwellAddress$2(shippingAddress),
2579
2658
  }),
2580
2659
  });
2581
2660
 
@@ -2612,7 +2691,7 @@ async function onShippingContactSelected(session, event) {
2612
2691
  // Update cart with Apple Pay shipping address to get shipping options and tax
2613
2692
  // This happens immediately when the sheet opens with the default address
2614
2693
  const cart = await this.updateCart({
2615
- shipping: convertToSwellAddress(event.shippingContact),
2694
+ shipping: convertToSwellAddress$1(event.shippingContact),
2616
2695
  });
2617
2696
 
2618
2697
  if (!cart.shipment_rating?.services?.length) {
@@ -2854,7 +2933,7 @@ function getShippingMethods(cart) {
2854
2933
  * @param {ApplePayJS.ApplePayPaymentContact} [address]
2855
2934
  * @returns {Address}
2856
2935
  */
2857
- function convertToSwellAddress(address = {}) {
2936
+ function convertToSwellAddress$1(address = {}) {
2858
2937
  return {
2859
2938
  first_name: address.givenName,
2860
2939
  last_name: address.familyName,
@@ -3059,10 +3138,10 @@ class BraintreeApplePayment extends Payment {
3059
3138
  nonce: payload.nonce,
3060
3139
  gateway: 'braintree',
3061
3140
  },
3062
- ...convertToSwellAddress(billingContact),
3141
+ ...convertToSwellAddress$1(billingContact),
3063
3142
  },
3064
3143
  ...(requireShipping && {
3065
- shipping: convertToSwellAddress(shippingContact),
3144
+ shipping: convertToSwellAddress$1(shippingContact),
3066
3145
  }),
3067
3146
  });
3068
3147
 
@@ -3240,7 +3319,7 @@ class AuthorizeNetGooglePayment extends Payment {
3240
3319
  throw new Error('Payment token is missing');
3241
3320
  }
3242
3321
 
3243
- const cart = await this.updateCart({
3322
+ await this.updateCart({
3244
3323
  account: {
3245
3324
  email,
3246
3325
  },
@@ -3252,14 +3331,14 @@ class AuthorizeNetGooglePayment extends Payment {
3252
3331
  gateway: 'authorizenet',
3253
3332
  token: base64Encode(token),
3254
3333
  },
3255
- ...convertToSwellAddress$1(billingAddress),
3334
+ ...convertToSwellAddress$2(billingAddress),
3256
3335
  },
3257
3336
  ...(requireShipping && {
3258
- shipping: convertToSwellAddress$1(shippingAddress),
3337
+ shipping: convertToSwellAddress$2(shippingAddress),
3259
3338
  }),
3260
3339
  });
3261
3340
 
3262
- this.onSuccess(cart);
3341
+ this.onSuccess();
3263
3342
  }
3264
3343
 
3265
3344
  /**
@@ -3541,7 +3620,7 @@ class AuthorizeNetApplePayment extends Payment {
3541
3620
  //
3542
3621
  // We store the payment token but DO NOT process the order yet.
3543
3622
  // The user must manually click "Place Order" to complete the transaction.
3544
- const cart = await this.updateCart({
3623
+ await this.updateCart({
3545
3624
  account: {
3546
3625
  email: shippingContact.emailAddress,
3547
3626
  },
@@ -3553,10 +3632,10 @@ class AuthorizeNetApplePayment extends Payment {
3553
3632
  token: base64Encode(JSON.stringify(token.paymentData)),
3554
3633
  gateway: 'authorizenet',
3555
3634
  },
3556
- ...convertToSwellAddress(billingContact),
3635
+ ...convertToSwellAddress$1(billingContact),
3557
3636
  },
3558
3637
  ...(requireShipping && {
3559
- shipping: convertToSwellAddress(shippingContact),
3638
+ shipping: convertToSwellAddress$1(shippingContact),
3560
3639
  }),
3561
3640
  });
3562
3641
 
@@ -3569,7 +3648,7 @@ class AuthorizeNetApplePayment extends Payment {
3569
3648
  // Notify that Apple Pay authorization is complete
3570
3649
  // The cart now has the payment token and is ready for submission
3571
3650
  // NOTE: This does NOT submit the order - user must click "Place Order"
3572
- this.onSuccess(cart);
3651
+ this.onSuccess();
3573
3652
  } catch (err) {
3574
3653
  session.completePayment({
3575
3654
  status: this.ApplePaySession.STATUS_FAILURE,
@@ -4395,66 +4474,438 @@ class AmazonDirectPayment extends Payment {
4395
4474
  }
4396
4475
  }
4397
4476
 
4398
- function adjustConfig(params) {
4399
- if (!params.config) {
4400
- return;
4477
+ /** @typedef {import('../../../types').Cart} Cart */
4478
+ /** @typedef {import('../../../types').Address} Address */
4479
+
4480
+ /**
4481
+ * @typedef {object} SezzleCheckoutSdkCheckoutPayloadOrderAmount
4482
+ * @prop {number} amount_in_cents
4483
+ * @prop {string} currency
4484
+ */
4485
+
4486
+ /**
4487
+ * @typedef {object} SezzleCheckoutSdkCheckoutPayloadOrderItem
4488
+ * @prop {string} name
4489
+ * @prop {string} sku
4490
+ * @prop {number} quantity
4491
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrderAmount} price
4492
+ */
4493
+
4494
+ /**
4495
+ * @typedef {object} SezzleCheckoutSdkCheckoutPayloadOrderDiscount
4496
+ * @prop {string} name
4497
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrderAmount} amount
4498
+ */
4499
+
4500
+ /**
4501
+ * @typedef {object} SezzleCheckoutSdkCheckoutPayloadOrder
4502
+ * @prop {'AUTH' | 'CAPTURE'} intent
4503
+ * @prop {string} reference_id
4504
+ * @prop {string} description
4505
+ * @prop {boolean} requires_shipping_info
4506
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrderItem[]} items
4507
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrderDiscount[]} discounts
4508
+ * @prop {Record<string, string>} [metadata]
4509
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrderAmount} tax_amount
4510
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrderAmount} order_amount
4511
+ */
4512
+
4513
+ /**
4514
+ * @typedef {object} SezzleCheckoutSdkCheckoutPayloadData
4515
+ * @prop {'single-step' | 'multi-step' | 'no-shipping'} express_checkout_type
4516
+ * @prop {SezzleCheckoutSdkCheckoutPayloadOrder} order
4517
+ */
4518
+
4519
+ /**
4520
+ * @typedef {object} SezzleCheckoutSdkCheckoutPayload
4521
+ * @prop {SezzleCheckoutSdkCheckoutPayloadData} checkout_payload
4522
+ */
4523
+
4524
+ /**
4525
+ * @typedef {object} SezzleCheckoutSdkCheckoutUrl
4526
+ * @prop {string} checkout_url
4527
+ */
4528
+
4529
+ /**
4530
+ * @typedef {object} SezzleCheckoutSdkCheckoutShippingAddress
4531
+ * @prop {string} uuid
4532
+ * @prop {string} name
4533
+ * @prop {string} street
4534
+ * @prop {string} street2
4535
+ * @prop {string} city
4536
+ * @prop {string} state
4537
+ * @prop {string} postal_code
4538
+ * @prop {string} country_code
4539
+ * @prop {string} phone
4540
+ */
4541
+
4542
+ /**
4543
+ * @typedef {object} SezzleCheckoutSdkCheckoutShippingError
4544
+ * @prop {'merchant_unsupported_shipping_address' | 'merchant_error'} code
4545
+ */
4546
+
4547
+ /**
4548
+ * @typedef {object} SezzleCheckoutSdkCheckoutShippingResult
4549
+ * @prop {boolean} ok
4550
+ * @prop {SezzleCheckoutSdkCheckoutShippingError} [error]
4551
+ */
4552
+
4553
+ /**
4554
+ * @typedef {object} SezzleCheckoutSdkInit
4555
+ * @prop {(event: MouseEvent) => void} onClick
4556
+ * @prop {(event: CustomEvent) => void} onComplete
4557
+ * @prop {() => void} onCancel
4558
+ * @prop {() => void} onFailure
4559
+ * @prop {(shippingAddress: SezzleCheckoutSdkCheckoutShippingAddress, orderUuid: string) => Promise<SezzleCheckoutSdkCheckoutShippingResult>} onCalculateAddressRelatedCosts
4560
+ */
4561
+
4562
+ /**
4563
+ * @typedef {object} SezzleCheckoutSdk
4564
+ * @prop {(containerId: string) => void} renderSezzleButton
4565
+ * @prop {(options: SezzleCheckoutSdkInit) => void} init
4566
+ * @prop {(options: SezzleCheckoutSdkCheckoutPayload | SezzleCheckoutSdkCheckoutUrl) => void} startCheckout
4567
+ */
4568
+
4569
+ /**
4570
+ * @typedef {object} SezzleCheckoutOptions
4571
+ * @prop {'popup' | 'iframe' | 'redirect'} [mode='popup']
4572
+ * @prop {string} publicKey
4573
+ * @prop {'live' | 'sandbox'} [apiMode='live']
4574
+ * @prop {'v2'} [apiVersion='v2']
4575
+ */
4576
+
4577
+ /** @typedef {new (options: SezzleCheckoutOptions) => SezzleCheckoutSdk} SezzleCheckout */
4578
+
4579
+ const SEZZLE_BUTTON_ID = 'sezzle-smart-button-container';
4580
+
4581
+ class SezzleDirectPayment extends Payment {
4582
+ constructor(api, options, params, methods) {
4583
+ super(api, options, params, methods.sezzle);
4584
+
4585
+ /** @type {SezzleCheckoutSdkCheckoutPayload | null} */
4586
+ this._paymentRequest = null;
4401
4587
  }
4402
4588
 
4403
- if (params.card) {
4404
- console.warn('Please move the "config" field to the "card.config"');
4589
+ get scripts() {
4590
+ return ['sezzle-sdk'];
4591
+ }
4405
4592
 
4406
- params.card.config = params.config;
4593
+ /** @returns {SezzleCheckout} */
4594
+ get SezzleCheckout() {
4595
+ if (!window.Checkout) {
4596
+ throw new LibraryNotLoadedError('Sezzle');
4597
+ }
4598
+
4599
+ return window.Checkout;
4407
4600
  }
4408
4601
 
4409
- if (params.ideal) {
4410
- console.warn('Please move the "config" field to the "ideal.config"');
4602
+ /** @returns {SezzleCheckoutSdk} */
4603
+ get SezzleSdk() {
4604
+ if (!this._sezzleSdk) {
4605
+ this._sezzleSdk = new this.SezzleCheckout({
4606
+ mode: 'popup',
4607
+ publicKey: this.method.public_key,
4608
+ apiMode: isLiveMode(this.method.mode) ? 'live' : 'sandbox',
4609
+ apiVersion: 'v2',
4610
+ });
4411
4611
 
4412
- params.ideal.config = params.config;
4612
+ if (!this._sezzleSdk) {
4613
+ throw new LibraryNotLoadedError('Sezzle sdk client');
4614
+ }
4615
+ }
4616
+
4617
+ return this._sezzleSdk;
4413
4618
  }
4414
4619
 
4415
- delete params.config;
4416
- }
4620
+ /**
4621
+ * @param {Cart} cart
4622
+ * @returns {SezzleCheckoutSdkCheckoutPayload}
4623
+ */
4624
+ _createPaymentRequest(cart) {
4625
+ const {
4626
+ settings: { name },
4627
+ } = cart;
4417
4628
 
4418
- function adjustElementId(methodParams) {
4419
- if (methodParams.cardNumber) {
4420
- adjustElementId(methodParams.cardNumber);
4629
+ const shipmentDelivery = Boolean(cart.shipment_delivery);
4630
+ const cartCurrency = getCartCurrency(cart);
4631
+
4632
+ return {
4633
+ checkout_payload: {
4634
+ express_checkout_type: shipmentDelivery ? 'multi-step' : 'no-shipping',
4635
+ order: {
4636
+ intent: 'AUTH',
4637
+ reference_id: cart.checkout_id,
4638
+ description: `${name} #${cart.number}`,
4639
+ requires_shipping_info: shipmentDelivery,
4640
+ items: getItems(cart),
4641
+ discounts: getDiscounts(cart),
4642
+ tax_amount: shipmentDelivery
4643
+ ? undefined
4644
+ : getAmountInCents(cartCurrency, cart.tax_total),
4645
+ order_amount: getAmountInCents(cartCurrency, cart.capture_total),
4646
+ },
4647
+ },
4648
+ };
4421
4649
  }
4422
4650
 
4423
- if (methodParams.cardExpiry) {
4424
- adjustElementId(methodParams.cardExpiry);
4651
+ _createButton() {
4652
+ const { style = {} } = this.params;
4653
+
4654
+ if (!style.templateText) {
4655
+ style.templateText = '%%logo%%';
4656
+ }
4657
+
4658
+ if (!style.borderType) {
4659
+ style.borderType = 'square';
4660
+ }
4661
+
4662
+ if (!style.width) {
4663
+ style.width = '100%';
4664
+ }
4665
+
4666
+ if (!style.height) {
4667
+ style.height = '45px';
4668
+ }
4669
+
4670
+ const button = document.createElement('div');
4671
+
4672
+ for (const attrId of Object.keys(style)) {
4673
+ button.setAttribute(attrId, style[attrId]);
4674
+ }
4675
+
4676
+ button.setAttribute('id', SEZZLE_BUTTON_ID);
4677
+ button.style.textAlign = 'center';
4678
+
4679
+ return button;
4425
4680
  }
4426
4681
 
4427
- if (methodParams.cardCvc) {
4428
- adjustElementId(methodParams.cardCvc);
4682
+ _initSezzleSdk() {
4683
+ let shippingServices = [];
4684
+
4685
+ this.SezzleSdk.init({
4686
+ onClick: (event) => {
4687
+ event.preventDefault();
4688
+
4689
+ this.SezzleSdk.startCheckout(this._paymentRequest);
4690
+ },
4691
+ onComplete: async (event) => {
4692
+ try {
4693
+ const { order_uuid } = event.data;
4694
+
4695
+ const details = await this.authorizeGateway({
4696
+ gateway: 'sezzle',
4697
+ params: {
4698
+ action: 'details',
4699
+ order_uuid,
4700
+ },
4701
+ });
4702
+
4703
+ let shippingService = null;
4704
+
4705
+ if (details.service_name) {
4706
+ shippingService =
4707
+ shippingServices.find(
4708
+ (service) => service.name === details.service_name,
4709
+ )?.id ?? null;
4710
+ }
4711
+
4712
+ await this.updateCart({
4713
+ account: details.account,
4714
+ shipping: {
4715
+ ...details.shipping,
4716
+ service: shippingService,
4717
+ },
4718
+ billing: {
4719
+ method: 'sezzle',
4720
+ account_card_id: null,
4721
+ card: null,
4722
+ sezzle: { order_uuid },
4723
+ ...details.billing,
4724
+ },
4725
+ });
4726
+
4727
+ this.onSuccess();
4728
+ } catch (err) {
4729
+ this.onError(err);
4730
+ }
4731
+ },
4732
+ onCancel(_event) {},
4733
+ onFailure: (event) => {
4734
+ this.onError(event.data);
4735
+ },
4736
+ onCalculateAddressRelatedCosts: async (address, orderUuid) => {
4737
+ const cart = await this.updateCart({
4738
+ shipping: convertToSwellAddress(address),
4739
+ });
4740
+
4741
+ if (!cart.shipment_rating?.services?.length) {
4742
+ return {
4743
+ ok: false,
4744
+ error: { code: 'merchant_unsupported_shipping_address' },
4745
+ };
4746
+ }
4747
+
4748
+ shippingServices = cart.shipment_rating.services;
4749
+
4750
+ const result = await this.authorizeGateway({
4751
+ gateway: 'sezzle',
4752
+ params: {
4753
+ action: 'shipping',
4754
+ order_uuid: orderUuid,
4755
+ currency_code: cart.currency,
4756
+ address_uuid: address.uuid,
4757
+ shipping_options: cart.shipment_rating.services.map((service) => ({
4758
+ name: service.name,
4759
+ description: service.description,
4760
+ shipping_amount_in_cents: amountInCents(
4761
+ cart.currency,
4762
+ service.price || 0,
4763
+ ),
4764
+ tax_amount_in_cents: amountInCents(
4765
+ cart.currency,
4766
+ cart.tax_total || 0,
4767
+ ),
4768
+ final_order_amount_in_cents: amountInCents(
4769
+ cart.currency,
4770
+ (cart.sub_total || 0) +
4771
+ (cart.tax_total || 0) +
4772
+ (service.price || 0),
4773
+ ),
4774
+ })),
4775
+ },
4776
+ });
4777
+
4778
+ if (result?.error) {
4779
+ console.error(
4780
+ '[Sezzle] Update checkout by order error:',
4781
+ result.error,
4782
+ );
4783
+
4784
+ return {
4785
+ ok: false,
4786
+ error: { code: 'merchant_error' },
4787
+ };
4788
+ }
4789
+
4790
+ return { ok: true };
4791
+ },
4792
+ });
4429
4793
  }
4430
4794
 
4431
- if (!methodParams.elementId) {
4432
- return;
4795
+ /**
4796
+ * Creates the Sezzle button element
4797
+ *
4798
+ * @param {Cart} cart
4799
+ */
4800
+ async createElements(cart) {
4801
+ const { elementId = 'sezzle-button' } = this.params;
4802
+
4803
+ this.setElementContainer(elementId);
4804
+ await this.loadScripts(this.scripts);
4805
+
4806
+ this._paymentRequest = this._createPaymentRequest(cart);
4807
+
4808
+ this.element = this._createButton();
4433
4809
  }
4434
4810
 
4435
- if (methodParams.elementId.startsWith('#')) {
4436
- console.warn(
4437
- `Please remove the "#" sign from the "${methodParams.elementId}" element ID`,
4438
- );
4811
+ /**
4812
+ * Mounts the Sezzle button to the DOM
4813
+ */
4814
+ mountElements() {
4815
+ const { classes = {} } = this.params;
4816
+ const container = this.elementContainer;
4439
4817
 
4440
- methodParams.elementId = methodParams.elementId.substring(1);
4818
+ container.appendChild(this.element);
4819
+
4820
+ this.SezzleSdk.renderSezzleButton(SEZZLE_BUTTON_ID);
4821
+
4822
+ if (classes.base) {
4823
+ container.classList.add(classes.base);
4824
+ }
4825
+
4826
+ this._initSezzleSdk();
4441
4827
  }
4442
4828
  }
4443
4829
 
4444
- function adjustParams(_params) {
4445
- const params = { ..._params };
4830
+ /**
4831
+ * @param {Cart} cart
4832
+ * @returns {string}
4833
+ */
4834
+ function getCartCurrency(cart) {
4835
+ const {
4836
+ settings: { currency },
4837
+ } = cart;
4446
4838
 
4447
- adjustConfig(params);
4839
+ return cart.currency || currency || 'USD';
4840
+ }
4448
4841
 
4449
- return params;
4842
+ /**
4843
+ * @param {string} currency
4844
+ * @param {number} amount
4845
+ * @returns {SezzleCheckoutSdkCheckoutPayloadOrderAmount}
4846
+ */
4847
+ function getAmountInCents(currency, amount) {
4848
+ return {
4849
+ amount_in_cents: amountInCents(currency, amount),
4850
+ currency,
4851
+ };
4450
4852
  }
4451
4853
 
4452
- function adjustMethodParams(_methodParams) {
4453
- const methodParams = { ..._methodParams };
4854
+ /**
4855
+ * @param {Cart} cart
4856
+ * @returns {SezzleCheckoutSdkCheckoutPayloadOrderItem[]}
4857
+ */
4858
+ function getItems(cart) {
4859
+ const currency = getCartCurrency(cart);
4860
+
4861
+ /** @type {SezzleCheckoutSdkCheckoutPayloadOrderItem[]} */
4862
+ const items = cart.items.map((item, index) => ({
4863
+ name: item.product?.name || `Product ${index + 1}`,
4864
+ sku: item.product?.sku || `Product SKU ${index + 1}`,
4865
+ quantity: item.quantity || 1,
4866
+ price: getAmountInCents(currency, item.price || 0),
4867
+ }));
4454
4868
 
4455
- adjustElementId(methodParams);
4869
+ return items;
4870
+ }
4456
4871
 
4457
- return methodParams;
4872
+ /**
4873
+ * @param {Cart} cart
4874
+ * @returns {SezzleCheckoutSdkCheckoutPayloadOrderDiscount[]}
4875
+ */
4876
+ function getDiscounts(cart) {
4877
+ const currency = getCartCurrency(cart);
4878
+
4879
+ return (cart.discounts || []).map((discount) => ({
4880
+ name: getDiscountLabel(discount, cart),
4881
+ amount: getAmountInCents(currency, discount.amount || 0),
4882
+ }));
4883
+ }
4884
+
4885
+ /**
4886
+ * @param {SezzleCheckoutSdkCheckoutShippingAddress} address
4887
+ * @returns {Address}
4888
+ */
4889
+ function convertToSwellAddress(address) {
4890
+ return {
4891
+ name: address.name || null,
4892
+ address1: address.street || null,
4893
+ address2: address.street2 || null,
4894
+ city: address.city || null,
4895
+ state: address.state || null,
4896
+ zip: address.postal_code || null,
4897
+ country: address.country_code || null,
4898
+ phone: address.phone || null,
4899
+ };
4900
+ }
4901
+
4902
+ /**
4903
+ * @param {string} currency
4904
+ * @param {number} amount
4905
+ * @returns {number}
4906
+ */
4907
+ function amountInCents(currency, amount) {
4908
+ return Math.round(amount * 100);
4458
4909
  }
4459
4910
 
4460
4911
  class PaymentController {
@@ -4623,6 +5074,9 @@ class PaymentController {
4623
5074
  return response;
4624
5075
  }
4625
5076
 
5077
+ /**
5078
+ * @returns {Promise<Payment[]>}
5079
+ */
4626
5080
  async _createPaymentInstances() {
4627
5081
  const paymentMethods = await this._getPaymentMethods();
4628
5082
  const params = adjustParams(this.params);
@@ -4668,6 +5122,12 @@ class PaymentController {
4668
5122
  }, []);
4669
5123
  }
4670
5124
 
5125
+ /**
5126
+ * @param {Payment[]} paymentInstances
5127
+ * @param {string} action
5128
+ * @param {...unknown} args
5129
+ * @returns {Promise<Payment[]>}
5130
+ */
4671
5131
  async _performPaymentAction(paymentInstances, action, ...args) {
4672
5132
  const actions = paymentInstances.reduce((acc, instance) => {
4673
5133
  const paymentAction = instance[action];
@@ -4688,9 +5148,7 @@ class PaymentController {
4688
5148
  await resultPromise;
4689
5149
  nextPaymentInstances.push(instance);
4690
5150
  } catch (error) {
4691
- const onPaymentError = instance.onError.bind(instance);
4692
-
4693
- onPaymentError(error);
5151
+ instance.onError(error);
4694
5152
  }
4695
5153
  }
4696
5154
 
@@ -4717,6 +5175,8 @@ class PaymentController {
4717
5175
  return this._getApplePaymentClass(gateway);
4718
5176
  case 'amazon':
4719
5177
  return this._getAmazonPaymentClass(gateway);
5178
+ case 'sezzle':
5179
+ return this._getSezzlePaymentClass(gateway);
4720
5180
  default:
4721
5181
  return null;
4722
5182
  }
@@ -4810,6 +5270,13 @@ class PaymentController {
4810
5270
  return AmazonDirectPayment;
4811
5271
  }
4812
5272
  }
5273
+
5274
+ _getSezzlePaymentClass(gateway) {
5275
+ switch (gateway) {
5276
+ default:
5277
+ return SezzleDirectPayment;
5278
+ }
5279
+ }
4813
5280
  }
4814
5281
 
4815
5282
  export { PaymentController as default };