swell-js 5.8.1 → 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
@@ -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;
@@ -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
 
@@ -4824,12 +4923,26 @@ function getKlarnaSessionData(cart) {
4824
4923
  };
4825
4924
  }
4826
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
+
4827
4939
  class PaypalDirectPayment extends Payment {
4828
4940
  constructor(api, options, params, methods) {
4829
4941
  super(api, options, params, methods.paypal);
4830
4942
  }
4831
4943
 
4832
- get scripts() {
4944
+ /** @param {Cart} cart */
4945
+ getScripts(cart) {
4833
4946
  const { client_id } = this.method;
4834
4947
 
4835
4948
  return [
@@ -4839,11 +4952,13 @@ class PaypalDirectPayment extends Payment {
4839
4952
  client_id,
4840
4953
  merchant_id: this.merchantId,
4841
4954
  cart: ['currency'],
4955
+ vault: cart.subscription_delivery,
4842
4956
  },
4843
4957
  },
4844
4958
  ];
4845
4959
  }
4846
4960
 
4961
+ /** @returns {PayPal} */
4847
4962
  get paypal() {
4848
4963
  if (!window.paypal) {
4849
4964
  throw new LibraryNotLoadedError('PayPal');
@@ -4867,7 +4982,6 @@ class PaypalDirectPayment extends Payment {
4867
4982
  async createElements(cart) {
4868
4983
  const {
4869
4984
  elementId = 'paypal-button',
4870
- locale = 'en_US',
4871
4985
  style: {
4872
4986
  layout = 'horizontal',
4873
4987
  height = 45,
@@ -4876,15 +4990,16 @@ class PaypalDirectPayment extends Payment {
4876
4990
  label = 'paypal',
4877
4991
  tagline = false,
4878
4992
  } = {},
4993
+ require: { shipping: requireShipping = true } = {},
4879
4994
  } = this.params;
4880
4995
 
4881
4996
  this.setElementContainer(elementId);
4882
4997
 
4883
4998
  this._validateCart(cart);
4884
- await this.loadScripts(this.scripts);
4999
+ await this.loadScripts(this.getScripts(cart));
4885
5000
 
4886
- this.element = this.paypal.Buttons({
4887
- locale,
5001
+ /** @type {import('@paypal/paypal-js').PayPalButtonsComponentOptions} */
5002
+ const buttonsOptions = {
4888
5003
  style: {
4889
5004
  layout,
4890
5005
  height,
@@ -4893,11 +5008,27 @@ class PaypalDirectPayment extends Payment {
4893
5008
  label,
4894
5009
  tagline,
4895
5010
  },
5011
+ fundingSource: this.paypal.FUNDING.PAYPAL,
4896
5012
  createOrder: this._onCreateOrder.bind(this, cart),
4897
- onShippingChange: this._onShippingChange.bind(this),
4898
5013
  onApprove: this._onApprove.bind(this),
4899
5014
  onError: this.onError.bind(this),
4900
- });
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;
4901
5032
  }
4902
5033
 
4903
5034
  mountElements() {
@@ -4911,6 +5042,7 @@ class PaypalDirectPayment extends Payment {
4911
5042
  }
4912
5043
  }
4913
5044
 
5045
+ /** @param {Cart} cart */
4914
5046
  _validateCart(cart) {
4915
5047
  const hasSubscriptionProduct = Boolean(cart.subscription_delivery);
4916
5048
 
@@ -4920,27 +5052,35 @@ class PaypalDirectPayment extends Payment {
4920
5052
  );
4921
5053
  }
4922
5054
 
4923
- if (!(cart.capture_total > 0)) {
5055
+ if ((cart.capture_total || 0) <= 0) {
4924
5056
  throw new Error(
4925
5057
  'Invalid PayPal button amount. Value should be greater than zero.',
4926
5058
  );
4927
5059
  }
4928
5060
  }
4929
5061
 
5062
+ /**
5063
+ * @param {Cart} cart
5064
+ * @param {CreateOrderData} _data
5065
+ * @param {CreateOrderActions} _actions
5066
+ * @returns {Promise<string>}
5067
+ */
4930
5068
  async _onCreateOrder(cart, _data, _actions) {
4931
- const { require: { shipping: requireShipping = true } = {} } = this.params;
5069
+ const {
5070
+ locale = 'en-US',
5071
+ require: { shipping: requireShipping = true } = {},
5072
+ } = this.params;
5073
+
4932
5074
  const { capture_total, currency, subscription_delivery } = cart;
4933
5075
  const hasSubscriptionProduct = Boolean(subscription_delivery);
4934
5076
  const merchantId = this.merchantId;
4935
5077
  const returnUrl = this.returnUrl;
4936
- const orderData = {
4937
- application_context: {
4938
- shipping_preference: requireShipping ? 'GET_FROM_FILE' : 'NO_SHIPPING',
4939
- },
4940
- };
5078
+ /** @type {import('@paypal/paypal-js').CreateOrderRequestBody} */
5079
+ const orderData = {};
5080
+ /** @type {import('@paypal/paypal-js').PurchaseUnit} */
4941
5081
  const purchaseUnit = {
4942
5082
  amount: {
4943
- value: Number(capture_total.toFixed(2)),
5083
+ value: Number(capture_total).toFixed(2),
4944
5084
  currency_code: currency,
4945
5085
  },
4946
5086
  };
@@ -4948,29 +5088,44 @@ class PaypalDirectPayment extends Payment {
4948
5088
  if (merchantId) {
4949
5089
  // express checkout and ppcp
4950
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
+
4951
5103
  purchaseUnit.payee = {
4952
5104
  merchant_id: merchantId,
4953
5105
  };
4954
5106
 
4955
5107
  if (hasSubscriptionProduct) {
4956
- orderData.payment_source = {
4957
- paypal: {
4958
- attributes: {
4959
- vault: {
4960
- store_in_vault: 'ON_SUCCESS',
4961
- usage_type: 'MERCHANT',
4962
- },
4963
- },
4964
- experience_context: {
4965
- return_url: `${returnUrl}&redirect_status=succeeded`,
4966
- cancel_url: `${returnUrl}&redirect_status=canceled`,
4967
- },
5108
+ orderData.payment_source.paypal.attributes = {
5109
+ vault: {
5110
+ store_in_vault: 'ON_SUCCESS',
5111
+ usage_type: 'MERCHANT',
4968
5112
  },
4969
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
+ });
4970
5119
  }
4971
5120
  } else {
4972
5121
  // progressive checkout
4973
5122
  orderData.intent = 'CAPTURE';
5123
+
5124
+ orderData.application_context = {
5125
+ locale,
5126
+ shipping_preference: requireShipping ? 'GET_FROM_FILE' : 'NO_SHIPPING',
5127
+ };
5128
+
4974
5129
  purchaseUnit.payee = {
4975
5130
  email_address: this.method.store_owner_email,
4976
5131
  };
@@ -4986,44 +5141,37 @@ class PaypalDirectPayment extends Payment {
4986
5141
  return order.id;
4987
5142
  }
4988
5143
 
4989
- async _onShippingChange(data, actions) {
5144
+ /**
5145
+ * @param {OnShippingAddressChangeData} data
5146
+ * @param {OnShippingAddressChangeActions} actions
5147
+ */
5148
+ async _onShippingAddressChange(data, actions) {
4990
5149
  try {
4991
- const { orderID, shipping_address, selected_shipping_option } = data;
4992
- const updateData = {
5150
+ const { orderID, shippingAddress } = data;
5151
+
5152
+ const cart = await this.updateCart({
4993
5153
  shipping: {
4994
- state: shipping_address.state,
4995
- city: shipping_address.city,
4996
- zip: shipping_address.postal_code,
4997
- country: shipping_address.country_code,
5154
+ state: shippingAddress.state,
5155
+ city: shippingAddress.city,
5156
+ zip: shippingAddress.postalCode,
5157
+ country: shippingAddress.countryCode,
4998
5158
  },
4999
5159
  shipment_rating: null,
5000
- };
5001
-
5002
- if (selected_shipping_option) {
5003
- updateData.shipping.service = selected_shipping_option.id;
5004
- updateData.$taxes = true;
5005
- }
5160
+ });
5006
5161
 
5007
- const cart = await this.updateCart(updateData);
5008
- const shippingServices = get(cart, 'shipment_rating.services');
5162
+ const shippingServices = cart.shipment_rating?.services;
5009
5163
 
5010
- // can't fulfill shipping to selected address
5164
+ // Can't fulfill shipping to selected address
5011
5165
  if (isEmpty(shippingServices)) {
5012
5166
  return actions.reject();
5013
5167
  }
5014
5168
 
5015
- let selectedShippingService;
5016
-
5017
- if (selected_shipping_option) {
5018
- selectedShippingService = shippingServices.find(
5019
- (shippingService) =>
5020
- shippingService.id === selected_shipping_option.id,
5021
- );
5022
- }
5169
+ let selectedShippingService = cart.shipping?.service;
5023
5170
 
5024
5171
  // need to set first service for cart by default
5025
5172
  if (!selectedShippingService) {
5026
5173
  const [firstShippingService] = shippingServices;
5174
+ selectedShippingService = firstShippingService.id;
5027
5175
 
5028
5176
  await this.updateCart({
5029
5177
  shipping: {
@@ -5040,8 +5188,33 @@ class PaypalDirectPayment extends Payment {
5040
5188
  paypal_order_id: orderID,
5041
5189
  },
5042
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
+ });
5043
5210
 
5044
- return orderID;
5211
+ await this.updateIntent({
5212
+ gateway: 'paypal',
5213
+ intent: {
5214
+ cart_id: cart.id,
5215
+ paypal_order_id: orderID,
5216
+ },
5217
+ });
5045
5218
  } catch (error) {
5046
5219
  this.onError(error);
5047
5220
 
@@ -5049,14 +5222,17 @@ class PaypalDirectPayment extends Payment {
5049
5222
  }
5050
5223
  }
5051
5224
 
5225
+ /**
5226
+ * @param {OnApproveData} data
5227
+ * @param {OnApproveActions} actions
5228
+ */
5052
5229
  async _onApprove(data, actions) {
5053
- const { require: { shipping: requireShipping = true } = {} } = this.params;
5054
5230
  const order = await actions.order.get();
5055
5231
  const orderId = order.id;
5056
- const payer = order.payer;
5232
+ const payer = order.payment_source?.paypal || order.payer;
5057
5233
  const billing = payer.address;
5058
- const shipping = get(order, 'purchase_units[0].shipping');
5059
- 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}`;
5060
5236
 
5061
5237
  await this.updateCart({
5062
5238
  account: {
@@ -5068,31 +5244,35 @@ class PaypalDirectPayment extends Payment {
5068
5244
  order_id: orderId,
5069
5245
  },
5070
5246
  name,
5071
- ...this._mapAddress(billing),
5247
+ ...convertToSwellAddress$1(billing),
5072
5248
  },
5073
- ...(requireShipping && {
5249
+ ...(shipping?.address && {
5074
5250
  shipping: {
5075
5251
  first_name: payer.name.given_name,
5076
5252
  last_name: payer.name.surname,
5077
5253
  name: shipping.name.full_name,
5078
- ...this._mapAddress(shipping.address),
5254
+ ...convertToSwellAddress$1(shipping.address),
5079
5255
  },
5080
5256
  }),
5081
5257
  });
5082
5258
 
5083
5259
  this.onSuccess();
5084
5260
  }
5261
+ }
5085
5262
 
5086
- _mapAddress(address) {
5087
- return {
5088
- address1: address.address_line_1,
5089
- address2: address.address_line_2,
5090
- state: address.admin_area_1,
5091
- city: address.admin_area_2,
5092
- zip: address.postal_code,
5093
- country: address.country_code,
5094
- };
5095
- }
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
+ };
5096
5276
  }
5097
5277
 
5098
5278
  class AmazonDirectPayment extends Payment {