Skip to content

Commit 7993e98

Browse files
committed
Merge remote-tracking branch 'origin/release/6.5.0' into trunk
2 parents aa29007 + d7c6f47 commit 7993e98

25 files changed

+694
-142
lines changed

assets/css/stripe-link.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.stripe-gateway-checkout-email-field {
2+
position: relative;
3+
}
4+
5+
.stripe-gateway-checkout-email-field button.stripe-gateway-stripelink-modal-trigger {
6+
display: none;
7+
position: absolute;
8+
right: 5px;
9+
width: 64px;
10+
height: 40px;
11+
background: no-repeat
12+
url( '../../client/payment-method-icons/link/icon.svg' );
13+
background-color: transparent !important;
14+
cursor: pointer;
15+
border: none;
16+
}

changelog.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
*** Changelog ***
22

3+
= 6.5.0 - 2022-07-28 =
4+
* Add - Stripe Link: Add beta headers for Stripe server requests
5+
* Add - Stripe Link payment method option in admin
6+
* Add - Stripe Link payment method on checkout form
7+
* Add - Stripe Link payment method on blocks checkout form
8+
39
= 6.4.3 - 2022-06-30 =
410
* Fix - Replace unnecessary throws with empty string when keys are invalid.
511

client/api/index.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,42 @@ export default class WCStripeAPI {
5858
* @return {Object} The Stripe Object.
5959
*/
6060
getStripe() {
61-
const { key, locale, isUPEEnabled } = this.options;
62-
61+
const {
62+
key,
63+
locale,
64+
isUPEEnabled,
65+
paymentMethodsConfig,
66+
} = this.options;
67+
const isStripeLinkEnabled =
68+
undefined !== paymentMethodsConfig.card &&
69+
undefined !== paymentMethodsConfig.link;
6370
if ( ! this.stripe ) {
6471
if ( isUPEEnabled ) {
65-
this.stripe = new Stripe( key, {
66-
betas: [ 'payment_element_beta_1' ],
67-
locale,
68-
} );
72+
let betas = [ 'payment_element_beta_1' ];
73+
if ( isStripeLinkEnabled ) {
74+
betas = betas.concat( [ 'link_autofill_modal_beta_1' ] );
75+
}
76+
this.stripe = this.createStripe( key, locale, betas );
6977
} else {
70-
this.stripe = new Stripe( key, {
71-
locale,
72-
} );
78+
this.stripe = this.createStripe( key, locale );
7379
}
7480
}
7581
return this.stripe;
7682
}
7783

84+
createStripe( key, locale, betas = [] ) {
85+
const options = { locale };
86+
87+
if ( betas.length ) {
88+
options.betas = betas;
89+
}
90+
91+
if ( betas.includes( 'link_autofill_modal_beta_1' ) ) {
92+
options.apiVersion = '2020-08-27;link_beta=v1';
93+
}
94+
return new Stripe( key, options );
95+
}
96+
7897
/**
7998
* Load Stripe for payment request button.
8099
*
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export const PAYMENT_METHOD_NAME = 'stripe';
2+
export const WC_STORE_CART = 'wc/store/cart';

client/blocks/upe/fields.js

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,52 @@
11
import { useState, useEffect } from '@wordpress/element';
2+
import { useDispatch, useSelect } from '@wordpress/data';
23
import { __ } from '@wordpress/i18n';
34
import {
45
Elements,
56
ElementsConsumer,
67
PaymentElement,
78
} from '@stripe/react-stripe-js';
9+
import { getAppearance } from '../../styles/upe';
810
import { confirmUpePayment } from './confirm-upe-payment';
911
import { getBlocksConfiguration } from 'wcstripe/blocks/utils';
10-
import { PAYMENT_METHOD_NAME } from 'wcstripe/blocks/credit-card/constants';
12+
import {
13+
PAYMENT_METHOD_NAME,
14+
WC_STORE_CART,
15+
} from 'wcstripe/blocks/credit-card/constants';
16+
import enableStripeLinkPaymentMethod from 'wcstripe/stripe-link';
17+
import './styles.scss';
18+
19+
const useCustomerData = () => {
20+
const { customerData, isInitialized } = useSelect( ( select ) => {
21+
const store = select( WC_STORE_CART );
22+
return {
23+
customerData: store.getCustomerData(),
24+
isInitialized: store.hasFinishedResolution( 'getCartData' ),
25+
};
26+
} );
27+
const {
28+
setShippingAddress,
29+
setBillingAddress,
30+
setBillingData,
31+
} = useDispatch( WC_STORE_CART );
32+
33+
let customerBillingAddress = customerData.billingData;
34+
let setCustomerBillingAddress = setBillingData;
35+
36+
//added for backwards compatibility -> billingData was renamed to billingAddress
37+
if ( customerData.billingData === undefined ) {
38+
customerBillingAddress = customerData.billingAddress;
39+
setCustomerBillingAddress = setBillingAddress;
40+
}
41+
42+
return {
43+
isInitialized,
44+
billingAddress: customerBillingAddress,
45+
shippingAddress: customerData.shippingAddress,
46+
setBillingAddress: setCustomerBillingAddress,
47+
setShippingAddress,
48+
};
49+
};
1150

1251
const UPEField = ( {
1352
api,
@@ -63,6 +102,131 @@ const UPEField = ( {
63102
createIntent();
64103
}, [ paymentIntentId, hasRequestedIntent, api, errorMessage ] );
65104

105+
const customerData = useCustomerData();
106+
107+
useEffect( () => {
108+
if (
109+
paymentMethodsConfig.link !== undefined &&
110+
paymentMethodsConfig.card !== undefined
111+
) {
112+
const shippingAddressFields = {
113+
line1: 'shipping-address_1',
114+
line2: 'shipping-address_2',
115+
city: 'shipping-city',
116+
state: 'components-form-token-input-1',
117+
postal_code: 'shipping-postcode',
118+
country: 'components-form-token-input-0',
119+
};
120+
const billingAddressFields = {
121+
line1: 'billing-address_1',
122+
line2: 'billing-address_2',
123+
city: 'billing-city',
124+
state: 'components-form-token-input-3',
125+
postal_code: 'billing-postcode',
126+
country: 'components-form-token-input-2',
127+
};
128+
129+
const appearance = getAppearance();
130+
elements.update( { appearance } );
131+
132+
enableStripeLinkPaymentMethod( {
133+
api,
134+
elements,
135+
emailId: 'email',
136+
fill_field_method: ( address, nodeId, key ) => {
137+
const setAddress =
138+
shippingAddressFields[ key ] === nodeId
139+
? customerData.setShippingAddress
140+
: customerData.setBillingAddress;
141+
const customerAddress =
142+
shippingAddressFields[ key ] === nodeId
143+
? customerData.shippingAddress
144+
: customerData.billingAddress;
145+
146+
if ( undefined === customerAddress ) {
147+
return;
148+
}
149+
150+
if ( address.address[ key ] === null ) {
151+
address.address[ key ] = '';
152+
}
153+
154+
if ( key === 'line1' ) {
155+
customerAddress.address_1 = address.address[ key ];
156+
} else if ( key === 'line2' ) {
157+
customerAddress.address_2 = address.address[ key ];
158+
} else if ( key === 'postal_code' ) {
159+
customerAddress.postcode = address.address[ key ];
160+
} else {
161+
customerAddress[ key ] = address.address[ key ];
162+
}
163+
164+
if ( undefined !== customerData.billingAddress ) {
165+
customerAddress.email = getEmail();
166+
}
167+
168+
setAddress( customerAddress );
169+
170+
function getEmail() {
171+
return document.getElementById( 'email' ).value;
172+
}
173+
174+
customerData.billingAddress.email = getEmail();
175+
customerData.setBillingAddress(
176+
customerData.billingAddress
177+
);
178+
},
179+
show_button: ( linkAutofill ) => {
180+
jQuery( '#email' )
181+
.parent()
182+
.append(
183+
'<button class="stripe-gateway-stripelink-modal-trigger"></button>'
184+
);
185+
if ( jQuery( '#email' ).val() !== '' ) {
186+
jQuery(
187+
'.stripe-gateway-stripelink-modal-trigger'
188+
).show();
189+
190+
const linkButtonTop =
191+
jQuery( '#email' ).position().top +
192+
( jQuery( '#email' ).outerHeight() - 40 ) / 2;
193+
jQuery(
194+
'.stripe-gateway-stripelink-modal-trigger'
195+
).show();
196+
jQuery(
197+
'.stripe-gateway-stripelink-modal-trigger'
198+
).css( 'top', linkButtonTop + 'px' );
199+
}
200+
201+
//Handle StripeLink button click.
202+
jQuery( '.stripe-gateway-stripelink-modal-trigger' ).on(
203+
'click',
204+
( event ) => {
205+
event.preventDefault();
206+
// Trigger modal.
207+
linkAutofill.launch( {
208+
email: jQuery( '#email' ).val(),
209+
} );
210+
}
211+
);
212+
},
213+
complete_shipping: () => {
214+
return (
215+
document.getElementById( 'shipping-address_1' ) !== null
216+
);
217+
},
218+
shipping_fields: shippingAddressFields,
219+
billing_fields: billingAddressFields,
220+
complete_billing: () => {
221+
return (
222+
document.getElementById( 'billing-address_1' ) !== null
223+
);
224+
},
225+
} );
226+
}
227+
// eslint-disable-next-line react-hooks/exhaustive-deps
228+
}, [ elements ] );
229+
66230
useEffect(
67231
() =>
68232
onPaymentProcessing( () => {

client/blocks/upe/styles.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
button.stripe-gateway-stripelink-modal-trigger {
2+
display: none;
3+
position: absolute;
4+
right: 5px;
5+
width: 64px;
6+
height: 40px;
7+
background: no-repeat
8+
url( '../../payment-method-icons/link/icon.svg' );
9+
background-color: transparent !important;
10+
cursor: pointer;
11+
border: none;
12+
}
13+
14+
button.stripe-gateway-stripelink-modal-trigger:hover {
15+
background-color: transparent;
16+
border-color: transparent;
17+
}

client/classic/upe/index.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import jQuery from 'jquery';
22
import WCStripeAPI from '../../api';
33
import { getStripeServerData, getUPETerms } from '../../stripe-utils';
44
import { getFontRulesFromPage, getAppearance } from '../../styles/upe';
5+
import enableStripeLinkPaymentMethod from '../../stripe-link';
56
import { legacyHashchangeHandler } from './legacy-support';
67
import './style.scss';
78

@@ -10,7 +11,9 @@ jQuery( function ( $ ) {
1011
const isUPEEnabled = getStripeServerData()?.isUPEEnabled;
1112
const paymentMethodsConfig = getStripeServerData()?.paymentMethodsConfig;
1213
const enabledBillingFields = getStripeServerData()?.enabledBillingFields;
13-
14+
const isStripeLinkEnabled =
15+
undefined !== paymentMethodsConfig.card &&
16+
undefined !== paymentMethodsConfig.link;
1417
if ( ! key ) {
1518
// If no configuration is present, probably this is not the checkout page.
1619
return;
@@ -93,6 +96,7 @@ jQuery( function ( $ ) {
9396
const elements = api.getStripe().elements( {
9497
fonts: getFontRulesFromPage(),
9598
} );
99+
96100
const sepaElementsOptions =
97101
getStripeServerData()?.sepaElementsOptions ?? {};
98102
const iban = elements.create( 'iban', sepaElementsOptions );
@@ -308,7 +312,6 @@ jQuery( function ( $ ) {
308312
hiddenElementsForUPE.cleanup();
309313
api.saveUPEAppearance( appearance );
310314
}
311-
312315
const businessName = getStripeServerData()?.accountDescriptor;
313316
const upeSettings = {
314317
clientSecret,
@@ -321,6 +324,43 @@ jQuery( function ( $ ) {
321324
};
322325
}
323326

327+
if ( isStripeLinkEnabled ) {
328+
enableStripeLinkPaymentMethod( {
329+
api,
330+
elements,
331+
emailId: 'billing_email',
332+
complete_billing: () => {
333+
return true;
334+
},
335+
complete_shipping: () => {
336+
return (
337+
document.getElementById(
338+
'ship-to-different-address-checkbox'
339+
) &&
340+
document.getElementById(
341+
'ship-to-different-address-checkbox'
342+
).checked
343+
);
344+
},
345+
shipping_fields: {
346+
line1: 'shipping_address_1',
347+
line2: 'shipping_address_2',
348+
city: 'shipping_city',
349+
state: 'shipping_state',
350+
postal_code: 'shipping_postcode',
351+
country: 'shipping_country',
352+
},
353+
billing_fields: {
354+
line1: 'billing_address_1',
355+
line2: 'billing_address_2',
356+
city: 'billing_city',
357+
state: 'billing_state',
358+
postal_code: 'billing_postcode',
359+
country: 'billing_country',
360+
},
361+
} );
362+
}
363+
324364
upeElement = elements.create( 'payment', upeSettings );
325365
upeElement.mount( '#wc-stripe-upe-element' );
326366

Lines changed: 28 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)