A CFD platform has two payment flows, not one
An e-commerce merchant collects money once and ships a good. A CFD broker does something fundamentally two-sided: it takes deposits to fund trading accounts, and it pays withdrawals back out when a trader closes positions and asks for their balance. Both legs are continuous, both are cross-border by default, and both fail in their own way on legacy rails. A deposit dies at an issuer that flags the merchant category; a withdrawal stalls in a correspondent-bank chain that adds days and intermediary fees the broker has to absorb or explain.
The trader does not separate these the way a payments team does — to them it is one relationship, and a slow or rejected withdrawal poisons the next deposit. A broker that funds an account in seconds but takes a week to return a balance has not solved its payment problem, only moved it downstream. The win is making both directions move on the same fast, predictable, border-agnostic rail, so the trader's experience of getting money in and getting money out is symmetrical.
Stablecoins make that symmetry possible because the same asset that funds the account is the asset that settles the withdrawal. The trader deposits USDT on Tron; the broker can return USDT on Tron. There is no on-ramp on one side and a wire on the other, no FX conversion the broker eats in the middle, and no asymmetry where deposits are instant but payouts are a support ticket. halfin gives the broker the deposit primitive and the payout primitive as two ends of one platform.
- Deposits and withdrawals are both continuous and both cross-border — a CFD broker can't optimize one and ignore the other.
- A slow withdrawal poisons the next deposit; the two legs are one relationship in the trader's eyes.
- The same stablecoin that funds the account settles the withdrawal — no on-ramp in, wire out asymmetry.
- halfin exposes the deposit primitive and the payout primitive as two ends of one platform.
Funding the account: a fiat-anchored deposit
A CFD broker prices in dollars even when it collects crypto — a $200 top-up is a $200 top-up whether the trader pays from Manila, Mumbai, or Madrid. You send halfin the fiat amount and currency, it quotes the equivalent in the stablecoins and native assets you accept, and it locks that rate when the invoice activates. The trader sees a concrete token amount, the network, and a countdown on hosted checkout, so you render no payment UI of your own and the trader pays a number that does not drift while they decide.
The rate lock is what keeps a deposit equal to the figure you credit to the trading account. Crypto prices move on a checkout timescale, so halfin freezes the quote at activation and stamps an expiry: pay inside the window and the shown amount settles to exactly the fiat figure; let it lapse and the invoice expires rather than silently re-pricing, so the trader re-issues at the current rate and your platform balance never reflects a deposit worth something other than what the trader funded.
Crediting is the part you must not shortcut. halfin applies the per-chain confirmation threshold, credits the deposit reorg-aware, and fires an HMAC-signed invoice.paid webhook the moment the deposit resolves. Verify the signature, then move the funds into the trading account. The redirect back from checkout is not proof of payment — only the verified webhook is — so the rule is the same as for any deposit on the platform: fund the account on the event, not on the browser coming back.
| Deposit stage | Webhook event | What the broker does |
|---|---|---|
| Invoice activated | invoice.confirming | Show hosted checkout: fixed token amount, network, countdown — rate is locked. |
| Deposit confirmed | invoice.paid | Verify the HMAC signature, then credit the trading account. |
| Trader sent less than quoted | invoice.underpaid | Surface the shortfall; request a top-up or credit partially per your policy. |
| Window lapsed unpaid | invoice.expired | Hand to dunning; the trader re-issues at the current rate. |
Settling withdrawals and IB commissions: payouts
The return leg is where a CFD broker's payment quality is judged. A trader who closed a position and requested a balance withdrawal is comparing you to every other broker they hold, and the comparison is mostly about how fast and how cleanly the money comes back. On stablecoin rails the broker controls that: a withdrawal is a single payout to the trader's wallet, in the asset they expect, on a chain that settles the same way for everyone, with no correspondent bank and no intermediary deduction in the path.
Withdrawals do not move funds the instant you call the API. Each payout enters a pending-approval state and is released from the dashboard, which keeps the broker's risk and finance control point exactly where it belongs — the trading desk and AML checks decide that a withdrawal is legitimate before anything leaves the balance, and the payment rail only executes an already-approved instruction. That separation lets the broker automate the mechanics of withdrawal while keeping a human sign-off on the decision.
Introducing-broker and affiliate commissions ride the same machinery as withdrawals, just fanned out. A monthly IB run is not a separate product — it is a set of single payouts submitted together, each line carrying its own currency, amount, destination, and a deterministic idempotency_key derived from your ledger (for example the IB's account id joined with the period). Because the key is deterministic, a run that times out or a worker that restarts can re-submit the whole file safely: halfin returns the payouts it already created instead of paying anyone twice, so the lines that already succeeded are no-ops and only corrected rows execute. That is the property a commission run needs and a hand-rolled transfer loop never has.
- A trader withdrawal is a single approved payout to their wallet — no correspondent bank, no intermediary deduction.
- Payouts are pending-approval and released from the dashboard, so risk and finance keep the human sign-off.
- IB and affiliate commissions are the same payouts fanned out over the single-payout API, one idempotency_key per line.
- Re-submitting a timed-out commission run is safe: the deterministic key returns existing payouts instead of double-paying.
Staging a commission run with per-line idempotency keys
There is no batch endpoint to learn — a payout run is a fan-out over the single-payout API, where each recipient is one POST carrying its own idempotency key. The key is the safety mechanism: derive it deterministically from your own ledger so that re-submitting the run reproduces the same keys and halfin matches each to the payout it already created. A flaky connection from a distant region is exactly when retries happen, and the idempotency key is what makes a retry harmless.
The line below stages one IB commission for a single recipient. Amounts are strings, because monetary values are strings end to end with no floating-point drift, and the currency names the asset and network together — pay an Asia-based IB in USDT on Tron, a US affiliate in USDC on Base, and a trader withdrawal in native SOL, each as its own line in the same run. The payout enters pending-approval; a treasurer releases the run from the dashboard. The exact response shape lives in the docs and the @halfin/sdk-merchant types.
# One line of a monthly IB run. Repeat per recipient — the run is a
# fan-out over the single-payout API, one idempotency_key per line.
# The key is deterministic (IB id + period), so a timed-out re-run
# returns the existing payout instead of paying twice.
curl -X POST https://api.thehalfin.com/api/v1/payouts \
-H "X-API-Key: $HALFIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currency": "USDT",
"amount": "1240.00",
"destination": "TXYZ...ib-wallet",
"idempotency_key": "ib_3318-2026-05"
}'
# The payout enters pending-approval; a treasurer releases the run
# from the dashboard before any funds leave the balance. Track the
# result on the payout.completed webhook. See docs.thehalfin.com.Where halfin stops and the broker's obligations begin
halfin is the payment layer on both sides of a CFD platform; it is not the trading platform. Position management, margin and leverage, the price feed, the order book, the trader's equity, and the decision to allow a deposit or release a withdrawal all live in the broker's stack. halfin's role is narrow and symmetrical: collect the stablecoin deposit and credit it reorg-aware, execute the approved withdrawal or commission, and report both directions through signed webhooks so finance reconciles every movement of money back to a trader ID.
The compliance boundary does not move because the rail is crypto. A CFD broker carries its own regulatory status, customer onboarding, suitability checks, and the AML and sanctions obligations of the markets it serves — KYB onboarding and AML awareness are process the broker runs, not a status halfin holds. Keep trader KYC and eligibility inside your own systems, screen wallets and counterparties before you accept a deposit or release a withdrawal, and keep each invoice and payout record attached to the trader or IB id so a funded account and every withdrawal reconcile to the payments behind them.
Deposits across several stablecoins and chains do not have to leave the platform to be tidied up. When inflows arrive as a mix of USDT on Tron, USDC on Base, and native assets, balance conversion consolidates the holding into the asset the broker wants to settle withdrawals in, without moving funds off-platform. The deposit funds the account, the payout returns the balance, conversion keeps the treasury in one shape, and the rest of the FX brokerage hub covers the adjacent funding and affiliate flows in the same vocabulary.
- Trust invoice.paid for the deposit leg and payout.completed for the withdrawal leg — those are the events your ledger acts on.
- Trading, margin, suitability, and the deposit/withdrawal decision stay in the broker's platform.
- KYB and AML are the broker's process; screen wallets on both legs and tie every record to a trader or IB id.
- Balance conversion consolidates a multi-asset deposit inflow into the asset you settle withdrawals in.