Skip to content
@pulgueta/wompi
Esc
navigateopen⌘Jpreview
On this page

Error handling

The error-first result tuple, how to narrow on error.type, and when each error subclass is returned.

Every method on WompiClient and WompiPayoutsClient returns a Result<T>: a discriminated tuple of either [error, null] or [null, data]. This makes failure a value, not a thrown exception, and narrows the types automatically.

The tuple

import { WompiClient } from "@pulgueta/wompi";

const wompi = new WompiClient({
  publicKey: process.env.WOMPI_PUBLIC_KEY!,
  sandbox: true,
});

const [error, response] =
  await wompi.transactions.getTransaction("txn_does_not_exist");

if (error) {
  // Inside this branch, response is typed as null.
  return;
}

// And inside this branch, response is fully typed.
response.status;

Error shapes

Every SDK error extends the base WompiError. The response source determines which subclass you receive:

Class Discriminant Returned when
WompiError Local validation, missing key, or another pre-request failure.
WompiValidationError type === "INPUT_VALIDATION_ERROR" The Payments API rejects the body with HTTP 422.
WompiNotFoundError type === "NOT_FOUND_ERROR" The Payments API answers HTTP 404.
WompiRequestError statusCode and body Another Payments API response has a non-2xx status.
WompiPayoutApiError type === "PAYOUT_API_ERROR"; code, statusCode, and body The Payouts API returns a structured error.
WompiWebhookVerificationError type === "WEBHOOK_VERIFICATION_ERROR" A payment or payout webhook fails parsing or signature verification.

Branching on type

The simplest pattern is to switch on the discriminant — no class import needed.

if (error) {
  if ("type" in error && error.type === "NOT_FOUND_ERROR") {
    return ctx.text("Transaction not found", 404);
  }

  if ("type" in error && error.type === "INPUT_VALIDATION_ERROR") {
    return ctx.json({ messages: error.messages }, 400);
  }

  if ("type" in error && error.type === "PAYOUT_API_ERROR") {
    return ctx.json({ statusCode: error.statusCode, code: error.code }, 502);
  }

  if ("statusCode" in error && "body" in error) {
    // WompiRequestError exposes the raw response as body.
    return ctx.json({ statusCode: error.statusCode, body: error.body }, 502);
  }

  // Generic WompiError — usually means a bad payload before we sent it.
  return ctx.text(error.message, 422);
}

Or with instanceof

If you prefer class-based branching, import the error classes from the /schemas subpath.

import {
  WompiError,
  WompiNotFoundError,
  WompiPayoutApiError,
  WompiRequestError,
  WompiValidationError,
  WompiWebhookVerificationError,
} from "@pulgueta/wompi/schemas";

if (error instanceof WompiValidationError) {
  // error.messages is Record<string, string[]>
}
if (error instanceof WompiNotFoundError) {
  // error.reason is the human-readable reason string
}
if (error instanceof WompiPayoutApiError) {
  // error.code and error.statusCode identify it; error.body keeps diagnostics
}
if (error instanceof WompiRequestError) {
  // error.statusCode and error.body
}
if (error instanceof WompiWebhookVerificationError) {
  // Reject the webhook without processing its payload
}
if (error instanceof WompiError) {
  // base class — every error above is also a WompiError
}

Pre-flight input validation

Every method parses its arguments with Zod before sending them. A bad input never reaches the network — you get a WompiError with a flattened message instead, listing each bad field.

const [error] = await wompi.transactions.createTransaction({
  // amount_in_cents missing on purpose
  acceptance_token: "...",
  currency: "COP",
  signature: "...",
  customer_email: "buyer@example.com",
  reference: "ref-1",
  payment_method: { type: "CARD", token: "tok_x", installments: 1 },
});

// error.message:
// "Invalid input: amount_in_cents: Required"

The SDK never throws (almost)

Three exceptions:

  • new WompiClient(options) throws if the options fail Zod validation — usually missing or empty publicKey.
  • new WompiPayoutsClient(options) throws if its API key or user principal ID fails Zod validation.
  • getSignatureKey from /server throws if amountInCents isn’t a non-negative integer.

Everywhere else, failure travels through the tuple.

Was this page helpful?