Back to Blog
Engineering
2026-07-2113 min readBy StableOps

Underpaid Crypto Payment, Overpayment, or Wrong Network: How to Handle Mismatches

An underpaid crypto payment, overpayment, wrong-network transfer, or late payment should never silently complete an order. Learn how to detect, resolve, and prevent each mismatch.

Stablecoin payments
Payment operations
Payment exceptions

A customer says they paid, sends a transaction hash, and can see the transfer in a block explorer. Your order still says created. This is not necessarily a scanner failure. The transfer may be real while failing to satisfy the payment instruction: it could be underpaid, overpaid, sent with the wrong asset or on the wrong network, or received after the order expired.

The safe rule is simple: do not turn a plausible transfer into a paid order by guesswork. Match the chain, asset, receiving address, and amount exactly while the order is open. Keep every other transfer outside the automatic fulfillment path, investigate it from on-chain evidence, and resolve it with a documented refund, top-up, or manual-credit policy. In a non-custodial system, the merchant still controls funds that reached its addresses; strict matching prevents ambiguous fulfillment, not recovery.

Why an underpaid crypto payment leaves the order open

A payment instruction is a complete tuple, not a suggestion:

Payment order
  chain + asset + receiving address + exact amount + expiry
                              |
                              v
Observed transfer ---- exact match while order is created? ---- yes
                              |                                  |
                              no                                 v
                              |                       payment.detected
                              v                                  |
                 unmatched on-chain event             confirmed -> finalized
                              |
                              v
              manual review; order later expires

StableOps compares the transfer amount in the token's smallest units after applying its decimals. A 9.99 transfer and a 10.01 transfer both miss a 10.00 order. Two transfers of 6.00 and 4.00 do not combine into one exact payment, either. This is especially important for shared receiving addresses, where accepting “close enough” or summing arbitrary transfers could assign one customer's funds to another customer's order.

The full matching boundary is:

Match keyWhat must be true
Organization and environmentThe event belongs to the same merchant and does not cross between Sandbox and Live
ChainThe transfer arrived on the chain in the selected payment instruction
AssetThe token contract or mint represents the accepted asset on that chain
Receiving addressThe destination is the address allocated to that order and chain/asset pair
Exact amountThe smallest-unit integer equals the order amount after token-decimal conversion
Open stateThe order is still created; an expired or canceled order cannot be revived by a later transfer

If any key misses, StableOps does not send payment.detected for that order. The order keeps waiting for an exact payment until it expires. That strictness is a feature: it keeps pricing, inventory, entitlements, accounting, and shared-address attribution from being changed by an ambiguous transfer.

First establish what actually happened

Do not begin with a refund. Begin with one evidence record that support, engineering, and finance can all use:

  • StableOps payment-order ID and your merchantOrderId.
  • Transaction hash supplied by the customer.
  • Chain the customer selected in their wallet, not merely the asset name.
  • Token contract or mint, destination address, and smallest-unit amount from the canonical chain record.
  • Order amount, accepted (chain, asset) pairs, allocated address, and expiresAt.
  • Current receipt status, block hash, and finality state.

Check the transaction on the explorer for the chain the customer actually used. Compare its token-transfer log, not the wallet's success screen, with the original instruction. A successful transaction can contain no qualifying token transfer, and the same 0x… destination can exist on several EVM networks.

StableOps collects possible amount mismatches for review

You do not have to discover every wrong amount from a customer ticket. StableOps scans supported token transfers into monitored merchant receiving addresses and retains the normalized on-chain event even when the amount prevents it from matching an order.

Open Dashboard → Orders → the affected order. For a non-finalized order, a Possibly related transfers card appears when StableOps finds candidates that meet all of these conditions:

  • the event belongs to the same organization and environment;
  • it has not been associated with any payment order;
  • its chain, asset, and destination address match one of this order's historical payment instructions;
  • StableOps detected it between the order's creation and expiration; and
  • after applying the token's decimals, its amount differs from order.amount.

The card shows the transaction, asset, amount, confirmations, and detection time so operations can open the event and compare it with the customer's claim. Candidate events are computed from current records rather than copied onto the order: if an event is later associated with any order, it automatically disappears from this list.

This collection is an investigation aid, not an automatic association. It does not change order status, combine multiple transfers, emit payment.detected, or move funds. The exact-match rule remains the only path into the automatic payment lifecycle.

Wrong-network, wrong-asset, and late transfers may require a broader chain-history search. They are visible in StableOps only if that chain, asset, and address were being scanned; they are always subject to what the canonical chain actually records. A payment.expired event says that no exact match arrived before the deadline. It does not prove that the customer sent nothing.

Response matrix for every mismatch

Use this table as the first-line support runbook.

CaseHow to identify itWhere the funds areCustomer messageResolution
UnderpaymentChain, asset, and address match; actual smallest-unit amount is below the orderIn the merchant-controlled receiving address“We received less than the exact order amount, so the order did not complete automatically.”Refund, manually credit the partial amount, or create a separate order for the remaining balance and combine them in your ledger
OverpaymentChain, asset, and address match; actual amount is above the orderIn the merchant-controlled receiving address“We received more than requested; the original order remains unmatched while we review it.”Manually credit according to policy and refund the surplus, or refund the full transfer
Wrong networkDestination text may match, but the transaction exists on a different chainUsually at the corresponding merchant-controlled address on that network; confirm key and wallet support first“The transfer succeeded on network X, but the order requested network Y.”Recover with the merchant's wallet tooling on the actual network, then refund or manually credit
Wrong assetChain and destination match, but token contract/mint differsAt the merchant-controlled address, provided it was a real token transfer“The order requested asset X, but the transfer contained asset Y.”Verify the token is supported and genuine before any valuation; refund or manually credit out of band
Payment after expiryCanonical transfer time/detection is after the order's deadline and terminal stateAt the address used in the old instruction, which may already have been released and assigned to another order“The payment arrived after this attempt expired and cannot reopen it.”Trace by transaction hash and address history; refund or manually credit without changing the expired order
Confirmed payment revertedA previously matched order emits payment.reverted; receipt failed or hash changedThe transfer no longer exists on the canonical chain, or it failed, so do not assume funds remain at the address“The earlier confirmation was rolled back; please wait for review before trying again.”Undo optimistic work, verify canonical state, and issue a fresh order if the customer must retry

The automatic order should remain untouched in all six rows. Manual credit belongs in the merchant's own business ledger with references to the original order, exception reason, approving operator, and every related transaction hash. Do not rewrite the original payment event or mark an unmatched StableOps order as finalized.

Underpayment: a top-up does not complete the original transfer

The most common support mistake is asking the customer to “send the missing 0.01” and expecting the original order to complete. Exact matching evaluates each transfer independently. If the order requested 10.00, neither a 9.99 transfer nor a later 0.01 transfer equals 10.00.

Choose one of three explicit policies:

  1. Refund the partial payment. This is the cleanest choice when automated fulfillment and accounting must remain one order to one transfer.
  2. Manually credit the partial payment and collect the balance separately. Create a new payment order for the remaining amount, then join both transaction hashes to the business order in your ledger. The original StableOps order still expires; your manual ledger decision is what settles the business obligation.
  3. Accept the shortfall as a manual adjustment. Appropriate only within a documented tolerance approved by finance. Record the adjustment; do not weaken the platform's exact-match rule.

If the customer instead sends a second transfer for the full exact amount while the original order is still open, that second transfer can match. The first partial transfer remains an exception that still needs a refund or separate credit, so this approach can temporarily collect more than intended.

Overpayment: decide whether to refund the surplus or the whole transfer

An overpayment is not a successful payment plus an automatically refundable remainder. It is one transfer whose amount does not match the instruction, so the order does not advance.

Your policy can manually apply the requested amount and refund the surplus, or refund the full transfer and ask the customer to start again. The first option creates a split accounting record: one incoming transaction funds both the sale and a refund liability. Store the actual received amount, credited amount, refund amount, refund transaction hash, network fee treatment, and approver.

Never blindly refund to the transaction's from address. Exchange withdrawals, smart-contract wallets, and relayers may send from an address that cannot identify or credit the customer. Ask the customer for a refund address on the exact network, validate its format, and follow your ownership, sanctions, and approval controls before sending a new on-chain transaction. A refund is a new transfer; blockchain payments cannot be reversed in place.

Wrong network: the address can look valid and still be wrong

Wrong-network transfers are common across EVM chains because the same 0x… address is valid on Ethereum, Base, Arbitrum, Polygon, Optimism, and BNB Chain. If a customer sends Ethereum USDC to the address shown for Base USDC, the funds may be at an address whose private key the merchant controls, but they are on Ethereum and cannot satisfy the Base instruction.

Confirm all of the following before promising recovery:

  • The transaction is on the network the customer claims and has a successful canonical receipt.
  • Its token-transfer log points to the exact merchant address.
  • The merchant can operate that same address on the actual network.
  • The treasury has the network's gas token and supports the received token contract.

Across incompatible address families, the wallet will often reject the destination before broadcast. If it did broadcast a valid transfer, rely on the actual chain record and key-control analysis, not assumptions based on the displayed address. StableOps cannot sign a recovery transaction because it never holds the merchant's private keys.

Wrong asset: verify the contract before assigning value

“USDC” or “USDT” displayed by a wallet is not enough. The asset identity is the token contract or mint on a specific chain. A look-alike token, bridged variant, unsupported contract, or an entirely different asset must not inherit the requested stablecoin's value merely because its symbol matches.

Check the contract against the supported assets table and your treasury policy. If it is genuine and recoverable, decide its valuation and refund path manually. If it is unsupported, illiquid, malicious, or impossible for your wallet tooling to move safely, escalate rather than telling the customer that recovery is guaranteed.

A payment after expiry belongs to an exception queue

An order deadline applies only while the order is created. Once the expiration worker moves it to expired, the allocated address is released back to its pool and payment.expired is emitted. A later transfer cannot revive the order. The address may already appear in another order's instruction, which is why a late transfer must be reconciled by transaction hash, timestamps, and allocation history rather than by address alone.

Ask the customer to stop retrying against the old instruction. If they still need to pay, issue a new order with a new active window. Resolve the late funds separately through a refund or manual credit, and keep the expired order unchanged for auditability. The order expiration FAQ documents the exact lifecycle.

A reverted payment is not a refund case

payment.reverted is different from all unmatched-transfer cases. The order previously matched a transfer, but the receipt later failed or the live receipt's block hash no longer matched the stored event after a reorganization. On the canonical chain, the payment no longer stands.

There may be no received funds to return. Reverse only optimistic UI, inventory reservations, or other reversible work, then ask the customer to pay a new order after you have checked canonical state. Irreversible fulfillment should wait for a verified, deduplicated payment.finalized event. See Stablecoin Payment Confirmations for the finality boundary and Stablecoin Payment Webhooks for replay-safe handlers.

Keep exception handling outside automatic fulfillment

This small TypeScript decision function illustrates the boundary. It compares normalized facts; it does not mutate the payment order or pretend that several transfers form one payment.

type PaymentFacts = {
  expected: { chain: string; asset: string; address: string; amountMinor: bigint }
  actual: { chain: string; asset: string; address: string; amountMinor: bigint }
  orderOpen: boolean
}

export function routePayment(facts: PaymentFacts) {
  if (!facts.orderOpen) return { outcome: 'manual_review', reason: 'order_not_open' } as const
  if (facts.actual.chain !== facts.expected.chain)
    return { outcome: 'manual_review', reason: 'wrong_network' } as const
  if (facts.actual.asset !== facts.expected.asset)
    return { outcome: 'manual_review', reason: 'wrong_asset' } as const
  if (facts.actual.address !== facts.expected.address)
    return { outcome: 'manual_review', reason: 'wrong_address' } as const
  if (facts.actual.amountMinor < facts.expected.amountMinor)
    return { outcome: 'manual_review', reason: 'underpaid' } as const
  if (facts.actual.amountMinor > facts.expected.amountMinor)
    return { outcome: 'manual_review', reason: 'overpaid' } as const

  return { outcome: 'continue_confirmation', reason: 'exact_match' } as const
}

In production, derive both amounts with the verified token's decimals and compare integers; never use JavaScript floating-point numbers for token amounts. Also preserve organization and environment scoping before this business-level comparison. The actual StableOps matcher enforces those boundaries server-side.

Prevention checklist

  • Render order.amount exactly as returned. With automatic amount adjustment, it may differ slightly from the amount you requested.
  • Render chain, asset, and address from the selected paymentInstructions entry; never show an address without its network.
  • Make the amount non-editable and generate the wallet request or QR code from the same returned values.
  • Show the token contract or mint where wallets can contain similarly named assets.
  • Warn EVM users to verify the active network immediately before signing.
  • Choose an expiration window long enough for a human or exchange withdrawal to arrive, but do not leave abandoned orders open indefinitely.
  • Stop irreversible fulfillment until a verified and deduplicated payment.finalized webhook arrives.
  • Give support read-only access to order details, possible mismatched events, and chain explorers; keep refund signing behind treasury approval.
  • Define underpayment, overpayment, wrong-network, wrong-asset, late-payment, and refund-fee policies before launch.
  • Reconcile every manual credit and refund back to merchantOrderId, payment-order ID, incoming transaction hash, and any outgoing refund hash.

For multi-chain USDT checkout design, including exact instruction rendering, see How to Accept USDT Payments Reliably.

FAQ

What happens when a customer underpays, overpays, or sends on the wrong chain?

The order does not advance because the transfer misses at least one exact-match key. Funds that reached a merchant-controlled address remain under the merchant's control, but refunding or manually crediting them happens outside the automatic order lifecycle. The wrong amount or chain FAQ gives the product behavior and recovery steps.

What happens if a payment arrives after the order expires?

It does not revive the order. The address has been released and may have been reused, so reconcile the transfer by transaction hash and allocation history, then refund or credit it manually. Ask the customer to use a new order for another attempt. See when orders expire.

Can a confirmed stablecoin payment be reverted?

Yes. A payment at detected or confirmed can still become reverted after a failed receipt or reorganization. Treat payment.reverted as a rollback signal, not as an unmatched payment holding refundable funds, and reserve irreversible fulfillment for payment.finalized. The reverted payment FAQ explains the distinction.

Turn the first support ticket into a repeatable runbook

Start with the wrong amount or chain FAQ, add the expiration and reverted payment branches, and assign an owner and approval rule to every row in the response matrix above. Then test one underpayment, one overpayment, and one expired order in Sandbox. A mismatch should produce evidence and a controlled exception—not accidental fulfillment.

Related articles

There is no single best chain for stablecoin payments. Compare payer-side fees, finality, and wallet distribution, then accept a set of chains and match each transfer to an order.

Use x402 for HTTP-native agent payments, but keep merchant-side order state, policy, finality, webhooks, and audit trails separate.

Build USDC subscription payments with recurring invoices, payer-initiated checkout, and verified subscription settlement events.