Crypto Payment Confirmation System with Reorg Protection

Crypto Payment Confirmation System Development Imagine a client pays an order in USDT on Polygon, but a minute later the network reorganizes — the transaction disappears. You already shipped the order, but the money never arrived. A reliable payment confirmation system is not just a hash check; i

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1450
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    1003
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1269
  • image_logo-advance_0.webp
    B2B Advance company logo design
    717
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1009

Crypto Payment Confirmation System Development

Imagine a client pays an order in USDT on Polygon, but a minute later the network reorganizes — the transaction disappears. You already shipped the order, but the money never arrived. A reliable payment confirmation system is not just a hash check; it's a finite state machine with explicit state transitions and protection against all edge cases. Our implementation uses separate monitors for each network, a tolerance window to handle amount fluctuations, and idempotency at the txHash level. For example, on Ethereum PoS we require 12 confirmations (average 12-second block time), providing reliability comparable to bank clearing but 10 times faster.

We build such systems from scratch or integrate them into existing infrastructure. We rely on the EIP-1559 and Ethereum JSON-RPC API specifications for correct transaction processing. Operational cost savings on payment processing can reach $2,000 per month. The system typically pays for itself in 3–4 months. Turnkey delivery in 2–4 weeks. Contact us to discuss your scenario.

What Problem Are We Solving?

A naive implementation: receive hash → check amount → credit. It breaks at the first reorg, double spend, or when the user sends payment an hour after session expiry. Main pain points:

  • Reorg: A block is abandoned, transaction disappears. Without status rollback, you credit non-existent funds.
  • Floating point: Conversion via wei introduces rounding errors; user pays 47.50 USDT, but the system sees 47.499999.
  • Exchange fees: Transferred amount is 1–2% less than expected.
  • Session timeouts: Payment arrives after the time limit, and the address is no longer valid.

Each of these problems is resolved within a unified finite state model.

How Does the System Protect Against Reorg?

Reorg is a chain reorganization where a previously accepted block is replaced by another. On Ethereum PoS this is unlikely (1–2 block depth), on Polygon it's more common. Our approach: on every confirmation check, we fetch a fresh transaction receipt. If the receipt disappears, the status rolls back to DETECTED, the counter resets, and the monitor begins re-searching.

async function processConfirmations(paymentId: string) { const payment = await db.findPayment(paymentId); const currentBlock = await provider.getBlockNumber(); const receipt = await provider.getTransactionReceipt(payment.txHash); if (!receipt) { await db.updatePayment(paymentId, { status: 'DETECTED', confirmations: 0, reorgDetected: true, }); return; } const confirmations = currentBlock - receipt.blockNumber + 1; const isConfirmed = confirmations >= payment.requiredConfirmations; await db.updatePayment(paymentId, { confirmations, status: isConfirmed ? 'CONFIRMED' : 'CONFIRMING', confirmedAt: isConfirmed ? new Date() : null, }); } 

Payments in CONFIRMING status are rechecked every N blocks — we never trust stale data.

What If the User Sends Less or More?

Due to fees and floating point, the transaction amount rarely matches the expected amount exactly. A sensible tolerance window solves this. Verification code:

function isAmountSufficient( received: bigint, expected: bigint, toleranceBps: number = 50 ): 'exact' | 'underpaid' | 'overpaid' { const tolerance = expected * BigInt(toleranceBps) / 10000n; const min = expected - tolerance; const max = expected + expected / 10n; if (received >= min && received <= max) return 'exact'; if (received < min) return 'underpaid'; return 'overpaid'; } 

On underpaid, the system notifies the operator; on overpaid (up to 10%), it accepts the payment and credits the surplus to the user's balance or generates a refund.

Payment State Machine

Each payment passes through strictly defined states: PENDING → DETECTED → CONFIRMING → CONFIRMED → SETTLED ↓ ↓ EXPIRED UNDERPAID / OVERPAID ↓ REFUNDED

State Description
PENDING Address issued, waiting for transaction
DETECTED Transaction in mempool (0 confirmations)
CONFIRMING 1+ confirmations, not yet final
CONFIRMED Confirmation threshold reached, amount correct
SETTLED Business logic executed (order created, subscription activated)
EXPIRED Timer elapsed, no transaction received
UNDERPAID Transaction received but amount less than expected

Blockchain Monitor Architecture

Monolithic monitoring of all networks in a single process is a bad idea. We use a separate worker per network with an independent retry mechanism. Implementation for EVM networks:

Basic monitor code (EVM)
interface ChainMonitor { network: string; start(): Promise<void>; stop(): void; onTransaction(handler: (tx: IncomingTransaction) => Promise<void>): void; } class EvmChainMonitor implements ChainMonitor { private provider: ethers.JsonRpcProvider; private watchedAddresses = new Set<string>(); async start() { const activePayments = await db.query( "SELECT address FROM payments WHERE status IN ('PENDING', 'DETECTING', 'CONFIRMING')" ); activePayments.rows.forEach(p => this.watchedAddresses.add(p.address)); this.provider.on('block', async (blockNumber) => { await this.processBlock(blockNumber); }); } private async processBlock(blockNumber: number) { const block = await this.provider.getBlock(blockNumber, true); for (const tx of block.transactions) { if (tx.to && this.watchedAddresses.has(tx.to.toLowerCase())) { await this.handleNativeTransfer(tx, blockNumber); } } await this.scanErc20Transfers(blockNumber); } } 

Confirmation Requirements for Different Networks

Network Recommended confirmations Average block time
Ethereum (L1) 12 ~12 s
Polygon (PoS) 64 ~60 s
BNB Chain 15 ~3 s
Arbitrum 12 ~0.5 s
Base 12 ~2 s

Idempotency and Duplicate Protection

One txHash must be credited exactly once. We use INSERT with ON CONFLICT DO NOTHING: if the same hash already processed, it returns an empty result.

INSERT INTO payment_transactions (payment_id, tx_hash, amount, block_number) VALUES ($1, $2, $3, $4) ON CONFLICT (tx_hash) DO NOTHING RETURNING id; 

Notifications and Webhooks

After transition to CONFIRMED — immediate notification to external systems via a queue (Bull/BullMQ) with exponential backoff. Direct HTTP call in the block handler would lose events on failures.

async function dispatchPaymentConfirmed(payment: Payment) { await eventBus.emit('payment.confirmed', { paymentId: payment.id, orderId: payment.orderId, amount: payment.receivedAmount, txHash: payment.txHash, }); if (payment.webhookUrl) { await webhookQueue.add('payment-webhook', { url: payment.webhookUrl, payload: { event: 'payment.confirmed', data: payment }, }, { attempts: 5, backoff: { type: 'exponential', delay: 2000 }, }); } } 

Process and Deliverables

  1. Analysis — we dissect your business requirements, number of networks, tokens, refund scenarios.
  2. State machine design — refine transitions, tolerance, confirmation thresholds.
  3. Implementation — code monitors, handlers, webhooks, integration tests.
  4. Testing — cover edge cases: reorg, underpaid, timeout, double-spend.
  5. Deployment and monitoring — deploy in your infrastructure, set up alerts.

What is included in the result:

  • Source code repository with launch instructions.
  • API and architecture documentation.
  • Database migrations.
  • Load tests and simulation scripts.
  • Support for 2 weeks after launch (extended support on request).

Order the development of a system for your project — we will prepare a detailed estimate within 1 day.

Timeline and Guarantees

Typical delivery time is 2 to 4 weeks, depending on the number of networks and business logic complexity. Pricing is calculated individually, but we guarantee transparent cost breakdown. We have been working with blockchain projects for over 5 years and have implemented dozens of such systems. We guarantee stable operation under a load of up to 10,000 transactions per hour.

Get a consultation: write to us, and we will evaluate your project for free.