Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9850dbb
Add an order not for payment details of Voucher payments
danielhuesken Aug 1, 2025
719433e
phpcs and psalm
danielhuesken Aug 1, 2025
3b202d5
phpcs
danielhuesken Aug 1, 2025
fdc6035
add order action and bulk order edit for checking payment on Mollie m…
danielhuesken Sep 2, 2025
2036b9e
add rest endpoint for webhooks
danielhuesken Sep 8, 2025
3eaabf4
fix wrang parameter name
danielhuesken Sep 8, 2025
450a764
fix phpcs
danielhuesken Sep 8, 2025
923040b
Getting orders by transaction, id also onWebhookAction and remove loo…
danielhuesken Sep 9, 2025
4fdf474
add php unit config for integration tests
mmaymo Sep 9, 2025
dad29fb
set rest api default to on
danielhuesken Sep 9, 2025
5070fb0
add @wordpress/scripts for linting
mmaymo Sep 9, 2025
45ab39e
add redux store
mmaymo Sep 9, 2025
1950def
Fix integration test for concurrent webhooks
danielhuesken Sep 9, 2025
df5359a
Exclude js files from phpcs linter
mmaymo Sep 9, 2025
e582e81
Add checkout block components and hoc
mmaymo Sep 9, 2025
609e50d
Merge pull request #1081 from mollie/PIWOO-770-add-order-action-to-sy…
danielhuesken Sep 9, 2025
ccb36d8
Merge branch 'develop' into webhooks-refactor
danielhuesken Sep 9, 2025
8c26920
Fix type error with Germanized
danielhuesken Sep 9, 2025
6424ea6
Merge pull request #1085 from mollie/PIWOO-769-type-error-in-order-li…
mmaymo Sep 10, 2025
5fb7a07
Merge branch 'develop' into webhooks-refactor
danielhuesken Sep 10, 2025
909af64
Merge pull request #1082 from mollie/webhooks-refactor
mmaymo Sep 10, 2025
f490d8a
Merge pull request #1068 from mollie/PIWOO-713-Add-vouchers-order-note
mmaymo Sep 10, 2025
f211260
Fix transaction id is not set in any case and the finding order by mo…
danielhuesken Sep 10, 2025
daa8aa7
removed additional setting of transaction id
danielhuesken Sep 10, 2025
5a467d1
check if the order is paid before mollie cancels it
danielhuesken Sep 11, 2025
efd23a9
prevent problems because of empty gateway classname
danielhuesken Sep 11, 2025
8d1d750
Changed Version to 8.0.6-beta1 and add changelog
danielhuesken Sep 11, 2025
123778c
Consume dispatch from function parameters
mmaymo Sep 17, 2025
394b87f
Merge branch 'develop' into PIWOO-761-redux-store
mmaymo Sep 17, 2025
cc7dfa1
Merge branch 'PIWOO-761-redux-store' into PIWOO-761-block-components-hoc
mmaymo Sep 17, 2025
b745bc4
Remove markup in label prop, pass data
mmaymo Sep 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mollie-payments-for-woocommerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Mollie Payments for WooCommerce
* Plugin URI: https://www.mollie.com
* Description: Accept payments in WooCommerce with the official Mollie plugin
* Version: 8.0.5
* Version: 8.0.6-beta1
* Author: Mollie
* Author URI: https://www.mollie.com
* Requires at least: 5.0
Expand Down
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@woocommerce/dependency-extraction-webpack-plugin": "^1.7.0",
"@wordpress/data": "^6.1.5",
"@wordpress/element": "^4.0.4",
"@wordpress/scripts": "^30.23.0",
"date-and-time": "^0.14.0",
"del": "^3.0.0",
"dotenv": "^16.0.0",
Expand All @@ -48,14 +49,21 @@
"pump": "^3.0.0",
"sass": "^1.83.1",
"sass-loader": "^13.3.3",
"webpack": "^5.97.1",
"webpack-cli": "^5.1.4",
"wp-pot": "^1.10.2",
"webpack": "^5.97.1"
"wp-pot": "^1.10.2"
},
"scripts": {
"watch": "BASE_PATH=. node_modules/.bin/encore dev --watch",
"build": "BASE_PATH=. node_modules/.bin/encore dev",
"setup": "gulp setup",
"lint:md": "wp-scripts lint-md-docs *.md",
"lint:js": "wp-scripts lint-js resources/js/src/*",
"lint:style": "wp-scripts lint-style resources/scss/*.scss",
"lint:js:fix": "wp-scripts lint-js resources/js/src/* --fix",
"lint:style:fix": "wp-scripts lint-style resources/scss/*.scss --fix",
"lint:php": "yarn phpcs && yarn psalm",
"lint:php-fix": "vendor/bin/phpcbf --parallel=8",
"e2e-activation": "npx playwright test --project=activation",
"e2e-simple": "npx playwright test --project=simple-classic",
"e2e-block": "npx playwright test --project=simple-block",
Expand Down
7 changes: 7 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
<file>./inc</file>
<file>./src</file>
<exclude-pattern>./tests/</exclude-pattern>
<exclude-pattern>*.js</exclude-pattern>
<exclude-pattern>*.jsx</exclude-pattern>
<exclude-pattern>*.ts</exclude-pattern>
<exclude-pattern>*.tsx</exclude-pattern>
<exclude-pattern>*.css</exclude-pattern>
<exclude-pattern>*.scss</exclude-pattern>
<exclude-pattern>*.json</exclude-pattern>

<config name="testVersion" value="7.4"/>
<config name="ignore_warnings_on_exit" value="1"/>
Expand Down
8 changes: 8 additions & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ Automatic updates should work like a charm; as always though, ensure you backup

== Changelog ==

= 8.0.6-beta1 - 11-09-2025 =
* Fixed - TypeError in OrderLines.php when processing vouchers with Germanized plugin
* Fixed - Apple Pay not eligible for Express Checkout in editor with non-Apple Pay compatible device
* Fixed - Block Editor error `This block encountered an error and cannot show a preview`
* Fixed - The new `woocommerce_cancel_unpaid_order` filter causes orders not created via checkout to be cancelled
* Improvement - Improve handling and validation of phone numbers
* Improvement - Improve handling of webhooks that uses now the REST API and depending on transaction id

= 8.0.5 - 11-08-2025 =
* Added - PayPal Subscriptions support through Vaulting
* Fixed - Save Mandates from Payments API
Expand Down
9 changes: 9 additions & 0 deletions resources/js/src/checkout/blocks/components/Label.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const Label = ({item}) => {
console.log(item.label.title, item.label.icon); // Debugging purposes
return (
<>
<span style={{marginRight: '1em'}}>{item.label.title}</span>
{item.label.icon && <img src={item.label.icon} alt=""/>}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import CreditCardComponent from './paymentMethods/CreditCardComponent';
import DefaultComponent from './paymentMethods/DefaultComponent';
import PaymentFieldsComponent from './paymentMethods/PaymentFieldsComponent';
import withMollieStore from '../hoc/withMollieStore';

/**
* Factory function to create appropriate payment component with store connection
* Maps payment method names to their corresponding components with proper configuration
* @param {Object} item
* @param {Object} commonProps
*/
export const createPaymentComponent = ( item, commonProps ) => {
if ( ! item || ! item.name ) {
return <div>Loading payment methods...</div>;
}

switch ( item.name ) {
case 'mollie_wc_gateway_creditcard':
const CreditCardWithStore = withMollieStore( CreditCardComponent );
return <CreditCardWithStore { ...commonProps } />;

case 'mollie_wc_gateway_billie':
const BillieFieldsWithStore = withMollieStore(
PaymentFieldsComponent
);
return (
<BillieFieldsWithStore
{ ...commonProps }
fieldConfig={ {
showCompany: true,
companyRequired: true,
companyLabel: item.companyPlaceholder || 'Company name',
} }
/>
);

case 'mollie_wc_gateway_in3':
const In3FieldsWithStore = withMollieStore(
PaymentFieldsComponent
);
return (
<In3FieldsWithStore
{ ...commonProps }
fieldConfig={ {
showPhone: true,
showBirthdate: true,
phoneRequired: true,
birthdateRequired: true,
phoneLabel: item.phoneLabel || 'Phone',
birthdateLabel:
item.birthdatePlaceholder || 'Birthdate',
} }
/>
);

case 'mollie_wc_gateway_riverty':
const RivertyFieldsWithStore = withMollieStore(
PaymentFieldsComponent
);
return (
<RivertyFieldsWithStore
{ ...commonProps }
fieldConfig={ {
showPhone: true,
showBirthdate: true,
phoneRequired: true,
birthdateRequired: true,
phoneLabel: item.phoneLabel || 'Phone',
birthdateLabel:
item.birthdatePlaceholder || 'Birthdate',
} }
/>
);

default:
const DefaultWithStore = withMollieStore( DefaultComponent );
return <DefaultWithStore { ...commonProps } />;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { MOLLIE_STORE_KEY } from '../store';
import { createPaymentComponent } from './PaymentComponentFactory';

/**
* Main Mollie Component - Orchestrates payment method rendering
* Handles common payment processing and delegates specific logic to child components
* @param {Object} props
*/
export const PaymentMethodContentRenderer = ( props ) => {
const { useEffect } = wp.element;
const { useSelect } = wp.data;
Comment on lines +10 to +11
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be imports - at the very least they should not be assigned in the render function

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I will tackle this in another PR


const {
activePaymentMethod,
billing,
item,
jQuery,
emitResponse,
eventRegistration,
requiredFields,
shippingData,
isPhoneFieldVisible,
} = props;

const { responseTypes } = emitResponse;
const { onPaymentSetup } = eventRegistration;

// Redux store selectors - only for payment processing
const selectedIssuer = useSelect(
( select ) => select( MOLLIE_STORE_KEY ).getSelectedIssuer(),
[]
);
const inputPhone = useSelect(
( select ) => select( MOLLIE_STORE_KEY ).getInputPhone(),
[]
);
const inputBirthdate = useSelect(
( select ) => select( MOLLIE_STORE_KEY ).getInputBirthdate(),
[]
);
const inputCompany = useSelect(
( select ) => select( MOLLIE_STORE_KEY ).getInputCompany(),
[]
);

const issuerKey =
'mollie-payments-for-woocommerce_issuer_' + activePaymentMethod;

// Main payment processing - stays centralized for all payment methods
useEffect( () => {
const onProcessingPayment = () => {
const data = {
payment_method: activePaymentMethod,
payment_method_title: item.title,
[ issuerKey ]: selectedIssuer,
billing_phone: inputPhone,
billing_company_billie: inputCompany,
billing_birthdate: inputBirthdate,
cardToken: '',
};
const tokenVal = jQuery( '.mollie-components > input' ).val();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is all we need jQuery for, should we not think about doing it in vanilla JS instead?

A more thorough solution would be to expose this type of data through te redux store and then consume it via withSelect and/or a higher-order-component

Copy link
Collaborator Author

@mmaymo mmaymo Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, using the token from the store is done in a later PR but I've also expanded on the use of the store there after your suggestion, I think I can also use in the payment fields, I will update those as well. Removing jQuery is also a job started later but I will take a look in case i missed somewhere. Thanks!

if ( tokenVal ) {
data.cardToken = tokenVal;
}
return {
type: responseTypes.SUCCESS,
meta: {
paymentMethodData: data,
},
};
};

const unsubscribePaymentProcessing =
onPaymentSetup( onProcessingPayment );
return () => {
unsubscribePaymentProcessing();
};
}, [
selectedIssuer,
onPaymentSetup,
inputPhone,
inputCompany,
inputBirthdate,
activePaymentMethod,
issuerKey,
item.title,
jQuery,
responseTypes.SUCCESS,
] );

// Prepare common props for child components
const commonProps = {
item,
jQuery,
useEffect,
billing,
shippingData,
eventRegistration,
requiredFields,
isPhoneFieldVisible,
activePaymentMethod,
};

return createPaymentComponent( item, commonProps );
};
33 changes: 33 additions & 0 deletions resources/js/src/checkout/blocks/components/molliePaymentMethod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { PaymentMethodContentRenderer } from './PaymentMethodContentRenderer';
import { Label } from './Label';

const molliePaymentMethod = (
item,
jQuery,
requiredFields,
isPhoneFieldVisible
) => {
return {
name: item.name,
label: <Label item={ item } />,
content: (
<PaymentMethodContentRenderer
item={ item }
jQuery={ jQuery }
requiredFields={ requiredFields }
isPhoneFieldVisible={ isPhoneFieldVisible }
/>
),
edit: <div>{ item.edit }</div>,
paymentMethodId: item.paymentMethodId,
canMakePayment: () => {
//only the methods that return is available on backend will be loaded here so we show them
return true;
},
ariaLabel: item.ariaLabel,
supports: {
features: item.supports,
},
};
};
export default molliePaymentMethod;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const BirthdateField = ( { label, value, onChange } ) => {
const handleChange = ( e ) => onChange( e.target.value );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be an optimization to declare this function outside of the component (so it does not get reinitialized everytime we re-render)

As an alternative, perhaps look into useCallback https://react.dev/reference/react/useCallback

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked some docs, cause makes sense what you say, but if I understood right, declaring the function outside or using useCallback only helps if we pass it down to children or use it as a dependency in an effect. In our case the handler is only attached to a native (not a React child), so its identity doesn’t cause any extra renders. And if taking out I would need a way to pass the prop to that function. But I will add a new task to check in the parent components if the child components can be memoized.

const className =
'wc-block-components-text-input wc-block-components-address-form__billing-birthdate';

return (
<div className="custom-input">
<label htmlFor="billing-birthdate">{ label }</label>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jsx tag isn't closed properly here, right? Also this can probably be a short tag

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, no, I think is closed right, is not a void element, but I will take a look at all the other elements around

<input
type="date"
className={ className }
name="billing-birthdate"
id="billing-birthdate"
value={ value }
onChange={ handleChange }
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const CompanyField = ( { label, value, onChange } ) => {
const handleChange = ( e ) => onChange( e.target.value );
const className =
'wc-block-components-text-input wc-block-components-address-form__billing_company_billie';

return (
<div className="custom-input">
<label htmlFor="billing_company_billie">{ label }</label>
<input
type="text"
className={ className }
name="billing_company_billie"
id="billing_company_billie"
value={ value }
onChange={ handleChange }
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const CreditCardField = ( { content } ) => {
return <div dangerouslySetInnerHTML={ { __html: content } } />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const IssuerSelect = ( {
issuerKey,
issuers,
selectedIssuer,
updateIssuer,
} ) => {
const handleChange = ( e ) => updateIssuer( e.target.value );

return (
<select
name={ issuerKey }
dangerouslySetInnerHTML={ { __html: issuers } }
value={ selectedIssuer }
onChange={ handleChange }
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const PhoneField = ( { id, label, value, onChange, placeholder } ) => {
const handleChange = ( e ) => onChange( e.target.value );
const className = `wc-block-components-text-input wc-block-components-address-form__${ id }`;

return (
<div className="custom-input">
<label htmlFor={ id }>{ label }</label>
<input
type="tel"
className={ className }
name={ id }
id={ id }
value={ value }
onChange={ handleChange }
placeholder={ placeholder }
/>
</div>
);
};
Loading