Checkout - Available Payment Method Filtering (https://github.com/woocommerce/woocommerce-blocks/pull/8441)
* Document payment_gateways() usage * Type in code comment * Return available methods in cart/checkout StoreAPI responses * Filter available methods in checkout * fix tests * fix TS error * fix TS warnings in tests * Update src/StoreApi/Schemas/V1/CartSchema.php Co-authored-by: Saad Tarhi <saad.trh@gmail.com> * Only apply filtering on frontend * Avoid filter on express methods --------- Co-authored-by: Saad Tarhi <saad.trh@gmail.com>
This commit is contained in:
parent
ad032a93e6
commit
95efc38d1f
|
@ -15,6 +15,7 @@ import {
|
|||
EMPTY_CART_ERRORS,
|
||||
EMPTY_SHIPPING_RATES,
|
||||
EMPTY_TAX_LINES,
|
||||
EMPTY_PAYMENT_METHODS,
|
||||
EMPTY_PAYMENT_REQUIREMENTS,
|
||||
EMPTY_EXTENSIONS,
|
||||
} from '@woocommerce/block-data';
|
||||
|
@ -112,6 +113,7 @@ export const defaultCartData: StoreCart = {
|
|||
shippingRates: EMPTY_SHIPPING_RATES,
|
||||
isLoadingRates: false,
|
||||
cartHasCalculatedShipping: false,
|
||||
paymentMethods: EMPTY_PAYMENT_METHODS,
|
||||
paymentRequirements: EMPTY_PAYMENT_REQUIREMENTS,
|
||||
receiveCart: () => undefined,
|
||||
receiveCartContents: () => undefined,
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
"../providers/cart-checkout/checkout-events/index.tsx",
|
||||
"../providers/cart-checkout/payment-events/index.tsx",
|
||||
"../providers/cart-checkout/shipping/index.js",
|
||||
"../../../editor-components/utils/*"
|
||||
"../../../editor-components/utils/*",
|
||||
"../../../data/index.ts"
|
||||
],
|
||||
"exclude": [ "**/test/**" ]
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import { dispatch } from '@wordpress/data';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import PaymentMethods from '../payment-methods';
|
||||
import { defaultCartState } from '../../../../data/cart/default-state';
|
||||
|
||||
jest.mock( '../saved-payment-method-options', () => ( { onChange } ) => {
|
||||
return (
|
||||
|
@ -102,9 +101,10 @@ describe( 'PaymentMethods', () => {
|
|||
wpDataFunctions
|
||||
.dispatch( CART_STORE_KEY )
|
||||
.invalidateResolutionForStore();
|
||||
wpDataFunctions
|
||||
.dispatch( CART_STORE_KEY )
|
||||
.receiveCart( defaultCartState.cartData );
|
||||
wpDataFunctions.dispatch( CART_STORE_KEY ).receiveCart( {
|
||||
...previewCart,
|
||||
payment_methods: [ 'cod', 'credit-card' ],
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
EMPTY_CART_ERRORS,
|
||||
EMPTY_SHIPPING_RATES,
|
||||
EMPTY_TAX_LINES,
|
||||
EMPTY_PAYMENT_METHODS,
|
||||
EMPTY_PAYMENT_REQUIREMENTS,
|
||||
EMPTY_EXTENSIONS,
|
||||
} from '../constants';
|
||||
|
@ -89,6 +90,7 @@ export const defaultCartState: CartState = {
|
|||
tax_lines: EMPTY_TAX_LINES,
|
||||
},
|
||||
errors: EMPTY_CART_ITEM_ERRORS,
|
||||
paymentMethods: EMPTY_PAYMENT_METHODS,
|
||||
paymentRequirements: EMPTY_PAYMENT_REQUIREMENTS,
|
||||
extensions: EMPTY_EXTENSIONS,
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ export const EMPTY_CART_FEES: [] = [];
|
|||
export const EMPTY_CART_ITEM_ERRORS: [] = [];
|
||||
export const EMPTY_CART_ERRORS: [] = [];
|
||||
export const EMPTY_SHIPPING_RATES: [] = [];
|
||||
export const EMPTY_PAYMENT_METHODS: [] = [];
|
||||
export const EMPTY_PAYMENT_REQUIREMENTS: [] = [];
|
||||
export const EMPTY_EXTENSIONS: Record< string, unknown > = {};
|
||||
export const EMPTY_TAX_LINES: [] = [];
|
||||
|
|
|
@ -18,7 +18,7 @@ export interface PaymentState {
|
|||
status: string;
|
||||
activePaymentMethod: string;
|
||||
activeSavedToken: string;
|
||||
// Avilable payment methods are payment methods which have been validated and can make payment
|
||||
// Available payment methods are payment methods which have been validated and can make payment.
|
||||
availablePaymentMethods: PlainPaymentMethods;
|
||||
availableExpressPaymentMethods: PlainExpressPaymentMethods;
|
||||
savedPaymentMethods:
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import * as wpDataFunctions from '@wordpress/data';
|
||||
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
|
||||
import { previewCart } from '@woocommerce/resource-previews';
|
||||
import { PAYMENT_STORE_KEY, CART_STORE_KEY } from '@woocommerce/block-data';
|
||||
import {
|
||||
registerPaymentMethod,
|
||||
registerExpressPaymentMethod,
|
||||
|
@ -23,6 +24,7 @@ const requiredKeyCheck = ( args: CanMakePaymentArgument ) => {
|
|||
'cart',
|
||||
'cartNeedsShipping',
|
||||
'cartTotals',
|
||||
'paymentMethods',
|
||||
'paymentRequirements',
|
||||
'selectedShippingMethods',
|
||||
'shippingAddress',
|
||||
|
@ -133,6 +135,10 @@ const registerMockPaymentMethods = ( savedCards = true ) => {
|
|||
wpDataFunctions
|
||||
.dispatch( PAYMENT_STORE_KEY )
|
||||
.__internalUpdateAvailablePaymentMethods();
|
||||
wpDataFunctions.dispatch( CART_STORE_KEY ).receiveCart( {
|
||||
...previewCart,
|
||||
payment_methods: [ 'cheque', 'bacs', 'credit-card' ],
|
||||
} );
|
||||
};
|
||||
|
||||
const resetMockPaymentMethods = () => {
|
||||
|
|
|
@ -12,8 +12,6 @@ import {
|
|||
emptyHiddenAddressFields,
|
||||
} from '@woocommerce/base-utils';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { store as noticesStore } from '@wordpress/notices';
|
||||
|
||||
import {
|
||||
getExpressPaymentMethods,
|
||||
getPaymentMethods,
|
||||
|
@ -33,10 +31,36 @@ import {
|
|||
} from '../../../data/constants';
|
||||
import { defaultCartState } from '../../../data/cart/default-state';
|
||||
|
||||
const registrationErrorNotice = (
|
||||
paymentMethod:
|
||||
| ExpressPaymentMethodConfigInstance
|
||||
| PaymentMethodConfigInstance,
|
||||
errorMessage: string,
|
||||
express = false
|
||||
) => {
|
||||
const { createErrorNotice } = dispatch( 'core/notices' );
|
||||
const noticeContext = express
|
||||
? noticeContexts.EXPRESS_PAYMENTS
|
||||
: noticeContexts.PAYMENTS;
|
||||
const errorText = sprintf(
|
||||
/* translators: %s the id of the payment method being registered (bank transfer, cheque...) */
|
||||
__(
|
||||
`There was an error registering the payment method with id '%s': `,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
paymentMethod.paymentMethodId
|
||||
);
|
||||
createErrorNotice( `${ errorText } ${ errorMessage }`, {
|
||||
context: noticeContext,
|
||||
id: `wc-${ paymentMethod.paymentMethodId }-registration-error`,
|
||||
} );
|
||||
};
|
||||
|
||||
export const checkPaymentMethodsCanPay = async ( express = false ) => {
|
||||
const isEditor = !! select( 'core/editor' );
|
||||
|
||||
let availablePaymentMethods = {};
|
||||
|
||||
const paymentMethods = express
|
||||
? getExpressPaymentMethods()
|
||||
: getPaymentMethods();
|
||||
|
@ -53,10 +77,6 @@ export const checkPaymentMethodsCanPay = async ( express = false ) => {
|
|||
};
|
||||
};
|
||||
|
||||
const noticeContext = express
|
||||
? noticeContexts.EXPRESS_PAYMENTS
|
||||
: noticeContexts.PAYMENTS;
|
||||
|
||||
let cartForCanPayArgument: Record< string, unknown > = {};
|
||||
let canPayArgument: Record< string, unknown > = {};
|
||||
|
||||
|
@ -94,7 +114,6 @@ export const checkPaymentMethodsCanPay = async ( express = false ) => {
|
|||
paymentRequirements: cart.paymentRequirements,
|
||||
receiveCart: dispatch( CART_STORE_KEY ).receiveCart,
|
||||
};
|
||||
|
||||
canPayArgument = {
|
||||
cart: cartForCanPayArgument,
|
||||
cartTotals: cart.totals,
|
||||
|
@ -103,6 +122,7 @@ export const checkPaymentMethodsCanPay = async ( express = false ) => {
|
|||
billingAddress: cart.billingAddress,
|
||||
shippingAddress: cart.shippingAddress,
|
||||
selectedShippingMethods,
|
||||
paymentMethods: cart.paymentMethods,
|
||||
paymentRequirements: cart.paymentRequirements,
|
||||
};
|
||||
} else {
|
||||
|
@ -139,68 +159,61 @@ export const checkPaymentMethodsCanPay = async ( express = false ) => {
|
|||
selectedShippingMethods: deriveSelectedShippingRates(
|
||||
cartForCanPayArgument.shippingRates
|
||||
),
|
||||
paymentMethods: previewCart.payment_methods,
|
||||
paymentRequirements: cartForCanPayArgument.paymentRequirements,
|
||||
};
|
||||
}
|
||||
|
||||
// Order payment methods
|
||||
let paymentMethodsOrder;
|
||||
if ( express ) {
|
||||
paymentMethodsOrder = Object.keys( paymentMethods );
|
||||
} else {
|
||||
paymentMethodsOrder = Array.from(
|
||||
new Set( [
|
||||
...( getSetting( 'paymentGatewaySortOrder', [] ) as [] ),
|
||||
...Object.keys( paymentMethods ),
|
||||
] )
|
||||
);
|
||||
}
|
||||
// Order payment methods.
|
||||
const paymentMethodsOrder = express
|
||||
? Object.keys( paymentMethods )
|
||||
: Array.from(
|
||||
new Set( [
|
||||
...( getSetting( 'paymentGatewaySortOrder', [] ) as [] ),
|
||||
...Object.keys( paymentMethods ),
|
||||
] )
|
||||
);
|
||||
const cartPaymentMethods = canPayArgument.paymentMethods as string[];
|
||||
|
||||
for ( let i = 0; i < paymentMethodsOrder.length; i++ ) {
|
||||
const paymentMethodName = paymentMethodsOrder[ i ];
|
||||
const paymentMethod = paymentMethods[ paymentMethodName ];
|
||||
|
||||
if ( ! paymentMethod ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// See if payment method should be available. This always evaluates to true in the editor context.
|
||||
try {
|
||||
const validForCart =
|
||||
isEditor || express
|
||||
? true
|
||||
: cartPaymentMethods.includes( paymentMethodName );
|
||||
const canPay = isEditor
|
||||
? true
|
||||
: await Promise.resolve(
|
||||
: validForCart &&
|
||||
( await Promise.resolve(
|
||||
paymentMethod.canMakePayment( canPayArgument )
|
||||
);
|
||||
) );
|
||||
|
||||
if ( canPay ) {
|
||||
if ( typeof canPay === 'object' && canPay.error ) {
|
||||
throw new Error( canPay.error.message );
|
||||
}
|
||||
|
||||
addAvailablePaymentMethod( paymentMethod );
|
||||
}
|
||||
} catch ( e ) {
|
||||
if ( CURRENT_USER_IS_ADMIN || isEditor ) {
|
||||
const { createErrorNotice } = dispatch( noticesStore );
|
||||
const errorText = sprintf(
|
||||
/* translators: %s the id of the payment method being registered (bank transfer, cheque...) */
|
||||
__(
|
||||
`There was an error registering the payment method with id '%s': `,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
paymentMethod.paymentMethodId
|
||||
);
|
||||
createErrorNotice( `${ errorText } ${ e }`, {
|
||||
context: noticeContext,
|
||||
id: `wc-${ paymentMethod.paymentMethodId }-registration-error`,
|
||||
} );
|
||||
registrationErrorNotice( paymentMethod, e as string, express );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const availablePaymentMethodNames = Object.keys( availablePaymentMethods );
|
||||
const currentlyAvailablePaymentMethods = express
|
||||
? select( PAYMENT_STORE_KEY ).getAvailableExpressPaymentMethods()
|
||||
: select( PAYMENT_STORE_KEY ).getAvailablePaymentMethods();
|
||||
|
||||
const availablePaymentMethodNames = Object.keys( availablePaymentMethods );
|
||||
if (
|
||||
Object.keys( currentlyAvailablePaymentMethods ).length ===
|
||||
availablePaymentMethodNames.length &&
|
||||
|
@ -216,10 +229,11 @@ export const checkPaymentMethodsCanPay = async ( express = false ) => {
|
|||
__internalSetAvailablePaymentMethods,
|
||||
__internalSetAvailableExpressPaymentMethods,
|
||||
} = dispatch( PAYMENT_STORE_KEY );
|
||||
if ( express ) {
|
||||
__internalSetAvailableExpressPaymentMethods( availablePaymentMethods );
|
||||
return true;
|
||||
}
|
||||
__internalSetAvailablePaymentMethods( availablePaymentMethods );
|
||||
|
||||
const setCallback = express
|
||||
? __internalSetAvailableExpressPaymentMethods
|
||||
: __internalSetAvailablePaymentMethods;
|
||||
|
||||
setCallback( availablePaymentMethods );
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -637,6 +637,7 @@ export const previewCart: CartResponse = {
|
|||
],
|
||||
},
|
||||
errors: [],
|
||||
payment_methods: [ 'cod', 'bacs', 'cheque' ],
|
||||
payment_requirements: [ 'products' ],
|
||||
extensions: {},
|
||||
};
|
||||
|
|
|
@ -178,6 +178,7 @@ export interface CartResponse {
|
|||
fees: Array< CartResponseFeeItem >;
|
||||
totals: CartResponseTotals;
|
||||
errors: Array< CartResponseErrorItem >;
|
||||
payment_methods: string[];
|
||||
payment_requirements: Array< unknown >;
|
||||
extensions: ExtensionsData;
|
||||
}
|
||||
|
|
|
@ -200,6 +200,7 @@ export interface Cart extends Record< string, unknown > {
|
|||
fees: Array< CartFeeItem >;
|
||||
totals: CartTotals;
|
||||
errors: Array< CartErrorItem >;
|
||||
paymentMethods: Array< string >;
|
||||
paymentRequirements: Array< string >;
|
||||
extensions: ExtensionsData;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ export interface StoreCart {
|
|||
extensions: Record< string, unknown >;
|
||||
isLoadingRates: boolean;
|
||||
cartHasCalculatedShipping: boolean;
|
||||
paymentMethods: string[];
|
||||
paymentRequirements: string[];
|
||||
receiveCart: ( cart: CartResponse ) => void;
|
||||
receiveCartContents: ( cart: CartResponse ) => void;
|
||||
|
|
|
@ -318,17 +318,17 @@ class Checkout extends AbstractBlock {
|
|||
}
|
||||
|
||||
if ( $is_block_editor && ! $this->asset_data_registry->exists( 'globalPaymentMethods' ) ) {
|
||||
$payment_gateways = $this->get_enabled_payment_gateways();
|
||||
// These are used to show options in the sidebar. We want to get the full list of enabled payment methods,
|
||||
// not just the ones that are available for the current cart (which may not exist yet).
|
||||
$payment_methods = $this->get_enabled_payment_gateways();
|
||||
$formatted_payment_methods = array_reduce(
|
||||
$payment_gateways,
|
||||
$payment_methods,
|
||||
function( $acc, $method ) {
|
||||
if ( 'yes' === $method->enabled ) {
|
||||
$acc[] = [
|
||||
'id' => $method->id,
|
||||
'title' => $method->method_title,
|
||||
'description' => $method->method_description,
|
||||
];
|
||||
}
|
||||
$acc[] = [
|
||||
'id' => $method->id,
|
||||
'title' => $method->method_title,
|
||||
'description' => $method->method_description,
|
||||
];
|
||||
return $acc;
|
||||
},
|
||||
[]
|
||||
|
|
|
@ -82,6 +82,8 @@ class Api {
|
|||
public function add_payment_method_script_data() {
|
||||
// Enqueue the order of enabled gateways as `paymentGatewaySortOrder`.
|
||||
if ( ! $this->asset_registry->exists( 'paymentGatewaySortOrder' ) ) {
|
||||
// We use payment_gateways() here to get the sort order of all enabled gateways. Some may be
|
||||
// programmatically disabled later on, but we still need to know where the enabled ones are in the list.
|
||||
$payment_gateways = WC()->payment_gateways->payment_gateways();
|
||||
$enabled_gateways = array_filter( $payment_gateways, array( $this, 'is_payment_gateway_enabled' ) );
|
||||
$this->asset_registry->add( 'paymentGatewaySortOrder', array_keys( $enabled_gateways ) );
|
||||
|
|
|
@ -309,6 +309,12 @@ class CartSchema extends AbstractSchema {
|
|||
'properties' => $this->force_schema_readonly( $this->error_schema->get_properties() ),
|
||||
],
|
||||
],
|
||||
'payment_methods' => [
|
||||
'description' => __( 'List of available payment method IDs that can be used to process the order.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'array',
|
||||
'context' => [ 'view', 'edit' ],
|
||||
'readonly' => true,
|
||||
],
|
||||
'payment_requirements' => [
|
||||
'description' => __( 'List of required payment gateway features to process the order.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'array',
|
||||
|
@ -373,6 +379,7 @@ class CartSchema extends AbstractSchema {
|
|||
]
|
||||
),
|
||||
'errors' => $cart_errors,
|
||||
'payment_methods' => array_values( wp_list_pluck( WC()->payment_gateways->get_available_payment_gateways(), 'id' ) ),
|
||||
'payment_requirements' => $this->extend->get_payment_requirements(),
|
||||
self::EXTENDING_KEY => $this->get_extended_data( self::IDENTIFIER ),
|
||||
];
|
||||
|
|
|
@ -112,7 +112,7 @@ class CheckoutSchema extends AbstractSchema {
|
|||
'description' => __( 'The ID of the payment method being used to process the payment.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'string',
|
||||
'context' => [ 'view', 'edit' ],
|
||||
'enum' => wc()->payment_gateways->get_payment_gateway_ids(),
|
||||
'enum' => array_values( wp_list_pluck( WC()->payment_gateways->get_available_payment_gateways(), 'id' ) ),
|
||||
],
|
||||
'create_account' => [
|
||||
'description' => __( 'Whether to create a new user account as part of order processing.', 'woo-gutenberg-products-block' ),
|
||||
|
|
|
@ -262,6 +262,7 @@ All endpoints under `/cart` (listed in this doc) return responses in the same fo
|
|||
"tax_lines": []
|
||||
},
|
||||
"errors": [],
|
||||
"payment_methods": [ "cod", "bacs", "cheque" ],
|
||||
"payment_requirements": [ "products" ],
|
||||
"extensions": {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue