swell-js 5.9.4 → 5.10.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/README.md +42 -0
- package/dist/api.mjs +21 -1
- package/dist/payment.mjs +700 -76
- package/dist/swell.cjs +721 -77
- package/dist/swell.cjs.map +1 -1
- package/dist/swell.umd.min.js +721 -77
- package/dist/swell.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/types/index.d.cts +2 -0
- package/types/index.d.ts +14 -1
- package/types/payment/index.ts +42 -3
package/dist/swell.cjs
CHANGED
|
@@ -6150,6 +6150,10 @@ class AppComponent {
|
|
|
6150
6150
|
}
|
|
6151
6151
|
}
|
|
6152
6152
|
|
|
6153
|
+
/**
|
|
6154
|
+
* @deprecated Payment components are mounted by `@swell/apps-sdk/components`
|
|
6155
|
+
* at the host place `checkout.payment.methods`.
|
|
6156
|
+
*/
|
|
6153
6157
|
class AppPaymentComponent extends AppComponent {
|
|
6154
6158
|
constructor(app, component, api, options) {
|
|
6155
6159
|
super(app, component, api, options);
|
|
@@ -6199,6 +6203,7 @@ class AppPaymentComponent extends AppComponent {
|
|
|
6199
6203
|
}
|
|
6200
6204
|
}
|
|
6201
6205
|
|
|
6206
|
+
/** @deprecated Use `@swell/apps-sdk/components` instead. */
|
|
6202
6207
|
class App {
|
|
6203
6208
|
constructor(app, api, options) {
|
|
6204
6209
|
this.app = app;
|
|
@@ -6307,6 +6312,13 @@ class App {
|
|
|
6307
6312
|
}
|
|
6308
6313
|
}
|
|
6309
6314
|
|
|
6315
|
+
let deprecationWarned = false;
|
|
6316
|
+
|
|
6317
|
+
/**
|
|
6318
|
+
* @deprecated App components are rendered by `@swell/apps-sdk/components`
|
|
6319
|
+
* (`createComponents().mount()`); this module will be removed in the next
|
|
6320
|
+
* major version.
|
|
6321
|
+
*/
|
|
6310
6322
|
class AppController {
|
|
6311
6323
|
#apps = new Map();
|
|
6312
6324
|
|
|
@@ -6315,7 +6327,15 @@ class AppController {
|
|
|
6315
6327
|
this.options = options;
|
|
6316
6328
|
}
|
|
6317
6329
|
|
|
6330
|
+
/** @deprecated Use `@swell/apps-sdk/components` instead. */
|
|
6318
6331
|
async load(appId) {
|
|
6332
|
+
if (!deprecationWarned) {
|
|
6333
|
+
deprecationWarned = true;
|
|
6334
|
+
console.warn(
|
|
6335
|
+
'swell.app is deprecated: render app components with @swell/apps-sdk/components',
|
|
6336
|
+
);
|
|
6337
|
+
}
|
|
6338
|
+
|
|
6319
6339
|
const loadedApp = this.#apps.get(appId);
|
|
6320
6340
|
|
|
6321
6341
|
if (loadedApp) {
|
|
@@ -7440,25 +7460,184 @@ async function createPaymentMethod(stripe, cardElement, cart) {
|
|
|
7440
7460
|
billing_details: billingDetails,
|
|
7441
7461
|
});
|
|
7442
7462
|
|
|
7443
|
-
return error
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7463
|
+
return error ? { error } : mapCardPaymentMethod(paymentMethod);
|
|
7464
|
+
}
|
|
7465
|
+
|
|
7466
|
+
/**
|
|
7467
|
+
* Elements group options of the card Payment Element for the cart amount,
|
|
7468
|
+
* matching the intent StripeCardPayment#_createIntent creates.
|
|
7469
|
+
*
|
|
7470
|
+
* @param {Cart} cart
|
|
7471
|
+
* @returns {import('@stripe/stripe-js').StripeElementsOptionsMode}
|
|
7472
|
+
*/
|
|
7473
|
+
function getCardElementsOptions(cart) {
|
|
7474
|
+
const { capture_total, auth_total } = cart;
|
|
7475
|
+
const currency = cart.currency || 'USD';
|
|
7476
|
+
const stripeCurrency = currency.toLowerCase();
|
|
7477
|
+
|
|
7478
|
+
// the card is only stored when the amount is not chargeable, no intent is created
|
|
7479
|
+
if (!isStripeChargeableAmount(capture_total, cart.currency)) {
|
|
7480
|
+
return {
|
|
7481
|
+
mode: 'setup',
|
|
7482
|
+
currency: stripeCurrency,
|
|
7483
|
+
paymentMethodTypes: ['card'],
|
|
7484
|
+
};
|
|
7485
|
+
}
|
|
7486
|
+
|
|
7487
|
+
return {
|
|
7488
|
+
mode: 'payment',
|
|
7489
|
+
amount: stripeAmountByCurrency(currency, capture_total + (auth_total || 0)),
|
|
7490
|
+
currency: stripeCurrency,
|
|
7491
|
+
captureMethod: 'manual',
|
|
7492
|
+
setupFutureUsage: 'off_session',
|
|
7493
|
+
paymentMethodTypes: ['card'],
|
|
7494
|
+
};
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
/**
|
|
7498
|
+
* Elements group options of the iDEAL Payment Element,
|
|
7499
|
+
* matching the intent StripeIDealPayment#_createIntent creates.
|
|
7500
|
+
*
|
|
7501
|
+
* @param {Cart} cart
|
|
7502
|
+
* @returns {import('@stripe/stripe-js').StripeElementsOptionsMode}
|
|
7503
|
+
*/
|
|
7504
|
+
function getIDealElementsOptions(cart) {
|
|
7505
|
+
const { capture_total } = cart;
|
|
7506
|
+
const currency = cart.currency || 'EUR';
|
|
7507
|
+
|
|
7508
|
+
return {
|
|
7509
|
+
mode: 'payment',
|
|
7510
|
+
amount: stripeAmountByCurrency(currency, capture_total),
|
|
7511
|
+
currency: currency.toLowerCase(),
|
|
7512
|
+
paymentMethodTypes: ['ideal'],
|
|
7513
|
+
};
|
|
7514
|
+
}
|
|
7515
|
+
|
|
7516
|
+
/**
|
|
7517
|
+
* Creates an Elements group without an intent and mounts the Payment Element
|
|
7518
|
+
* into `#${params.elementId || 'payment-element'}`.
|
|
7519
|
+
*
|
|
7520
|
+
* @param {Stripe} stripe
|
|
7521
|
+
* @param {import('@stripe/stripe-js').StripeElementsOptionsMode} elementsOptions
|
|
7522
|
+
* @param {object} params
|
|
7523
|
+
* @returns {import('@stripe/stripe-js').StripeElements}
|
|
7524
|
+
*/
|
|
7525
|
+
function createPaymentElements(stripe, elementsOptions, params) {
|
|
7526
|
+
const elements = stripe.elements({
|
|
7527
|
+
...params.config,
|
|
7528
|
+
...elementsOptions,
|
|
7529
|
+
// lets stripe.createPaymentMethod read the Payment Element
|
|
7530
|
+
paymentMethodCreation: 'manual',
|
|
7531
|
+
...(params.appearance && { appearance: params.appearance }),
|
|
7532
|
+
});
|
|
7533
|
+
|
|
7534
|
+
createElement('payment', elements, {
|
|
7535
|
+
...params,
|
|
7536
|
+
options: {
|
|
7537
|
+
layout: 'tabs',
|
|
7538
|
+
fields: { billingDetails: 'never' },
|
|
7539
|
+
// wallets are separate express buttons
|
|
7540
|
+
wallets: { applePay: 'never', googlePay: 'never' },
|
|
7541
|
+
...params.options,
|
|
7542
|
+
},
|
|
7543
|
+
});
|
|
7544
|
+
|
|
7545
|
+
return elements;
|
|
7546
|
+
}
|
|
7547
|
+
|
|
7548
|
+
/**
|
|
7549
|
+
* Creates a PaymentMethod from the Payment Element.
|
|
7550
|
+
* Brings the Elements amount up to date with the cart first.
|
|
7551
|
+
*
|
|
7552
|
+
* @param {Stripe} stripe
|
|
7553
|
+
* @param {import('@stripe/stripe-js').StripeElements} elements
|
|
7554
|
+
* @param {import('@stripe/stripe-js').StripeElementsOptionsMode} elementsOptions
|
|
7555
|
+
* @param {Cart} cart
|
|
7556
|
+
* @returns {Promise<import('@stripe/stripe-js').PaymentMethodResult>}
|
|
7557
|
+
*/
|
|
7558
|
+
async function createPaymentMethodFromElements(
|
|
7559
|
+
stripe,
|
|
7560
|
+
elements,
|
|
7561
|
+
elementsOptions,
|
|
7562
|
+
cart,
|
|
7563
|
+
) {
|
|
7564
|
+
// setup mode stores the card without an intent, there is no amount to update
|
|
7565
|
+
if (elementsOptions.mode === 'payment') {
|
|
7566
|
+
await elements.update(elementsOptions);
|
|
7567
|
+
}
|
|
7568
|
+
|
|
7569
|
+
const { error } = await elements.submit();
|
|
7570
|
+
|
|
7571
|
+
if (error) {
|
|
7572
|
+
return { error };
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7575
|
+
const { address, ...details } = getBillingDetails(cart);
|
|
7576
|
+
|
|
7577
|
+
// fields the Payment Element does not collect must all be passed, null when unknown
|
|
7578
|
+
return stripe.createPaymentMethod({
|
|
7579
|
+
elements,
|
|
7580
|
+
params: {
|
|
7581
|
+
billing_details: {
|
|
7582
|
+
name: null,
|
|
7583
|
+
email: null,
|
|
7584
|
+
phone: null,
|
|
7585
|
+
...details,
|
|
7586
|
+
address: {
|
|
7587
|
+
line1: null,
|
|
7588
|
+
line2: null,
|
|
7589
|
+
city: null,
|
|
7590
|
+
state: null,
|
|
7591
|
+
postal_code: null,
|
|
7592
|
+
country: null,
|
|
7593
|
+
...address,
|
|
7594
|
+
},
|
|
7595
|
+
},
|
|
7596
|
+
},
|
|
7597
|
+
});
|
|
7598
|
+
}
|
|
7599
|
+
|
|
7600
|
+
/**
|
|
7601
|
+
* @param {Stripe} stripe
|
|
7602
|
+
* @param {import('@stripe/stripe-js').StripeElements} elements
|
|
7603
|
+
* @param {Cart} cart
|
|
7604
|
+
*/
|
|
7605
|
+
async function createCardPaymentMethodFromElements(
|
|
7606
|
+
stripe,
|
|
7607
|
+
elements,
|
|
7608
|
+
cart,
|
|
7609
|
+
) {
|
|
7610
|
+
const { paymentMethod, error } = await createPaymentMethodFromElements(
|
|
7611
|
+
stripe,
|
|
7612
|
+
elements,
|
|
7613
|
+
getCardElementsOptions(cart),
|
|
7614
|
+
cart,
|
|
7615
|
+
);
|
|
7616
|
+
|
|
7617
|
+
return error ? { error } : mapCardPaymentMethod(paymentMethod);
|
|
7618
|
+
}
|
|
7619
|
+
|
|
7620
|
+
/**
|
|
7621
|
+
* @param {import('@stripe/stripe-js').PaymentMethod} paymentMethod
|
|
7622
|
+
*/
|
|
7623
|
+
function mapCardPaymentMethod(paymentMethod) {
|
|
7624
|
+
return {
|
|
7625
|
+
gateway: 'stripe',
|
|
7626
|
+
token: paymentMethod.id,
|
|
7627
|
+
last4: paymentMethod.card.last4,
|
|
7628
|
+
exp_month: paymentMethod.card.exp_month,
|
|
7629
|
+
exp_year: paymentMethod.card.exp_year,
|
|
7630
|
+
brand: paymentMethod.card.brand,
|
|
7631
|
+
display_brand: paymentMethod.card.display_brand,
|
|
7632
|
+
address_check: paymentMethod.card.checks.address_line1_check,
|
|
7633
|
+
cvc_check: paymentMethod.card.checks.cvc_check,
|
|
7634
|
+
zip_check: paymentMethod.card.checks.address_postal_code_check,
|
|
7635
|
+
};
|
|
7457
7636
|
}
|
|
7458
7637
|
|
|
7459
7638
|
/**
|
|
7460
7639
|
* @param {Stripe} stripe
|
|
7461
|
-
* @param {import('@stripe/stripe-js').StripeIdealBankElement} element
|
|
7640
|
+
* @param {import('@stripe/stripe-js').StripeIdealBankElement | {}} element `{}` when there is no idealBank element
|
|
7462
7641
|
* @param {Cart} cart
|
|
7463
7642
|
*/
|
|
7464
7643
|
async function createIDealPaymentMethod(stripe, element, cart) {
|
|
@@ -7646,6 +7825,29 @@ function isStripeChargeableAmount(amount, currency) {
|
|
|
7646
7825
|
return !minAmount || amount >= minAmount;
|
|
7647
7826
|
}
|
|
7648
7827
|
|
|
7828
|
+
// The cart keeps what checkout v1 keeps, so the next tokenize can retrieve and reuse
|
|
7829
|
+
// the intent; the vault cancels the replaced ones when the order is paid
|
|
7830
|
+
const cartIntent = (cart, intent) => {
|
|
7831
|
+
const exIntent = cart.billing?.intent?.stripe;
|
|
7832
|
+
|
|
7833
|
+
return {
|
|
7834
|
+
id: intent.id,
|
|
7835
|
+
client_secret: intent.client_secret,
|
|
7836
|
+
amount: intent.amount,
|
|
7837
|
+
currency: intent.currency,
|
|
7838
|
+
...(Boolean(cart.auth_total) && { auth_amount: cart.auth_total }),
|
|
7839
|
+
...(exIntent?.id &&
|
|
7840
|
+
exIntent.id !== intent.id && {
|
|
7841
|
+
canceled: [...(exIntent.canceled || []), exIntent.id],
|
|
7842
|
+
}),
|
|
7843
|
+
};
|
|
7844
|
+
};
|
|
7845
|
+
|
|
7846
|
+
const intentAmount = ({ currency, capture_total, auth_total }) =>
|
|
7847
|
+
stripeAmountByCurrency(currency, capture_total + auth_total);
|
|
7848
|
+
|
|
7849
|
+
const intentCurrency = ({ currency }) => (currency || 'USD').toLowerCase();
|
|
7850
|
+
|
|
7649
7851
|
/** @typedef {import('@stripe/stripe-js').Stripe} Stripe */
|
|
7650
7852
|
/** @typedef {import('@stripe/stripe-js').StripeCardElement} StripeCardElement */
|
|
7651
7853
|
/** @typedef {import('@stripe/stripe-js').StripeCardNumberElement} StripeCardNumberElement */
|
|
@@ -7689,9 +7891,30 @@ class StripeCardPayment extends Payment {
|
|
|
7689
7891
|
StripeCardPayment.stripeElement = stripeElement;
|
|
7690
7892
|
}
|
|
7691
7893
|
|
|
7692
|
-
|
|
7894
|
+
/** @returns {import('@stripe/stripe-js').StripeElements} */
|
|
7895
|
+
get stripeElements() {
|
|
7896
|
+
return StripeCardPayment.stripeElements;
|
|
7897
|
+
}
|
|
7898
|
+
|
|
7899
|
+
/** @param {import('@stripe/stripe-js').StripeElements} stripeElements */
|
|
7900
|
+
set stripeElements(stripeElements) {
|
|
7901
|
+
StripeCardPayment.stripeElements = stripeElements;
|
|
7902
|
+
}
|
|
7903
|
+
|
|
7904
|
+
/** @param {import('../../../types').Cart} cart */
|
|
7905
|
+
async createElements(cart) {
|
|
7693
7906
|
await this.loadScripts(this.scripts);
|
|
7694
7907
|
|
|
7908
|
+
if (this.params.paymentElement) {
|
|
7909
|
+
this.stripeElements = createPaymentElements(
|
|
7910
|
+
this.stripe,
|
|
7911
|
+
getCardElementsOptions(cart),
|
|
7912
|
+
this.params,
|
|
7913
|
+
);
|
|
7914
|
+
|
|
7915
|
+
return;
|
|
7916
|
+
}
|
|
7917
|
+
|
|
7695
7918
|
const elements = this.stripe.elements(this.params.config);
|
|
7696
7919
|
|
|
7697
7920
|
if (this.params.separateElements) {
|
|
@@ -7704,18 +7927,26 @@ class StripeCardPayment extends Payment {
|
|
|
7704
7927
|
}
|
|
7705
7928
|
|
|
7706
7929
|
async tokenize() {
|
|
7707
|
-
|
|
7930
|
+
const { paymentElement, paymentMethod: token } = this.params;
|
|
7931
|
+
|
|
7932
|
+
if (token) {
|
|
7933
|
+
return this._authorizePaymentMethod(token);
|
|
7934
|
+
}
|
|
7935
|
+
|
|
7936
|
+
if (!(paymentElement ? this.stripeElements : this.stripeElement)) {
|
|
7708
7937
|
throw new Error('Stripe payment element is not defined');
|
|
7709
7938
|
}
|
|
7710
7939
|
|
|
7711
7940
|
await this.loadScripts(this.scripts);
|
|
7712
7941
|
|
|
7713
7942
|
const cart = await this.getCart();
|
|
7714
|
-
const paymentMethod =
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7943
|
+
const paymentMethod = paymentElement
|
|
7944
|
+
? await createCardPaymentMethodFromElements(
|
|
7945
|
+
this.stripe,
|
|
7946
|
+
this.stripeElements,
|
|
7947
|
+
cart,
|
|
7948
|
+
)
|
|
7949
|
+
: await createPaymentMethod(this.stripe, this.stripeElement, cart);
|
|
7719
7950
|
|
|
7720
7951
|
if (paymentMethod.error) {
|
|
7721
7952
|
throw new Error(paymentMethod.error.message);
|
|
@@ -7725,8 +7956,12 @@ class StripeCardPayment extends Payment {
|
|
|
7725
7956
|
paymentMethod.test = true;
|
|
7726
7957
|
}
|
|
7727
7958
|
|
|
7728
|
-
// should save payment method data when payment amount is not chargeable
|
|
7729
|
-
|
|
7959
|
+
// should save payment method data when payment amount is not chargeable,
|
|
7960
|
+
// or when the host collects the card without authorizing it now
|
|
7961
|
+
if (
|
|
7962
|
+
!isStripeChargeableAmount(cart.capture_total, cart.currency) ||
|
|
7963
|
+
this.params.createIntent === false
|
|
7964
|
+
) {
|
|
7730
7965
|
await this.updateCart({
|
|
7731
7966
|
billing: {
|
|
7732
7967
|
method: 'card',
|
|
@@ -7737,60 +7972,129 @@ class StripeCardPayment extends Payment {
|
|
|
7737
7972
|
return this.onSuccess();
|
|
7738
7973
|
}
|
|
7739
7974
|
|
|
7740
|
-
const intent = await this.
|
|
7975
|
+
const intent = await this._authorizeIntent(cart, paymentMethod.token);
|
|
7741
7976
|
|
|
7742
7977
|
await this.updateCart({
|
|
7743
7978
|
billing: {
|
|
7744
7979
|
method: 'card',
|
|
7745
7980
|
card: paymentMethod,
|
|
7746
|
-
intent: {
|
|
7747
|
-
stripe: {
|
|
7748
|
-
id: intent.id,
|
|
7749
|
-
...(Boolean(cart.auth_total) && {
|
|
7750
|
-
auth_amount: cart.auth_total,
|
|
7751
|
-
}),
|
|
7752
|
-
},
|
|
7753
|
-
},
|
|
7981
|
+
intent: { stripe: cartIntent(cart, intent) },
|
|
7754
7982
|
},
|
|
7755
7983
|
});
|
|
7756
7984
|
|
|
7757
7985
|
this.onSuccess();
|
|
7758
7986
|
}
|
|
7759
7987
|
|
|
7760
|
-
|
|
7761
|
-
|
|
7988
|
+
// A card the shopper already has (a saved card, a wallet's payment method) gets
|
|
7989
|
+
// the intent and 3-D Secure of a new one; other cards are authorized with the order
|
|
7990
|
+
async _authorizePaymentMethod(token) {
|
|
7991
|
+
const cart = await this.getCart();
|
|
7762
7992
|
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7993
|
+
if (
|
|
7994
|
+
token.startsWith('pm_') &&
|
|
7995
|
+
isStripeChargeableAmount(cart.capture_total, cart.currency) &&
|
|
7996
|
+
this.params.createIntent !== false
|
|
7997
|
+
) {
|
|
7998
|
+
await this.loadScripts(this.scripts);
|
|
7767
7999
|
|
|
7768
|
-
|
|
7769
|
-
|
|
8000
|
+
const intent = await this._authorizeIntent(cart, token);
|
|
8001
|
+
|
|
8002
|
+
if (intent.id !== cart.billing?.intent?.stripe?.id) {
|
|
8003
|
+
await this.updateCart({
|
|
8004
|
+
billing: {
|
|
8005
|
+
method: 'card',
|
|
8006
|
+
intent: { stripe: cartIntent(cart, intent) },
|
|
8007
|
+
},
|
|
8008
|
+
});
|
|
8009
|
+
}
|
|
7770
8010
|
}
|
|
7771
8011
|
|
|
7772
|
-
|
|
8012
|
+
this.onSuccess();
|
|
8013
|
+
}
|
|
8014
|
+
|
|
8015
|
+
// Reuses the cart's intent while Stripe still has it for this card and amount
|
|
8016
|
+
async _authorizeIntent(cart, token) {
|
|
8017
|
+
const exIntent = cart.billing?.intent?.stripe;
|
|
8018
|
+
const intent =
|
|
8019
|
+
(exIntent?.client_secret &&
|
|
8020
|
+
(await this._retrieveIntent(cart, exIntent, token))) ||
|
|
8021
|
+
(await this._createIntent(cart, token));
|
|
7773
8022
|
|
|
8023
|
+
switch (intent.status) {
|
|
8024
|
+
case 'requires_capture':
|
|
8025
|
+
break;
|
|
8026
|
+
|
|
8027
|
+
case 'requires_confirmation':
|
|
8028
|
+
await this._confirmCardPayment(intent);
|
|
8029
|
+
break;
|
|
8030
|
+
|
|
8031
|
+
case 'requires_action':
|
|
8032
|
+
case 'requires_source_action': {
|
|
8033
|
+
const actionResult = await this.stripe.handleCardAction(
|
|
8034
|
+
intent.client_secret,
|
|
8035
|
+
);
|
|
8036
|
+
|
|
8037
|
+
if (actionResult.error) {
|
|
8038
|
+
throw new Error(actionResult.error.message);
|
|
8039
|
+
}
|
|
8040
|
+
|
|
8041
|
+
break;
|
|
8042
|
+
}
|
|
8043
|
+
|
|
8044
|
+
default:
|
|
8045
|
+
throw new Error(`Unsupported intent status: ${intent.status}`);
|
|
8046
|
+
}
|
|
8047
|
+
|
|
8048
|
+
return intent;
|
|
8049
|
+
}
|
|
8050
|
+
|
|
8051
|
+
async _retrieveIntent(cart, exIntent, token) {
|
|
8052
|
+
const { paymentIntent: intent, error } =
|
|
8053
|
+
await this.stripe.retrievePaymentIntent(exIntent.client_secret);
|
|
8054
|
+
|
|
8055
|
+
const matches =
|
|
8056
|
+
!error &&
|
|
8057
|
+
intent.amount === intentAmount(cart) &&
|
|
8058
|
+
intent.currency === intentCurrency(cart) &&
|
|
8059
|
+
intent.payment_method === token &&
|
|
8060
|
+
!['canceled', 'requires_source', 'requires_payment_method'].includes(
|
|
8061
|
+
intent.status,
|
|
8062
|
+
);
|
|
8063
|
+
|
|
8064
|
+
return matches ? intent : null;
|
|
8065
|
+
}
|
|
8066
|
+
|
|
8067
|
+
async authenticate(payment) {
|
|
8068
|
+
const { transaction_id: id, card: { token } = {} } = payment;
|
|
8069
|
+
|
|
8070
|
+
// The payment is re-queried whatever happens here: an intent the shopper
|
|
8071
|
+
// already confirmed can't be updated, and the payment must still learn it
|
|
7774
8072
|
try {
|
|
7775
|
-
const
|
|
8073
|
+
const intent = await this.updateIntent({
|
|
8074
|
+
gateway: 'stripe',
|
|
8075
|
+
intent: { id, payment_method: token },
|
|
8076
|
+
});
|
|
7776
8077
|
|
|
7777
|
-
|
|
8078
|
+
if (intent.error) {
|
|
8079
|
+
throw new Error(intent.error.message);
|
|
8080
|
+
}
|
|
8081
|
+
|
|
8082
|
+
await this.loadScripts(this.scripts);
|
|
8083
|
+
|
|
8084
|
+
return await this._confirmCardPayment(intent);
|
|
7778
8085
|
} finally {
|
|
7779
8086
|
await this.resetAsyncPayment(payment.id);
|
|
7780
8087
|
}
|
|
7781
8088
|
}
|
|
7782
8089
|
|
|
7783
|
-
async _createIntent(cart,
|
|
7784
|
-
const
|
|
7785
|
-
const stripeCustomer = account && account.stripe_customer;
|
|
7786
|
-
const stripeCurrency = (currency || 'USD').toLowerCase();
|
|
7787
|
-
const amount = stripeAmountByCurrency(currency, capture_total + auth_total);
|
|
8090
|
+
async _createIntent(cart, token) {
|
|
8091
|
+
const stripeCustomer = cart.account && cart.account.stripe_customer;
|
|
7788
8092
|
const intent = await this.createIntent({
|
|
7789
8093
|
gateway: 'stripe',
|
|
7790
8094
|
intent: {
|
|
7791
|
-
amount,
|
|
7792
|
-
currency:
|
|
7793
|
-
payment_method:
|
|
8095
|
+
amount: intentAmount(cart),
|
|
8096
|
+
currency: intentCurrency(cart),
|
|
8097
|
+
payment_method: token,
|
|
7794
8098
|
capture_method: 'manual',
|
|
7795
8099
|
setup_future_usage: 'off_session',
|
|
7796
8100
|
...(stripeCustomer ? { customer: stripeCustomer } : {}),
|
|
@@ -7801,20 +8105,6 @@ class StripeCardPayment extends Payment {
|
|
|
7801
8105
|
throw new Error('Stripe payment intent is not defined');
|
|
7802
8106
|
}
|
|
7803
8107
|
|
|
7804
|
-
switch (intent.status) {
|
|
7805
|
-
case 'requires_capture':
|
|
7806
|
-
case 'requires_confirmation':
|
|
7807
|
-
break;
|
|
7808
|
-
|
|
7809
|
-
default:
|
|
7810
|
-
throw new Error(`Unsupported intent status: ${intent.status}`);
|
|
7811
|
-
}
|
|
7812
|
-
|
|
7813
|
-
// Confirm the payment intent
|
|
7814
|
-
if (intent.status === 'requires_confirmation') {
|
|
7815
|
-
await this._confirmCardPayment(intent);
|
|
7816
|
-
}
|
|
7817
|
-
|
|
7818
8108
|
return intent;
|
|
7819
8109
|
}
|
|
7820
8110
|
|
|
@@ -7882,24 +8172,58 @@ class StripeIDealPayment extends Payment {
|
|
|
7882
8172
|
StripeIDealPayment.stripeElement = stripeElement;
|
|
7883
8173
|
}
|
|
7884
8174
|
|
|
7885
|
-
|
|
8175
|
+
/** @returns {import('@stripe/stripe-js').StripeElements} */
|
|
8176
|
+
get stripeElements() {
|
|
8177
|
+
return StripeIDealPayment.stripeElements;
|
|
8178
|
+
}
|
|
8179
|
+
|
|
8180
|
+
/** @param {import('@stripe/stripe-js').StripeElements} stripeElements */
|
|
8181
|
+
set stripeElements(stripeElements) {
|
|
8182
|
+
StripeIDealPayment.stripeElements = stripeElements;
|
|
8183
|
+
}
|
|
8184
|
+
|
|
8185
|
+
/** @param {import('../../../types').Cart} cart */
|
|
8186
|
+
async createElements(cart) {
|
|
7886
8187
|
await this.loadScripts(this.scripts);
|
|
7887
8188
|
|
|
8189
|
+
if (this.params.paymentElement) {
|
|
8190
|
+
this.stripeElements = createPaymentElements(
|
|
8191
|
+
this.stripe,
|
|
8192
|
+
getIDealElementsOptions(cart),
|
|
8193
|
+
this.params,
|
|
8194
|
+
);
|
|
8195
|
+
|
|
8196
|
+
return;
|
|
8197
|
+
}
|
|
8198
|
+
|
|
7888
8199
|
const elements = this.stripe.elements(this.params.config);
|
|
7889
8200
|
|
|
7890
8201
|
this.stripeElement = createElement('idealBank', elements, this.params);
|
|
7891
8202
|
}
|
|
7892
8203
|
|
|
7893
8204
|
async tokenize() {
|
|
7894
|
-
|
|
8205
|
+
const { paymentElement } = this.params;
|
|
8206
|
+
|
|
8207
|
+
if (paymentElement && !this.stripeElements) {
|
|
7895
8208
|
throw new Error('Stripe payment element is not defined');
|
|
7896
8209
|
}
|
|
7897
8210
|
|
|
7898
8211
|
await this.loadScripts(this.scripts);
|
|
7899
8212
|
|
|
7900
8213
|
const cart = await this.getCart();
|
|
7901
|
-
const { paymentMethod, error: paymentMethodError } =
|
|
7902
|
-
await
|
|
8214
|
+
const { paymentMethod, error: paymentMethodError } = paymentElement
|
|
8215
|
+
? await createPaymentMethodFromElements(
|
|
8216
|
+
this.stripe,
|
|
8217
|
+
this.stripeElements,
|
|
8218
|
+
getIDealElementsOptions(cart),
|
|
8219
|
+
cart,
|
|
8220
|
+
)
|
|
8221
|
+
: // without an idealBank element the shopper picks the bank on the iDEAL page
|
|
8222
|
+
await createIDealPaymentMethod(
|
|
8223
|
+
this.stripe,
|
|
8224
|
+
this.stripeElement || {},
|
|
8225
|
+
cart,
|
|
8226
|
+
);
|
|
7903
8227
|
|
|
7904
8228
|
if (paymentMethodError) {
|
|
7905
8229
|
throw new Error(paymentMethodError.message);
|
|
@@ -7910,6 +8234,38 @@ class StripeIDealPayment extends Payment {
|
|
|
7910
8234
|
await this.stripe.handleCardAction(intent.client_secret);
|
|
7911
8235
|
}
|
|
7912
8236
|
|
|
8237
|
+
async handleRedirect(queryParams) {
|
|
8238
|
+
const { redirect_status, payment_intent_client_secret } = queryParams;
|
|
8239
|
+
|
|
8240
|
+
if (redirect_status !== 'succeeded') {
|
|
8241
|
+
throw new UnableAuthenticatePaymentMethodError();
|
|
8242
|
+
}
|
|
8243
|
+
|
|
8244
|
+
await this.loadScripts(this.scripts);
|
|
8245
|
+
|
|
8246
|
+
const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(
|
|
8247
|
+
payment_intent_client_secret,
|
|
8248
|
+
);
|
|
8249
|
+
|
|
8250
|
+
if (error) {
|
|
8251
|
+
throw new Error(error.message);
|
|
8252
|
+
}
|
|
8253
|
+
|
|
8254
|
+
if (
|
|
8255
|
+
!['succeeded', 'requires_capture', 'processing'].includes(
|
|
8256
|
+
paymentIntent.status,
|
|
8257
|
+
)
|
|
8258
|
+
) {
|
|
8259
|
+
throw new Error(
|
|
8260
|
+
paymentIntent.last_payment_error?.message ||
|
|
8261
|
+
`Unsupported intent status (${paymentIntent.status})`,
|
|
8262
|
+
);
|
|
8263
|
+
}
|
|
8264
|
+
|
|
8265
|
+
// the cart's billing was written in `tokenize`
|
|
8266
|
+
this.onSuccess();
|
|
8267
|
+
}
|
|
8268
|
+
|
|
7913
8269
|
/**
|
|
7914
8270
|
* @param {object} cart
|
|
7915
8271
|
* @param {import('@stripe/stripe-js').PaymentMethod} paymentMethod
|
|
@@ -7927,7 +8283,8 @@ class StripeIDealPayment extends Payment {
|
|
|
7927
8283
|
payment_method_types: 'ideal',
|
|
7928
8284
|
confirmation_method: 'manual',
|
|
7929
8285
|
confirm: true,
|
|
7930
|
-
|
|
8286
|
+
// `gateway` lets `handleRedirect` run on return; Stripe appends the intent and `redirect_status`
|
|
8287
|
+
return_url: getReturnUrl(),
|
|
7931
8288
|
},
|
|
7932
8289
|
});
|
|
7933
8290
|
|
|
@@ -7959,6 +8316,14 @@ class StripeIDealPayment extends Payment {
|
|
|
7959
8316
|
}
|
|
7960
8317
|
}
|
|
7961
8318
|
|
|
8319
|
+
function getReturnUrl() {
|
|
8320
|
+
const url = new URL(window.location.href);
|
|
8321
|
+
|
|
8322
|
+
url.searchParams.set('gateway', 'stripe');
|
|
8323
|
+
|
|
8324
|
+
return url.toString();
|
|
8325
|
+
}
|
|
8326
|
+
|
|
7962
8327
|
/** @typedef {import('@stripe/stripe-js').Stripe} Stripe */
|
|
7963
8328
|
|
|
7964
8329
|
class StripeBancontactPayment extends Payment {
|
|
@@ -8278,6 +8643,7 @@ class StripeGooglePayment extends Payment {
|
|
|
8278
8643
|
async _onPaymentMethod(event) {
|
|
8279
8644
|
const {
|
|
8280
8645
|
payerEmail,
|
|
8646
|
+
payerName,
|
|
8281
8647
|
paymentMethod: { id: paymentMethod, card, billing_details },
|
|
8282
8648
|
shippingAddress,
|
|
8283
8649
|
shippingOption,
|
|
@@ -8296,6 +8662,8 @@ class StripeGooglePayment extends Payment {
|
|
|
8296
8662
|
}),
|
|
8297
8663
|
billing: {
|
|
8298
8664
|
...this._mapBillingAddress(billing_details),
|
|
8665
|
+
// a wallet may leave the card's address without a name; the payer's, when asked for, fills it
|
|
8666
|
+
name: billing_details?.name || payerName,
|
|
8299
8667
|
method: 'card',
|
|
8300
8668
|
card: {
|
|
8301
8669
|
gateway: 'stripe',
|
|
@@ -8534,6 +8902,7 @@ class StripeApplePayment extends Payment {
|
|
|
8534
8902
|
async _onPaymentMethod(event) {
|
|
8535
8903
|
const {
|
|
8536
8904
|
payerEmail,
|
|
8905
|
+
payerName,
|
|
8537
8906
|
paymentMethod: { id: paymentMethod, card, billing_details },
|
|
8538
8907
|
shippingAddress,
|
|
8539
8908
|
shippingOption,
|
|
@@ -8552,6 +8921,8 @@ class StripeApplePayment extends Payment {
|
|
|
8552
8921
|
}),
|
|
8553
8922
|
billing: {
|
|
8554
8923
|
...this._mapBillingAddress(billing_details),
|
|
8924
|
+
// a wallet may leave the card's address without a name; the payer's, when asked for, fills it
|
|
8925
|
+
name: billing_details?.name || payerName,
|
|
8555
8926
|
method: 'card',
|
|
8556
8927
|
card: {
|
|
8557
8928
|
gateway: 'stripe',
|
|
@@ -10982,6 +11353,238 @@ class QuickpayCardPayment extends Payment {
|
|
|
10982
11353
|
}
|
|
10983
11354
|
}
|
|
10984
11355
|
|
|
11356
|
+
// Saferpay payment-page brands per Swell method id; a list is joined because the
|
|
11357
|
+
// vault rejects arrays in `PaymentMethods`
|
|
11358
|
+
const PAYMENT_METHODS = {
|
|
11359
|
+
card: [
|
|
11360
|
+
'AMEX',
|
|
11361
|
+
'DINERS',
|
|
11362
|
+
'MAESTRO',
|
|
11363
|
+
'JCB',
|
|
11364
|
+
'VISA',
|
|
11365
|
+
'BANCONTACT',
|
|
11366
|
+
'MASTERCARD',
|
|
11367
|
+
'SAFERPAYTEST',
|
|
11368
|
+
'UNIONPAY',
|
|
11369
|
+
].join(','),
|
|
11370
|
+
twint: 'TWINT',
|
|
11371
|
+
};
|
|
11372
|
+
|
|
11373
|
+
/**
|
|
11374
|
+
* @param {string} method 'card' or 'twint'
|
|
11375
|
+
* @returns {string}
|
|
11376
|
+
*/
|
|
11377
|
+
function getSaferpayPaymentMethods(method) {
|
|
11378
|
+
return PAYMENT_METHODS[method];
|
|
11379
|
+
}
|
|
11380
|
+
|
|
11381
|
+
/**
|
|
11382
|
+
* The Saferpay payment page request for a cart.
|
|
11383
|
+
*
|
|
11384
|
+
* @param {object} cart
|
|
11385
|
+
* @param {string} method 'card' or 'twint'
|
|
11386
|
+
* @returns {object}
|
|
11387
|
+
*/
|
|
11388
|
+
function getSaferpayPaymentPageData(cart, method) {
|
|
11389
|
+
const currency = (cart.currency || 'USD').toUpperCase();
|
|
11390
|
+
const returnUrl = window.location.origin + window.location.pathname;
|
|
11391
|
+
|
|
11392
|
+
return {
|
|
11393
|
+
Payment: {
|
|
11394
|
+
Amount: {
|
|
11395
|
+
Value: amountInCents(currency, cart.grand_total || 0),
|
|
11396
|
+
CurrencyCode: currency,
|
|
11397
|
+
},
|
|
11398
|
+
Description: 'Saferpay payment powered by Swell',
|
|
11399
|
+
},
|
|
11400
|
+
PaymentMethods: getSaferpayPaymentMethods(method),
|
|
11401
|
+
ReturnUrls: {
|
|
11402
|
+
Success: `${returnUrl}?gateway=saferpay&redirect_status=succeeded`,
|
|
11403
|
+
Fail: `${returnUrl}?gateway=saferpay&redirect_status=failed`,
|
|
11404
|
+
Abort: `${returnUrl}?gateway=saferpay&redirect_status=canceled`,
|
|
11405
|
+
},
|
|
11406
|
+
};
|
|
11407
|
+
}
|
|
11408
|
+
|
|
11409
|
+
/**
|
|
11410
|
+
* @param {object} state the Saferpay transaction state
|
|
11411
|
+
* @returns {boolean}
|
|
11412
|
+
*/
|
|
11413
|
+
function isSaferpayCardPayment(state) {
|
|
11414
|
+
const brand = state?.PaymentMeans?.Brand;
|
|
11415
|
+
|
|
11416
|
+
return Boolean(
|
|
11417
|
+
state?.RegistrationResult?.Success &&
|
|
11418
|
+
brand &&
|
|
11419
|
+
PAYMENT_METHODS.card.split(',').includes(brand.PaymentMethod),
|
|
11420
|
+
);
|
|
11421
|
+
}
|
|
11422
|
+
|
|
11423
|
+
/**
|
|
11424
|
+
* @param {object} state the Saferpay transaction state
|
|
11425
|
+
* @returns {boolean}
|
|
11426
|
+
*/
|
|
11427
|
+
function isSaferpayTwintPayment(state) {
|
|
11428
|
+
return state?.PaymentMeans?.Brand?.PaymentMethod === 'TWINT';
|
|
11429
|
+
}
|
|
11430
|
+
|
|
11431
|
+
/**
|
|
11432
|
+
* The card stored on the cart's billing from a Saferpay card state.
|
|
11433
|
+
*
|
|
11434
|
+
* @param {object} state
|
|
11435
|
+
* @returns {object}
|
|
11436
|
+
*/
|
|
11437
|
+
function getSaferpayCard(state) {
|
|
11438
|
+
const {
|
|
11439
|
+
RegistrationResult: {
|
|
11440
|
+
Alias: { Id: token },
|
|
11441
|
+
},
|
|
11442
|
+
PaymentMeans: {
|
|
11443
|
+
Card: { ExpMonth: exp_month, ExpYear: exp_year, MaskedNumber },
|
|
11444
|
+
Brand: { Name: brand },
|
|
11445
|
+
},
|
|
11446
|
+
} = state;
|
|
11447
|
+
|
|
11448
|
+
return {
|
|
11449
|
+
token,
|
|
11450
|
+
exp_month,
|
|
11451
|
+
exp_year,
|
|
11452
|
+
brand,
|
|
11453
|
+
last4: MaskedNumber.slice(-4),
|
|
11454
|
+
gateway: 'saferpay',
|
|
11455
|
+
};
|
|
11456
|
+
}
|
|
11457
|
+
|
|
11458
|
+
/**
|
|
11459
|
+
* @param {object} state
|
|
11460
|
+
* @returns {string | undefined}
|
|
11461
|
+
*/
|
|
11462
|
+
function getSaferpayTransactionId(state) {
|
|
11463
|
+
return state?.Transaction?.Id;
|
|
11464
|
+
}
|
|
11465
|
+
|
|
11466
|
+
/**
|
|
11467
|
+
* Saferpay payment page: `tokenize` sends the shopper to Saferpay, and
|
|
11468
|
+
* `handleRedirect` turns the returned transaction into the cart's billing.
|
|
11469
|
+
* The card and TWINT classes share it; each one handles only the redirect of
|
|
11470
|
+
* the method the cart was tokenized with, so a host can pass both.
|
|
11471
|
+
*/
|
|
11472
|
+
class SaferpayPayment extends Payment {
|
|
11473
|
+
constructor(api, options, params, methods, methodId) {
|
|
11474
|
+
super(api, options, params, methods[methodId]);
|
|
11475
|
+
this.methodId = methodId;
|
|
11476
|
+
this.cardMethod = methods.card;
|
|
11477
|
+
}
|
|
11478
|
+
|
|
11479
|
+
async tokenize() {
|
|
11480
|
+
const cart = await this.getCart();
|
|
11481
|
+
|
|
11482
|
+
// a card Saferpay already registered on this cart pays again without a new page visit
|
|
11483
|
+
if (
|
|
11484
|
+
this.methodId === 'card' &&
|
|
11485
|
+
cart.billing?.card?.gateway === 'saferpay' &&
|
|
11486
|
+
!this.params?.replace
|
|
11487
|
+
) {
|
|
11488
|
+
return this.onSuccess();
|
|
11489
|
+
}
|
|
11490
|
+
|
|
11491
|
+
const intent = await this.createIntent({
|
|
11492
|
+
gateway: 'saferpay',
|
|
11493
|
+
intent: getSaferpayPaymentPageData(cart, this.methodId),
|
|
11494
|
+
});
|
|
11495
|
+
|
|
11496
|
+
if (!intent) {
|
|
11497
|
+
throw new Error('Saferpay payment page is not defined');
|
|
11498
|
+
}
|
|
11499
|
+
|
|
11500
|
+
await this.updateCart({
|
|
11501
|
+
billing: {
|
|
11502
|
+
method: this.methodId,
|
|
11503
|
+
intent: {
|
|
11504
|
+
saferpay: {
|
|
11505
|
+
token: intent.token,
|
|
11506
|
+
},
|
|
11507
|
+
},
|
|
11508
|
+
},
|
|
11509
|
+
});
|
|
11510
|
+
|
|
11511
|
+
window.location.replace(intent.redirect_url);
|
|
11512
|
+
}
|
|
11513
|
+
|
|
11514
|
+
async handleRedirect(queryParams) {
|
|
11515
|
+
const { redirect_status: status } = queryParams;
|
|
11516
|
+
const cart = await this.getCart();
|
|
11517
|
+
|
|
11518
|
+
// another Saferpay method of this host was tokenized: its instance answers
|
|
11519
|
+
if (cart.billing?.method !== this.methodId) {
|
|
11520
|
+
return;
|
|
11521
|
+
}
|
|
11522
|
+
|
|
11523
|
+
switch (status) {
|
|
11524
|
+
case 'succeeded':
|
|
11525
|
+
return this._handleSuccessfulRedirect(cart);
|
|
11526
|
+
case 'canceled':
|
|
11527
|
+
return this.onCancel();
|
|
11528
|
+
case 'failed':
|
|
11529
|
+
throw new UnableAuthenticatePaymentMethodError();
|
|
11530
|
+
default:
|
|
11531
|
+
throw new Error(`Unknown redirect status: ${status}.`);
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11534
|
+
|
|
11535
|
+
async _handleSuccessfulRedirect(cart) {
|
|
11536
|
+
const token = cart.billing?.intent?.saferpay?.token;
|
|
11537
|
+
|
|
11538
|
+
if (!token) {
|
|
11539
|
+
throw new Error('Saferpay transaction token is not defined');
|
|
11540
|
+
}
|
|
11541
|
+
|
|
11542
|
+
const intent = await this.updateIntent({
|
|
11543
|
+
gateway: 'saferpay',
|
|
11544
|
+
intent: { token },
|
|
11545
|
+
});
|
|
11546
|
+
|
|
11547
|
+
if (!intent) {
|
|
11548
|
+
throw new Error('Saferpay transaction is not defined');
|
|
11549
|
+
}
|
|
11550
|
+
|
|
11551
|
+
const { state } = intent;
|
|
11552
|
+
const transaction = { transaction_id: getSaferpayTransactionId(state) };
|
|
11553
|
+
|
|
11554
|
+
if (isSaferpayCardPayment(state)) {
|
|
11555
|
+
const card = getSaferpayCard(state);
|
|
11556
|
+
|
|
11557
|
+
if (!isLiveMode(this.cardMethod?.mode)) {
|
|
11558
|
+
card.test = true;
|
|
11559
|
+
}
|
|
11560
|
+
|
|
11561
|
+
await this.updateCart({
|
|
11562
|
+
billing: { method: 'card', card, intent: { saferpay: transaction } },
|
|
11563
|
+
});
|
|
11564
|
+
} else if (isSaferpayTwintPayment(state)) {
|
|
11565
|
+
await this.updateCart({
|
|
11566
|
+
billing: { method: 'twint', intent: { saferpay: transaction } },
|
|
11567
|
+
});
|
|
11568
|
+
} else {
|
|
11569
|
+
throw new Error('Payment method not supported by this gateway.');
|
|
11570
|
+
}
|
|
11571
|
+
|
|
11572
|
+
this.onSuccess();
|
|
11573
|
+
}
|
|
11574
|
+
}
|
|
11575
|
+
|
|
11576
|
+
class SaferpayCardPayment extends SaferpayPayment {
|
|
11577
|
+
constructor(api, options, params, methods) {
|
|
11578
|
+
super(api, options, params, methods, 'card');
|
|
11579
|
+
}
|
|
11580
|
+
}
|
|
11581
|
+
|
|
11582
|
+
class SaferpayTwintPayment extends SaferpayPayment {
|
|
11583
|
+
constructor(api, options, params, methods) {
|
|
11584
|
+
super(api, options, params, methods, 'twint');
|
|
11585
|
+
}
|
|
11586
|
+
}
|
|
11587
|
+
|
|
10985
11588
|
let convesioSettings = null;
|
|
10986
11589
|
|
|
10987
11590
|
function getBaseConvesioApiUrl(mode) {
|
|
@@ -12662,6 +13265,33 @@ class PaymentController {
|
|
|
12662
13265
|
return this.api.request('get', '/payments', id);
|
|
12663
13266
|
}
|
|
12664
13267
|
|
|
13268
|
+
/**
|
|
13269
|
+
* Sends the shopper to the gateway when a submitted order waits for an
|
|
13270
|
+
* asynchronous payment; resolves `true` after starting the redirect.
|
|
13271
|
+
*
|
|
13272
|
+
* @param {object} order the result of `cart.submitOrder()`
|
|
13273
|
+
* @returns {Promise<boolean>}
|
|
13274
|
+
*/
|
|
13275
|
+
async handleAsyncRedirect(order) {
|
|
13276
|
+
const paymentId =
|
|
13277
|
+
order?.authorized_payment_id ?? order?.authorizedPaymentId;
|
|
13278
|
+
|
|
13279
|
+
if (order?.status !== 'payment_pending' || !paymentId) {
|
|
13280
|
+
return false;
|
|
13281
|
+
}
|
|
13282
|
+
|
|
13283
|
+
const payment = await this.get(paymentId);
|
|
13284
|
+
const url = payment?.async_data?.url ?? payment?.asyncData?.url;
|
|
13285
|
+
|
|
13286
|
+
if (!payment || payment.captured || !url) {
|
|
13287
|
+
return false;
|
|
13288
|
+
}
|
|
13289
|
+
|
|
13290
|
+
window.location.replace(url);
|
|
13291
|
+
|
|
13292
|
+
return true;
|
|
13293
|
+
}
|
|
13294
|
+
|
|
12665
13295
|
async methods() {
|
|
12666
13296
|
if (this.methodSettings) {
|
|
12667
13297
|
return this.methodSettings;
|
|
@@ -12679,7 +13309,7 @@ class PaymentController {
|
|
|
12679
13309
|
throw new Error('Payment element parameters are not provided');
|
|
12680
13310
|
}
|
|
12681
13311
|
|
|
12682
|
-
const paymentInstances = await this._createPaymentInstances();
|
|
13312
|
+
const paymentInstances = await this._createPaymentInstances(params);
|
|
12683
13313
|
const cart = await this.payment.getCart();
|
|
12684
13314
|
|
|
12685
13315
|
await this._performPaymentAction(
|
|
@@ -12698,7 +13328,7 @@ class PaymentController {
|
|
|
12698
13328
|
throw new Error('Tokenization parameters are not provided');
|
|
12699
13329
|
}
|
|
12700
13330
|
|
|
12701
|
-
const paymentInstances = await this._createPaymentInstances();
|
|
13331
|
+
const paymentInstances = await this._createPaymentInstances(params);
|
|
12702
13332
|
|
|
12703
13333
|
await this._performPaymentAction(paymentInstances, 'tokenize');
|
|
12704
13334
|
}
|
|
@@ -12718,7 +13348,7 @@ class PaymentController {
|
|
|
12718
13348
|
|
|
12719
13349
|
removeUrlParams();
|
|
12720
13350
|
|
|
12721
|
-
const paymentInstances = await this._createPaymentInstances();
|
|
13351
|
+
const paymentInstances = await this._createPaymentInstances(params);
|
|
12722
13352
|
|
|
12723
13353
|
await this._performPaymentAction(
|
|
12724
13354
|
paymentInstances,
|
|
@@ -12811,9 +13441,10 @@ class PaymentController {
|
|
|
12811
13441
|
/**
|
|
12812
13442
|
* @returns {Promise<Payment[]>}
|
|
12813
13443
|
*/
|
|
12814
|
-
|
|
13444
|
+
// Takes the caller's params: this.params can change while the methods load (two createElements at once)
|
|
13445
|
+
async _createPaymentInstances(callParams) {
|
|
12815
13446
|
const paymentMethods = await this._getPaymentMethods();
|
|
12816
|
-
const params = adjustParams(
|
|
13447
|
+
const params = adjustParams(callParams);
|
|
12817
13448
|
|
|
12818
13449
|
return Object.entries(params).reduce((acc, [method, params]) => {
|
|
12819
13450
|
const methodSettings = paymentMethods[method];
|
|
@@ -12911,6 +13542,8 @@ class PaymentController {
|
|
|
12911
13542
|
return this._getAmazonPaymentClass(gateway);
|
|
12912
13543
|
case 'sezzle':
|
|
12913
13544
|
return this._getSezzlePaymentClass(gateway);
|
|
13545
|
+
case 'twint':
|
|
13546
|
+
return this._getTwintPaymentClass(gateway);
|
|
12914
13547
|
default:
|
|
12915
13548
|
return null;
|
|
12916
13549
|
}
|
|
@@ -12924,6 +13557,17 @@ class PaymentController {
|
|
|
12924
13557
|
return QuickpayCardPayment;
|
|
12925
13558
|
case 'convesiopay':
|
|
12926
13559
|
return ConvesioCardPayment;
|
|
13560
|
+
case 'saferpay':
|
|
13561
|
+
return SaferpayCardPayment;
|
|
13562
|
+
default:
|
|
13563
|
+
return null;
|
|
13564
|
+
}
|
|
13565
|
+
}
|
|
13566
|
+
|
|
13567
|
+
_getTwintPaymentClass(gateway) {
|
|
13568
|
+
switch (gateway) {
|
|
13569
|
+
case 'saferpay':
|
|
13570
|
+
return SaferpayTwintPayment;
|
|
12927
13571
|
default:
|
|
12928
13572
|
return null;
|
|
12929
13573
|
}
|
|
@@ -13475,7 +14119,7 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
|
|
|
13475
14119
|
const api = {};
|
|
13476
14120
|
|
|
13477
14121
|
Object.assign(api, {
|
|
13478
|
-
version: '5.
|
|
14122
|
+
version: '5.10.0',
|
|
13479
14123
|
options,
|
|
13480
14124
|
request,
|
|
13481
14125
|
|