Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 013d859

Browse files
ralucaStanopr
andauthored
Add extensibility point for extensions to filter payment methods (#4668)
* Add extensionsConfig when registering a payment method The extension config has its own canMakePayment where extensions can add callback using a payment method's name. * Make canMakePayment a getter on PaymentMethodConfig Because extensions can register canMakePayment callbacks for a payment method before it is registered we need to transform canMakePayment into a getter so that it's always recalculating it's value based on the registered callbacks/ * Rename extension related config and method * Format comments * Add an extension namespace to registerPaymentMethodExtensionCallback utility This commit changes the API for how extensions will register their own callbacks to canMakePayment, so that they can add their namespace and also callbacks for multiple payment methods. * Format comments * Update assets/js/blocks-registry/payment-methods/payment-method-config.js Co-authored-by: Thomas Roberts <[email protected]> * Update assets/js/blocks-registry/payment-methods/registry.js Co-authored-by: Thomas Roberts <[email protected]> * Update assets/js/blocks-registry/payment-methods/payment-method-config-helper.js Co-authored-by: Thomas Roberts <[email protected]> * Update assets/js/blocks-registry/payment-methods/payment-method-config-helper.js Co-authored-by: Thomas Roberts <[email protected]> * Update assets/js/blocks-registry/payment-methods/registry.js Co-authored-by: Thomas Roberts <[email protected]> * Fix eslint warning * Handle errors at registerPaymentMethodExtensionCallbacks level * Update assets/js/blocks-registry/payment-methods/registry.js Co-authored-by: Thomas Roberts <[email protected]> * Fix formatting issues Co-authored-by: Thomas Roberts <[email protected]> Co-authored-by: Thomas Roberts <[email protected]>
1 parent e2e1d68 commit 013d859

File tree

4 files changed

+104
-5
lines changed

4 files changed

+104
-5
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Keeps callbacks registered by extensions for different payment methods
2+
/* eslint prefer-const: 0 */
3+
export let canMakePaymentExtensionsCallbacks = {};
4+
5+
export const extensionsConfig = {
6+
canMakePayment: canMakePaymentExtensionsCallbacks,
7+
};

assets/js/blocks-registry/payment-methods/payment-method-config-helper.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,41 @@ export const canMakePaymentWithFeaturesCheck = ( canMakePayment, features ) => (
88
);
99
return featuresSupportRequirements && canMakePayment( canPayArgument );
1010
};
11+
12+
// Filter out payment methods by callbacks registered by extensions.
13+
export const canMakePaymentWithExtensions = (
14+
canMakePayment,
15+
extensionsCallbacks,
16+
paymentMethodName
17+
) => ( canPayArgument ) => {
18+
// Validate whether the payment method is available based on its own criteria first.
19+
let canPay = canMakePayment( canPayArgument );
20+
21+
if ( canPay ) {
22+
const namespacedCallbacks = {};
23+
Object.entries( extensionsCallbacks ).forEach(
24+
( [ namespace, callbacks ] ) => {
25+
if ( typeof callbacks[ paymentMethodName ] === 'function' ) {
26+
namespacedCallbacks[ namespace ] =
27+
callbacks[ paymentMethodName ];
28+
}
29+
}
30+
);
31+
canPay = Object.keys( namespacedCallbacks ).every( ( namespace ) => {
32+
try {
33+
return namespacedCallbacks[ namespace ]( canPayArgument );
34+
} catch ( err ) {
35+
// eslint-disable-next-line no-console
36+
console.error(
37+
`Error when executing callback for ${ paymentMethodName } in ${ namespace }`,
38+
err
39+
);
40+
// every expects a return value at the end of every arrow function and
41+
// this ensures that the error is ignored when computing the whole result.
42+
return true;
43+
}
44+
} );
45+
}
46+
47+
return canPay;
48+
};

assets/js/blocks-registry/payment-methods/payment-method-config.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import {
1212
assertValidElementOrString,
1313
} from './assertions';
1414

15-
import { canMakePaymentWithFeaturesCheck } from './payment-method-config-helper';
16-
15+
import {
16+
canMakePaymentWithFeaturesCheck,
17+
canMakePaymentWithExtensions,
18+
} from './payment-method-config-helper';
19+
import { extensionsConfig } from './extensions-config';
1720
const NullComponent = () => {
1821
return null;
1922
};
@@ -39,10 +42,25 @@ export default class PaymentMethodConfig {
3942
showSaveOption: config?.supports?.showSaveOption || false,
4043
features: config?.supports?.features || [ 'products' ],
4144
};
42-
this.canMakePayment = canMakePaymentWithFeaturesCheck(
43-
config.canMakePayment,
45+
this.canMakePaymentFromConfig = config.canMakePayment;
46+
}
47+
48+
// canMakePayment is calculated each time based on data that modifies outside of the class (eg: cart data).
49+
get canMakePayment() {
50+
const canPay = canMakePaymentWithFeaturesCheck(
51+
this.canMakePaymentFromConfig,
4452
this.supports.features
4553
);
54+
// Loop through all callbacks to check if there are any registered for this payment method.
55+
return Object.values( extensionsConfig.canMakePayment ).some(
56+
( callbacks ) => this.name in callbacks
57+
)
58+
? canMakePaymentWithExtensions(
59+
canPay,
60+
extensionsConfig.canMakePayment,
61+
this.name
62+
)
63+
: canPay;
4664
}
4765

4866
static assertValidConfig = ( config ) => {

assets/js/blocks-registry/payment-methods/registry.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import deprecated from '@wordpress/deprecated';
1313
*/
1414
import { default as PaymentMethodConfig } from './payment-method-config';
1515
import { default as ExpressPaymentMethodConfig } from './express-payment-method-config';
16-
16+
import { canMakePaymentExtensionsCallbacks } from './extensions-config';
1717
const paymentMethods = {};
1818
const expressPaymentMethods = {};
1919

@@ -67,6 +67,42 @@ export const registerExpressPaymentMethod = ( options ) => {
6767
}
6868
};
6969

70+
/**
71+
* Allows extension to register callbacks for specific payment methods to determine if they can make payments
72+
*
73+
* @param {string} namespace A unique string to identify the extension registering payment method callbacks.
74+
* @param {Record<string, function():any>} callbacks Example {stripe: () => {}, cheque: => {}}
75+
*/
76+
export const registerPaymentMethodExtensionCallbacks = (
77+
namespace,
78+
callbacks
79+
) => {
80+
if ( canMakePaymentExtensionsCallbacks[ namespace ] ) {
81+
// eslint-disable-next-line no-console
82+
console.error(
83+
`The namespace provided to registerPaymentMethodExtensionCallbacks must be unique. Callbacks have already been registered for the ${ namespace } namespace.`
84+
);
85+
} else {
86+
// Set namespace up as an empty object.
87+
canMakePaymentExtensionsCallbacks[ namespace ] = {};
88+
89+
Object.entries( callbacks ).forEach(
90+
( [ paymentMethodName, callback ] ) => {
91+
if ( typeof callback === 'function' ) {
92+
canMakePaymentExtensionsCallbacks[ namespace ][
93+
paymentMethodName
94+
] = callback;
95+
} else {
96+
// eslint-disable-next-line no-console
97+
console.error(
98+
`All callbacks provided to registerPaymentMethodExtensionCallbacks must be functions. The callback for the ${ paymentMethodName } payment method in the ${ namespace } namespace was not a function.`
99+
);
100+
}
101+
}
102+
);
103+
}
104+
};
105+
70106
export const __experimentalDeRegisterPaymentMethod = ( paymentMethodName ) => {
71107
delete paymentMethods[ paymentMethodName ];
72108
};

0 commit comments

Comments
 (0)