Asset

Accept ETH payments on the Ethereum network

Some customers hold their value in ETH and want to pay you with it directly — out of a hot wallet, a DeFi position, or an exchange withdrawal, not a token sitting on top of the chain. This page is about one specific job: taking that payment as native ETH on the Ethereum network. You price the sale in your own currency, the customer sends ether, and halfin credits the deposit once it clears the confirmations the network needs. When mainnet gas makes a small ETH payment uneconomical, the same payer can settle on a lower-cost EVM L2 instead — Base, Arbitrum, or Polygon — and that route is part of the same supported surface, not a workaround.

01

Native ETH — the chain's own asset, not a token on top of it

When a customer pays this invoice in ETH, what moves on-chain is a direct balance transfer of the Ethereum network's native asset. That is a different thing from an ERC-20 token transfer, which is a smart-contract call moving a token that rides on top of ETH. Native ETH has no token contract to verify and no second issuer to vet — it is the asset the chain itself is denominated in, and it is the asset a customer holds when they hold "Ethereum" rather than a stablecoin. The parent Ethereum asset page covers the full ERC-20 and stablecoin surface; this page stays narrow on native ETH on its home network.

Because ETH is also the gas asset, a customer paying in ETH already holds what they need to cover the network fee — there is no separate token to fund for gas. That network fee is a customer-side cost on a public chain, never a halfin charge, but it is the reason mainnet ETH suits a payer settling a meaningful amount more than a payer settling a few dollars. For the small-payment case, the lower-cost L2 routes below exist precisely so the gas overhead does not swallow the payment.

halfin's Ethereum gate watches the canonical chain for the incoming transfer, matches it to the invoice, and moves the payment through a visible sequence of states. The customer never leaves the wallet they already trust, and you never integrate a chain — you create an invoice and let the ETH the customer holds settle against it.

02

Confirmations and reorg-aware crediting

An Ethereum transaction is not final the instant it is included in a block. Under proof-of-stake the chain produces blocks on a fixed cadence, and a transaction becomes economically final only after the validator set justifies and finalizes the block it sits in, which spans a couple of epochs. In the window before that, a short reorganization can still reorder or drop a recently included transaction. A payment surface that credited on the first block it saw would occasionally mark an invoice paid against a transfer that later got unwound.

halfin does not do that. Crediting on Ethereum waits for the per-chain confirmation threshold before an invoice flips to paid, and it is reorg-aware — if a chain reorganization unwinds a transfer halfin was already tracking, that is reflected rather than ignored. You do not implement confirmation counting or reorg handling yourself; that logic lives in the Ethereum gate. The amount you see as settled is one that actually held on the canonical chain.

Practically, treat the early states as in-flight, not done. The table below is the shape of a single native-ETH payment as your integration observes it — through the dashboard, the REST API, or a signed webhook. The only state that should release goods is the settled one.

StateWhat is happening on the Ethereum networkWhat you should do
Awaiting paymentInvoice is live, the rate is locked, and the gate is watching for an incoming ETH transfer before the invoice expires.Show the ETH address, the exact amount, and the countdown.
Payment seenA matching ETH transfer has appeared on-chain but has not yet accumulated enough confirmations.Tell the customer the payment is in flight. Do not release goods.
ConfirmingConfirmations are accumulating toward the Ethereum threshold; finality is not yet reached.Keep waiting — crediting is reorg-aware and not yet final.
PaidThe confirmation threshold is met; the billed amount settles to your balance.Fulfil the order and reconcile against the fiat figure you billed.
ExpiredThe invoice window elapsed before a sufficient ETH payment arrived.Re-issue at the current rate if the customer still wants to pay.
03

Lower-cost routes: the same ETH payment on an EVM L2

Ethereum mainnet is the canonical home for native ETH, but it is not always the cheapest place to move it. EVM L2s — Base, Arbitrum, and Polygon — are separate networks that settle back to Ethereum while charging a fraction of mainnet gas. For a native-asset payer who already holds value on one of these chains, or for a small payment where mainnet gas would dominate, an L2 route keeps the same kind of payment economical.

These are distinct on-chain networks, not a single "Ethereum" bucket — an ETH transfer on Arbitrum is a different deposit on a different chain from an ETH transfer on Ethereum mainnet, and halfin tracks each on its own gate with its own confirmation behavior. The table below is the real supported native-asset surface for Ethereum and the EVM L2s; an invoice draws from it rather than from a longer aspirational list. If the customer wants to pay in a dollar-denominated stablecoin rather than ETH, the USDT and USDC asset pages are where to compare networks for those.

NetworkNative assetWhy a payer picks it
Ethereum (mainnet)ETHWhere most native ETH balances actually live; the canonical settlement layer.
BaseETH (L2 gas)Lower-cost EVM route that settles to Ethereum, for payers already on Base.
ArbitrumETH (L2 gas)Cheaper EVM execution for native-asset payers already holding value on Arbitrum.
PolygonNative chain assetLow-fee EVM route for payers who already hold value on Polygon.
04

Three ways to take an ETH payment

How you collect ether depends on the flow. Billing a known customer for a known amount is one shape; needing a standing place for value to land is another. halfin gives you the same Ethereum gate underneath three surfaces, so you pick the one that fits instead of rebuilding the on-chain plumbing each time. Each option produces an Ethereum address and an expected amount — the difference is lifecycle.

An invoice is a one-time, rate-locked request with an expiry. A static deposit address is a persistent place to receive ETH that does not expire. Hosted checkout is a halfin-rendered page wrapped around an invoice so you have nothing to build yourself. Most businesses use invoices for sales and a static address for top-ups, deposits, or anything that is not a single priced order.

  • Invoicing — bill in your own currency (USD, EUR) and let the customer pay the equivalent in ETH. The rate locks when the invoice activates, so the ETH amount you book never drifts while the customer fetches their wallet. Underpaid and overpaid amounts are tracked against the expected figure rather than lost.
  • Hosted checkout — hand the customer a halfin-hosted link that shows the ETH address, a scannable QR, the exact amount, the network, and a live status. Nothing to render yourself; the page walks the same states as the table above.
  • Static deposit addresses — a persistent per-merchant Ethereum address that does not expire, for deposits and top-ups rather than a single priced sale. Funds arriving on it are credited the same reorg-aware way.
05

Billing in fiat, settling in volatile ETH

Native ETH is more volatile than a dollar-denominated stablecoin, which is exactly why the fiat anchor and the rate lock matter on this rail. You create a halfin invoice with an amount and a fiat currency — say 120.00 USD — and halfin computes the payable ETH amount from the live rate and pins it when the invoice activates. The customer sees a concrete amount of ETH to send and you reconcile back to the 120.00 USD you billed, rather than booking a number that drifts with the ETH price between draft and payment.

The lock is bounded by an expiry window. If the customer does not pay within it, the invoice expires rather than silently re-pricing against a stale quote, and you re-issue at the current rate. That is the protection the window buys you on a volatile asset: you are never exposed to a price that moved while the invoice sat open, and the customer cannot pay against an old number.

Underpaid and overpaid amounts are handled the same way they are everywhere on the platform. An ETH deposit that arrives short is recorded as a shortfall against the invoice, and an overpayment is recorded as visible excess, rather than either being stranded as an unmatched transfer. The fiat anchor plus explicit under/over handling is what makes month-end reconciliation mechanical instead of a screenshot-matching exercise.

06

Create an ETH-payable invoice

Accepting ETH runs on the same spec-first REST API as every other asset. Authenticate with a scoped API key via the X-API-Key header, post the fiat amount and currency, and halfin locks the rate at activation, computes the payable ETH amount, and returns an invoice you can present through hosted checkout or render yourself. Send idempotency_key in the request body so a retried request never creates a duplicate invoice.

The call below is the minimal create. The amount is a string — monetary values are strings end to end, never floats — and the currency is your fiat anchor, not the asset. You do not name ETH here; the customer chooses it at pay time from the networks you support, on Ethereum mainnet or a lower-cost EVM L2. The exact request and response fields are defined in the docs and the @halfin/sdk-merchant types.

curl -X POST https://api.thehalfin.com/api/v1/invoices \
  -H "X-API-Key: $HALFIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_fiat": "120.00",
    "fiat_currency": "USD",
    "deferred": true,
    "description": "Annual license",
    "idempotency_key": "00000000-0000-4000-8000-000000000002"
  }'

# The customer chooses ETH at pay time, on Ethereum mainnet or a
# lower-cost EVM L2. halfin locks the rate at activation, pins the
# payable ETH amount, and credits the deposit reorg-aware once it
# meets the confirmation threshold. See docs.thehalfin.com for the
# full request/response schema.