Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WooPay compatibility with deferred intent creation UPE #6656

Merged
merged 23 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3fc3f2a
Shortcode checkout compatibility between WooPay and card payment meth…
Jul 3, 2023
f57c096
Use proper Stripe account depending on payment method on shortcode ch…
Jul 9, 2023
e88a2bd
Support WooPay with deferred intent UPE on Blocks checkout
Jul 9, 2023
920d49f
Ensure backwards compatibility of payment processing for legacy card
Jul 10, 2023
f27e823
Merge branch 'develop' into feature/deferred-intent-woopay
Jul 10, 2023
3058aea
Add changelog entry
Jul 10, 2023
076fc4a
Fix unit tests for split UPE
Jul 10, 2023
61050c0
Extend payment information with Stripe ID of payment method and fix c…
Jul 10, 2023
d645802
Merge branch 'develop' into feature/deferred-intent-woopay
Jul 10, 2023
e0f03bd
Fix JS unit tests
Jul 10, 2023
783accf
Simplify the interface of the stripe object helper function
Jul 10, 2023
8513b0e
Add function description
Jul 10, 2023
1c33980
Clarify payment_information logic and avoid magic numbers
Jul 10, 2023
de89d1f
Simplify gateway registration logic by removing unnecessary branching
Jul 10, 2023
cf6b0de
Merge branch 'develop' into feature/deferred-intent-woopay
Jul 10, 2023
eb5876d
Merge branch 'develop' into feature/deferred-intent-woopay
timur27 Jul 11, 2023
8991d36
Improve Stripe platform account logic
Jul 12, 2023
3fc9362
Merge branch 'develop' into feature/deferred-intent-woopay-1
Jul 12, 2023
d0fcfac
Merge branch 'develop' into feature/deferred-intent-woopay
timur27 Jul 13, 2023
cfec6e8
Support WC Subscription renewals with deferred intent creation UPE
Jul 14, 2023
9243b62
Merge branch 'develop' into feature/deferred-intent-woopay
Jul 17, 2023
119067a
Merge branch 'develop' into feature/deferred-intent-woopay
Jul 17, 2023
3af7500
Provide payment method Stripe ID while adding a new payment method
Jul 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/feature-deferred-intent-woopay
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Ensure WooPay compatibility with the split UPE that has deferred intent creation implementation.
20 changes: 18 additions & 2 deletions client/checkout/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Internal dependencies
*/
import { getConfig } from 'utils/checkout';
import { getConfig, getUPEConfig } from 'utils/checkout';
import {
getPaymentRequestData,
getPaymentRequestAjaxURL,
Expand Down Expand Up @@ -41,6 +41,21 @@ export default class WCPayAPI {
return new Stripe( publishableKey, options );
}

/**
* Overloaded method to get the Stripe object for UPE. Leverages the original getStripe method but before doing
* so, sets the forceNetworkSavedCards option to the proper value for the payment method type.
* forceNetworkSavedCards is currently the flag that among others determines whether or not to use the Stripe Platform on the checkout.
*
* @param {string} paymentMethodType The payment method type.
* @return {Object} The Stripe Object.
*/
getStripeForUPE( paymentMethodType ) {
this.options.forceNetworkSavedCards = getUPEConfig(
'paymentMethodsConfig'
)[ paymentMethodType ].forceNetworkSavedCards;
return this.getStripe();
}

/**
* Generates a new instance of Stripe.
*
Expand All @@ -54,13 +69,14 @@ export default class WCPayAPI {
forceNetworkSavedCards,
locale,
isUPEEnabled,
isUPEDeferredEnabled,
isStripeLinkEnabled,
} = this.options;

if (
forceNetworkSavedCards &&
! forceAccountRequest &&
! isUPEEnabled
! ( isUPEEnabled && ! isUPEDeferredEnabled )
) {
if ( ! this.stripePlatform ) {
this.stripePlatform = this.createStripe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useEffect, useState } from 'react';
import PaymentProcessor from './payment-processor';

const PaymentElements = ( { api, ...props } ) => {
const stripe = api.getStripe();
const stripe = api.getStripeForUPE( props.paymentMethodId );
const [ errorMessage, setErrorMessage ] = useState( null );
const [ appearance, setAppearance ] = useState(
getUPEConfig( 'wcBlocksUPEAppearance' )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const PaymentProcessor = ( {
await validateElements( elements );

const paymentMethodObject = await api
.getStripe()
.getStripeForUPE( paymentMethodId )
.createPaymentMethod( {
elements,
params: {
Expand Down
5 changes: 4 additions & 1 deletion client/checkout/blocks/upe-split.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const api = new WCPayAPI(
locale: getUPEConfig( 'locale' ),
isUPEEnabled: getUPEConfig( 'isUPEEnabled' ),
isUPESplitEnabled: getUPEConfig( 'isUPESplitEnabled' ),
isUPEDeferredEnabled: getUPEConfig( 'isUPEDeferredEnabled' ),
isStripeLinkEnabled,
},
request
Expand Down Expand Up @@ -103,7 +104,9 @@ Object.entries( enabledPaymentMethodsConfig )
const isAvailableInTheCountry =
! isRestrictedInAnyCountry ||
upeConfig.countries.includes( billingCountry );
return isAvailableInTheCountry && !! api.getStripe();
return (
isAvailableInTheCountry && !! api.getStripeForUPE( upeName )
);
},
paymentMethodId: upeMethods[ upeName ],
// see .wc-block-checkout__payment-method styles in blocks/style.scss
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import enqueueFraudScripts from 'fraud-scripts';
import { showAuthenticationModalIfRequired } from './3ds-flow-handling';
import WCPayAPI from 'wcpay/checkout/api';
import apiRequest from '../../utils/request';
import { handleWooPayEmailInput } from 'wcpay/checkout/woopay/email-input-iframe';
import { isPreviewing } from 'wcpay/checkout/preview';

jQuery( function ( $ ) {
enqueueFraudScripts( getUPEConfig( 'fraudServices' ) );
Expand All @@ -28,6 +30,8 @@ jQuery( function ( $ ) {
accountId: getUPEConfig( 'accountId' ),
forceNetworkSavedCards: getUPEConfig( 'forceNetworkSavedCards' ),
locale: getUPEConfig( 'locale' ),
isUPEEnabled: getUPEConfig( 'isUPEEnabled' ),
isUPEDeferredEnabled: getUPEConfig( 'isUPEDeferredEnabled' ),
},
apiRequest
);
Expand Down Expand Up @@ -82,6 +86,10 @@ jQuery( function ( $ ) {
return processPaymentIfNotUsingSavedMethod( $( 'form#order_review' ) );
} );

if ( getUPEConfig( 'isWooPayEnabled' ) && ! isPreviewing() ) {
handleWooPayEmailInput( '#billing_email', api );
}

function processPaymentIfNotUsingSavedMethod( $form ) {
const paymentMethodType = getSelectedUPEGatewayPaymentMethod();
if ( ! isUsingSavedPaymentMethod( paymentMethodType ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,15 @@ function submitForm( jQueryForm ) {
* @param {Object} api The API object used to call the Stripe API's createPaymentMethod method.
* @param {Object} elements The Stripe elements object used to create a Stripe payment method.
* @param {Object} jQueryForm The jQuery object for the form being submitted.
* @param {string} paymentMethodType The type of Stripe payment method to create.
* @return {Promise<Object>} A promise that resolves with the created Stripe payment method.
*/
function createStripePaymentMethod( api, elements, jQueryForm ) {
function createStripePaymentMethod(
api,
elements,
jQueryForm,
paymentMethodType
) {
let params = {};
if ( 'checkout' === jQueryForm.attr( 'name' ) ) {
params = {
Expand All @@ -118,7 +124,10 @@ function createStripePaymentMethod( api, elements, jQueryForm ) {
},
};
}
return api.getStripe().createPaymentMethod( { elements, params: params } );

return api
.getStripeForUPE( paymentMethodType )
.createPaymentMethod( { elements, params: params } );
}

/**
Expand All @@ -141,7 +150,9 @@ async function createStripePaymentElement( api, paymentMethodType ) {
appearance: initializeAppearance( api ),
};

const elements = api.getStripe().elements( options );
const elements = api
.getStripeForUPE( paymentMethodType )
.elements( options );
const createdStripePaymentElement = elements.create( 'payment', {
...getUpeSettings(),
wallets: {
Expand Down Expand Up @@ -278,7 +289,8 @@ export const processPayment = (
const paymentMethodObject = await createStripePaymentMethod(
api,
elements,
jQueryForm
jQueryForm,
paymentMethodType
);
appendFingerprintInputToForm( jQueryForm, fingerprint );
appendPaymentMethodIdToForm(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ jest.mock( 'wcpay/utils/checkout', () => {
return {
card: {
label: 'Card',
forceNetworkSavedCards: true,
},
giropay: {
label: 'Giropay',
forceNetworkSavedCards: false,
},
ideal: {
label: 'iDEAL',
forceNetworkSavedCards: false,
},
sepa: {
label: 'SEPA',
forceNetworkSavedCards: false,
},
};
}
Expand Down Expand Up @@ -92,7 +96,7 @@ const mockCreatePaymentMethod = jest.fn( () => {
};
} );

const mockGetStripe = jest.fn( () => {
const mockGetStripeForUPE = jest.fn( () => {
return {
elements: mockElements,
createPaymentMethod: mockCreatePaymentMethod,
Expand All @@ -110,11 +114,17 @@ const setupIntentMock = jest.fn( () => {

const apiMock = {
saveUPEAppearance: saveUPEAppearanceMock,
getStripe: mockGetStripe,
getStripeForUPE: mockGetStripeForUPE,
setupIntent: setupIntentMock,
};

describe( 'Stripe Payment Element mounting', () => {
let mockDomElement;

beforeEach( () => {
mockDomElement = document.createElement( 'div' );
} );

afterEach( () => {
jest.clearAllMocks();
} );
Expand All @@ -130,7 +140,6 @@ describe( 'Stripe Payment Element mounting', () => {
return 'fingerprint';
} );

const mockDomElement = document.createElement( 'div' );
mockDomElement.dataset.paymentMethodType = 'giropay';

mountStripePaymentElement( apiMock, mockDomElement );
Expand Down Expand Up @@ -160,10 +169,30 @@ describe( 'Stripe Payment Element mounting', () => {
backgroundColor: '#fff',
};
}

if ( 'paymentMethodsConfig' === argument ) {
return {
ideal: {
label: 'iDEAL',
forceNetworkSavedCards: false,
},
card: {
label: 'Card',
forceNetworkSavedCards: true,
},
giropay: {
label: 'Giropay',
forceNetworkSavedCards: false,
},
sepa: {
label: 'SEPA',
forceNetworkSavedCards: false,
},
};
}
} );

const mockDomElement = document.createElement( 'div' );
mockDomElement.dataset.paymentMethodType = 'ideal';
mockDomElement.dataset.paymentMethodType = 'giropay';

mountStripePaymentElement( apiMock, mockDomElement );

Expand All @@ -184,7 +213,7 @@ describe( 'Stripe Payment Element mounting', () => {
expect( showErrorCheckout ).toHaveBeenCalledWith(
'No fingerprint'
);
expect( apiMock.getStripe ).not.toHaveBeenCalled();
expect( apiMock.getStripeForUPE ).not.toHaveBeenCalled();
expect( mockElements ).not.toHaveBeenCalled();
expect( mockCreateFunction ).not.toHaveBeenCalled();
} );
Expand All @@ -195,13 +224,12 @@ describe( 'Stripe Payment Element mounting', () => {
return 'fingerprint';
} );

const mockDomElement = document.createElement( 'div' );
mockDomElement.dataset.paymentMethodType = 'card';

mountStripePaymentElement( apiMock, mockDomElement );

await waitFor( () => {
expect( apiMock.getStripe ).toHaveBeenCalled();
expect( apiMock.getStripeForUPE ).toHaveBeenCalled();
expect( mockElements ).toHaveBeenCalled();
expect( mockCreateFunction ).toHaveBeenCalled();
expect( mockMountFunction ).toHaveBeenCalled();
Expand Down Expand Up @@ -247,20 +275,50 @@ describe( 'Stripe Payment Element mounting', () => {
return 'fingerprint';
} );

const mockDomElement = document.createElement( 'div' );
getUPEConfig.mockImplementation( ( argument ) => {
if ( 'currency' === argument ) {
return 'eur';
}

if ( 'paymentMethodsConfig' === argument ) {
return {
sepa: {
label: 'SEPA',
forceNetworkSavedCards: false,
},
};
}
} );

mockDomElement.dataset.paymentMethodType = 'sepa';

mountStripePaymentElement( apiMock, mockDomElement );
mountStripePaymentElement( apiMock, mockDomElement );

await waitFor( () => {
expect( apiMock.getStripe ).toHaveBeenCalled();
expect( apiMock.getStripeForUPE ).toHaveBeenCalled();
expect( mockElements ).toHaveBeenCalledTimes( 1 );
} );
} );
} );

describe( 'Payment processing', () => {
beforeEach( () => {
getUPEConfig.mockImplementation( ( argument ) => {
if ( 'currency' === argument ) {
return 'eur';
}

if ( 'paymentMethodsConfig' === argument ) {
return {
card: {
label: 'card',
forceNetworkSavedCards: false,
},
};
}
} );
} );
afterEach( () => {
jest.clearAllMocks();
} );
Expand Down
Loading