diff --git a/docs/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration.md b/docs/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration.md
index 602bcb7deaa..c7daa61ac23 100644
--- a/docs/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration.md
+++ b/docs/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration.md
@@ -139,23 +139,24 @@ The options you feed the configuration instance are the same as those for expres
A big part of the payment method integration is the interface that is exposed for payment methods to use via props when the node provided is cloned and rendered on block mount. While all the props are listed below, you can find more details about what the props reference, their types etc via the [typedefs described in this file](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce-blocks/assets/js/types/type-defs/payment-method-interface.ts).
-| Property | Type | Description | Values |
-| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `activePaymentMethod` | String | The slug of the current active payment method in the checkout. | - |
-| `billing` | Object | Contains everything related to billing. | `billingAddress`, `cartTotal`, `currency`, `cartTotalItems`, `displayPricesIncludingTax`, `appliedCoupons`, `customerId` |
-| `cartData` | Object | Data exposed from the cart including items, fees, and any registered extension data. Note that this data should be treated as immutable (should not be modified/mutated) or it will result in errors in your application. | `cartItems`, `cartFees`, `extensions` |
-| `checkoutStatus` | Object | The current checkout status exposed as various boolean state. | `isCalculating`, `isComplete`, `isIdle`, `isProcessing` |
+| Property | Type | Description | Values |
+| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `activePaymentMethod` | String | The slug of the current active payment method in the checkout. | - |
+| `billing` | Object | Contains everything related to billing. | `billingAddress`, `cartTotal`, `currency`, `cartTotalItems`, `displayPricesIncludingTax`, `appliedCoupons`, `customerId` |
+| `cartData` | Object | Data exposed from the cart including items, fees, and any registered extension data. Note that this data should be treated as immutable (should not be modified/mutated) or it will result in errors in your application. | `cartItems`, `cartFees`, `extensions` |
+| `checkoutStatus` | Object | The current checkout status exposed as various boolean state. | `isCalculating`, `isComplete`, `isIdle`, `isProcessing` |
| `components` | Object | It exposes React components that can be implemented by your payment method for various common interface elements used by payment methods. |
`ValidationInputError`: a container for holding validation errors which typically you'll include after any inputs.
[`PaymentMethodLabel`](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/e089ae17043fa525e8397d605f0f470959f2ae95/assets/js/payment-method-extensions/payment-methods/paypal/index.js#L37-L40): use this component for the payment method label, including an optional icon.
`PaymentMethodIcons`: a React component used for displaying payment method icons.
- `LoadingMask`: a wrapper component that handles displaying a loading state when the isLoading prop is true. Exposes the [LoadingMask component](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/c9074a4941919987dbad16a80f358b960336a09d/assets/js/base/components/loading-mask/index.js)
|
| `emitResponse` | Object | Contains some constants that can be helpful when using the event emitter. Read the _[Emitting Events](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/e267cd96a4329a4eeef816b2ef627e113ebb72a5/docs/extensibility/checkout-flow-and-events.md#emitting-events)_ section for more details. |
`noticeContexts`: This is an object containing properties referencing areas where notices can be targeted in the checkout. The object has the following properties:
`PAYMENTS`: This is a reference to the notice area in the payment methods step.
`EXPRESS_PAYMENTS`: This is a reference to the notice area in the express payment methods step.
`responseTypes`: This is an object containing properties referencing the various response types that can be returned by observers for some event emitters. It makes it easier for autocompleting the types and avoiding typos due to human error. The types are `SUCCESS`, `FAIL`, `ERROR`. The values for these types also correspond to the [payment status types](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/34e17c3622637dbe8b02fac47b5c9b9ebf9e3596/src/Payments/PaymentResult.php#L21) from the [checkout endpoint response from the server](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/34e17c3622637dbe8b02fac47b5c9b9ebf9e3596/src/RestApi/StoreApi/Schemas/CheckoutSchema.php#L103-L113).
|
-| `eventRegistration` | object | Contains all the checkout event emitter registration functions. These are functions the payment method can register observers on to interact with various points in the checkout flow (see [this doc](./checkout-flow-and-events.md) for more info). | `onCheckoutValidation`, `onCheckoutSuccess`, `onCheckoutFail`, `onPaymentSetup`, `onShippingRateSuccess`, `onShippingRateFail`, `onShippingRateSelectSuccess`, `onShippingRateSelectFail` |
-| `onClick` | Function | **Provided to express payment methods** that should be triggered when the payment method button is clicked (which will signal to checkout the payment method has taken over payment processing) | - |
-| `onClose` | Function | **Provided to express payment methods** that should be triggered when the express payment method modal closes and control is returned to checkout. | - |
-| `onSubmit` | Function | Submits the checkout and begins processing | - |
-| `paymentStatus` | Object | Various payment status helpers. Note, your payment method does not have to handle setting this status client side. Checkout will handle this via the responses your payment method gives from observers registered to [checkout event emitters](./checkout-flow-and-events.md). | `isPristine`, `isStarted`, `isProcessing`, `isFinished`, `hasError`, `hasFailed`, `isSuccessful` (see below for explanation) |
-| `setExpressPaymentError` | Function | Receives a string and allows express payment methods to set an error notice for the express payment area on demand. This can be necessary because some express payment method processing might happen outside of checkout events. | - |
-| `shippingData` | Object | Contains all shipping related data (outside of the shipping status). | `shippingRates`, `shippingRatesLoading`, `selectedRates`, `setSelectedRates`, `isSelectingRate`, `shippingAddress`, `setShippingAddress`, and `needsShipping` |
+| `eventRegistration` | object | Contains all the checkout event emitter registration functions. These are functions the payment method can register observers on to interact with various points in the checkout flow (see [this doc](./checkout-flow-and-events.md) for more info). | `onCheckoutValidation`, `onCheckoutSuccess`, `onCheckoutFail`, `onPaymentSetup`, `onShippingRateSuccess`, `onShippingRateFail`, `onShippingRateSelectSuccess`, `onShippingRateSelectFail` |
+| `onClick` | Function | **Provided to express payment methods** that should be triggered when the payment method button is clicked (which will signal to checkout the payment method has taken over payment processing) | - |
+| `onClose` | Function | **Provided to express payment methods** that should be triggered when the express payment method modal closes and control is returned to checkout. | - |
+| `onSubmit` | Function | Submits the checkout and begins processing | - |
+| `buttonAttributes` | Object | Styles set by the merchant that should be respected by all express payment buttons | `height, borderRadius, darkMode` |
+| `paymentStatus` | Object | Various payment status helpers. Note, your payment method does not have to handle setting this status client side. Checkout will handle this via the responses your payment method gives from observers registered to [checkout event emitters](./checkout-flow-and-events.md). | `isPristine`, `isStarted`, `isProcessing`, `isFinished`, `hasError`, `hasFailed`, `isSuccessful`(see below for explanation) |
+| `setExpressPaymentError` | Function | Receives a string and allows express payment methods to set an error notice for the express payment area on demand. This can be necessary because some express payment method processing might happen outside of checkout events. | - |
+| `shippingData` | Object | Contains all shipping related data (outside of the shipping status). | `shippingRates`, `shippingRatesLoading`, `selectedRates`, `setSelectedRates`, `isSelectingRate`, `shippingAddress`, `setShippingAddress`, and `needsShipping` |
| `shippingStatus` | Object | Various shipping status helpers. |
`shippingErrorStatus`: an object with various error statuses that might exist for shipping
`shippingErrorTypes`: an object containing all the possible types for shipping error status
|
-| `shouldSavePayment` | Boolean | Indicates whether or not the shopper has selected to save their payment method details (for payment methods that support saved payments). True if selected, false otherwise. Defaults to false. | - |
+| `shouldSavePayment` | Boolean | Indicates whether or not the shopper has selected to save their payment method details (for payment methods that support saved payments). True if selected, false otherwise. Defaults to false. | - |
- `isPristine`: This is true when the current payment status is `PRISTINE`.
- `isStarted`: This is true when the current payment status is `EXPRESS_STARTED`.
@@ -167,6 +168,33 @@ A big part of the payment method integration is the interface that is exposed fo
Any registered `savedTokenComponent` node will also receive a `token` prop which includes the id for the selected saved token in case your payment method needs to use it for some internal logic. However, keep in mind, this is just the id representing this token in the database (and the value of the radio input the shopper checked), not the actual customer payment token (since processing using that usually happens on the server for security).
+### Button Attributes for Express Payment Methods
+
+This API provides a way to synchronise the look and feel of the express payment buttons for a coherent shopper experience. Express Payment Methods must prefer the values provided in the `buttonAttributes`, and use it's own configuration settings as backup when the buttons are rendered somewhere other than the Cart or Checkout block.
+
+For example, in your button component, you would do something like this:
+
+```js
+// Get your extension specific settings and set defaults if not available
+let {
+ theme = 'dark',
+ borderRadius = '4',
+ height = '48',
+} = getButtonSettingsFromConfig();
+
+// In a cart & checkout block context, we receive `buttonAttributes` as a prop which overwrite the extension specific settings
+if ( typeof buttonAttributes !== 'undefined' ) {
+ height = buttonAttributes.height;
+ borderRadius = buttonAttributes.borderRadius;
+ theme = buttonAttributes.darkMode ? 'light' : 'dark';
+}
+...
+
+return
+```
+
+Note the `height` and `borderRadius` properties should always be respected. The `darkMode` is an optional property that may be used by an extension as a clue to help identify what colour button would be best rendered, given the user's theme.
+
## Server Side Integration
### Processing Payment
diff --git a/docs/docs-manifest.json b/docs/docs-manifest.json
index 5299dd14bc9..0835c0562dd 100644
--- a/docs/docs-manifest.json
+++ b/docs/docs-manifest.json
@@ -180,7 +180,7 @@
"menu_title": "Payment Method Integration",
"tags": "reference",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration.md",
- "hash": "f60acaaea4a6ac4adf637bc7069c966e01db089f9dfaa937def91165a71a4255",
+ "hash": "dd8caf9a8f79bb0806887bb41f64f9e32da5542e1c4cabdf0057c7e17f4208b5",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration.md",
"id": "c9a763b6976ecf03aeb961577c17c31f1ac7c420",
"links": {
@@ -1016,7 +1016,7 @@
"menu_title": "DOM Events",
"tags": "how-to",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/product-collection-block/dom-events.md",
- "hash": "78bce4ab5b5e902232b5ff73fd7a7c197e4f4417a490ccb45c9a27400d003787",
+ "hash": "fbad20bc55cc569161e80478c0789db3c34cf35513e669554af36db1de967a26",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/product-collection-block/dom-events.md",
"id": "c8d247b91472740075871e6b57a9583d893ac650"
}
@@ -1706,5 +1706,5 @@
"categories": []
}
],
- "hash": "402a928bfa0399c09151423796f98caeb8f99a70f8a4cf009db6703ec7949468"
+ "hash": "7c588f43c6f82a8f916b4ab0ff8046539685db971ad9c6ccb26da9a8dfef0fa4"
}
\ No newline at end of file
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx
similarity index 81%
rename from plugins/woocommerce-blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js
rename to plugins/woocommerce-blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx
index af627dacac1..b174403b183 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.js
+++ b/plugins/woocommerce-blocks/assets/js/blocks/cart-checkout-shared/payment-methods/express-payment-methods.tsx
@@ -15,16 +15,31 @@ import {
import { useEditorContext } from '@woocommerce/base-context';
import deprecated from '@wordpress/deprecated';
import { useDispatch, useSelect } from '@wordpress/data';
+import { useCheckoutBlockContext } from '@woocommerce/blocks/checkout/context';
/**
* Internal dependencies
*/
import PaymentMethodErrorBoundary from './payment-method-error-boundary';
import { STORE_KEY as PAYMENT_STORE_KEY } from '../../../data/payment/constants';
+import { useExpressCheckoutContext } from '../../checkout/inner-blocks/checkout-express-payment-block/context';
const ExpressPaymentMethods = () => {
const { isEditor } = useEditorContext();
+ const { hasDarkControls } = useCheckoutBlockContext();
+ const { showButtonStyles, buttonHeight, buttonBorderRadius } =
+ useExpressCheckoutContext();
+
+ // API for passing styles to express payment buttons
+ const buttonAttributes = showButtonStyles
+ ? {
+ height: buttonHeight,
+ borderRadius: buttonBorderRadius,
+ darkMode: hasDarkControls,
+ }
+ : undefined;
+
const { activePaymentMethod, paymentMethodData } = useSelect(
( select ) => {
const store = select( PAYMENT_STORE_KEY );
@@ -127,6 +142,20 @@ const ExpressPaymentMethods = () => {
[ __internalSetExpressPaymentError, onExpressPaymentError ]
);
+ // In the editor, we apply styles to the button containers to show the changes of the height and border-radius controls,
+ // which would be passed to the payment APIs on the front-end
+ const stylesForButtonContainers = isEditor
+ ? {
+ height: `${ showButtonStyles ? buttonHeight : '48' }px`,
+ borderRadius: `${
+ showButtonStyles ? buttonBorderRadius : '4'
+ }px`,
+ pointerEvents: 'none',
+ userSelect: 'none',
+ ariaDisabled: true,
+ }
+ : {};
+
/**
* @todo Find a way to Memoize Express Payment Method Content
*
@@ -142,7 +171,11 @@ const ExpressPaymentMethods = () => {
? paymentMethod.edit
: paymentMethod.content;
return isValidElement( expressPaymentMethod ) ? (
-