/
Developers
v1
Documentation / Hosted checkoutMerchant API · v1

Your first payment, end to end.

Create a payment on your server. Let Wegopay handle checkout. Fulfill the order when payment is verified.

API BASE URLhttps://api.wegopay.tech

Server-side REST API · JSON responses · Hosted card entry

Create your first payment

i

Server-to-server API + hosted checkout

Your server creates payments using its secret API key and verifies payment status through the API. Your customer completes payment in the browser at the returned checkoutUrl. Once configured, signed webhooks notify your server of payment changes. For an activated merchant-owned card form, follow the token payment guide. Raw card details go directly to PCI Vault. Keep API keys on your server and fulfill only after verifying status: paid.

POST/v1/payments
curl --fail-with-body \
  "$WEGOPAY_API_BASE_URL/v1/payments" \
  -H "Authorization: Bearer $WEGOPAY_SANDBOX_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: sandbox-order-20260909-0001' \
  --data '{
    "amountCents": 1500,
    "currency": "USD",
    "reference": "order-0001",
    "successUrl": "https://merchant.example/orders/0001",
    "cancelUrl": "https://merchant.example/cart"
  }'

Use your sandbox key. These examples do not send requests.

Get started

One API. Two isolated environments.#

Request an activated merchant account and a sandbox API key. In Dashboard → Settings → Webhook, select Sandbox, enter your HTTPS receiver URL, and securely store the one-time signing secret. You can configure, rotate, and disable your own webhook; sandbox and live defaults are separate. In Dashboard → API Keys → Key settings, each key can inherit its environment’s merchant default, use a custom receiver with its own signing secret, or disable delivery for that key. The key fixes the environment. Default changes do not replace custom key receivers. Configure delivery before creating payments.

SANDBOX

Build and test safely

wgp_test_…

Sandbox payments and sandbox webhooks. Use the sandbox test card below for a successful card payment.

LIVE

Accept real payments

wgp_live_…

Separate credentials, webhook configuration, and approval. Live access is enabled after provider and operational sign-off.

Server environment · sandbox
export WEGOPAY_API_BASE_URL="https://api.wegopay.tech"
# Load WEGOPAY_SANDBOX_API_KEY from your secret manager.
# For retrieve/list examples, use the matching key:
export WEGOPAY_API_KEY="$WEGOPAY_SANDBOX_API_KEY"
i

The API key selects the environment

Omit the optional environment field on creation. A conflicting value returns 422; a payment from another merchant or environment returns 404. Existing legacy keys remain live-only. Request a new key for sandbox access.

Get started / Sandbox

Try a sandbox payment#

Create a $5.00 USD payment (amountCents: 500) with your wgp_test_… key, open the returned checkout URL, and choose Pay by card. Choose the scenario below and enter its synthetic card number in the secure card form. Use card holder TEST TEST, expiry 12/2030 and CVV 123 for either card.

Sandbox scenarioCard numberExpected flow
3DS authentication4000 0025 0000 3155Complete the customer challenge in the browser, then verify payment status.
Successful payment without 3DS4242 4242 4242 4242No customer challenge; verify the final payment status.

For merchant token payments, submit these card details directly to PCI Vault from your backend. Send only the returned token and reference to Wegopay. See token submission and customer actions.

Additional sandbox success fixture · previously verified
Card number: 4111 1111 1111 1111
Card holder: TEST TEST
Expiry: 12/2030
CVV: 123
i

Sandbox only

All cards above are synthetic sandbox fixtures and do not charge real money. Use them only with a sandbox key and a checkout showing the sandbox notice, never with live credentials or real card details. The additional success fixture was verified on September 9, 2026.

After checkout, retrieve the payment and confirm status: paid, environment: sandbox, and the expected amount, currency, and reference. For 3DS, complete the challenge; merchant token integrations must open the returned action URL in the customer browser without collecting the card again. Challenge completion or a success redirect alone is not proof of payment. Verify signed webhook delivery separately. Request matching approved fixtures from Wegopay for declines or failed/canceled challenges.

i

Device-free integration scenarios

Configure your sandbox webhook, then use Dashboard → Test checkout → Device-free sandbox scenarios for success, decline, required action, and expiry with card or wallet. Complete a simulated action as paid or declined and inspect signed callback delivery. No card or wallet device is needed. These are platform simulations; they do not contact a provider or change actual payments. Callbacks carry simulation: trueand use sandbox.simulation.* event types. Handle them only in test code. Server integrations can use POST /v1/sandbox/simulations with a sandbox key. Download the simulation API contract.

i

Negative tests and wallets

Additional decline and failed/canceled challenge fixtures must be confirmed for your sandbox deployment. For checkout expiry, create a payment and leave its checkout unused until its expiry time; this does not force the payment into a failed status. Local emulator scenarios do not establish provider sandbox behavior.

Wallet availability depends on the provider page and the device, browser, and wallet setup. The merchant API has no operation to force a transaction status. Device-free wallet completion requires a provider-approved test procedure that Wegopay must confirm; a sandbox key alone does not bypass wallet availability checks. Sandbox results do not establish live wallet acceptance, issuer approvals, settlement, or production fraud and risk behavior.

Payments / Create

Create a payment#

POST/v1/payments

Create on your server with a persisted Idempotency-Key. Save the returned payment ID with your order and redirect the customer to checkoutUrl exactly as supplied.

Request body · application/json

amountCentsintegerrequired

Amount in cents. 1500 = $15.00. Range: 500–100000000; provider limits may be lower.

currencystringrequired

USD is currently supported.

referencestringrequired

Your order label, 1–255 nonblank characters. This does not enforce uniqueness.

successUrlURLrequired

Where to send the customer after checkout. HTTPS, no credentials or fragment.

cancelUrlURLrequired

Where to send the customer when they leave checkout. Same URL rules.

Optional: paymentMethod (card or wallet) opens only that method and prevents switching. It must be enabled for your API key and merchant, otherwise creation returns 422 validation_failed witherror.field: paymentMethod. Omit it to offer all configured methods. Custom card capture supports card only.

Also optional: customer (email/name), metadata (up to 100 keys and 16,384 canonical JSON bytes). Unknown fields are rejected. Full schema ↗

Customer details and language

If supplied, customer.name must contain 1–255 Unicode characters and cannot be whitespace only. There is no Latin-only or first/last-name format requirement. Join your given and family names with a space, preserving the customer's spelling. Omit an unavailable optional name instead of sending an empty string.

Send accurate customer details. The create-payment API has no additional approval-rate or fraud-scoring parameters; metadata is for your own reconciliation. Customer and metadata fields from this request are not forwarded by the current card-charge path. For wallet checkout, supply customer.name, customer.email, customer.documentNumber (11–14 digits), and customer.phone (10–15 digits). With all four supplied, selecting Wallet opens the provider checkout directly for authorization. Otherwise, only missing fields are collected. Saved document and phone values stay server-side and are sent to the wallet provider. Provider use of those fields for risk scoring and any approval-rate recommendations require provider confirmation.

Set optional locale on payment creation to en, es, or pt for English, Spanish, or Portuguese. English is the default. The choice stays with the checkout when the customer returns or reloads. Language selection inside provider-hosted card, wallet, or authentication content is separate and must be confirmed with the provider.

i

Keep checkout URLs private

The example URL is illustrative. A real checkout URL grants access to that session. Sessions last approximately 30 minutes and allow five card attempts.

Request POST /v1/payments

curl --fail-with-body \
  "$WEGOPAY_API_BASE_URL/v1/payments" \
  -H "Authorization: Bearer $WEGOPAY_SANDBOX_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: sandbox-order-20260909-0001' \
  --data '{
    "amountCents": 1500,
    "currency": "USD",
    "reference": "order-0001",
    "successUrl": "https://merchant.example/orders/0001",
    "cancelUrl": "https://merchant.example/cart"
  }'

Response 201 Created

201 Created · application/json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000001",
    "status": "pending",
    "environment": "sandbox",
    "checkoutUrl": "https://wegopay.tech/c/EXAMPLE_CHECKOUT_TOKEN",
    "expiresAt": "2026-09-09T12:30:00Z"
  }
}

Illustrative sandbox examples. These do not send requests.

Merchant backend integration

Your card form. Payment-scoped capture.#

i

Activation required

Hosted checkout remains the default. Payment-scoped capture is available to enabled merchants. Request activation for your merchant and environment before using it; sandbox and live access are configured separately. Use the API key for the intended environment. Your backend handles card data and must meet the card-handling requirements agreed during onboarding.

Your backend collects the card and sends it directly to PCI Vault. Wegopay receives only an opaque token and capture reference. Your customer may still need a browser to complete 3D Secure (3DS). The merchant API key, capture secret and token stay on your backend.

StepDestinationData
1. Create paymentYour backend → WegopayOrder, amount, currency and redirects
2. Prepare captureYour backend → WegopayExisting payment ID; returns bounded capture access
3. Capture cardYour backend → PCI VaultRaw card fields; returns token and reference
4. Submit paymentYour backend → WegopayPayment ID, card token and capture reference
5. Complete 3DS if requiredCustomer browser → returned action URLExisting challenge; no card collection again
6. Verify and fulfillYour backend ↔ WegopayAuthenticated status and signed webhooks

How merchants and payments are separated

Your API key identifies your merchant and selects sandbox or live. Wegopay checks that the payment belongs to both before allowing capture or payment submission. Each new custom payment has its own server-owned vault encryption key and random capture reference. Wegopay revokes capture access and checks the submitted token through an authenticated, metadata-only lookup within that payment's key. A matching reference alone does not prove ownership. Tokens from another merchant, payment or environment cannot be substituted. Amount and payment routing come from the stored payment.

You receive a capture-only secret. Wegopay keeps vault account credentials, encryption-key passphrases and payment credentials private. Access expires at most ten minutes after payment creation, or at checkout expiry if earlier; read the returned expiresAt. Access is also revoked when the token is bound. There is one card binding per payment, without saved-card reuse, recurring payments or permanent shared capture credentials.

Merchant backend / Capture

Create, prepare, then capture at PCI Vault#

Create through POST /v1/payments using the normal order fields and a persisted Idempotency-Key, adding "checkoutMode": "custom". Custom mode returns 422 until configured for your merchant and environment. Save the returned data.id against the order before continuing.

Your backend → Wegopay · prepare capture
// Run on your backend. Load the matching merchant key from your secret manager.
const response = await fetch(apiBase + '/v1/payments/' + paymentId + '/capture', {
  method: 'POST', // No request body.
  headers: { Authorization: 'Bearer ' + apiKey },
  redirect: 'error',
});
if (!response.ok) throw new Error('Capture preparation unavailable');
const prepared = (await response.json()).data;
// prepared.capture: { url, secret, reference, expiresAt }
// prepared.actionUrl: browser continuation URL; keep private until needed.
// Never print this response or return capture credentials to a browser.

Preparation performs no charge. Retry preparation for the same payment; completed preparation returns the existing access, rather than extending its lifetime. If provisioning has an uncertain result, access stays fenced and may require Wegopay support.

Your backend → PCI Vault · raw card capture
// This request goes to PCI Vault, NOT the Wegopay API.
// Validate capture.url against the exact vault origin approved for your environment.
// Use the downloadable example for origin, path, expiry and response checks.
const response = await fetch(prepared.capture.url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-PCIVault-Capture-Secret': prepared.capture.secret,
  },
  body: JSON.stringify({
    card_number: card.number,
    card_cvv: card.cvv,
    card_expiry_month: card.month, // Two-digit string: MM
    card_expiry_year: card.year,   // Two-digit string: YY, not YYYY
  }),
  redirect: 'error',
});
if (!response.ok) throw new Error('Card capture rejected');
const captured = await response.json();
if (captured.reference !== prepared.capture.reference) {
  throw new Error('Capture binding mismatch');
}
// Durably store only captured.token and captured.reference, encrypted,
// against this payment before submitting them. Do not store the raw card.

The supplied URL targets PCI Vault's POST /v1/capture/{unique_id}. Its capture reference is different from your order reference. Use the exact URL and secret returned by Wegopay; do not change references or add vault account credentials. Never send raw card fields to any Wegopay route, metadata or support message. See the PCI Vault capture API.

Merchant backend / Payment and customer action

Submit the token. Resume the customer action.#

Your backend → Wegopay · submit captured identifiers
POST /v1/payments/{id}/pay
Authorization: Bearer <your merchant API key>
Content-Type: application/json

{
  "cardToken": "EXAMPLE_OPAQUE_CAPTURE_TOKEN",
  "captureReference": "EXAMPLE_SERVER_ISSUED_REFERENCE"
}

These placeholders are illustrative, never valid capture credentials. Submit the actual returned token and reference with the original payment ID. Wegopay validates their binding and executes payment using the existing durable attempt fence. Competing hosted and token submissions cannot charge this payment independently.

Sandbox cards: test both customer-action paths

Sandbox scenarioCard numberCustomer action
3DS authentication4000 0025 0000 3155Open the action URL and complete the browser challenge.
Successful payment without 3DS4242 4242 4242 4242No browser challenge; verify the final payment status.

Sandbox only: use a wgp_test_… key, card holder TEST TEST, expiry 12/2030 and CVV 123. Submit the raw test card only to PCI Vault from your backend, then submit its returned token and reference to Wegopay. Create a separate payment and capture for each scenario. Never use these cards with live credentials. See sandbox setup and verification.

A merchant backend cannot complete a 3DS challenge

When pay or status returns data.status: requiresAction, send the customer browser to prepared.actionUrl. You can also retrieve it through authenticated GET /v1/payments/{id}/checkout. The Wegopay action page resumes the current challenge and confirmation without asking for a card again, including reloads. Give only that action URL to the browser; keep API keys and capture access on the backend.

Prefer the supplied action page. An explicitly approved custom action integration may call POST /v1/payments/{id}/confirm with {} after the customer completes the action; further returned actions must be completed too. A challenge completion, confirm response or success redirect does not establish paid status.

Poll and receive signed webhooks

Poll GET /v1/payments/{id}/status from your backend for checkout progress, with backoff such as 2s, 5s, then 10s. Stop the active polling loop for a terminal checkout or required customer action, and respect 429. Retrieve GET /v1/payments/{id}for authoritative payment state. Signed webhooks use the existing exact-byte verification and durable event deduplication flow. Match ID, merchant order reference, environment, currency and amount, then fulfill exactly once only on verified paid. Checkout expiry alone is not definitive payment failure.

Merchant backend / Recovery and acceptance

Keep the original payment through uncertainty#

ResultRequired handling
Creation timeoutReplay the identical body and original persisted creation idempotency key. Never create another payment just because the response was lost.
Capture timeout or missing tokenDo not submit pay or capture a replacement blindly. Preserve the payment and request recovery guidance.
Pay timeout, 409 or 503Poll the existing payment first. Preserve original token/reference for same-payment recovery; do not switch cards or replace the payment.
Expired capture / 410Access cannot be refreshed. Recover any existing attempt; establish a definitive non-paid outcome before creating another payment.
Definitive retryable declineRetry only the same bound token within the allowed expiry and attempt limit. A different card requires a new payment after a definitive non-paid outcome.
404Check ID, merchant, environment and activation. Unknown, foreign, hosted and unconfigured payments are indistinguishable.
requiresActionOpen the existing action URL in the customer browser. Keep the order pending until authenticated paid verification.

Same-token recovery preserves the original payment operation after an uncertain result, even if capture access subsequently expires. Retain identifiers encrypted only for this payment's agreed recovery period. Disable card, token, secret, action URL and request/response body logging in your own backend, tracing, error reporting and proxies. Agree card/CVC lifecycle and PCI responsibilities during onboarding.

Download the backend example

The sanitized Node.js module validates vault destination, expiry and returned reference, separates capture from payment, requires durable encrypted token retention before combined capture/payment, and supports status polling and verified fulfillment. It sends no requests when imported. Supply your own order persistence, encrypted retention and browser redirect handling. Use the webhook verifier with environment-specific secrets and transactional event acceptance. The complete guide covers the same flow.

Acceptance and availability

Local fixtures cover ownership and environment isolation, expiry, concurrent submission, retries and uncertain outcomes. External sandbox checks on October 6, 2026 passed immediate payment success and a real browser 3DS challenge with authenticated paid verification and signed webhook delivery. Those checks predate the current repository refactor; repeat acceptance against the exact deployed candidate. Frictionless, failed/canceled actions and deployed callback networking still need acceptance. Production activation also requires account capacity, card/CVC deletion and retention evidence, merchant PCI onboarding, and operational cleanup and monitoring. Contact Wegopay for your environment's activation status.

Payments / Retrieve

Verify before you fulfill#

GET/v1/payments/{id}

A redirect back to your site is navigation, not proof of payment. Fetch the current payment on your server with the same environment key.

Fulfill only when status is paid

Match the payment ID, order reference, exact amount, currency, and environment. Commit fulfillment atomically once in your database.

Payment statusWhat your integration should do
pendingKeep the order pending. Reconcile until verified.
requiresActionAwait customer action. Complete the checkout action before confirmation.
paidValidate the payment against your order, then fulfill once.
failedA definitive failed payment is recorded. Do not fulfill; inspect failureCode.
refundedThe full payment amount was refunded. Apply your refund workflow.
partiallyRefundedPart of the amount was refunded. Use refundedCents.
disputedApply your dispute workflow.
disputeWonApply your workflow for a dispute won.
disputeLostApply your workflow for a dispute lost.

Checkout cancellation or expiry alone does not prove payment failure. Eligible payments support full and partial refunds through the refund API below.

Pending payments and reconciliation

Checkout sessions default to approximately 30 minutes; their displayed expiry time governs checkout availability. There is no fixed guaranteed resolution time for a payment in pending or requiresAction. A checkout deadline or your own polling timeout must not be treated as a declined payment. Keep unresolved orders pending and contact Wegopay when they exceed your operational waiting window.

As an integration recommendation, fetch after the customer returns, then poll about every 10 seconds briefly while they wait. Back off to 30–60 seconds with jitter and continue background reconciliation after the waiting page closes. Use webhooks for prompt updates, honor Retry-After, and cap aggregate polling across all payments sharing your API access or outbound IP. Polling reads current stored state; it does not force a provider refresh or a final result.

Request GET /v1/payments/{id}

curl --fail-with-body \
  "$WEGOPAY_API_BASE_URL/v1/payments/$PAYMENT_ID" \
  -H "Authorization: Bearer $WEGOPAY_SANDBOX_API_KEY"

Response 200 OK

200 OK · application/json
{
  "data": {
    "id": "00000000-0000-4000-8000-000000000001",
    "reference": "order-0001",
    "status": "paid",
    "environment": "sandbox",
    "method": "card",
    "amountCents": 1500,
    "refundedCents": 0,
    "netCents": 1500,
    "currency": "USD",
    "customer": {
      "email": null,
      "name": null
    },
    "metadata": {},
    "checkoutUrl": null,
    "expiresAt": "2026-09-09T12:30:00Z",
    "cardBin": "411111",
    "cardIssuer": {
      "bank": "Example Bank",
      "countryCode": "US",
      "countryName": "United States",
      "type": "CREDIT",
      "level": "CLASSIC",
      "category": "PERSONAL",
      "regulated": null
    },
    "cardBrand": "visa",
    "cardLast4": "1111",
    "failureCode": null,
    "failureMessage": null,
    "createdAt": "2026-09-09T12:00:00Z",
    "paidAt": "2026-09-09T12:01:00Z",
    "refundedAt": null
  }
}

Illustrative sandbox examples. These do not send requests.

Payments / List

Keep your orders in sync#

GET/v1/payments

Reconcile periodically so your business does not depend on webhook delivery alone. Results are newest first and scoped to your merchant and key environment.

Query parameterUsage
statusFilter by a payment status listed above.
from / toInclusive UTC creation dates, in YYYY-MM-DD format.
pageStarts at 1; defaults to 1.
perPage1–100 results; defaults to 20.

Responses contain a data array and meta with page, perPage, and total. An empty page is valid.

Request GET /v1/payments?status=paid&page=1&perPage=20

curl --fail-with-body \
  "$WEGOPAY_API_BASE_URL/v1/payments?status=paid&page=1&perPage=20" \
  -H "Authorization: Bearer $WEGOPAY_SANDBOX_API_KEY"

Response 200 OK

200 OK · application/json
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000001",
      "reference": "order-0001",
      "status": "paid",
      "environment": "sandbox",
      "method": "card",
      "amountCents": 1500,
      "refundedCents": 0,
      "netCents": 1500,
      "currency": "USD",
      "customer": {
        "email": null,
        "name": null
      },
      "metadata": {},
      "checkoutUrl": null,
      "expiresAt": "2026-09-09T12:30:00Z",
      "cardBin": "411111",
      "cardIssuer": {
        "bank": "Example Bank",
        "countryCode": "US",
        "countryName": "United States",
        "type": "CREDIT",
        "level": "CLASSIC",
        "category": "PERSONAL",
        "regulated": null
      },
      "cardBrand": "visa",
      "cardLast4": "1111",
      "failureCode": null,
      "failureMessage": null,
      "createdAt": "2026-09-09T12:00:00Z",
      "paidAt": "2026-09-09T12:01:00Z",
      "refundedAt": null
    }
  ],
  "meta": {
    "page": 1,
    "perPage": 20,
    "total": 1
  }
}

Illustrative sandbox examples. These do not send requests.

Payments / Refunds

Create a refund#

POST/v1/payments/{id}/refund

To refund, POST to /v1/payments/{id}/refund with expectedAmountCents equal to the reviewed remaining balance, and amountCents for the amount to return. For example, {"expectedAmountCents":500,"amountCents":200} refunds $2.00 from a $5.00 balance. Omit amountCents for a full refund. Eligible USD card and wallet payments return funds to the original payment method.

Repeat the exact body after a lost response; never automatically substitute a newly reduced balance. Concurrent API and dashboard requests share duplicate protection. A different amount or stale balance returns 409. Pending or uncertain requests block further refunds. Contact support for unknown, failed, canceled, or action-required outcomes. Refunds cannot be undone.

Poll the refund GET route. A successful HTTP response is not proof of returned funds. Further refunds unlock after the request succeeds and payment totals are verified. The dashboard displays Partially refunded or Refunded, the refunded total, and remaining balance. Verified changes produce payment.partially_refunded orpayment.refunded webhooks. See the downloadable integration guide for curl examples.

Request POST /v1/payments/{id}/refund

curl --fail-with-body -X POST \
  "$WEGOPAY_API_BASE_URL/v1/payments/$PAYMENT_ID/refund" \
  -H "Authorization: Bearer $WEGOPAY_SANDBOX_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"expectedAmountCents":500,"amountCents":200}'

Response 200 OK

200 OK · application/json
{
  "data": {
    "eligible": false,
    "remainingCents": 500,
    "refundedCents": 0,
    "request": {
      "id": "40000000-0000-4000-8000-000000000001",
      "amountCents": 200,
      "status": "pending",
      "createdAt": "2026-10-05T12:00:00Z"
    }
  }
}

Illustrative sandbox examples. These do not send requests.

Payments / Refunds

Retrieve refund status#

GET/v1/payments/{id}/refund

Use your server-side API key to call GET /v1/payments/{id}/refund. This returns eligible, remainingCents, refundedCents, and the latest request. Keys can access only their own merchant and environment.

Request GET /v1/payments/{id}/refund

curl --fail-with-body \
  "$WEGOPAY_API_BASE_URL/v1/payments/$PAYMENT_ID/refund" \
  -H "Authorization: Bearer $WEGOPAY_SANDBOX_API_KEY"

Response 200 OK

200 OK · application/json
{
  "data": {
    "eligible": true,
    "remainingCents": 300,
    "refundedCents": 200,
    "request": {
      "id": "40000000-0000-4000-8000-000000000001",
      "amountCents": 200,
      "status": "succeeded",
      "createdAt": "2026-10-05T12:00:00Z"
    }
  }
}

Illustrative sandbox examples. These do not send requests.

Integration essentials

Signed events. Reliable delivery.#

Webhooks tell your server when a payment changes. Delivery is at least once and events may arrive out of order. Use an event as a trigger to retrieve current payment state.

1

Verify the raw body

Read bytes before JSON parsing. Verify the signature, timestamp, signed environment, and matching event ID.

2

Accept durably

Insert eventId under a durable unique constraint and queue processing in the same database transaction.

3

Acknowledge, then process

Return 2xx promptly after durable acceptance. A duplicate is a successful acknowledgement. Fetch current payment state before fulfillment.

Node.js verifier · Companion tests ↗

Node.js 22.18+. No npm dependencies. Implement the database callback shown in this wiring example.

Receiver wiring · Node.js
import { webhookHandler } from './webhook.mjs';
import { acceptEventDurably } from './your-database.mjs';

// Mount on your registered HTTPS endpoint,
// before any JSON body parser.
const handler = webhookHandler({
  secrets: [process.env.WEGOPAY_WEBHOOK_SECRET],
  expectedEnvironment: 'sandbox',
  acceptEvent: acceptEventDurably,
});

// Implement acceptEventDurably in your database:
// 1. Insert event.eventId under a UNIQUE constraint.
// 2. Queue processing in the SAME transaction.
// 3. Treat an already accepted duplicate as success.
// The handler returns 2xx only after acceptance.

Signature format & event types

Wegopay-Signature: t=<unix-seconds>,v1=<hex-digest>

Verify HMAC-SHA256 over timestamp + "." + rawBody using the full issued secret as UTF-8. Compare in constant time with a five-minute timestamp tolerance. Match Wegopay-Event-Id to the signed eventId.

The envelope includes version (1), eventId, type, createdAt, environment, and data. Event types: payment.paid, payment.failed, payment.refunded, payment.partially_refunded, payment.disputed, payment.dispute_won, payment.dispute_lost.

The complete data object is shown below. Use id andreference to resolve your order after signature verification. It includes status, amounts, currency, and nullable failure details; it excludes customer, metadata, checkout URLs, and provider identifiers. Nullable keys remain present asnull. Because an event is a snapshot and delivery can be out of order, retrieve the payment for its current state before fulfillment. The signed environment is on the envelope, outside data.

payment.paid · complete event example
{
  "version": 1,
  "eventId": "01K4Q75T30GZN5VR8JZ6F0D1XM",
  "type": "payment.paid",
  "createdAt": "2026-09-09T12:01:00Z",
  "environment": "sandbox",
  "data": {
    "id": "00000000-0000-4000-8000-000000000001",
    "reference": "order-0001",
    "status": "paid",
    "method": "card",
    "amountCents": 1500,
    "refundedCents": 0,
    "netCents": 1500,
    "currency": "USD",
    "cardBin": "411111",
    "cardIssuer": {
      "bank": "Example Bank",
      "countryCode": "US",
      "countryName": "United States",
      "type": "CREDIT",
      "level": "CLASSIC",
      "category": "PERSONAL",
      "regulated": null
    },
    "cardBrand": "visa",
    "cardLast4": "1111",
    "failureCode": null,
    "failureMessage": null,
    "failureIsFinal": true,
    "refunds": [],
    "dispute": null,
    "verifiedAt": "2026-09-09T12:01:00Z",
    "statusVersion": 2,
    "observationVersion": 1,
    "createdAt": "2026-09-09T12:00:00Z",
    "paidAt": "2026-09-09T12:01:00Z",
    "refundedAt": null
  }
}

Card details

Payment webhooks include data.cardBrand (for example, visa) and data.cardLast4 from verified provider status. The last four digits are a four-character string, so leading zeros are preserved (for example, "0042"). Both keys remain null when unavailable; card details are not guaranteed for every paid payment or wallet payment. The same fields are available from GET /v1/payments/{id}.

BIN/IIN is the first six or eight digits identifying the card issuer, separate from the last four digits. When PCI Vault capture metadata is configured, payment reads, transaction details, and webhooks include cardBin and cardIssuer with issuing bank, country, card type, level, personal/commercial category, and regulation classification where available. These values come from an authenticated capture callback bound to the payment attempt, and remain null when unavailable. Historical events may omit these fields. A later event with a higher observation version may supply metadata that arrived after payment verification. Full card numbers, expiry dates, and CVV/CVC are not included in payment reads or webhooks.

Refunds, disputes & verification

New events include failureIsFinal, the provider's finality flag or null when unavailable; it does not guarantee settlement or prevent later refunds or disputes. refunds contains individual amountCents entries when the provider supplies a complete list matching refundedCents. An empty array means a reported empty list; null means unavailable. dispute contains the card-network stage, sanitized reason, amount in cents, currency, and nullable creation time. Unknown network stages and reasons become other.

verifiedAt records when Wegopay authenticated this observation. Use observationVersion per payment to recognize older snapshots; gaps are normal. statusVersion increases when normalized status changes. A new dispute stage can produce another payment.disputed event with the same status and a higher observation version. Unchanged reads do not emit another event. These additive version-1 fields can be absent from older queued events; accept additional fields after verifying the raw-body signature.

Processor names and identifiers, processing costs, platform balances and FX, credentials, action secrets, raw evidence, and raw provider responses are excluded. See PaymentWebhookEvent in OpenAPI for the typed event schema. These additional details are webhook fields; payment GET retains its existing schema.

Delivery retries & secret rotation

Six attempts: immediate, then 1 minute, 5 minutes, 30 minutes, 2 hours, and 12 hours after each previous failure. Non-2xx responses and timeouts retry. Redirects are not followed; webhook Retry-After is ignored.

Retries have fresh signatures. Queued deliveries retain their original signing-secret snapshot, even after rotation or disabling a configuration. Keep draining old secrets until Wegopay confirms they can be removed. Use separate sandbox and live handlers and secrets.

Integration essentials

Retry requests, not payments.#

01 / PERSIST

One order, one creation key

Generate a unique 16–255 character visible ASCII key before the request. Persist it with the exact order payload.

02 / REPLAY

Reuse the same key and body

After a timeout, retry identically. The same canonical request replays the original 201 response for 24 hours.

03 / RECONCILE

Keep durable order uniqueness

Protect your order beyond 24 hours. If the outcome remains unknown near that window, reconcile and contact Wegopay.

i

Use distinct keys across environments

Idempotency is scoped to the merchant and operation. Reusing a key across sandbox and live conflicts. Never create a replacement payment simply because a response was lost.

Integration essentials

Match statuses and failure reasons#

Keep payment status, checkout availability, failure reason, and request errors as separate fields. Values are case-sensitive. Use the full payment status list to map your order's payment outcome from GET /v1/payments/{id}. There is no expired, canceled, or declined payment status. A browser cancellation, 3DS error, timeout, or HTTP error does not establish payment failure; keep unresolved payments pending and reconcile.

Checkout states

Merchant-owned checkout reads these states from GET /v1/payments/{id}/status. Checkout configuration reads use the same states. They describe checkout separately from the payment outcome.

Checkout statusMeaning / action
openCheckout is available for a payment attempt.
processingAn attempt or verification is in progress. Poll the same payment.
requiresActionComplete the outstanding action before confirmation or another attempt.
paidCheckout reflects verified success. Verify the payment against your order.
failed_retryableAn attempt failed. Another may be allowed if attempts and time remain; the payment can still be pending.
expiredCheckout is closed. Inspect terminalReason and reconcile the payment separately.

Checkout terminal reasons

terminalReasonMeaning / action
checkout_expiredThe local deadline elapsed or the provider confirmed session expiry. Read payment GET for the financial outcome.
attempts_exhaustedNo further attempts remain. Use payment GET for the payment outcome.
provider_failedAn authenticated provider failure closed checkout. Use payment GET and failure details for the current outcome.
i

A failed attempt can still leave the payment pending

Pay/confirm can return HTTP 200 with data.status: failed, failureCode, message, and attemptsLeft. This is an attempt result. Read checkout status and payment GET before marking the order failed or offering another attempt. A later attempt can succeed. Wallet initiation returns walletReady for a redirect; it is not payment success.

Failure codes

Payment GET and signed payment webhook snapshots expose nullable failureCode and failureMessage. Failed pay/confirm attempts expose failureCode and message. Match codes, not message text; messages can differ between surfaces. A retryable attempt's reason need not appear in the pending payment's failure fields. The current recognized business declines and technical fallbacks are:

Failure code(s)Reason / handling
card_declined / issuer_declined / declined / do_not_honor / generic_declineCard or issuer decline. Another payment method may be needed.
insufficient_fundsInsufficient funds.
expired_cardExpired card.
incorrect_cvc / invalid_cvcInvalid card security code.
incorrect_numberIncorrect card number.
transaction_not_allowedThe card does not support this transaction type.
fraudulentProvider risk checks declined the payment.
lost_card / stolen_card / pickup_cardIssuer restriction. The customer should contact the card issuer.
invalid_accountInvalid card account; use another card or contact the issuer.
payment_intent_authentication_failureCard verification failed; retry verification or use another method.
try_again_laterBank could not approve; wait or use another method.
card_velocity_exceededCard payment limit reached; wait or contact the issuer.
test_mode_live_cardTest checkout requires a test card; configuration failure.
expiredPayment session expired; request a new link. Distinct from expired_card.
processing_errorThe provider could not process this payment.
provider_declinedGeneric sanitized provider failure when its code is absent or unsuitable.
card_token_rejectedCard token rejected. Can be returned as HTTP 422 error.code; correct capture/tokenization before another permitted attempt.
processor_unavailableGeneric checkout failure fallback. Check current payment and checkout state before retrying.

failureCode is an open string, not a closed enum. Authenticated provider status verification can retain other sanitized codes, including error, failed, cancelled, or canceled when no separate code is supplied. Unknown decoded HTTP 2xx decline codes fall back tocard_declined; unknown HTTP 422 codes remain unresolved. Status verification may preserve a different code. Support an unknown-code category, retain the code for support, and derive the outcome from payment status. A failure reason alone does not authorize a new charge.

Example: insufficient_funds with checkout failed_retryable and payment pending maps to Pending payment with last attempt reason Insufficient funds. Checkout expired with reason checkout_expired and payment pending maps to Checkout expired / payment unresolved. Only payment GET with status: failed maps the payment itself to Failed.

The complete request error list follows below. Request errors describe an API operation and do not replace payment status. Sandbox simulation states and events belong to separate test resources; never fulfill real orders from them.

Integration essentials

How approval rate is calculated#

The dashboard measures successful live payments against selected bank/provider rejections. Each payment counts once, regardless of the number of attempts.

i

Approval rate formula

Approved payments / (approved payments + eligible bank/provider rejections) × 100. Incorrect card details, insufficient funds and expired cards do not enter this calculation.

Outcome or reasonIncluded?
Successful payment, including one later refunded or disputedYes, as approved.
card_declined, issuer_declined, declined, do_not_honor, generic_declineYes, as a bank/provider rejection.
try_again_later, fraudulent, transaction_not_allowed, lost_card, stolen_card, pickup_card, card_velocity_exceededYes, as a bank/provider rejection. A risk code does not prove customer fraud.
Incorrect card number or security code, insufficient funds, expired cardNo. Card details and account conditions are outside this metric.
Authentication or 3DS failure, technical or processing errorNo. Tracked outside this metric.
Unused or expired checkout without an eligible rejection, ongoing processing, unresolved outcomeNo. These do not establish a bank/provider rejection.
Any other or missing failure code, including provider_declinedNo. Only the explicit rejection codes above count.

A rejection counts when the payment is definitively failed with an eligible code. A pending payment also counts when its latest attempt failed with an eligible code and its checkout is expired or past its deadline, provided it is not processing. This reporting classification does not change the payment's pending status. An earlier rejection is not counted if the latest attempt is excluded or unresolved.

A successful retry counts as approved. Reporting windows use payment creation time and UTC calendar boundaries; sandbox payments are excluded. With no qualifying payments, the dashboard displays an em dash. For example, 100 approvals, 10 eligible rejections and 20 card-detail or account-condition failures give 100 / (100 + 10) = 90.9%.

This measures bank/provider approval, rather than completion of all created payments or platform reliability. Excluded outcomes remain in transaction history with their actual statuses and reasons. See the status and failure-code reference for integration handling.

Integration essentials

Handle errors predictably#

Branch on error.code, not message text. Error bodies contain error.code and error.message; validation errors may also identify error.field. On payment creation, an out-of-range amount returns422 validation_failed with error.field: amountCents; unknown properties use error.field: body. Malformed JSON remains400 invalid_request.

HTTPCodeNext step
400invalid_request / invalid_queryCorrect the JSON, headers, or query filters.
401unauthorizedCheck your full API key and merchant activation. Revoked keys use this same response.
404not_foundCheck the payment ID, merchant, and environment.
409idempotency_in_progressRetry the identical request later with the same key.
409idempotency_key_reusedRestore the original key/body pairing. Do not automatically retry changed input.
409payment_in_progressAn attempt is processing. Poll the same payment; do not submit a new charge.
409action_requiredComplete and confirm the outstanding customer action.
409confirmation_in_progressConfirmation is processing. Poll the same payment.
409confirmation_not_requiredNo action awaits confirmation. Read current checkout/payment state.
409result_pendingThe previous result is being reconciled. Poll the same payment before another submission.
410checkout_expiredCheckout is closed. Reconcile payment state before deciding whether to create another checkout.
410attempts_exhaustedNo attempts remain. Reconcile payment state before deciding whether to create another checkout.
422validation_failedCorrect the field. An explicit environment must match the API key.
422card_token_rejectedCorrect capture/tokenization; read checkout state before another permitted attempt.
429rate_limitedHonor Retry-After and reduce concurrency.
503service_unavailableHonor Retry-After when present; otherwise use bounded backoff with jitter.

For transport failures, preserve the original key and body. Stop and investigate persistent errors. Never log authorization headers, checkout URLs, signing secrets, or payment payloads.

Next steps

Prove the flow in sandbox#

Complete a joint deployed sandbox test with Wegopay using provider-approved test methods. Agree the acceptance evidence and an operational contact before live access.

  • ✓Verified paid state, signed event accepted, and exactly one fulfillment
  • ✓Required actions, declines, cancellation, and expiry without premature fulfillment
  • ✓Concurrent creation, duplicate events, and out-of-order delivery
  • ✓Tampered signatures, environment isolation, key revocation, and secret rotation
  • ✓Receiver recovery and reconciliation after provider timeouts

Ready to connect your business?

Get your sandbox credentials and integration support.

Request access

Live payments require separate provider, commercial, security, and operational approval. The full OpenAPI also includes dashboard and checkout operations with different authentication; the three merchant operations above are all you need for standard hosted checkout.