Skip to content

feat(mollie): charge an invoice in its billing currency (optional) - #182

Merged
Panelica merged 5 commits into
Panelica:mainfrom
ertugrulkeyvan:feat/mollie-billing-currency
Oct 12, 2026
Merged

Panelica merged 5 commits into
Panelica:mainfrom
ertugrulkeyvan:feat/mollie-billing-currency

Conversation

@ertugrulkeyvan

@ertugrulkeyvan ertugrulkeyvan commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

What

The Stripe option from #172, for Mollie: charge an invoice in the currency the customer is billed in.

With customers billed in their own currency (Setup → Currencies, the invoice's billing stamp), Mollie still charged in the shop currency.

Take a shop that prices in euros and bills a Polish customer in zloty. A €10.00 invoice raised at 4.30 shows zł43.00, but the customer was charged €10.00. Their bank then converted it at its own rate, often with a fee, and the statement matched nothing they had seen.

A new Mollie setting, Charge in the invoice's billing currency, off by default:

  • On, for an invoice billed in another currency:
    • the payment is created in that currency at the invoice's own rate;
    • it carries what it stands for in its metadata: source currency, source amount and rate.
  • Recording: the webhook reads that back.
    • A payment in full is recorded as exactly the invoice amount, with no cent lost to rounding there and back.
    • A part is converted at the same rate.
  • Refunds: an amount in the invoice's currency goes back in the currency the payment was taken in. A full refund returns exactly what was taken, never more.
  • Off, or for an invoice billed in the shop currency: every request is the one sent today. A refund looks the payment up only for invoices billed in another currency.
  • Currencies Mollie takes: only the 28 currencies on Mollie's multicurrency page (https://docs.mollie.com/docs/multicurrency, fetched 2026-10-12). A billing currency not on it, such as TRY, falls back to the shop currency, exactly as with the option off.
  • Decimals: amounts are written with the currency's own decimals. ISK and JPY have none (1625, not 1625.00).

One small fix on the way: the pay button said "Pay ", formatted in the shop currency. It now names what is still due, in the currency that will be charged, the same as the Stripe button since #166.

How

  • Module: MollieModule::billingCharge(), recordedAmount(), billedAmount() and billedInAnotherCurrency() follow the Stripe module's methods of the same names. They are adapted to Mollie's decimal amount.value and its payment metadata.
  • Hooks: capture(), processWebhook(), refund() and getPaymentForm() use them.
  • Admin: the setting uses the field description added in feat(stripe): charge an invoice in its billing currency (optional) #172.
  • Docs: a Mollie section in docs/guides/payment-gateways.md, with the option.

The work is split into five commits: module, docs and tests, then the currency list and decimals from review and their tests.

Tests

tests/Feature/MollieBillingCurrencyTest.php, 12 tests, all against faked API responses. We have no live Mollie account, so this has not been run against Mollie itself.

  • Off: a zloty-billed invoice is charged in euros as before.
  • On: it is charged zł43.00 at the invoice's rate, with the metadata.
  • Shop-currency invoice: charged in euros either way.
  • Pay button: names what is due in the currency to be charged, with the option on and off.
  • Webhook:
    • a payment in full is recorded as €10.00;
    • a part (zł21.50) as €5.00;
    • a payment in the shop currency as it came.
  • Refunds:
    • a full refund sends zł43.00 and a part (€2.50) sends zł10.75;
    • a refund for a shop-currency invoice sends what it always sent, with no lookup.
  • Unsupported currency: a lira-billed invoice with the option on is charged in euros, and the pay button says so.
  • Decimals: a yen-billed invoice is sent as 1625.

Eight of them fail on main or on the first version of this branch. All gateway and payment tests pass (106).

Full suite: 4098 passed. The one failure is the CurrencyAndBackupTest db backup test, which fails on main too in my local setup.

@Panelica Panelica left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thank you, @ertugrulkeyvan. Off by default, with the payment carrying its own rate in the metadata, this is a careful port of the Stripe option, and the webhook and refund paths read it back the right way.

Two cases need handling before it can go in:

  1. A billing currency Mollie does not take blocks the payment. Mollie's multicurrency list has 34 currencies and Turkish lira is not one of them. With the option on, a lira-billed invoice is sent in TRY, a currency Mollie does not support, so the customer cannot pay that invoice with Mollie; today it is charged in the shop currency. The option should fall back to the shop currency for a billing currency Mollie does not list.
  2. Currencies without minor units. Mollie lists JPY and ISK with zero decimals, while amount.value is always written with two (1625.00 for a yen invoice). Its documentation does not say what it does with that, and neither of us can try it without a live account, so the value should follow the currency's own decimals (or fall back as in 1).

Neither changes anything while the option is off. Could you add both, with a test each?

@ertugrulkeyvan

Copy link
Copy Markdown
Contributor Author

Thank you, both handled:

  1. Unsupported currency: the option only applies to a billing currency on Mollie's multicurrency page (https://docs.mollie.com/docs/multicurrency, fetched today). It lists 28 there; if you counted 34 somewhere else, tell me where and I will align the list. Anything else, such as TRY, is charged in the shop currency exactly as with the option off, and the pay button names that amount.
  2. Decimals: amount.value follows the currency's decimals from the same page. ISK and JPY have none, so a yen invoice is sent as 1625. The same goes for refunds and the shop-currency path.

There is a test for each. Both fail on the previous version of the branch.

@Panelica
Panelica merged commit 19031a8 into Panelica:main Oct 12, 2026
1 check passed
@Panelica

Copy link
Copy Markdown
Owner

Thank you, @ertugrulkeyvan, merged. Mollie can now charge an invoice in its billing currency, off by default; a billing currency Mollie does not take falls back to the shop currency, and amounts are written with the currency's own decimals.

Checked before merging: your currency list and the two zero-decimal currencies match Mollie's multicurrency page, the requests with the option off are the ones sent before, your tests fail on main and pass here, and the full suite passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants