Result codes

Complete reference for VINR payment and authentication result codes, decline codes, and ECI values — with recommended actions for each.

View as MarkdownInstall skills

VINR attaches a resultCode to every payment and authentication attempt. Use it — not the HTTP status code — to route application logic: retry the charge, prompt the customer to update their payment method, or suppress a specific error message. The HTTP status only tells you whether the API call succeeded; resultCode tells you what the payment did.

How to read codesAsk

Every payment object and payment.* webhook payload carries two fields:

  • resultCode — the top-level outcome of the attempt (e.g. Authorised, Refused, Pending).
  • declineCode — present only when resultCode is Refused; the specific reason the issuer or VINR returned.
{
  "id": "pay_3Kd9aZ2eRb",
  "resultCode": "Refused",
  "declineCode": "insufficient_funds",
  "amount": { "value": 2500, "currency": "EUR" },
  "customer": "cust_8Qm2"
}

The same structure appears on the payment.failed webhook event:

{
  "event": "payment.failed",
  "data": {
    "id": "pay_3Kd9aZ2eRb",
    "resultCode": "Refused",
    "declineCode": "insufficient_funds"
  }
}

Branch your control flow on resultCode first, then on declineCode for fine-grained handling. Never key logic off the human-readable message — it can change without notice.

Authorization result codesAsk

resultCodeMeaningRecommended action
AuthorisedThe issuer approved the charge.Fulfil the order or capture the hold.
RefusedThe issuer or VINR declined the charge.Inspect declineCode and act accordingly (see below).
ErrorA technical error prevented a decision.Retry with a fresh idempotency key; contact support if it persists.
CancelledThe shopper or merchant cancelled before completion.Allow the customer to restart checkout.
PendingThe payment is waiting for an out-of-band confirmation (e.g. bank transfer).Poll or listen for a payment.completed webhook before fulfilling.
ReceivedVINR has received the request but the outcome is not yet known (async rails).Wait for a status update event; do not fulfil yet.
PresentToShopperA voucher or payment instruction must be presented to the customer.Render the voucher or instructions from the payment response.
IdentifyShopperThe issuer needs the shopper fingerprinted before proceeding.Trigger the device fingerprint step in your front end.
ChallengeShopperA 3DS2 challenge is required.Open the challenge window; resume the payment on completion.
RedirectShopperThe customer must be redirected to complete authentication (e.g. 3DS1, iDEAL).Redirect to the URL in action.url; handle the return.
AuthenticationFinished3DS authentication completed.Submit the authentication result to finalise the payment.
AuthenticationNotRequiredLiability shift applied without a challenge (frictionless).Proceed to authorisation; no customer action needed.

Decline codesAsk

declineCode is populated whenever resultCode is Refused. It carries the issuer's or VINR's specific reason.

declineCodeDescriptionRecoverableRecommended action
insufficient_fundsThe card does not have enough balance or credit for this amount.YesRetry later or ask for a different payment method.
card_declinedGeneric issuer decline — no specific reason given.YesRetry once later; if it fails again, ask for a different method.
expired_cardThe card's expiry date has passed.NoPrompt the customer to update or replace their card.
incorrect_cvcThe CVC/CVV entered does not match issuer records.NoRe-prompt for the security code; do not retry without correction.
stolen_cardThe card was reported stolen.NoBlock further attempts on this card; do not surface reason to the customer.
lost_cardThe card was reported lost.NoBlock further attempts on this card; do not surface reason to the customer.
do_not_honorThe issuer declined without a specific reason (often a soft decline).YesRetry on a delayed schedule (e.g. 24 h); if persistent, ask for a different method.
do_not_honor_retryIssuer declined and explicitly asked not to retry immediately.Yes (later)Wait at least 24 h before retrying; respect the issuer's signal.
issuer_unavailableThe issuer's systems could not be reached.YesRetry with the same idempotency key after a short delay.
transaction_not_permittedThe card type or account does not permit this transaction type.NoAsk the customer to use a different card.
restricted_cardThe card is subject to a restriction (region, merchant category, etc.).NoAsk for an alternative payment method.
card_velocity_exceededThe card has hit its transaction frequency or spend limit.Yes (later)Retry after the limit window resets; inform the customer.
invalid_amountThe transaction amount is outside the range permitted for this card.NoVerify amount formatting (positive integer, minor units); check minimum/maximum for the currency.
invalid_card_numberThe card number did not pass the Luhn check or is not a recognised BIN.NoRe-prompt the customer for their card number.
processing_errorA transient error at the card network or VINR rail; no issuer decision reached.YesRetry immediately with the same idempotency key.
fraud_declineBlocked by VINR Radar or a custom fraud rule before reaching the issuer.NoReview your Radar rule set; do not retry without investigating.
authentication_failedA 3DS authentication was attempted but the cardholder failed the challenge.YesRe-initiate the 3DS flow; the customer may have entered the wrong OTP.
card_not_supportedThe card does not support the requested transaction type (e.g. recurring on a prepaid card).NoAsk the customer to use a credit or debit card that supports this transaction.
currency_not_supportedThe card or issuer does not accept the presented currency.NoOffer local currency checkout or ask for a different payment method.
pickup_cardThe issuer has instructed the merchant to retain the card (in-person) or to permanently block it (online).NoTreat as a hard block; do not retry or surface the reason to the customer.

Never display the raw declineCode or issuer message to the cardholder for fraud-related codes (stolen_card, lost_card, fraud_decline, pickup_card). Show a generic decline message and log the code server-side.

Authentication result codesAsk

3D Secure outcomes are reported as an ECI (Electronic Commerce Indicator) value alongside the card scheme's authentication result. The ECI drives liability shift.

ECIMeaningLiability shift
05Fully authenticated — the cardholder completed the 3DS challenge successfully.Yes — shifted to the issuer.
06Authentication attempted — 3DS was triggered but the issuer or cardholder did not complete a full challenge (frictionless or unavailable).Partial — check scheme rules for your region.
07Not authenticated — 3DS was not performed or failed.No — merchant bears liability.
01 (Amex)Successful authentication (American Express equivalent of 05).Yes
02 (Amex)Attempted authentication (American Express equivalent of 06).Partial

VINR sets resultCode: AuthenticationNotRequired for low-value or low-risk transactions where the issuer grants a frictionless exemption. These payments still receive ECI 06 and benefit from partial liability shift in most schemes.

Using codes in your applicationAsk

The pattern below covers the most common routing decisions based on declineCode:

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

async function handlePaymentResult(paymentId: string) {
  const payment = await vinr.payments.retrieve(paymentId);

  if (payment.resultCode === 'Authorised') {
    return fulfillOrder(payment);
  }

  if (payment.resultCode === 'ChallengeShopper' || payment.resultCode === 'RedirectShopper') {
    return initiateAuthenticationFlow(payment.action);
  }

  if (payment.resultCode !== 'Refused') {
    return waitForWebhook(payment.id);
  }

  switch (payment.declineCode) {
    case 'insufficient_funds':
    case 'do_not_honor':
    case 'do_not_honor_retry':
    case 'issuer_unavailable':
    case 'processing_error':
      return scheduleRetry(payment.id, { delayMs: 24 * 60 * 60 * 1000 });

    case 'expired_card':
    case 'incorrect_cvc':
    case 'invalid_card_number':
    case 'card_not_supported':
    case 'currency_not_supported':
      return promptCustomerToUpdateCard(payment.customer, {
        reason: 'Your card details need to be updated.',
      });

    case 'authentication_failed':
      return reinitiateThreeDSecure(payment);

    case 'stolen_card':
    case 'lost_card':
    case 'fraud_decline':
    case 'pickup_card':
      return blockFurtherAttempts(payment.customer);

    default:
      return promptCustomerForAlternativeMethod(payment.customer);
  }
}

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page