Summary
On block checkout, if the customer selects Paytrail as the payment method but does not pick an individual payment provider (bank/card/mobile), clicking Place order fails with WooCommerce's generic error "There was a problem with your payment option." instead of the plugin's own, descriptive message.
The cause is a ReferenceError in assets/js/containers/paytrail-container.jsx: defaultProvider is referenced in the onPaymentSetup callback but declared inside an if block, so it is out of scope whenever provider groups exist (the normal case).
Environment
- Paytrail for WooCommerce: 2.7.0 (also present on
master as of today)
- WooCommerce: 10.9.4
- WordPress: 7.0.2
- PHP: 8.3
- Checkout: block checkout (
wp:woocommerce/checkout)
- Provider groups configured normally (bank / mobile / card / invoice —
settings.groups is non-empty)
Steps to reproduce
- Use the block checkout with Paytrail enabled and the provider list visible (groups rendered as accordion).
- Add a product to the cart, go to checkout, fill in the required fields.
- Select Paytrail as the payment method, but do not open a group or select an individual provider.
- Click Place order.
Expected
A clear message telling the customer to choose a payment provider — the plugin already has exactly this string in src/Gateway.php:
if ( ! $payment_provider && ! $is_token_payment ) {
wc_add_notice(
__( 'The payment provider was not chosen.', 'paytrail-for-woocommerce' ),
'error'
);
Actual
WooCommerce Blocks shows its generic fallback "There was a problem with your payment option." and the browser console logs ReferenceError: defaultProvider is not defined.
The request never reaches the server, so the PHP-side validation above never runs and its message is never seen.
Root cause
assets/js/containers/paytrail-container.jsx:
let cleanProvider = activeProvider.replace(/-\d+$/, '');
if (!settings.groups || settings.groups.length === 0) {
const defaultProvider = 'paytrail'; // <-- block-scoped to this if
if (!cleanProvider) {
cleanProvider = defaultProvider;
}
}
// ...
useEffect(() => {
const paymentSetup = onPaymentSetup(async () => {
return {
type: emitResponse.responseTypes.SUCCESS,
meta: { paymentMethodData: { payment_provider: cleanProvider || defaultProvider } }
}
});
return () => { paymentSetup(); };
}, [...]);
When settings.groups is non-empty and the customer has not selected a provider, cleanProvider is an empty string, so the || falls through to defaultProvider, which is not defined in that scope. The thrown ReferenceError is caught by WooCommerce Blocks' payment setup handling and replaced with the generic notice.
Impact
This is the default state of every block checkout session — the customer has to actively notice the accordion and pick a provider before the button works. When they don't, they get an error that does not tell them what to do. Since the error appears in the payment section's notice area and the page is not scrolled to it, on mobile the message is typically off-screen entirely, so the customer only sees that nothing happens. In our case this was a measurable drop-off point in checkout.
Suggested fix
Two independent changes:
- Fix the scope bug so the reference resolves:
const defaultProvider = 'paytrail';
let cleanProvider = activeProvider.replace(/-\d+$/, '');
if (!settings.groups || settings.groups.length === 0) {
if (!cleanProvider) {
cleanProvider = defaultProvider;
}
}
- Return a proper error instead of silently falling back. Note that with fix 1 alone, an unselected provider would submit
payment_provider: 'paytrail', which is probably not desirable when groups exist. Emitting an error from the observer would surface a useful message directly in the payment section:
if (settings.groups?.length > 0 && !cleanProvider) {
return {
type: emitResponse.responseTypes.ERROR,
message: __( 'The payment provider was not chosen.', 'paytrail-for-woocommerce' ),
};
}
This reuses the string that already exists on the PHP side, so no new translations are needed.
Summary
On block checkout, if the customer selects Paytrail as the payment method but does not pick an individual payment provider (bank/card/mobile), clicking Place order fails with WooCommerce's generic error "There was a problem with your payment option." instead of the plugin's own, descriptive message.
The cause is a
ReferenceErrorinassets/js/containers/paytrail-container.jsx:defaultProvideris referenced in theonPaymentSetupcallback but declared inside anifblock, so it is out of scope whenever provider groups exist (the normal case).Environment
masteras of today)wp:woocommerce/checkout)settings.groupsis non-empty)Steps to reproduce
Expected
A clear message telling the customer to choose a payment provider — the plugin already has exactly this string in
src/Gateway.php:Actual
WooCommerce Blocks shows its generic fallback "There was a problem with your payment option." and the browser console logs
ReferenceError: defaultProvider is not defined.The request never reaches the server, so the PHP-side validation above never runs and its message is never seen.
Root cause
assets/js/containers/paytrail-container.jsx:When
settings.groupsis non-empty and the customer has not selected a provider,cleanProvideris an empty string, so the||falls through todefaultProvider, which is not defined in that scope. The thrownReferenceErroris caught by WooCommerce Blocks' payment setup handling and replaced with the generic notice.Impact
This is the default state of every block checkout session — the customer has to actively notice the accordion and pick a provider before the button works. When they don't, they get an error that does not tell them what to do. Since the error appears in the payment section's notice area and the page is not scrolled to it, on mobile the message is typically off-screen entirely, so the customer only sees that nothing happens. In our case this was a measurable drop-off point in checkout.
Suggested fix
Two independent changes:
payment_provider: 'paytrail', which is probably not desirable when groups exist. Emitting an error from the observer would surface a useful message directly in the payment section:This reuses the string that already exists on the PHP side, so no new translations are needed.