Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ency07/B2B-import/llms.txt

Use this file to discover all available pages before exploring further.

B2B Import ERP exposes a single inbound webhook endpoint to receive payment lifecycle events from the Wompi payment gateway. When Wompi processes a transaction it fires an HTTP notification to this URL; the handler validates the payload, delegates processing to processWompiWebhook(), and returns a structured acknowledgement. The endpoint is stateless and idempotent — re-delivering the same event will attempt to update the same transaction record.
Configure this URL in the Wompi merchant dashboard → Configuración → Webhooks. The full URL is https://<your-domain>/api/webhooks/wompi.

Endpoint Overview

POST /api/webhooks/wompi

Receives payment event notifications from Wompi. Processes the event and updates the corresponding invoice and transaction records.

GET /api/webhooks/wompi

Verification probe used by Wompi to confirm the endpoint is reachable. Returns a simple status object.

GET — Verification Probe

Wompi may issue a GET request to verify that the endpoint is live before registering it in the dashboard. Response 200 OK:
{
  "status": "Wompi webhook endpoint active"
}

POST — Payment Event

Request Format

Wompi sends a JSON body with the following structure. The event field identifies the event type and data contains the transaction details directly.
{
  "event": "transaction.updated",
  "data": {
    "id": "TXN_123456",
    "reference_id": "INV-2026-0042",
    "status": "APPROVED",
    "amount_in_cents": 150000000,
    "currency": "COP",
    "customer_email": "buyer@example.com",
    "payment_method_type": "CARD"
  }
}
FieldTypeDescription
eventstringEvent type. Currently transaction.updated is the primary event.
dataobjectEvent payload container.
data.idstringWompi’s unique transaction identifier.
data.reference_idstringThe reference string set when creating the checkout — matches payment_transactions.reference_id. Falls back to data.id if absent.
data.statusstringAPPROVED, DECLINED, VOIDED, or ERROR.
data.amount_in_centsnumberAmount in centavos COP (divide by 100 for COP).
data.currencystringAlways "COP" for Colombian Peso.
data.customer_emailstringPayer email (optional).
data.payment_method_typestringPayment method, e.g. "CARD" or "PSE" (optional).

Validation

The handler performs a structural check before any processing:
if (!body.event || !body.data) {
  return NextResponse.json(
    { error: "Payload inválido: falta 'event' o 'data'" },
    { status: 400 }
  );
}
If either event or data is absent, the request is rejected immediately with HTTP 400.

Processing Flow

POST body received


  Validate: event + data present?
        │  No  → 400 Bad Request

        ▼  Yes
  processWompiWebhook(body)

        ├─ Look up payment_transactions by reference_id
        ├─ Map Wompi status → internal status (APPROVED / DECLINED / VOIDED / ERROR)
        ├─ Update payment_transactions record

        └─ If APPROVED:
              ├─ Create payments record (PAG-2026-XXXX)
              ├─ Update invoice paid_amount, balance_amount, status
              ├─ Create payment_reconciliations entry
              └─ revalidatePath("/portal") + revalidatePath("/dashboard/invoices")


  Return { received: true, success: boolean, status: string }

Response Codes

StatusBodyMeaning
200{ "received": true, "success": true, "status": "APPROVED" }Event processed successfully.
400{ "error": "Payload inválido: falta 'event' o 'data'" }Request body missing required fields.
500{ "error": "<message>" }Unexpected server-side error during processing.

Handler Source

The handler is located at src/app/api/webhooks/wompi/route.ts:
import { NextRequest, NextResponse } from "next/server";
import { processWompiWebhook } from "@/app/actions/payments";

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();

    // Validate required fields
    if (!body.event || !body.data) {
      return NextResponse.json(
        { error: "Payload inválido: falta 'event' o 'data'" },
        { status: 400 }
      );
    }

    console.log(
      `[Wompi Webhook] Event: ${body.event}, Reference: ${body.data?.reference_id || body.data?.id}`
    );

    const result = await processWompiWebhook(body);

    return NextResponse.json({ received: true, ...result });
  } catch (error: unknown) {
    const message =
      error instanceof Error ? error.message : "Error interno del servidor";
    console.error("[Wompi Webhook] Error:", message);
    return NextResponse.json({ error: message }, { status: 500 });
  }
}

// Wompi may send GET for verification
export async function GET() {
  return NextResponse.json({ status: "Wompi webhook endpoint active" });
}

Error Handling

Any uncaught exception inside processWompiWebhook propagates to the catch block, which extracts the message and returns HTTP 500. This prevents Wompi from receiving an unstructured error response, which would cause it to retry the delivery.
Wompi will retry unacknowledged webhook deliveries (non-2xx responses) with an exponential back-off. Ensure your database is reachable and the payment_transactions reference exists before the webhook fires. For sandbox testing, Wompi provides a Reintentar button in the dashboard event log.

Build docs developers (and LLMs) love