diff --git a/changelog.txt b/changelog.txt index 8ed1b7b8d..be8ab0db8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,6 +10,8 @@ * Fix - Prevent marking orders on-hold with order note "Process order to take payment" when the payment has failed. * Fix - Fix payment methods count on settings page. * Tweak - Add error logging in ECE critical Ajax requests. +* Add - Add support for Stripe Link payments via the new Stripe Checkout Element on the block cart and block checkout pages. +* Add - Add support for Stripe Link payments via the new Stripe Checkout Element on the product, cart, checkout and pay for order pages. = 8.8.0 - 2024-10-17 = * Fix - Update URL and path constants to support use of symlinked plugin. diff --git a/client/blocks/express-checkout/express-checkout-container.js b/client/blocks/express-checkout/express-checkout-container.js index c861948e7..ace659c7c 100644 --- a/client/blocks/express-checkout/express-checkout-container.js +++ b/client/blocks/express-checkout/express-checkout-container.js @@ -1,14 +1,18 @@ import React from 'react'; import { Elements } from '@stripe/react-stripe-js'; import ExpressCheckoutComponent from './express-checkout-component'; +import { getPaymentMethodTypesForExpressMethod } from 'wcstripe/express-checkout/utils'; export const ExpressCheckoutContainer = ( props ) => { - const { stripe, billing } = props; + const { stripe, billing, expressPaymentMethod } = props; const options = { mode: 'payment', paymentMethodCreation: 'manual', amount: billing.cartTotal.value, currency: billing.currency.code.toLowerCase(), + paymentMethodTypes: getPaymentMethodTypesForExpressMethod( + expressPaymentMethod + ), }; return ( diff --git a/client/blocks/express-checkout/index.js b/client/blocks/express-checkout/index.js index 24c642046..18aafc81b 100644 --- a/client/blocks/express-checkout/index.js +++ b/client/blocks/express-checkout/index.js @@ -4,6 +4,7 @@ import { PAYMENT_METHOD_EXPRESS_CHECKOUT_ELEMENT } from './constants'; import { ExpressCheckoutContainer } from './express-checkout-container'; import ApplePayPreview from './apple-pay-preview'; import GooglePayPreview from './google-pay-preview'; +import StripeLinkPreview from './stripe-link-preview'; import { loadStripe } from 'wcstripe/blocks/load-stripe'; import { getBlocksConfiguration } from 'wcstripe/blocks/utils'; import { checkPaymentMethodIsAvailable } from 'wcstripe/express-checkout/utils/check-payment-method-availability'; @@ -66,4 +67,34 @@ const expressCheckoutElementsApplePay = ( api ) => ( { }, } ); -export { expressCheckoutElementsGooglePay, expressCheckoutElementsApplePay }; +const expressCheckoutElementsStripeLink = ( api ) => ( { + name: PAYMENT_METHOD_EXPRESS_CHECKOUT_ELEMENT + '_link', + content: ( + + ), + edit: , + canMakePayment: ( { cart } ) => { + // eslint-disable-next-line camelcase + if ( typeof wc_stripe_express_checkout_params === 'undefined' ) { + return false; + } + + return new Promise( ( resolve ) => { + checkPaymentMethodIsAvailable( 'link', api, cart, resolve ); + } ); + }, + paymentMethodId: PAYMENT_METHOD_EXPRESS_CHECKOUT_ELEMENT, + supports: { + features: getBlocksConfiguration()?.supports ?? [], + }, +} ); + +export { + expressCheckoutElementsGooglePay, + expressCheckoutElementsApplePay, + expressCheckoutElementsStripeLink, +}; diff --git a/client/blocks/express-checkout/stripe-link-preview/icon.svg b/client/blocks/express-checkout/stripe-link-preview/icon.svg new file mode 100644 index 000000000..f9f427828 --- /dev/null +++ b/client/blocks/express-checkout/stripe-link-preview/icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/client/blocks/express-checkout/stripe-link-preview/index.js b/client/blocks/express-checkout/stripe-link-preview/index.js new file mode 100644 index 000000000..c71eb7e81 --- /dev/null +++ b/client/blocks/express-checkout/stripe-link-preview/index.js @@ -0,0 +1,11 @@ +/* eslint-disable max-len */ +import icon from './icon.svg'; +import './style.scss'; + +const useStripeLinkPreview = () => ( +
+ +
+); + +export default useStripeLinkPreview; diff --git a/client/blocks/express-checkout/stripe-link-preview/style.scss b/client/blocks/express-checkout/stripe-link-preview/style.scss new file mode 100644 index 000000000..0aa7f3826 --- /dev/null +++ b/client/blocks/express-checkout/stripe-link-preview/style.scss @@ -0,0 +1,12 @@ +.stripe-link-preview { + display: flex; + justify-content: center; + align-items: center; + background-color: #00d66f; + border-radius: 5px; + height: 40px; + &:hover { + cursor: pointer; + filter: opacity(0.7); + } +} diff --git a/client/blocks/upe/index.js b/client/blocks/upe/index.js index 7afa5306d..189fed2ca 100644 --- a/client/blocks/upe/index.js +++ b/client/blocks/upe/index.js @@ -11,6 +11,7 @@ import paymentRequestPaymentMethod from 'wcstripe/blocks/payment-request'; import { expressCheckoutElementsGooglePay, expressCheckoutElementsApplePay, + expressCheckoutElementsStripeLink, } from 'wcstripe/blocks/express-checkout'; import WCStripeAPI from 'wcstripe/api'; import { getBlocksConfiguration } from 'wcstripe/blocks/utils'; @@ -97,6 +98,7 @@ if ( getBlocksConfiguration()?.isECEEnabled ) { // Register Express Checkout Element. registerExpressPaymentMethod( expressCheckoutElementsGooglePay( api ) ); registerExpressPaymentMethod( expressCheckoutElementsApplePay( api ) ); + registerExpressPaymentMethod( expressCheckoutElementsStripeLink( api ) ); } else { // Register Stripe Payment Request. registerExpressPaymentMethod( paymentRequestPaymentMethod ); diff --git a/client/entrypoints/express-checkout/index.js b/client/entrypoints/express-checkout/index.js index 22a7b4503..64633975c 100644 --- a/client/entrypoints/express-checkout/index.js +++ b/client/entrypoints/express-checkout/index.js @@ -621,7 +621,11 @@ jQuery( function ( $ ) { }; // We don't need to initialize ECE on the checkout page now because it will be initialized by updated_checkout event. - if ( ! getExpressCheckoutData( 'is_checkout_page' ) ) { + if ( + getExpressCheckoutData( 'is_product_page' ) || + getExpressCheckoutData( 'is_pay_for_order' ) || + getExpressCheckoutData( 'is_cart_page' ) + ) { wcStripeECE.init(); } diff --git a/client/express-checkout/utils/check-payment-method-availability.js b/client/express-checkout/utils/check-payment-method-availability.js index b52bf23b3..a75a8aa14 100644 --- a/client/express-checkout/utils/check-payment-method-availability.js +++ b/client/express-checkout/utils/check-payment-method-availability.js @@ -1,6 +1,7 @@ import ReactDOM from 'react-dom'; import { ExpressCheckoutElement, Elements } from '@stripe/react-stripe-js'; import { memoize } from 'lodash'; +import { getPaymentMethodTypesForExpressMethod } from 'wcstripe/express-checkout/utils'; export const checkPaymentMethodIsAvailable = memoize( ( paymentMethod, api, cart, resolve ) => { @@ -22,6 +23,9 @@ export const checkPaymentMethodIsAvailable = memoize( paymentMethodCreation: 'manual', amount: Number( cart.cartTotals.total_price ), currency: cart.cartTotals.currency_code.toLowerCase(), + paymentMethodTypes: getPaymentMethodTypesForExpressMethod( + paymentMethod + ), } } > { * @param {string} paymentMethodType Payment method type Stripe ID. * @return {Array} Array of payment method types to use with intent, for Express Checkout. */ -export const getExpressPaymentMethodTypes = ( paymentMethodType = null ) => - getPaymentMethodTypes( paymentMethodType ).filter( ( type ) => - [ 'link', 'paypal', 'amazon_pay', 'card' ].includes( type ) - ); +export const getExpressPaymentMethodTypes = ( paymentMethodType = null ) => { + const expressPaymentMethodTypes = getPaymentMethodTypes( + paymentMethodType + ).filter( ( type ) => [ 'paypal', 'amazon_pay', 'card' ].includes( type ) ); + + if ( isLinkEnabled() ) { + expressPaymentMethodTypes.push( 'link' ); + } + + return expressPaymentMethodTypes; +}; + +/** + * Fetches the payment method types required to process a payment for an Express method. + * + * @see https://docs.stripe.com/elements/express-checkout-element/accept-a-payment#enable-payment-methods - lists the method types + * supported and which ones are required by each Express Checkout method. + * + * @param {*} paymentMethodType The express payment method type. eg 'link', 'googlePay', or 'applePay'. + * @return {Array} Array of payment method types necessary to process a payment for an Express method. + */ +export const getPaymentMethodTypesForExpressMethod = ( paymentMethodType ) => { + const paymentMethodsConfig = getBlocksConfiguration()?.paymentMethodsConfig; + const paymentMethodTypes = []; + + if ( ! paymentMethodsConfig ) { + return paymentMethodTypes; + } + + // All express payment methods require 'card' payments. Add it if it's enabled. + if ( paymentMethodsConfig?.card !== undefined ) { + paymentMethodTypes.push( 'card' ); + } + + // Add 'link' payment method type if enabled and requested. + if ( + paymentMethodType === 'link' && + isLinkEnabled( paymentMethodsConfig ) + ) { + paymentMethodTypes.push( 'link' ); + } + + return paymentMethodTypes; +}; diff --git a/client/stripe-utils/utils.js b/client/stripe-utils/utils.js index 302d388bf..8611acdd3 100644 --- a/client/stripe-utils/utils.js +++ b/client/stripe-utils/utils.js @@ -220,6 +220,8 @@ export { getStripeServerData, getErrorMessageForTypeAndCode }; * @return {boolean} True, if enabled; false otherwise. */ export const isLinkEnabled = ( paymentMethodsConfig ) => { + paymentMethodsConfig = + paymentMethodsConfig || getStripeServerData()?.paymentMethodsConfig; return ( paymentMethodsConfig?.link !== undefined && paymentMethodsConfig?.card !== undefined diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-element.php b/includes/payment-methods/class-wc-stripe-express-checkout-element.php index e760b4a90..007b2cd2e 100644 --- a/includes/payment-methods/class-wc-stripe-express-checkout-element.php +++ b/includes/payment-methods/class-wc-stripe-express-checkout-element.php @@ -214,6 +214,7 @@ public function javascript_params() { 'is_product_page' => $this->express_checkout_helper->is_product(), 'is_checkout_page' => $this->express_checkout_helper->is_checkout(), 'product' => $this->express_checkout_helper->get_product_data(), + 'is_cart_page' => is_cart(), ]; } diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php index 6b7503be3..a479eec51 100644 --- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php +++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php @@ -1620,9 +1620,17 @@ public function set_payment_method_title_for_order( $order, $payment_method_type if ( ! isset( $this->payment_methods[ $payment_method_type ] ) ) { return; } - $payment_method = $this->payment_methods[ $payment_method_type ]; - $payment_method_title = $payment_method->get_title( $stripe_payment_method ); - $payment_method_id = $payment_method instanceof WC_Stripe_UPE_Payment_Method_CC ? $this->id : $payment_method->id; + + $payment_method = $this->payment_methods[ $payment_method_type ]; + $payment_method_id = $payment_method instanceof WC_Stripe_UPE_Payment_Method_CC ? $this->id : $payment_method->id; + $is_stripe_link = isset( $stripe_payment_method->type ) && WC_Stripe_Payment_Methods::LINK === $stripe_payment_method->type; + + // Stripe Link uses the main gateway to process payments, however Link payments should use the title of the Link payment method. + if ( $is_stripe_link && isset( $this->payment_methods[ WC_Stripe_Payment_Methods::LINK ] ) ) { + $payment_method_title = $this->payment_methods[ WC_Stripe_Payment_Methods::LINK ]->get_title( $stripe_payment_method ); + } else { + $payment_method_title = $payment_method->get_title( $stripe_payment_method ); + } $order->set_payment_method( $payment_method_id ); $order->set_payment_method_title( $payment_method_title ); diff --git a/readme.txt b/readme.txt index a9e2a3df3..df8f10d0c 100644 --- a/readme.txt +++ b/readme.txt @@ -120,5 +120,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o * Fix - Prevent marking orders on-hold with order note "Process order to take payment" when the payment has failed. * Fix - Fix payment methods count on settings page. * Tweak - Add error logging in ECE critical Ajax requests. +* Add - Add support for Stripe Link payments via the new Stripe Checkout Element on the block cart and block checkout pages. +* Add - Add support for Stripe Link payments via the new Stripe Checkout Element on the product, cart, checkout and pay for order pages. [See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).