Most of the flow is the same as Stripe and OPP: payway calls the same functions (`payment_url`, `process_payments`, `refund`, `process_cancel`, `onboard`) and gets the same response shapes. The differences are below.
Mollie's webhook has no signature or hash, and its body contains only the payment id. Instead of checking a signature like Stripe does, the module re-fetches the payment from Mollie's API with the merchant's token and compares it with the record it created: amount, currency, profile, and the `company_id`, `shop_id`, `order_id` and `payment_id` in the payment's metadata. If anything doesn't match, nothing is updated and a Telegram alert is sent.
The order ids travel in the webhook URL's query string (`/v1/webhooks/mollie/payments?company_id=…&shop_id=…&order_id=…`), because payway only forwards a JSON body and Mollie sends a form body.
`onboard` only registers the company, without a merchant id. The merchant connects their own Mollie account through the "Connect with Mollie" link returned by the new `session` endpoint. After they approve, Mollie sends them back to `process_oauth` through payway's `/v1/webhooks/mollie/oauth`.
`process_oauth` then stores the tokens and sets `provider_id` and `provider_identifier` (the Mollie organization id) on the company. If the company is already live on another provider, it leaves those alone and returns `switch_required: true`.
Every call uses the merchant's own OAuth token, not a platform key, and every payment is created on the merchant's own Mollie profile. Both tokens are stored encrypted in the new `companies.metadata` column (payments DB), since Mollie requires the refresh token to be stored encrypted. The access token is refreshed automatically when it expires.
OAuth tokens have no test or live mode of their own, so the mode is set by `testmode` in the config. Without it, payments are live.
Returns the merchant's Mollie onboarding status and capabilities, plus `reauthorization_required: true` when the merchant connected before a permission the module now needs and has to reconnect.
Config
"orderlemon_config": {
"payments": {
"mollie": {
"testmode": true,
"oauth": {
"client_id": "app_xxx",
"client_secret": "xxx",
"encryption": {
"key_id": "v1",
"keys": { "v1": "<output of openssl rand -base64 32>" }
}
}
}
}
}
- `client_id` and `client_secret` come from the OAuth app in the Mollie dashboard. Its redirect URL must be exactly `<payway_url>/webhooks/mollie/oauth`, e.g. `https://payway.orderlemon.dev/v1/webhooks/mollie/oauth` on dev.
- The encryption key is generated by us and never sent to Mollie. Every environment that shares the same payments database must use the same key.
Every endpoint must be listed in `allowed_functions` in the Mollie microservice's config, or the call returns 405:
"allowed_functions": {
"GET": ["payment_url", "embed", "expire_payment", "payouts_list", "account_info", "session_token", "session"],
"POST": ["onboard", "process_payments", "refund", "process_cancel", "process_oauth"],
"PUT": [],
"DELETE": []
}
New compared to the other providers: `session`, `session_token`, `process_oauth` and `account_info`.
payway's own config also needs `session` under `GET`, because the panel calls payway, which forwards it to this microservice.
The "Connect with Mollie" link asks the merchant for these permissions:
| Scope | Used for |
| organizations.read | Reading the merchant's organization id when they connect, and `account_info` |
| profiles.read | Reading the profile the payments are created on |
| payments.read | Re-fetching a payment in the webhook (`process_payments`), and before cancelling it |
| payments.write | Creating payments (`payment_url`) and cancelling them (`process_cancel`, `expire_payment`) |
| refunds.read | Not used yet; requested together with `refunds.write` |
| refunds.write | Creating refunds (`refund`) |
| settlements.read | Listing payouts (`payouts_list`) |
| onboarding.read | Onboarding status and capabilities (`account_info`) |
They are set in code (`mollieOAuthScopes()` in `v1/general/ms_functions.php`). A `scopes` list under `orderlemon_config.payments.mollie.oauth` replaces the default completely, so only set it if you need a different list.
Mollie doesn't add new permissions to an existing connection. When a scope is added, merchants who are already connected have to go through "Connect with Mollie" again.