Blog

How to Build Stripe Webhook Reconciliation in Next.js

A practical blueprint from ConsultChat showing how to reconcile Stripe Checkout and PaymentIntent events with internal wallet and consultation records.

How to Build Stripe Webhook Reconciliation in Next.js
ConsultChat3 min read2026-04-21
By Published Updated

Build Stripe reconciliation in Next.js by treating webhooks as source of truth, mapping each event to explicit state transitions, and updating domain records idempotently. ConsultChat applies this pattern to consultations, transactions, refunds, and wallet balances with reliable financial consistency daily.

Stripe Reconciliation Sequence

Event-driven architecture over optimistic assumptions

A common failure mode is marking payments complete right after session creation. That creates finance drift when cards fail, intents get canceled, or refunds happen later. ConsultChat avoids that by processing Stripe events in app/api/stripe/webhook/route.ts:

switch (event.type) {
  case 'checkout.session.completed':
    await handleCheckoutSessionCompleted(event.data.object)
    break
  case 'charge.refunded':
    await handleChargeRefunded(event.data.object)
    break
  case 'payment_intent.succeeded':
    await handlePaymentIntentSucceeded(event.data.object)
    break
  case 'payment_intent.payment_failed':
    await handlePaymentIntentFailed(event.data.object)
    break
  case 'payment_intent.canceled':
    await handlePaymentIntentCanceled(event.data.object)
    break
  default:
    console.log(`Unhandled event type: ${event.type}`)
}

That event matrix matters because consultation commerce is not a single state change. It is a lifecycle.

In checkout.session.completed, the implementation computes explicit fee split values:

  • Total amount from Stripe (amount_total / 100).
  • Platform commission (20%).
  • Consultant earnings (80%).

Those percentages are not hidden in BI reports; they are encoded in transaction logic where auditability is stronger.

Metadata contract design

The checkout session carries context as metadata, including type, userId, consultantId, and consultationId:

metadata: {
  type: 'consultation',
  userId: decoded.userId,
  consultantId,
  consultationId,
  consultationType,
  subject,
  scheduledAt: new Date(`${date}T${time}`).toISOString(),
}

This pattern turns webhooks from "opaque payment signals" into deterministic domain commands. When the webhook arrives, ConsultChat can identify the consultation row, update payment status, and attach Stripe IDs without fuzzy matching.

Refund handling with user feedback

Refund flows are where many marketplace stacks break. ConsultChat explicitly handles charge.refunded and then:

  1. Locates consultation by payment.stripePaymentIntentId.
  2. Updates consultation payment status to refunded.
  3. Marks related transaction as refunded.
  4. Sends a system notification to the client with refunded amount.

This is operationally important. Finance reconciliation is not complete until users receive visible confirmation.

Security details and signature verification

Webhook routes are only trustworthy if signature verification is enforced. The implementation uses stripe.webhooks.constructEvent(body, signature, webhookSecret) when the secret is configured and allows local bypass only for development fallback.

In production, keep strict controls:

  • Always set STRIPE_WEBHOOK_SECRET.
  • Reject missing signature headers with 400.
  • Log unhandled event types for observability but do not mutate state.
  • Keep webhook body raw (request.text()) before signature verification.

These safeguards prevent forged webhook payloads and accidental drift.

Gotchas and how to avoid them

Gotcha 1: Updating wallet balances too early

If you credit wallets on checkout initiation, failed payments create phantom balances. ConsultChat credits balances only after successful intent events and matching transaction lookup.

Gotcha 2: Missing refund propagation

Without explicit refund event handling, support teams manually patch balances and trust erodes. Here, refund status updates and notifications are first-class workflow paths.

Gotcha 3: Weak metadata contracts

If metadata does not include stable IDs, teams resort to brittle matching via amount/time/user. Add consultation and user identifiers at session creation, then use them in webhook processors.

Practical outcomes and measurable reliability

This reconciliation architecture complements broader platform reliability metrics already tracked in the repo:

  • Payment transitions are represented as explicit statuses (completed, failed, cancelled, refunded).
  • Consultation and transaction records stay in sync through webhook handlers, not UI assumptions.
  • Notifications bridge backend state changes to user trust.
  • Real-time and DB optimizations in the same codebase report 70-80% faster message delivery and up to 50-70% faster query speed, reducing perceived delays after payment actions.

In commerce platforms, correctness is a performance feature.

Implementation checklist you can reuse

If you are implementing this pattern in your Next.js app:

  1. Create a single webhook route with explicit event dispatch.
  2. Define metadata contracts for every checkout type.
  3. Persist transaction rows before settlement and reconcile on events.
  4. Handle refund and failure paths as first-class states.
  5. Emit user-facing notifications on money-state changes.

For related architecture patterns, read How to Implement UGC Safety in Next.js, Why We Optimized Socket.IO for Marketplace Chat, and About the engineering team. Validate webhook behavior against Stripe webhook best practices and Stripe Checkout Sessions.

Treat webhooks as your financial control plane, not a side utility. For the full platform context, visit /case-studies/consultchat-platform-engineering.

Related reading

Why We Optimized Socket.IO for Marketplace Chat

ConsultChat’s real-time architecture changes that improved delivery speed, reconnection behavior, and reliability for chat in a production networking platform.

Continue reading

Why Agentic Rollout Plans Improve Compliance Shipping

How ConsultChat used structured agentic planning to ship UGC safety and guest access controls across user and admin surfaces with fewer regressions.

Continue reading

How to Implement UGC Safety in Next.js

A production walkthrough of how ConsultChat implemented reporting, blocking, moderation filters, and guest-safe controls in a Next.js social feed.

Continue reading

Advertisement