Skip to content

Commit 6e7e04c

Browse files
authored
Add BLIK payment method (#4043)
* Add BLIK payment method behind feature flag * Update PHP unit tests to account for BLIK * Add BLIK-specific logic for processing a payment * Fix test constant value * Fix test constant value * Add changelog entry * Update BLIK icon and description * Update BLIK checkout icon * Handle premature validation on BLIK code input * Fix PHP test to include Amazon Pay * Update comment * Update variable name * Make BLIK compatible with Blocks checkout * Fix indentation after fixing conflicts * Fix indentation * Fix indentation * Add BLIK feature flag to dev tools mapping * Move BLIK code element out of the payment processing logic * Refactor repeated BLIK code validation helper * Extract BLIK code validation clearing to helper
1 parent f22c855 commit 6e7e04c

22 files changed

+392
-33
lines changed

assets/images/blik.svg

Lines changed: 15 additions & 0 deletions
Loading

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* Tweak - Updates the Single Payment Element setting copy. Now it is labeled "Smart Checkout".
2121
* Update - Enable/disable Amazon Pay by adding/removing it from the enabled payment methods list.
2222
* Add - Add ACSS payment tokenization.
23+
* Add - Add BLIK payment method.
2324
* Fix - Prevent reuse of payment intents when order total doesn't match intent amount.
2425
* Update - Update payment method type for Amazon Pay orders.
2526
* Fix - Compatibility with email preview in the Auth Requested email
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ValidatedTextInput } from '@woocommerce/blocks-checkout';
2+
import { __ } from '@wordpress/i18n';
3+
import { useState } from 'react';
4+
5+
const BlikCodeElement = () => {
6+
const [ blikCode, setBlikCode ] = useState( '' );
7+
8+
return (
9+
<>
10+
<ValidatedTextInput
11+
id="wc-stripe-blik-code"
12+
label="BLIK Code"
13+
maxLength={ 6 }
14+
onChange={ setBlikCode }
15+
pattern="[0-9]{6}"
16+
value={ blikCode }
17+
customValidityMessage={ ( validity ) => {
18+
if ( validity.valueMissing ) {
19+
return __(
20+
'Please enter a valid BLIK code',
21+
'woocommerce-gateway-stripe'
22+
);
23+
}
24+
25+
if ( validity.patternMismatch ) {
26+
return __(
27+
'BLIK Code is invalid',
28+
'woocommerce-gateway-stripe'
29+
);
30+
}
31+
} }
32+
required
33+
/>
34+
<p
35+
style={ {
36+
marginTop: 'var(--wp--preset--spacing--50)',
37+
} }
38+
>
39+
{ __(
40+
'After submitting your order, please authorize the payment in your mobile banking application.',
41+
'woocommerce-gateway-stripe'
42+
) }
43+
</p>
44+
</>
45+
);
46+
};
47+
48+
export default BlikCodeElement;

client/blocks/upe/upe-deferred-intent-creation/payment-processor.js

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ import { useEffect, useState, useRef } from 'react';
1414
* Internal dependencies
1515
*/
1616
import { usePaymentCompleteHandler, usePaymentFailHandler } from '../hooks';
17+
import BlikCodeElement from './blik-code-element';
1718
import { getBlocksConfiguration } from 'wcstripe/blocks/utils';
1819
import WCStripeAPI from 'wcstripe/api';
1920
import {
2021
maybeShowCashAppLimitNotice,
2122
removeCashAppLimitNotice,
2223
} from 'wcstripe/stripe-utils/cash-app-limit-notice-handler';
23-
import { isLinkEnabled } from 'wcstripe/stripe-utils';
24+
import { isLinkEnabled, validateBlikCode } from 'wcstripe/stripe-utils';
2425
import {
26+
PAYMENT_METHOD_BLIK,
2527
PAYMENT_METHOD_CARD,
2628
PAYMENT_METHOD_CASHAPP,
2729
} from 'wcstripe/stripe-utils/constants';
@@ -158,6 +160,7 @@ const PaymentProcessor = ( {
158160
: '';
159161
const paymentMethodsConfig = getBlocksConfiguration()?.paymentMethodsConfig;
160162
const gatewayConfig = getPaymentMethods()[ upeMethods[ paymentMethodId ] ];
163+
const isBlikSelected = selectedPaymentMethodType === PAYMENT_METHOD_BLIK;
161164

162165
// Make sure shouldSavePayment is set to true if the cart contains a subscription.
163166
// shouldSavePayment might be set to false because the cart contains a subscription and so the save checkbox isn't shown.
@@ -192,7 +195,8 @@ const PaymentProcessor = ( {
192195
};
193196
}
194197

195-
if ( ! isPaymentElementComplete ) {
198+
// BLIK is a special case which is not handled through the Stripe element.
199+
if ( ! ( isPaymentElementComplete || isBlikSelected ) ) {
196200
return {
197201
type: 'error',
198202
message: __(
@@ -222,29 +226,38 @@ const PaymentProcessor = ( {
222226
};
223227
}
224228

225-
await validateElements( elements );
229+
if ( isBlikSelected ) {
230+
validateBlikCode();
231+
} else {
232+
await validateElements( elements );
233+
}
226234

227235
const billingAddress = billing.billingAddress;
236+
const params = {
237+
billing_details: {
238+
name: `${ billingAddress.first_name } ${ billingAddress.last_name }`.trim(),
239+
email: billingAddress.email,
240+
phone: billingAddress.phone || null, // Phone is optional, but an empty string is not allowed by Stripe.
241+
address: {
242+
city: billingAddress.city,
243+
country: billingAddress.country,
244+
line1: billingAddress.address_1,
245+
line2: billingAddress.address_2,
246+
postal_code: billingAddress.postcode,
247+
state: billingAddress.state,
248+
},
249+
},
250+
};
251+
const paymentMethodData = isBlikSelected
252+
? {
253+
billing_details: params.billing_details,
254+
blik: {},
255+
type: selectedPaymentMethodType,
256+
}
257+
: { elements, params };
228258
const paymentMethodObject = await api
229259
.getStripe()
230-
.createPaymentMethod( {
231-
elements,
232-
params: {
233-
billing_details: {
234-
name: `${ billingAddress.first_name } ${ billingAddress.last_name }`.trim(),
235-
email: billingAddress.email,
236-
phone: billingAddress.phone || null, // Phone is optional, but an empty string is not allowed by Stripe.
237-
address: {
238-
city: billingAddress.city,
239-
country: billingAddress.country,
240-
line1: billingAddress.address_1,
241-
line2: billingAddress.address_2,
242-
postal_code: billingAddress.postcode,
243-
state: billingAddress.state,
244-
},
245-
},
246-
},
247-
} );
260+
.createPaymentMethod( paymentMethodData );
248261

249262
if ( paymentMethodObject.error ) {
250263
return {
@@ -253,10 +266,19 @@ const PaymentProcessor = ( {
253266
};
254267
}
255268

269+
const dynamicPaymentData = isBlikSelected
270+
? {
271+
'wc-stripe-blik-code': document?.querySelector(
272+
'#wc-stripe-blik-code'
273+
)?.value,
274+
}
275+
: {};
276+
256277
return {
257278
type: 'success',
258279
meta: {
259280
paymentMethodData: {
281+
...dynamicPaymentData,
260282
payment_method: upeMethods[ paymentMethodId ],
261283
wc_payment_intent_id: paymentIntentId ?? '',
262284
'wc-stripe-is-deferred-intent': true,
@@ -296,6 +318,8 @@ const PaymentProcessor = ( {
296318
isPaymentElementComplete,
297319
billing.billingAddress,
298320
paymentIntentId,
321+
selectedPaymentMethodType,
322+
isBlikSelected,
299323
]
300324
);
301325

@@ -355,12 +379,16 @@ const PaymentProcessor = ( {
355379
__html: testingInstructionsIfAppropriate,
356380
} }
357381
/>
358-
<PaymentElement
359-
options={ getStripeElementOptions() }
360-
onChange={ onSelectedPaymentMethodChange }
361-
onLoadError={ setHasLoadError }
362-
className="wcstripe-payment-element"
363-
/>
382+
{ isBlikSelected ? (
383+
<BlikCodeElement />
384+
) : (
385+
<PaymentElement
386+
options={ getStripeElementOptions() }
387+
onChange={ onSelectedPaymentMethodChange }
388+
onLoadError={ setHasLoadError }
389+
className="wcstripe-payment-element"
390+
/>
391+
) }
364392
</>
365393
);
366394
};

client/classic/upe/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import jQuery from 'jquery';
22
import WCStripeAPI from '../../api';
3-
import { getStripeServerData, getUPETerms } from '../../stripe-utils';
3+
import {
4+
getStripeServerData,
5+
getUPETerms,
6+
maybeClearBlikCodeValidation,
7+
} from '../../stripe-utils';
48
import { legacyHashchangeHandler } from './legacy-support';
59
import './style.scss';
610
import './deferred-intent.js';
@@ -306,6 +310,8 @@ jQuery( function ( $ ) {
306310
} else {
307311
removeCashAppLimitNotice();
308312
}
313+
314+
maybeClearBlikCodeValidation();
309315
} );
310316

311317
// Add terms parameter to UPE if save payment information checkbox is checked.

client/classic/upe/payment-processing.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import {
1414
unblockBlockCheckout,
1515
resetBlockCheckoutPaymentState,
1616
getAdditionalSetupIntentData,
17+
validateBlikCode,
1718
} from '../../stripe-utils';
1819
import { getFontRulesFromPage } from '../../styles/upe';
1920
import {
2021
PAYMENT_INTENT_STATUS_REQUIRES_ACTION,
22+
PAYMENT_METHOD_BLIK,
2123
PAYMENT_METHOD_BOLETO,
2224
PAYMENT_METHOD_CARD,
2325
PAYMENT_METHOD_CASHAPP,
@@ -276,9 +278,19 @@ function createStripePaymentMethod(
276278
};
277279
}
278280

281+
// BLIK uses a controlled form instead of Stripe Elements.
282+
const paymentMethodData =
283+
paymentMethodType === PAYMENT_METHOD_BLIK
284+
? {
285+
billing_details: params?.billing_details,
286+
blik: {},
287+
type: paymentMethodType,
288+
}
289+
: { elements, params };
290+
279291
return api
280292
.getStripe( paymentMethodType )
281-
.createPaymentMethod( { elements, params } )
293+
.createPaymentMethod( paymentMethodData )
282294
.then( ( paymentMethod ) => {
283295
if ( paymentMethod.error ) {
284296
throw paymentMethod.error;
@@ -418,7 +430,11 @@ export const processPayment = (
418430
);
419431
}
420432

421-
await validateElements( elements );
433+
if ( paymentMethodType === PAYMENT_METHOD_BLIK ) {
434+
validateBlikCode( jQueryForm );
435+
} else {
436+
await validateElements( elements );
437+
}
422438

423439
const paymentMethodObject = await createStripePaymentMethod(
424440
api,
Lines changed: 13 additions & 0 deletions
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import styled from '@emotion/styled';
3+
import IconWithShell from '../styles/icon-with-shell';
4+
import icon from './icon.svg';
5+
6+
const Wrapper = styled( IconWithShell )`
7+
background: #010101;
8+
`;
9+
10+
const BlikIcon = ( props ) => <Wrapper { ...props } src={ icon } />;
11+
12+
export default BlikIcon;

client/payment-method-icons/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import AlipayIcon from './alipay';
2+
import BlikIcon from './blik';
23
import CreditCardIcon from './cards';
34
import GiropayIcon from './giropay';
45
import KlarnaIcon from './klarna';
@@ -22,6 +23,7 @@ import StripeIcon from './stripe';
2223

2324
export default {
2425
alipay: AlipayIcon,
26+
blik: BlikIcon,
2527
card: CreditCardIcon,
2628
giropay: GiropayIcon,
2729
klarna: KlarnaIcon,

client/payment-methods-map.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
PAYMENT_METHOD_BACS,
1010
PAYMENT_METHOD_BANCONTACT,
1111
PAYMENT_METHOD_BECS,
12+
PAYMENT_METHOD_BLIK,
1213
PAYMENT_METHOD_BOLETO,
1314
PAYMENT_METHOD_CARD,
1415
PAYMENT_METHOD_CASHAPP,
@@ -31,6 +32,8 @@ const isAcssEnabled = window.wc_stripe_settings_params?.is_acss_enabled === '1';
3132
const isBacsEnabled = window.wc_stripe_settings_params?.is_bacs_enabled === '1';
3233
const isBecsDebitEnabled =
3334
window.wc_stripe_settings_params?.is_becs_debit_enabled === '1';
35+
const isBlikEnabled = window.wc_stripe_settings_params?.is_blik_enabled === '1';
36+
3437
const paymentMethodsMap = {
3538
card: {
3639
id: PAYMENT_METHOD_CARD,
@@ -324,4 +327,18 @@ if ( isBecsDebitEnabled ) {
324327
};
325328
}
326329

330+
// Enable BLIK according to feature flag value.
331+
if ( isBlikEnabled ) {
332+
paymentMethodsMap.blik = {
333+
id: PAYMENT_METHOD_BLIK,
334+
label: 'BLIK',
335+
description: __(
336+
'BLIK enables customers in Poland to pay directly via online payouts from their bank account.',
337+
'woocommerce-gateway-stripe'
338+
),
339+
Icon: icons.blik,
340+
currencies: [ 'PLN' ],
341+
};
342+
}
343+
327344
export default paymentMethodsMap;

0 commit comments

Comments
 (0)