Your first payment, end to end.
Create a payment on your server. Let Wegopay handle checkout. Fulfill the order when payment is verified.
https://api.wegopay.techServer-side REST API · JSON responses · Hosted card entry
Create your first payment
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.
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.
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.
Build and test safely
wgp_test_…
Sandbox payments and sandbox webhooks. Use the sandbox test card below for a successful card payment.
Accept real payments
wgp_live_…
Separate credentials, webhook configuration, and approval. Live access is enabled after provider and operational sign-off.
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"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.
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 scenario | Card number | Expected flow |
|---|---|---|
| 3DS authentication | 4000 0025 0000 3155 | Complete the customer challenge in the browser, then verify payment status. |
| Successful payment without 3DS | 4242 4242 4242 4242 | No 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.
Card number: 4111 1111 1111 1111
Card holder: TEST TEST
Expiry: 12/2030
CVV: 123Sandbox 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.
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.
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.
Create a payment#
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
Amount in cents. 1500 = $15.00. Range: 500–100000000; provider limits may be lower.
USD is currently supported.
Your order label, 1–255 nonblank characters. This does not enforce uniqueness.
Where to send the customer after checkout. HTTPS, no credentials or fragment.
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.
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
{
"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.
Your card form. Payment-scoped capture.#
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.
| Step | Destination | Data |
|---|---|---|
| 1. Create payment | Your backend → Wegopay | Order, amount, currency and redirects |
| 2. Prepare capture | Your backend → Wegopay | Existing payment ID; returns bounded capture access |
| 3. Capture card | Your backend → PCI Vault | Raw card fields; returns token and reference |
| 4. Submit payment | Your backend → Wegopay | Payment ID, card token and capture reference |
| 5. Complete 3DS if required | Customer browser → returned action URL | Existing challenge; no card collection again |
| 6. Verify and fulfill | Your backend ↔ Wegopay | Authenticated 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.
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.
// 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.
// 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.
Submit the token. Resume the customer action.#
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 scenario | Card number | Customer action |
|---|---|---|
| 3DS authentication | 4000 0025 0000 3155 | Open the action URL and complete the browser challenge. |
| Successful payment without 3DS | 4242 4242 4242 4242 | No 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.
Keep the original payment through uncertainty#
| Result | Required handling |
|---|---|
| Creation timeout | Replay the identical body and original persisted creation idempotency key. Never create another payment just because the response was lost. |
| Capture timeout or missing token | Do not submit pay or capture a replacement blindly. Preserve the payment and request recovery guidance. |
| Pay timeout, 409 or 503 | Poll the existing payment first. Preserve original token/reference for same-payment recovery; do not switch cards or replace the payment. |
| Expired capture / 410 | Access cannot be refreshed. Recover any existing attempt; establish a definitive non-paid outcome before creating another payment. |
| Definitive retryable decline | Retry 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. |
| 404 | Check ID, merchant, environment and activation. Unknown, foreign, hosted and unconfigured payments are indistinguishable. |
| requiresAction | Open 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.
Verify before you fulfill#
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 status | What your integration should do |
|---|---|
pending | Keep the order pending. Reconcile until verified. |
requiresAction | Await customer action. Complete the checkout action before confirmation. |
paid | Validate the payment against your order, then fulfill once. |
failed | A definitive failed payment is recorded. Do not fulfill; inspect failureCode. |
refunded | The full payment amount was refunded. Apply your refund workflow. |
partiallyRefunded | Part of the amount was refunded. Use refundedCents. |
disputed | Apply your dispute workflow. |
disputeWon | Apply your workflow for a dispute won. |
disputeLost | Apply 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
{
"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.
Keep your orders in sync#
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 parameter | Usage |
|---|---|
status | Filter by a payment status listed above. |
from / to | Inclusive UTC creation dates, in YYYY-MM-DD format. |
page | Starts at 1; defaults to 1. |
perPage | 1–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
{
"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.
Create a refund#
/v1/payments/{id}/refundTo 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
{
"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.
Retrieve refund status#
/v1/payments/{id}/refundUse 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
{
"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.
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.
Verify the raw body
Read bytes before JSON parsing. Verify the signature, timestamp, signed environment, and matching event ID.
Accept durably
Insert eventId under a durable unique constraint and queue processing in the same database transaction.
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.
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.
{
"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.
Retry requests, not payments.#
One order, one creation key
Generate a unique 16–255 character visible ASCII key before the request. Persist it with the exact order payload.
Reuse the same key and body
After a timeout, retry identically. The same canonical request replays the original 201 response for 24 hours.
Keep durable order uniqueness
Protect your order beyond 24 hours. If the outcome remains unknown near that window, reconcile and contact Wegopay.
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.
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 status | Meaning / action |
|---|---|
open | Checkout is available for a payment attempt. |
processing | An attempt or verification is in progress. Poll the same payment. |
requiresAction | Complete the outstanding action before confirmation or another attempt. |
paid | Checkout reflects verified success. Verify the payment against your order. |
failed_retryable | An attempt failed. Another may be allowed if attempts and time remain; the payment can still be pending. |
expired | Checkout is closed. Inspect terminalReason and reconcile the payment separately. |
Checkout terminal reasons
| terminalReason | Meaning / action |
|---|---|
checkout_expired | The local deadline elapsed or the provider confirmed session expiry. Read payment GET for the financial outcome. |
attempts_exhausted | No further attempts remain. Use payment GET for the payment outcome. |
provider_failed | An authenticated provider failure closed checkout. Use payment GET and failure details for the current outcome. |
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_decline | Card or issuer decline. Another payment method may be needed. |
insufficient_funds | Insufficient funds. |
expired_card | Expired card. |
incorrect_cvc / invalid_cvc | Invalid card security code. |
incorrect_number | Incorrect card number. |
transaction_not_allowed | The card does not support this transaction type. |
fraudulent | Provider risk checks declined the payment. |
lost_card / stolen_card / pickup_card | Issuer restriction. The customer should contact the card issuer. |
invalid_account | Invalid card account; use another card or contact the issuer. |
payment_intent_authentication_failure | Card verification failed; retry verification or use another method. |
try_again_later | Bank could not approve; wait or use another method. |
card_velocity_exceeded | Card payment limit reached; wait or contact the issuer. |
test_mode_live_card | Test checkout requires a test card; configuration failure. |
expired | Payment session expired; request a new link. Distinct from expired_card. |
processing_error | The provider could not process this payment. |
provider_declined | Generic sanitized provider failure when its code is absent or unsuitable. |
card_token_rejected | Card token rejected. Can be returned as HTTP 422 error.code; correct capture/tokenization before another permitted attempt. |
processor_unavailable | Generic 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.
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.
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 reason | Included? |
|---|---|
| Successful payment, including one later refunded or disputed | Yes, as approved. |
card_declined, issuer_declined, declined, do_not_honor, generic_decline | Yes, as a bank/provider rejection. |
try_again_later, fraudulent, transaction_not_allowed, lost_card, stolen_card, pickup_card, card_velocity_exceeded | Yes, as a bank/provider rejection. A risk code does not prove customer fraud. |
| Incorrect card number or security code, insufficient funds, expired card | No. Card details and account conditions are outside this metric. |
| Authentication or 3DS failure, technical or processing error | No. Tracked outside this metric. |
| Unused or expired checkout without an eligible rejection, ongoing processing, unresolved outcome | No. These do not establish a bank/provider rejection. |
Any other or missing failure code, including provider_declined | No. 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.
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.
| HTTP | Code | Next step |
|---|---|---|
| 400 | invalid_request / invalid_query | Correct the JSON, headers, or query filters. |
| 401 | unauthorized | Check your full API key and merchant activation. Revoked keys use this same response. |
| 404 | not_found | Check the payment ID, merchant, and environment. |
| 409 | idempotency_in_progress | Retry the identical request later with the same key. |
| 409 | idempotency_key_reused | Restore the original key/body pairing. Do not automatically retry changed input. |
| 409 | payment_in_progress | An attempt is processing. Poll the same payment; do not submit a new charge. |
| 409 | action_required | Complete and confirm the outstanding customer action. |
| 409 | confirmation_in_progress | Confirmation is processing. Poll the same payment. |
| 409 | confirmation_not_required | No action awaits confirmation. Read current checkout/payment state. |
| 409 | result_pending | The previous result is being reconciled. Poll the same payment before another submission. |
| 410 | checkout_expired | Checkout is closed. Reconcile payment state before deciding whether to create another checkout. |
| 410 | attempts_exhausted | No attempts remain. Reconcile payment state before deciding whether to create another checkout. |
| 422 | validation_failed | Correct the field. An explicit environment must match the API key. |
| 422 | card_token_rejected | Correct capture/tokenization; read checkout state before another permitted attempt. |
| 429 | rate_limited | Honor Retry-After and reduce concurrency. |
| 503 | service_unavailable | Honor 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.
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 accessLive 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.