swell-js 5.8.0 → 5.8.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/dist/payment.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import methods from './cart.mjs';
2
2
  import methods$1 from './settings.mjs';
3
- import { b as isObject, k as isFunction, l as loadScript, c as cloneDeep, v as vaultRequest, t as toSnake, p as pick, n as isEmpty, o as isLiveMode, q as base64Encode, w as map, r as reduce, x as getLocationParams, y as removeUrlParams, a as toCamel } from './index.a227b03d.mjs';
4
- import { g as get, t as toNumber } from './find.18f1ac6d.mjs';
3
+ import { b as isObject, k as isFunction, l as loadScript, c as cloneDeep, v as vaultRequest, t as toSnake, p as pick, n as isEmpty, o as isLiveMode, q as base64Encode, w as getLocationParams, x as removeUrlParams, a as toCamel } from './index.a8d5c125.mjs';
4
+ import { g as get } from './find.18f1ac6d.mjs';
5
5
  import 'qs';
6
6
  import 'deepmerge';
7
7
  import 'fast-case';
@@ -47,13 +47,18 @@ async function loadStripe() {
47
47
 
48
48
  async function loadPaypal(params) {
49
49
  if (!window.paypal) {
50
- const { currency, client_id, merchant_id } = params;
50
+ const { vault, currency, client_id, merchant_id } = params;
51
+
51
52
  const paypalParams = {
52
53
  currency,
53
54
  'client-id': client_id,
54
55
  commit: false,
55
56
  };
56
57
 
58
+ if (vault) {
59
+ paypalParams.vault = vault;
60
+ }
61
+
57
62
  if (merchant_id) {
58
63
  // paypal express and ppcp onboarded
59
64
  paypalParams['merchant-id'] = merchant_id;
@@ -592,7 +597,7 @@ const MINIMUM_CHARGE_AMOUNT = {
592
597
  THB: 10,
593
598
  };
594
599
 
595
- const addressFieldsMap$1 = {
600
+ const addressFieldsMap = {
596
601
  city: 'city',
597
602
  country: 'country',
598
603
  line1: 'address1',
@@ -630,7 +635,7 @@ function getBillingDetails(cart) {
630
635
  details.email = cart.account.email;
631
636
  }
632
637
 
633
- const address = mapValues(addressFieldsMap$1, cart.billing);
638
+ const address = mapValues(addressFieldsMap, cart.billing);
634
639
  if (!isEmpty(address)) {
635
640
  details.address = address;
636
641
  }
@@ -2180,7 +2185,7 @@ async function onPaymentDataChanged(intermediatePaymentData) {
2180
2185
 
2181
2186
  // Update cart with new shipping address and force tax recalculation
2182
2187
  let cart = await this.updateCart({
2183
- shipping: convertToSwellAddress$2(
2188
+ shipping: convertToSwellAddress$3(
2184
2189
  intermediatePaymentData.shippingAddress,
2185
2190
  ),
2186
2191
  });
@@ -2446,7 +2451,7 @@ function getOfferInfo(cart) {
2446
2451
  * @param {google.payments.api.Address} address
2447
2452
  * @returns {Address}
2448
2453
  */
2449
- function convertToSwellAddress$2(address) {
2454
+ function convertToSwellAddress$3(address) {
2450
2455
  return {
2451
2456
  name: address.name,
2452
2457
  address1: address.address1,
@@ -2710,11 +2715,11 @@ class BraintreeGooglePayment extends Payment {
2710
2715
  nonce,
2711
2716
  gateway: 'braintree',
2712
2717
  },
2713
- ...convertToSwellAddress$2(billingAddress),
2718
+ ...convertToSwellAddress$3(billingAddress),
2714
2719
  },
2715
2720
  ...(shippingAddress && {
2716
2721
  shipping: {
2717
- ...convertToSwellAddress$2(shippingAddress),
2722
+ ...convertToSwellAddress$3(shippingAddress),
2718
2723
  service: shippingOptionData?.id || undefined,
2719
2724
  },
2720
2725
  }),
@@ -2751,6 +2756,7 @@ class BraintreeGooglePayment extends Payment {
2751
2756
  /** @typedef {import('./payment').default} Payment */
2752
2757
  /** @typedef {import('../../types').Cart} Cart */
2753
2758
  /** @typedef {import('../../types').Address} Address */
2759
+ /** @typedef {import('../../types').CartItemBillingSchedule} CartItemBillingSchedule */
2754
2760
 
2755
2761
  /**
2756
2762
  * @this {Payment}
@@ -2762,7 +2768,7 @@ async function onShippingAddressChange(shippingContact) {
2762
2768
  // Update cart with Apple Pay shipping address to get shipping options and tax
2763
2769
  // This happens immediately when the sheet opens with the default address
2764
2770
  const cart = await this.updateCart({
2765
- shipping: convertToSwellAddress$1(shippingContact),
2771
+ shipping: convertToSwellAddress$2(shippingContact),
2766
2772
  });
2767
2773
 
2768
2774
  if (!cart.shipment_rating?.services?.length) {
@@ -3051,7 +3057,7 @@ function getShippingMethods(cart) {
3051
3057
  * @param {ApplePayJS.ApplePayPaymentContact} [address]
3052
3058
  * @returns {Address}
3053
3059
  */
3054
- function convertToSwellAddress$1(address = {}) {
3060
+ function convertToSwellAddress$2(address = {}) {
3055
3061
  return {
3056
3062
  first_name: address.givenName,
3057
3063
  last_name: address.familyName,
@@ -3094,6 +3100,99 @@ function getRequiredContactFields(params) {
3094
3100
  return { requiredBillingContactFields, requiredShippingContactFields };
3095
3101
  }
3096
3102
 
3103
+ /**
3104
+ * @param {CartItemBillingSchedule} schedule
3105
+ * @returns {{ intervalUnit: ApplePayJS.ApplePayRecurringPaymentDateUnit, intervalCount: number }}
3106
+ */
3107
+ function convertSwellScheduleToAppleRecurringInterval(schedule) {
3108
+ let intervalCount = schedule.interval_count || 1;
3109
+ let intervalUnit = 'day';
3110
+
3111
+ switch (schedule.interval) {
3112
+ case 'daily':
3113
+ intervalUnit = 'day';
3114
+ break;
3115
+
3116
+ case 'weekly':
3117
+ intervalUnit = 'day';
3118
+ intervalCount *= 7;
3119
+ break;
3120
+
3121
+ case 'monthly':
3122
+ intervalUnit = 'month';
3123
+ break;
3124
+
3125
+ case 'yearly':
3126
+ intervalUnit = 'year';
3127
+ break;
3128
+ }
3129
+
3130
+ return { intervalUnit, intervalCount };
3131
+ }
3132
+
3133
+ /**
3134
+ * @param {Cart} cart
3135
+ * @param {string} managementUrl
3136
+ * @returns {ApplePayJS.ApplePayRecurringPaymentRequest | undefined}
3137
+ */
3138
+ function getRecurringPaymentRequest(cart, managementUrl) {
3139
+ if (!cart.subscription_delivery) {
3140
+ return undefined;
3141
+ }
3142
+
3143
+ const item = cart.items.find(
3144
+ (item) =>
3145
+ item.purchase_option?.type === 'subscription' &&
3146
+ item.purchase_option.billing_schedule,
3147
+ );
3148
+
3149
+ if (item === undefined) {
3150
+ return undefined;
3151
+ }
3152
+
3153
+ const { intervalUnit, intervalCount } =
3154
+ convertSwellScheduleToAppleRecurringInterval(
3155
+ item.purchase_option.billing_schedule,
3156
+ );
3157
+
3158
+ /** @type {ApplePayJS.ApplePayRecurringPaymentRequest} */
3159
+ const recurringPaymentRequest = {
3160
+ paymentDescription:
3161
+ item.purchase_option.plan_description ||
3162
+ item.purchase_option.plan_name ||
3163
+ item.product_name ||
3164
+ item.product?.name,
3165
+ regularBilling: {
3166
+ type: 'final',
3167
+ label: item.purchase_option.plan_name || 'Subscription',
3168
+ // Calculate the subscription price manually if a trial period is specified.
3169
+ // However, discounts are not included in the calculation due to the difficulty of taking into account all scenarios.
3170
+ amount: (item.purchase_option.billing_schedule.trial_days
3171
+ ? (item.price || 0) * (item.quantity || 1)
3172
+ : item.price_total
3173
+ ).toFixed(2),
3174
+ paymentTiming: 'recurring',
3175
+ recurringPaymentIntervalUnit: intervalUnit,
3176
+ recurringPaymentIntervalCount: intervalCount,
3177
+ },
3178
+ trialBilling: item.purchase_option.billing_schedule.trial_days
3179
+ ? {
3180
+ type: 'final',
3181
+ label: 'Trial',
3182
+ // When a trial period is set, price_total is 0 (usually)
3183
+ amount: Number(item.price_total || 0).toFixed(2),
3184
+ paymentTiming: 'recurring',
3185
+ recurringPaymentIntervalUnit: 'day',
3186
+ recurringPaymentIntervalCount:
3187
+ item.purchase_option.billing_schedule.trial_days,
3188
+ }
3189
+ : undefined,
3190
+ managementURL: managementUrl,
3191
+ };
3192
+
3193
+ return recurringPaymentRequest;
3194
+ }
3195
+
3097
3196
  /** @typedef {import('../../../types').Cart} Cart */
3098
3197
 
3099
3198
  const VERSION$1 = 14;
@@ -3296,10 +3395,10 @@ class BraintreeApplePayment extends Payment {
3296
3395
  nonce: payload.nonce,
3297
3396
  gateway: 'braintree',
3298
3397
  },
3299
- ...convertToSwellAddress$1(billingContact),
3398
+ ...convertToSwellAddress$2(billingContact),
3300
3399
  },
3301
3400
  ...(shippingContact && {
3302
- shipping: convertToSwellAddress$1(shippingContact),
3401
+ shipping: convertToSwellAddress$2(shippingContact),
3303
3402
  }),
3304
3403
  });
3305
3404
 
@@ -3505,7 +3604,7 @@ class AbstractGooglePayment extends Payment {
3505
3604
  * @returns {Promise<object>}
3506
3605
  */
3507
3606
  // eslint-disable-next-line no-unused-vars
3508
- async preparePaymentDataForCartUpdate(paymentData) {
3607
+ async processPaymentData(paymentData) {
3509
3608
  throw new Error('Implement this!');
3510
3609
  }
3511
3610
 
@@ -3527,9 +3626,7 @@ class AbstractGooglePayment extends Payment {
3527
3626
  throw new Error('Google Pay token is missing');
3528
3627
  }
3529
3628
 
3530
- await this.preparePaymentDataForCartUpdate(paymentData).then((data) =>
3531
- this.updateCart(data),
3532
- );
3629
+ await this.processPaymentData(paymentData);
3533
3630
 
3534
3631
  this.onSuccess();
3535
3632
  }
@@ -3652,7 +3749,7 @@ class AuthorizeNetGooglePayment extends AbstractGooglePayment {
3652
3749
  * @param {google.payments.api.PaymentData} paymentData
3653
3750
  * @returns {Promise<object>}
3654
3751
  */
3655
- async preparePaymentDataForCartUpdate(paymentData) {
3752
+ async processPaymentData(paymentData) {
3656
3753
  const { email, shippingAddress, shippingOptionData, paymentMethodData } =
3657
3754
  paymentData;
3658
3755
 
@@ -3661,7 +3758,7 @@ class AuthorizeNetGooglePayment extends AbstractGooglePayment {
3661
3758
  tokenizationData: { token },
3662
3759
  } = paymentMethodData;
3663
3760
 
3664
- return {
3761
+ await this.updateCart({
3665
3762
  account: { email },
3666
3763
  billing: {
3667
3764
  method: 'google',
@@ -3671,15 +3768,15 @@ class AuthorizeNetGooglePayment extends AbstractGooglePayment {
3671
3768
  gateway: 'authorizenet',
3672
3769
  token: base64Encode(token),
3673
3770
  },
3674
- ...convertToSwellAddress$2(billingAddress),
3771
+ ...convertToSwellAddress$3(billingAddress),
3675
3772
  },
3676
3773
  ...(shippingAddress && {
3677
3774
  shipping: {
3678
- ...convertToSwellAddress$2(shippingAddress),
3775
+ ...convertToSwellAddress$3(shippingAddress),
3679
3776
  service: shippingOptionData?.id || undefined,
3680
3777
  },
3681
3778
  }),
3682
- };
3779
+ });
3683
3780
  }
3684
3781
  }
3685
3782
 
@@ -3794,6 +3891,10 @@ class AbstractApplePayment extends Payment {
3794
3891
  requiredShippingContactFields,
3795
3892
  requiredBillingContactFields,
3796
3893
  supportsCouponCode: true,
3894
+ recurringPaymentRequest: getRecurringPaymentRequest(
3895
+ cart,
3896
+ this.params.managementUrl,
3897
+ ),
3797
3898
  lineItems: getLineItems(cart),
3798
3899
  };
3799
3900
  }
@@ -3816,7 +3917,7 @@ class AbstractApplePayment extends Payment {
3816
3917
  * @returns {Promise<object>}
3817
3918
  */
3818
3919
  // eslint-disable-next-line no-unused-vars
3819
- async preparePaymentDataForCartUpdate(event) {
3920
+ async processPaymentData(event) {
3820
3921
  throw new Error('Implement this!');
3821
3922
  }
3822
3923
 
@@ -3874,9 +3975,7 @@ class AbstractApplePayment extends Payment {
3874
3975
  //
3875
3976
  // We store the payment token but DO NOT process the order yet.
3876
3977
  // The user must manually click "Place Order" to complete the transaction.
3877
- await this.preparePaymentDataForCartUpdate(event).then((data) =>
3878
- this.updateCart(data),
3879
- );
3978
+ await this.processPaymentData(event);
3880
3979
 
3881
3980
  // Complete Apple Pay session successfully
3882
3981
  // This closes the Apple Pay sheet and shows success to the user
@@ -4012,12 +4111,12 @@ class AuthorizeNetApplePayment extends AbstractApplePayment {
4012
4111
  * @param {ApplePayJS.ApplePayPaymentAuthorizedEvent} event
4013
4112
  * @returns {Promise<object>}
4014
4113
  */
4015
- async preparePaymentDataForCartUpdate(event) {
4114
+ async processPaymentData(event) {
4016
4115
  const {
4017
4116
  payment: { token, shippingContact, billingContact },
4018
4117
  } = event;
4019
4118
 
4020
- return {
4119
+ return this.updateCart({
4021
4120
  account: {
4022
4121
  email: shippingContact?.emailAddress || billingContact?.emailAddress,
4023
4122
  },
@@ -4029,12 +4128,12 @@ class AuthorizeNetApplePayment extends AbstractApplePayment {
4029
4128
  gateway: 'authorizenet',
4030
4129
  token: base64Encode(JSON.stringify(token.paymentData)),
4031
4130
  },
4032
- ...convertToSwellAddress$1(billingContact),
4131
+ ...convertToSwellAddress$2(billingContact),
4033
4132
  },
4034
4133
  ...(shippingContact && {
4035
- shipping: convertToSwellAddress$1(shippingContact),
4134
+ shipping: convertToSwellAddress$2(shippingContact),
4036
4135
  }),
4037
- };
4136
+ });
4038
4137
  }
4039
4138
  }
4040
4139
 
@@ -4402,7 +4501,7 @@ class ConvesioPayApplePayment extends AbstractApplePayment {
4402
4501
  * @param {ApplePayJS.ApplePayPaymentAuthorizedEvent} event
4403
4502
  * @returns {Promise<object>}
4404
4503
  */
4405
- async preparePaymentDataForCartUpdate(event) {
4504
+ async processPaymentData(event) {
4406
4505
  const {
4407
4506
  payment: { token, shippingContact, billingContact },
4408
4507
  } = event;
@@ -4426,7 +4525,7 @@ class ConvesioPayApplePayment extends AbstractApplePayment {
4426
4525
  },
4427
4526
  );
4428
4527
 
4429
- return {
4528
+ return this.updateCart({
4430
4529
  account: {
4431
4530
  email: shippingContact?.emailAddress || billingContact?.emailAddress,
4432
4531
  },
@@ -4441,12 +4540,12 @@ class ConvesioPayApplePayment extends AbstractApplePayment {
4441
4540
  convesiopay: {
4442
4541
  return_url: this.params.returnUrl,
4443
4542
  },
4444
- ...convertToSwellAddress$1(billingContact),
4543
+ ...convertToSwellAddress$2(billingContact),
4445
4544
  },
4446
4545
  ...(shippingContact && {
4447
- shipping: convertToSwellAddress$1(shippingContact),
4546
+ shipping: convertToSwellAddress$2(shippingContact),
4448
4547
  }),
4449
- };
4548
+ });
4450
4549
  }
4451
4550
  }
4452
4551
 
@@ -4572,7 +4671,7 @@ class ConvesioPayGooglePayment extends AbstractGooglePayment {
4572
4671
  * @param {google.payments.api.PaymentData} paymentData
4573
4672
  * @returns {Promise<object>}
4574
4673
  */
4575
- async preparePaymentDataForCartUpdate(paymentData) {
4674
+ async processPaymentData(paymentData) {
4576
4675
  const { email, shippingAddress, shippingOptionData, paymentMethodData } =
4577
4676
  paymentData;
4578
4677
 
@@ -4600,7 +4699,7 @@ class ConvesioPayGooglePayment extends AbstractGooglePayment {
4600
4699
  },
4601
4700
  );
4602
4701
 
4603
- return {
4702
+ return this.updateCart({
4604
4703
  account: { email },
4605
4704
  billing: {
4606
4705
  method: 'google',
@@ -4613,15 +4712,15 @@ class ConvesioPayGooglePayment extends AbstractGooglePayment {
4613
4712
  convesiopay: {
4614
4713
  return_url: this.params.returnUrl,
4615
4714
  },
4616
- ...convertToSwellAddress$2(billingAddress),
4715
+ ...convertToSwellAddress$3(billingAddress),
4617
4716
  },
4618
4717
  ...(shippingAddress && {
4619
4718
  shipping: {
4620
- ...convertToSwellAddress$2(shippingAddress),
4719
+ ...convertToSwellAddress$3(shippingAddress),
4621
4720
  service: shippingOptionData?.id || undefined,
4622
4721
  },
4623
4722
  }),
4624
- };
4723
+ });
4625
4724
  }
4626
4725
  }
4627
4726
 
@@ -4767,105 +4866,6 @@ class PaysafecardDirectPayment extends Payment {
4767
4866
  }
4768
4867
  }
4769
4868
 
4770
- const addressFieldsMap = {
4771
- given_name: 'first_name',
4772
- family_name: 'last_name',
4773
- city: 'city',
4774
- country: 'country',
4775
- phone: 'phone',
4776
- postal_code: 'zip',
4777
- street_address: 'address1',
4778
- street_address2: 'address2',
4779
- region: 'state',
4780
- };
4781
-
4782
- const mapFields = (fieldsMap, data) =>
4783
- reduce(
4784
- fieldsMap,
4785
- (acc, srcKey, destKey) => {
4786
- const value = data[srcKey];
4787
- if (value) {
4788
- acc[destKey] = value;
4789
- }
4790
- return acc;
4791
- },
4792
- {},
4793
- );
4794
-
4795
- const mapAddressFields = (cart, addressField) => ({
4796
- ...mapFields(addressFieldsMap, cart[addressField]),
4797
- email: get(cart, 'account.email'),
4798
- });
4799
-
4800
- function getOrderLines(cart) {
4801
- const items = map(cart.items, (item) => ({
4802
- type: 'physical',
4803
- name: get(item, 'product.name'),
4804
- reference: get(item, 'product.sku') || get(item, 'product.slug'),
4805
- quantity: item.quantity,
4806
- unit_price: Math.round(toNumber(item.price - item.discount_each) * 100),
4807
- total_amount: Math.round(
4808
- toNumber(item.price_total - item.discount_total) * 100,
4809
- ),
4810
- tax_rate: 0,
4811
- total_tax_amount: 0,
4812
- }));
4813
-
4814
- const tax = get(cart, 'tax_included_total');
4815
- const taxAmount = toNumber(tax) * 100;
4816
- if (tax) {
4817
- items.push({
4818
- type: 'sales_tax',
4819
- name: 'Taxes',
4820
- quantity: 1,
4821
- unit_price: taxAmount,
4822
- total_amount: taxAmount,
4823
- tax_rate: 0,
4824
- total_tax_amount: 0,
4825
- });
4826
- }
4827
-
4828
- const shipping = get(cart, 'shipping', {});
4829
- const shippingTotal = get(cart, 'shipment_total', {});
4830
- const shippingAmount = toNumber(shippingTotal) * 100;
4831
- if (shipping.price) {
4832
- items.push({
4833
- type: 'shipping_fee',
4834
- name: shipping.service_name,
4835
- quantity: 1,
4836
- unit_price: shippingAmount,
4837
- total_amount: shippingAmount,
4838
- tax_rate: 0,
4839
- total_tax_amount: 0,
4840
- });
4841
- }
4842
-
4843
- return items;
4844
- }
4845
-
4846
- function getKlarnaSessionData(cart) {
4847
- const returnUrl = `${window.location.origin}${window.location.pathname}?gateway=klarna_direct&sid={{session_id}}`;
4848
- const successUrl = `${returnUrl}&authorization_token={{authorization_token}}`;
4849
-
4850
- return {
4851
- locale: cart.display_locale || get(cart, 'settings.locale') || 'en-US',
4852
- purchase_country:
4853
- get(cart, 'billing.country') || get(cart, 'shipping.country'),
4854
- purchase_currency: cart.currency,
4855
- billing_address: mapAddressFields(cart, 'billing'),
4856
- shipping_address: mapAddressFields(cart, 'shipping'),
4857
- order_amount: Math.round(get(cart, 'capture_total', 0) * 100),
4858
- order_lines: JSON.stringify(getOrderLines(cart)),
4859
- merchant_urls: {
4860
- success: successUrl,
4861
- back: returnUrl,
4862
- cancel: returnUrl,
4863
- error: returnUrl,
4864
- failure: returnUrl,
4865
- },
4866
- };
4867
- }
4868
-
4869
4869
  class KlarnaDirectPayment extends Payment {
4870
4870
  constructor(api, options, params, methods) {
4871
4871
  super(api, options, params, methods.klarna);
@@ -4874,6 +4874,7 @@ class KlarnaDirectPayment extends Payment {
4874
4874
  async tokenize() {
4875
4875
  const cart = await this.getCart();
4876
4876
  const sessionData = getKlarnaSessionData(cart);
4877
+
4877
4878
  const session = await this.createIntent({
4878
4879
  gateway: 'klarna',
4879
4880
  intent: sessionData,
@@ -4906,12 +4907,42 @@ class KlarnaDirectPayment extends Payment {
4906
4907
  }
4907
4908
  }
4908
4909
 
4910
+ function getKlarnaSessionData(cart) {
4911
+ const returnUrl = `${window.location.origin}${window.location.pathname}?gateway=klarna_direct&sid={{session_id}}`;
4912
+ const successUrl = `${returnUrl}&authorization_token={{authorization_token}}`;
4913
+
4914
+ return {
4915
+ cart_id: cart.id,
4916
+ merchant_urls: {
4917
+ success: successUrl,
4918
+ back: returnUrl,
4919
+ cancel: returnUrl,
4920
+ error: returnUrl,
4921
+ failure: returnUrl,
4922
+ },
4923
+ };
4924
+ }
4925
+
4926
+ /** @typedef {import('@paypal/paypal-js').PayPalNamespace} PayPal */
4927
+ /** @typedef {import('@paypal/paypal-js').ShippingAddress} ShippingAddress */
4928
+ /** @typedef {import('@paypal/paypal-js').CreateOrderData} CreateOrderData */
4929
+ /** @typedef {import('@paypal/paypal-js').CreateOrderActions} CreateOrderActions */
4930
+ /** @typedef {import('@paypal/paypal-js').OnShippingAddressChangeData} OnShippingAddressChangeData */
4931
+ /** @typedef {import('@paypal/paypal-js').OnShippingAddressChangeActions} OnShippingAddressChangeActions */
4932
+ /** @typedef {import('@paypal/paypal-js').OnShippingOptionsChangeData} OnShippingOptionsChangeData */
4933
+ /** @typedef {import('@paypal/paypal-js').OnShippingOptionsChangeActions} OnShippingOptionsChangeActions */
4934
+ /** @typedef {import('@paypal/paypal-js').OnApproveData} OnApproveData */
4935
+ /** @typedef {import('@paypal/paypal-js').OnApproveActions} OnApproveActions */
4936
+ /** @typedef {import('../../../types').Cart} Cart */
4937
+ /** @typedef {import('../../../types').Address} Address */
4938
+
4909
4939
  class PaypalDirectPayment extends Payment {
4910
4940
  constructor(api, options, params, methods) {
4911
4941
  super(api, options, params, methods.paypal);
4912
4942
  }
4913
4943
 
4914
- get scripts() {
4944
+ /** @param {Cart} cart */
4945
+ getScripts(cart) {
4915
4946
  const { client_id } = this.method;
4916
4947
 
4917
4948
  return [
@@ -4921,11 +4952,13 @@ class PaypalDirectPayment extends Payment {
4921
4952
  client_id,
4922
4953
  merchant_id: this.merchantId,
4923
4954
  cart: ['currency'],
4955
+ vault: cart.subscription_delivery,
4924
4956
  },
4925
4957
  },
4926
4958
  ];
4927
4959
  }
4928
4960
 
4961
+ /** @returns {PayPal} */
4929
4962
  get paypal() {
4930
4963
  if (!window.paypal) {
4931
4964
  throw new LibraryNotLoadedError('PayPal');
@@ -4949,7 +4982,6 @@ class PaypalDirectPayment extends Payment {
4949
4982
  async createElements(cart) {
4950
4983
  const {
4951
4984
  elementId = 'paypal-button',
4952
- locale = 'en_US',
4953
4985
  style: {
4954
4986
  layout = 'horizontal',
4955
4987
  height = 45,
@@ -4958,15 +4990,16 @@ class PaypalDirectPayment extends Payment {
4958
4990
  label = 'paypal',
4959
4991
  tagline = false,
4960
4992
  } = {},
4993
+ require: { shipping: requireShipping = true } = {},
4961
4994
  } = this.params;
4962
4995
 
4963
4996
  this.setElementContainer(elementId);
4964
4997
 
4965
4998
  this._validateCart(cart);
4966
- await this.loadScripts(this.scripts);
4999
+ await this.loadScripts(this.getScripts(cart));
4967
5000
 
4968
- this.element = this.paypal.Buttons({
4969
- locale,
5001
+ /** @type {import('@paypal/paypal-js').PayPalButtonsComponentOptions} */
5002
+ const buttonsOptions = {
4970
5003
  style: {
4971
5004
  layout,
4972
5005
  height,
@@ -4975,11 +5008,27 @@ class PaypalDirectPayment extends Payment {
4975
5008
  label,
4976
5009
  tagline,
4977
5010
  },
5011
+ fundingSource: this.paypal.FUNDING.PAYPAL,
4978
5012
  createOrder: this._onCreateOrder.bind(this, cart),
4979
- onShippingChange: this._onShippingChange.bind(this),
4980
5013
  onApprove: this._onApprove.bind(this),
4981
5014
  onError: this.onError.bind(this),
4982
- });
5015
+ };
5016
+
5017
+ if (requireShipping) {
5018
+ buttonsOptions.onShippingAddressChange =
5019
+ this._onShippingAddressChange.bind(this);
5020
+
5021
+ buttonsOptions.onShippingOptionsChange =
5022
+ this._onShippingOptionsChange.bind(this);
5023
+ }
5024
+
5025
+ const component = this.paypal.Buttons(buttonsOptions);
5026
+
5027
+ if (!component.isEligible()) {
5028
+ throw new Error('PayPal button with provided options is not eligible.');
5029
+ }
5030
+
5031
+ this.element = component;
4983
5032
  }
4984
5033
 
4985
5034
  mountElements() {
@@ -4993,6 +5042,7 @@ class PaypalDirectPayment extends Payment {
4993
5042
  }
4994
5043
  }
4995
5044
 
5045
+ /** @param {Cart} cart */
4996
5046
  _validateCart(cart) {
4997
5047
  const hasSubscriptionProduct = Boolean(cart.subscription_delivery);
4998
5048
 
@@ -5002,27 +5052,35 @@ class PaypalDirectPayment extends Payment {
5002
5052
  );
5003
5053
  }
5004
5054
 
5005
- if (!(cart.capture_total > 0)) {
5055
+ if ((cart.capture_total || 0) <= 0) {
5006
5056
  throw new Error(
5007
5057
  'Invalid PayPal button amount. Value should be greater than zero.',
5008
5058
  );
5009
5059
  }
5010
5060
  }
5011
5061
 
5062
+ /**
5063
+ * @param {Cart} cart
5064
+ * @param {CreateOrderData} _data
5065
+ * @param {CreateOrderActions} _actions
5066
+ * @returns {Promise<string>}
5067
+ */
5012
5068
  async _onCreateOrder(cart, _data, _actions) {
5013
- const { require: { shipping: requireShipping = true } = {} } = this.params;
5069
+ const {
5070
+ locale = 'en-US',
5071
+ require: { shipping: requireShipping = true } = {},
5072
+ } = this.params;
5073
+
5014
5074
  const { capture_total, currency, subscription_delivery } = cart;
5015
5075
  const hasSubscriptionProduct = Boolean(subscription_delivery);
5016
5076
  const merchantId = this.merchantId;
5017
5077
  const returnUrl = this.returnUrl;
5018
- const orderData = {
5019
- application_context: {
5020
- shipping_preference: requireShipping ? 'GET_FROM_FILE' : 'NO_SHIPPING',
5021
- },
5022
- };
5078
+ /** @type {import('@paypal/paypal-js').CreateOrderRequestBody} */
5079
+ const orderData = {};
5080
+ /** @type {import('@paypal/paypal-js').PurchaseUnit} */
5023
5081
  const purchaseUnit = {
5024
5082
  amount: {
5025
- value: Number(capture_total.toFixed(2)),
5083
+ value: Number(capture_total).toFixed(2),
5026
5084
  currency_code: currency,
5027
5085
  },
5028
5086
  };
@@ -5030,29 +5088,44 @@ class PaypalDirectPayment extends Payment {
5030
5088
  if (merchantId) {
5031
5089
  // express checkout and ppcp
5032
5090
  orderData.intent = 'AUTHORIZE';
5091
+
5092
+ orderData.payment_source = {
5093
+ paypal: {
5094
+ experience_context: {
5095
+ locale,
5096
+ shipping_preference: requireShipping
5097
+ ? 'GET_FROM_FILE'
5098
+ : 'NO_SHIPPING',
5099
+ },
5100
+ },
5101
+ };
5102
+
5033
5103
  purchaseUnit.payee = {
5034
5104
  merchant_id: merchantId,
5035
5105
  };
5036
5106
 
5037
5107
  if (hasSubscriptionProduct) {
5038
- orderData.payment_source = {
5039
- paypal: {
5040
- attributes: {
5041
- vault: {
5042
- store_in_vault: 'ON_SUCCESS',
5043
- usage_type: 'MERCHANT',
5044
- },
5045
- },
5046
- experience_context: {
5047
- return_url: `${returnUrl}&redirect_status=succeeded`,
5048
- cancel_url: `${returnUrl}&redirect_status=canceled`,
5049
- },
5108
+ orderData.payment_source.paypal.attributes = {
5109
+ vault: {
5110
+ store_in_vault: 'ON_SUCCESS',
5111
+ usage_type: 'MERCHANT',
5050
5112
  },
5051
5113
  };
5114
+
5115
+ Object.assign(orderData.payment_source.paypal.experience_context, {
5116
+ return_url: `${returnUrl}&redirect_status=succeeded`,
5117
+ cancel_url: `${returnUrl}&redirect_status=canceled`,
5118
+ });
5052
5119
  }
5053
5120
  } else {
5054
5121
  // progressive checkout
5055
5122
  orderData.intent = 'CAPTURE';
5123
+
5124
+ orderData.application_context = {
5125
+ locale,
5126
+ shipping_preference: requireShipping ? 'GET_FROM_FILE' : 'NO_SHIPPING',
5127
+ };
5128
+
5056
5129
  purchaseUnit.payee = {
5057
5130
  email_address: this.method.store_owner_email,
5058
5131
  };
@@ -5068,44 +5141,37 @@ class PaypalDirectPayment extends Payment {
5068
5141
  return order.id;
5069
5142
  }
5070
5143
 
5071
- async _onShippingChange(data, actions) {
5144
+ /**
5145
+ * @param {OnShippingAddressChangeData} data
5146
+ * @param {OnShippingAddressChangeActions} actions
5147
+ */
5148
+ async _onShippingAddressChange(data, actions) {
5072
5149
  try {
5073
- const { orderID, shipping_address, selected_shipping_option } = data;
5074
- const updateData = {
5150
+ const { orderID, shippingAddress } = data;
5151
+
5152
+ const cart = await this.updateCart({
5075
5153
  shipping: {
5076
- state: shipping_address.state,
5077
- city: shipping_address.city,
5078
- zip: shipping_address.postal_code,
5079
- country: shipping_address.country_code,
5154
+ state: shippingAddress.state,
5155
+ city: shippingAddress.city,
5156
+ zip: shippingAddress.postalCode,
5157
+ country: shippingAddress.countryCode,
5080
5158
  },
5081
5159
  shipment_rating: null,
5082
- };
5083
-
5084
- if (selected_shipping_option) {
5085
- updateData.shipping.service = selected_shipping_option.id;
5086
- updateData.$taxes = true;
5087
- }
5160
+ });
5088
5161
 
5089
- const cart = await this.updateCart(updateData);
5090
- const shippingServices = get(cart, 'shipment_rating.services');
5162
+ const shippingServices = cart.shipment_rating?.services;
5091
5163
 
5092
- // can't fulfill shipping to selected address
5164
+ // Can't fulfill shipping to selected address
5093
5165
  if (isEmpty(shippingServices)) {
5094
5166
  return actions.reject();
5095
5167
  }
5096
5168
 
5097
- let selectedShippingService;
5098
-
5099
- if (selected_shipping_option) {
5100
- selectedShippingService = shippingServices.find(
5101
- (shippingService) =>
5102
- shippingService.id === selected_shipping_option.id,
5103
- );
5104
- }
5169
+ let selectedShippingService = cart.shipping?.service;
5105
5170
 
5106
5171
  // need to set first service for cart by default
5107
5172
  if (!selectedShippingService) {
5108
5173
  const [firstShippingService] = shippingServices;
5174
+ selectedShippingService = firstShippingService.id;
5109
5175
 
5110
5176
  await this.updateCart({
5111
5177
  shipping: {
@@ -5122,8 +5188,33 @@ class PaypalDirectPayment extends Payment {
5122
5188
  paypal_order_id: orderID,
5123
5189
  },
5124
5190
  });
5191
+ } catch (error) {
5192
+ this.onError(error);
5193
+
5194
+ return actions.reject();
5195
+ }
5196
+ }
5197
+
5198
+ /**
5199
+ * @param {OnShippingOptionsChangeData} data
5200
+ * @param {OnShippingOptionsChangeActions} actions
5201
+ */
5202
+ async _onShippingOptionsChange(data, actions) {
5203
+ try {
5204
+ const { orderID, selectedShippingOption } = data;
5205
+
5206
+ const cart = await this.updateCart({
5207
+ shipping: { service: selectedShippingOption.id },
5208
+ $taxes: true,
5209
+ });
5125
5210
 
5126
- return orderID;
5211
+ await this.updateIntent({
5212
+ gateway: 'paypal',
5213
+ intent: {
5214
+ cart_id: cart.id,
5215
+ paypal_order_id: orderID,
5216
+ },
5217
+ });
5127
5218
  } catch (error) {
5128
5219
  this.onError(error);
5129
5220
 
@@ -5131,14 +5222,17 @@ class PaypalDirectPayment extends Payment {
5131
5222
  }
5132
5223
  }
5133
5224
 
5225
+ /**
5226
+ * @param {OnApproveData} data
5227
+ * @param {OnApproveActions} actions
5228
+ */
5134
5229
  async _onApprove(data, actions) {
5135
- const { require: { shipping: requireShipping = true } = {} } = this.params;
5136
5230
  const order = await actions.order.get();
5137
5231
  const orderId = order.id;
5138
- const payer = order.payer;
5232
+ const payer = order.payment_source?.paypal || order.payer;
5139
5233
  const billing = payer.address;
5140
- const shipping = get(order, 'purchase_units[0].shipping');
5141
- const name = `${payer.name.given_name} ${payer.name.surname}`;
5234
+ const shipping = order.purchase_units?.[0]?.shipping;
5235
+ const name = `${payer.name?.given_name} ${payer.name?.surname}`;
5142
5236
 
5143
5237
  await this.updateCart({
5144
5238
  account: {
@@ -5150,31 +5244,35 @@ class PaypalDirectPayment extends Payment {
5150
5244
  order_id: orderId,
5151
5245
  },
5152
5246
  name,
5153
- ...this._mapAddress(billing),
5247
+ ...convertToSwellAddress$1(billing),
5154
5248
  },
5155
- ...(requireShipping && {
5249
+ ...(shipping?.address && {
5156
5250
  shipping: {
5157
5251
  first_name: payer.name.given_name,
5158
5252
  last_name: payer.name.surname,
5159
5253
  name: shipping.name.full_name,
5160
- ...this._mapAddress(shipping.address),
5254
+ ...convertToSwellAddress$1(shipping.address),
5161
5255
  },
5162
5256
  }),
5163
5257
  });
5164
5258
 
5165
5259
  this.onSuccess();
5166
5260
  }
5261
+ }
5167
5262
 
5168
- _mapAddress(address) {
5169
- return {
5170
- address1: address.address_line_1,
5171
- address2: address.address_line_2,
5172
- state: address.admin_area_1,
5173
- city: address.admin_area_2,
5174
- zip: address.postal_code,
5175
- country: address.country_code,
5176
- };
5177
- }
5263
+ /**
5264
+ * @param {ShippingAddress} address
5265
+ * @returns {Address}
5266
+ */
5267
+ function convertToSwellAddress$1(address) {
5268
+ return {
5269
+ address1: address.address_line_1,
5270
+ address2: address.address_line_2,
5271
+ state: address.admin_area_1,
5272
+ city: address.admin_area_2,
5273
+ zip: address.postal_code,
5274
+ country: address.country_code,
5275
+ };
5178
5276
  }
5179
5277
 
5180
5278
  class AmazonDirectPayment extends Payment {