Building a buy now, pay later payment module for Magento 2 from scratch

Home / Blog / Building a buy now, pay later payment module for Magento 2 from scratch


A shopper fills a cart, reaches the total, and stalls. Not because they don't want the thing, but because they don't want the whole price this month. That's the sale you lose without ever hearing about it. Over the last eight weeks we built a Magento 2 payment module for Float, a South African provider that lets shoppers split a purchase into interest-free monthly instalments on the credit card they already carry, while the merchant is paid in full upfront. It's live now, and this is what building a buy-now-pay-later payment method into Magento actually takes.

What the shopper gets, and what the merchant gets

Float is card-linked instalments, which is worth distinguishing from most buy-now-pay-later. There's no new credit line and no application. Your customer uses the available limit on the Visa or Mastercard already in their wallet, splits the purchase into interest-free monthly instalments, and keeps earning their usual rewards. It's their own credit, rearranged.

The merchant side is the part that decides whether this is interesting: you're paid the full amount upfront. You aren't financing anything and you aren't holding the risk. What changes is how your price reads. A number that felt like a lot all at once becomes a monthly figure the shopper can say yes to, and carts that used to die at the total get completed.

The option has to appear before the checkout

Here's the thing we got wrong in our first sketch, and it's the most important design decision in the module. We started by treating this as a checkout feature. Add the payment method, done.

But a shopper who reaches your checkout has already decided what they can afford. They built the cart under one set of assumptions. Telling them about instalments at the payment step is too late to change what's in it. The information has to reach them while they're still choosing.

So the module puts an instalment preview on the product page and on product listings: this item, from this much per month. Three details we care about:

  • It reads the merchant's own Float configuration to find the real maximum term available to them, rather than hardcoding a number in a template. Terms change on the provider's side without anyone having to remember to edit a phtml file.
  • If no instalment plan is available for a product, the preview hides itself rather than rendering something confusing.
  • It respects catalog price rules, which took a fix. A preview that quotes the pre-discount price is worse than no preview at all.

There's also a "How it works" modal, because the first question a shopper has is "what is this, and what is it going to do to my credit card." Answering that in place, without sending them off to another site, keeps them in the flow. Most of our final week went into that modal's mobile behaviour, which tells you something about where the real work in ecommerce lives.

Building it on Magento's payment framework, not around it

The fast way to build a payment method is a controller that calls the provider's API and writes to the order. We didn't do that, and the reason is upgrades: a module that reaches around the framework becomes the merchant's problem the next time Magento moves.

So it's a proper gateway integration. A facade virtualType on `Magento\Payment\Model\Method\Adapter`, a command pool, and a validator pool:

<virtualType name="FloatValidatorPool" type="Magento\Payment\Gateway\Validator\ValidatorPool">
    <arguments>
        <argument name="validators" xsi:type="array">
            <item name="currency" xsi:type="string">X2Y\FloatPayments\Gateway\Validator\Currency</item>
            <item name="country" xsi:type="string">X2Y\FloatPayments\Gateway\Validator\Country</item>
        </argument>
    </arguments>
</virtualType>

Eligibility as declarative validators means Float appears only where the merchant configured it to, and adding a rule later doesn't mean touching checkout logic. Availability is an observer that checks the cart against configured minimum and maximum amounts, so a tiny cart doesn't get offered instalments. A single `sale` command records the transaction, because a Float order moves to processing on success rather than going through a separate capture.

Credentials are the one genuinely fiddly part. The module logs into Float through an SPI, then caches the returned login data and token in config. The token doesn't expire, so there's no refresh cycle to babysit, and re-saving the payment configuration in admin refreshes it if it ever goes stale. That's a deliberately dumb design and we'd pick it again.

The parts nobody asks for but everybody needs

A payment module is judged on the day it misbehaves, so a good part of eight weeks went into things no feature list mentions:

  • Multi-site configuration scope. Credentials and settings per store view, not global. Merchants with two brands are common and they find global config immediately.
  • Configurable order status on success, because the merchant's fulfilment workflow is theirs. We shouldn't be dictating what state a paid order lands in.
  • Multishipping support, which is one of those Magento surfaces that quietly breaks payment methods that only ever got tested on the standard one-page flow.
  • Its own log file with a debug toggle in admin. Every request and response to Float, in one place, switchable. This has already earned its keep several times.
  • CSRF handling on the return endpoints, which a provider POSTing back to you will make you think about whether you wanted to or not.

And a long tail of the specific: a customer first-and-last-name mismatch, a logo cropped on listing pages, currency missing from the preview message, a module that wouldn't fully disable when switched off. None of it is interesting. All of it is the difference between a module a merchant trusts and one they route around.

What's next

The order flow is where our attention goes from here. Right now the order is created when the customer returns to our success controller, and the interesting question in any redirect payment method is whether that's the right moment: too early and you accumulate orders nobody paid for, too late and a payment can land with nothing to attach it to. We have opinions forming and production data coming in, which is the right order for those two things to happen.

Compatibility-wise it's built for current Magento 2.4 on PHP 7.4 and up, and it installs the same way on Adobe Commerce.

If you need a gateway in your checkout

If you're a payment provider who needs merchants to be able to install you, or a merchant who wants a specific gateway at checkout and found there's no module or a stale one, that's the work we do. We build these from scratch on the platform's own payment framework and then maintain them as both sides change.

Tell us which gateway and which platform, and we'll tell you honestly what it involves.


Written by X2Y.DEV
Float Magento Adobe Commerce BNPL Payments Payment Gateway
Back to Blog

0%