Paying affiliates in stablecoins without holding a float
A disclosure first, because it is the kind of thing a reader should not have to discover on their own: Affiliar is built by the same team as Coinflux. It is not an arm’s-length customer and this is not a testimonial. It is the more useful thing — an account of what happened when we pointed our own live money system at our own gateway, including the parts we designed defensively because we did not trust the migration.
The problem an affiliate platform has
Affiliar is an affiliate marketing platform for iGaming operators. Operators pay affiliates a commission; affiliates in turn pay their own sub-affiliates. Both are real outbound money movements, and both have a property that makes the usual gateway shape a bad fit: the recipients are not customers of the gateway. An affiliate with a USDT wallet has no account anywhere. They are an address and an amount owed.
The conventional answer is a custodial provider. The platform funds a balance, the provider holds it, payouts draw it down. That means a float — platform money sitting in someone else’s wallet, sized to cover the next payout run, exposed to that provider’s security and solvency for as long as it sits there. It is the arrangement that made Alphapo’s hot wallets worth stealing.
We did not want a float, so we built the payout path so it would not need one.
Non-custodial changes what the gateway is
In the Coinflux model the gateway never holds the money, which sounds like a security claim and is really an architectural one. Every commission payout originates from the operator’s own wallet. Coinflux opens a withdrawal request, the operator approves it in the dashboard and pays from funds they controlled the whole time, and a signed webhook reports the outcome back.
So the gateway is two things: an approval desk and a record. It is not a vault. There is no balance of Affiliar’s to drain, no float to size, and no question of what happens to our money if the provider has a bad quarter — there is no money of ours there to have a question about.
What this does require is that the platform keeps its own books honestly. Coinflux does not know what an affiliate has earned, so Affiliar runs an internal balance ledger: approved commissions earned, minus everything already paid or reserved against them, including what an affiliate owes their own sub-affiliates. A payout entering pending earmarks the funds immediately. That is the piece that stops an operator funding the same commission dollar twice, and it belongs to the platform, not to the gateway.
The migration was designed to be undone
Affiliar was already live on another provider, moving real commissions. Nobody sane cuts a working money path over in a single deploy, so the Coinflux integration shipped behind an environment variable with the old provider as the default. The code went to production and changed nothing. Flipping one variable and restarting switched providers; flipping it back reverted, in the time it takes a process to boot.
That flag is the only reason the cutover was a calm afternoon rather than an event. Affiliate payouts moved first. Sub-affiliate payouts followed the next day, onto the same withdrawal call. Subscription billing — Affiliar charging its own operators for their plans — moved a few hours later, through the deposit side of the same tenant.
Two weeks later the old provider was deleted from the codebase. Not deprecated: removed, along with its chapter in the documentation. The flag had done its job and there was nothing left to fall back to, which is a decision you only make after watching the new path carry real money for a while.
Where the webhook meets the state machine
The interesting failure surface in a payout system is not the send. It is the gap between “we asked” and “we know what happened”, and that gap is a webhook.
Affiliar’s payout rows move from pending to processing to paid or failed. The dispatch call returns a withdrawal id and nothing else; paid is only ever written by an incoming Coinflux callback, verified with HMAC-SHA256 over the raw body and matched back to the payout by the reference we sent. An approval cascades further than the payout row — the commission reports behind it flip to paid too, because from the affiliate’s side the money and its paperwork are one event. A decline flips the row to failed with the provider’s reason attached, and the affiliate can retry from the same row without anyone rebuilding it by hand.
A signature that does not verify is a 401, not a shrug. It is worth saying plainly, because the tempting implementation — log the mismatch, process the payload anyway, sort it out later — turns your payout state machine into something anyone who can find your callback URL can drive.
Then the recipients got a choice
Once one provider was carrying everything, affiliates were given a choice of network and stablecoin rather than a single hardcoded rail: USDT on Tron, USDT or USDC on Ethereum and BNB Smart Chain. The pair is re-validated against a registry on the platform side before dispatch, and again by the gateway, because an address sent to the wrong chain is not an error message — it is a support ticket about money.
This is the part that is hard to retrofit onto a custodial float and straightforward when the payment leaves the operator’s own wallet.
What to ask, if you are paying people rather than collecting
Three questions separate a payout path you can sleep through from one you cannot. Whose wallet does the money leave from, and how much of mine sits with you between runs? Can I switch providers with a configuration change, or is the integration welded to one API? And when a payout is approved, what writes paid in my database — your callback with a signature I verify, or a polling loop that guesses?
The float question is the one most platforms never ask, because a balance at a provider feels like a feature until the morning it is a headline.