BazPay for Magento 2 and Adobe Commerce

Built for the stores Magento is actually used for: several websites, several currencies, a cache layer in front and a finance team that works in invoices and credit memos.

  • TypeExtension
  • Runs onOpen Source / Adobe Commerce 2.4.6+
  • First installHalf a day
  • Reviewed

Install with Composer

There is no zip and no admin uploader, deliberately. A Magento store has a deploy pipeline, and a payment extension that arrives outside it is an extension nobody can roll back.

Install, enable, compile
# from the project root, in maintenance modecomposer require bazpay/module-paymentsbin/magento module:enable BazPay_Paymentsbin/magento setup:upgradebin/magento setup:di:compilebin/magento setup:static-content:deploy -fbin/magento cache:flush# confirm it is enabled and schema-currentbin/magento module:status BazPay_PaymentsModule is enabled

On Adobe Commerce cloud, commit the composer.json and composer.lock changes and let the deploy hooks run setup:upgrade — do not run these by hand on an ephemeral node.

Then set the keys at Stores → Configuration → Sales → Payment Methods → BazPay. Start in test mode; the sandbox key pair is visible in the dashboard immediately after sign-up, without an approval step. The module writes its webhook endpoint into your BazPay account when you first save a valid secret key.

Before you deploy

Pin an exact version rather than a caret range on production. We follow semantic versioning and the API is dated, but a payment path is the last place you want an unattended minor upgrade landing during a sale weekend.

Configuration scope

Every field the module exposes can be overridden at website or store-view level. This is the part worth planning before you start clicking, because the wrong choice at default scope quietly propagates to storefronts you did not mean to touch.

What to set at which scope
Setting Sensible scope Why
API keys Website One legal entity per website is the common shape; store views share its keys.
Enabled methods Store view A Dutch view leads with iDEAL; a German one does not need it at all.
Statement descriptor Store view The shopper should recognise the brand they bought from, not the group name.
Payment action Website Capture policy is a finance decision, and it should not differ per language.
Debug logging Default You want it on or off everywhere while diagnosing, never in one view only.

Method availability can be narrowed further by billing country, order total and customer group, so a wholesale group can be shown SEPA Direct Debit while retail sees cards and wallets. The full list of what can be enabled is on the method reference.

Invoices, captures, credit memos

Magento already has vocabulary for taking and returning money, and the module maps onto it rather than inventing a parallel one. Your finance team keeps working in the screens they know.

Magento action to gateway operation
In Magento At the gateway Notes
Order placed Payment authorised Funds reserved. Nothing has moved yet under Authorize.
Invoice, capture online Capture Partial invoices produce partial captures, one per invoice.
Invoice, capture offline Nothing Only correct when the money was already taken outside Magento.
Credit memo, refund online Refund Full or partial, unlimited count, up to the captured total.
Cancel order Void the authorisation Only possible before capture. After capture it is a refund.

Under Authorize, the authorisation holds for seven days on cards and is voided automatically when it expires. Under Authorize and Capture, the invoice is created with the order. Bank methods such as iDEAL and BLIK are inherently capture-at-authorisation, so they invoice immediately whatever the setting says — the money has already moved and pretending otherwise would misstate the ledger.

Cron and reconciliation

Order state is driven by signed webhooks, so payments do not depend on cron. Cron is the safety net for the cases webhooks cannot cover on their own.

  • Re-fetching events that arrived while the store was down or mid-deploy.
  • Voiding authorisations that reached expiry without an invoice.
  • Cancelling pending orders whose payment was abandoned at the bank.
  • Writing the daily settlement reference onto captured transactions.
Check the reconciliation job
# is the reconciliation job running at all?bin/magento cron:run --group=bazpay# last ten runs, newest firstmysql -e "SELECT job_code, status, executed_at        FROM cron_schedule        WHERE job_code LIKE 'bazpay%'        ORDER BY schedule_id DESC LIMIT 10"bazpay_reconcile  success  2026-07-27 09:15:02bazpay_reconcile  success  2026-07-27 09:00:01# rows stuck at 'pending' mean Magento cron is not running

The job group is bazpay and it runs every fifteen minutes. Rows that stay pending mean Magento's own cron is not running, which will be breaking far more than payments.

Cache, indexers, inventory

Most Magento payment incidents are not payment bugs. They are a cache layer, an asynchronous indexer or an inventory reservation behaving exactly as documented, in a place nobody expected to matter.

Full-page cache

The webhook route must not be cached and must not require a session. Add /bazpay/webhook to the pass list in Varnish or Fastly, and exclude it from any bot-protection rule that challenges unknown user agents — our sender will not solve a JavaScript challenge, and a challenged webhook is a webhook that never arrives.

Asynchronous order grid

With sales_grid_async_indexing enabled, the sales grid trails the actual order state by an indexer run. An order can be paid, invoiced and correct while the grid still shows it as pending. Check the order view, not the grid, when verifying a payment.

Inventory reservations

Multi-source inventory places a reservation when the order is created and releases it if the payment fails. A store that cancels pending orders manually, rather than letting the module's cron job do it, will slowly leak reservations and show stock that is not really committed.

Diagnostics

Turn on debug logging in the module configuration and reproduce the order. Every request and response is written to var/log/bazpay.log with the request id, and card data is redacted before it is written — the log is safe to attach to a support ticket.

Common symptoms on Magento specifically
Symptom Usually Fix
BazPay missing from checkout Config set at the wrong scope Switch the scope selector to the store view and check it is enabled there.
Payment succeeds, order stays pending Webhook cached or challenged Pass /bazpay/webhook through Varnish and the WAF.
Method list ignores a change Config cache bin/magento cache:clean config after every scope change.
Refund button does nothing Offline credit memo chosen Use Refund, not Refund Offline.
Class not found after deploy Stale compiled DI Re-run setup:di:compile; the module ships plugins and factories.

For the wire-level detail — signature verification, the retry ladder, replaying an event by hand — see the webhooks guide. For forcing a specific decline while you test the unhappy paths, sandbox behaviour lists the cards that produce each reason code on demand.

Common questions

Does the extension work on Adobe Commerce as well as Open Source?
The same package serves both from 2.4.6 upward, including Adobe Commerce on cloud infrastructure. Cloud projects install it as an ordinary Composer dependency and deploy it through the normal pipeline; nothing needs to be uploaded through an admin screen.
Can different store views take different payment methods?
Yes, and this is the main reason merchants choose Magento in the first place. Every setting the module exposes can be overridden at website or store-view scope, so a German view can show cards and SEPA while a Dutch one leads with iDEAL, each with its own descriptor on the shopper's statement.
Do we have to run Magento cron for payments to work?
Payments succeed without it, because order state is driven by webhooks. Cron handles the safety net: reconciling any event that was missed while the store was unreachable, expiring stale authorisations and cleaning up abandoned pending orders. A store with no cron will eventually accumulate orders that never resolve.
How does the module handle a store with several currencies?
We charge in the currency the order was placed in, not the base currency. The presented amount, the authorised amount and the settled amount are therefore the same number, which is what keeps the reconciliation file matching the sales report without a manual conversion step.
Is the source available for review before we install it?
Yes. The package is source-available to merchants using it, so your team can read it, run it through a static analyser and pin a version in composer.json like any other dependency. Nothing is obfuscated and there is no phone-home beyond the API calls the payment itself requires.

Continue

Not sure this is the right route?

Send the platform, the version and the markets you sell into. An engineer answers with the plugin, the API path or an honest "not yet" — not a discovery call.