HSRCPAY Documentation

Testing Declines

Guide to testing sandbox decline, provider error, retry/fallback, and the normalized error model.

Integration quality shows not in successful payments, but in what you do when declined. In sandbox, deliberately trigger decline and timeout to map UI, webhooks, and state before production.

Finish these scenarios before going live so you do not panic on the first real hard decline in production.

Why Test Declines?

Testing only successful payments is not enough. Real product quality is measured by correctly handling invalid card, insufficient funds, issuer unavailable, provider timeout, 3DS failed, suspected fraud, duplicate request, expired card, hard decline, soft decline, and retryable/non-retryable error behavior.

HSRC Pay's normalized error model makes provider chaos readable for merchants.

Hard Decline vs Soft Decline

Decline typeMeaningMerchant behavior
Hard declineRetrying with the same payment method is not appropriateDo not retry; ask the user for a different payment method
Soft declineTransient condition or error that may resolve with a different routeFallback/retry, status check, or try again later if appropriate
UnknownResult is uncertain or provider response is ambiguousProceed carefully with idempotency and status query

Error Codes (Public Summary)

In the confirm orchestrator summary, errCode comes as a string; for declines there is also a hard / soft distinction. Common codes:

CodeUsually
invalid_payment_methodCard/method invalid (hard)
invalid_currencyCurrency mismatch (hard)
invalid_credentials / invalid_api_keyProvider configuration (hard)
system_proxy_timeoutTransient; status check (soft)
system_network_other / system_network_econnresetTransient network (soft)
system_no_provider_strategy_available_for_routing_planRouting/config (hard)
system_unknownAmbiguous; proceed carefully

Do not show raw provider messages to users; simplify based on errCode and decline type.

Expected Error Simulation in Sandbox

Sandbox scenario configuration can produce specific outcomes. If your integration exposes this field under a different name, the same mental model applies: explicit sandbox configuration triggers a provider or normalized error result; sandbox card numbers do not.

{
  "amount": 12500,
  "currency": "TRY",
  "payment_method": "pm_sandbox_card",
  "sandbox": {
    "expectedErr": "system_unknown"
  }
}
{
  "kind": "declined",
  "errCode": "system_unknown",
  "errMsg": "Felix Sandbox: Simulation of system_unknown",
  "type": "hard",
  "providerRef": "sandbox_provider_ref",
  "traceId": "trace_sandbox_123"
}

Card Validation Decline

Sandbox cards do not simulate specific decline reasons. A sandbox card number does not mean insufficient funds, expired card, invalid account, stolen card, issuer unavailable, or any other issuer-specific response.

The only card-list decline behavior is validation: if a card payment method does not match the sandbox card allowlist, the sandbox provider returns invalid_payment_method with Invalid sandbox test card.

Use expectedSandboxErr or provider-specific sandbox configuration for explicit error simulation, not card numbers.

Provider-Based Decline Scenarios

Provider simulation can test:

  • Provider timeout
  • Provider maintenance/unavailable
  • Unsupported operation
  • Invalid credential configuration
  • Mapping failure or unknown provider result

The merchant must not rely on raw provider errors; behave according to the normalized error model.

3DS Failure Scenarios

If a 3DS challenge is cancelled, expires, or completes unsuccessfully, the result may be decline. In this case, give the user a path to restart the challenge or choose a different payment method.

Retry and Fallback

  • Hard: Request a new payment method; do not auto-loop with the same card.
  • Soft: Next provider candidate or controlled re-confirm; first GET /payments/:id?include_charges=true.
  • Ambiguous timeout: Check the existing record and webhook logs before opening a new payment.

Webhook/Event Handling

Multiple events may arrive during decline or retry. The merchant webhook handler:

  • Must be idempotent.
  • Must interpret payment state transitions in order.
  • Must perform status check on unknown/timeout.
  • Must produce UI messages from normalized error, not raw provider strings.

Example Test Matrix

ScenarioNormalized ErrorDecline TypeRetry/FallbackMerchant UI MessageWebhook Expectation
Invalid card/payment methodinvalid_payment_methodhardNo retryCheck card detailspayment.requires_payment_method / charge declined
Stolen payment methodstolen_payment_methodhardNo retryRequest different payment methodcharge declined
Invalid currencyinvalid_currencyhardFix configurationCurrency not supportedfailed/declined event
Invalid API keyinvalid_api_keyhardFix provider configProvider connection could not be configuredfailed event
Invalid credentialsinvalid_credentialshardFix provider configProvider credentials invalidfailed event
Proxy connection failedsystem_proxy_conn_failedsoftFallback/retry may be triedProvider connection could not be establishedretry/fallback trace
Proxy auth failedsystem_proxy_auth_failedhardFix proxy configProvider connection could not be configuredfailed event
Proxy timeoutsystem_proxy_timeoutsoftStatus checkTransaction is being verifiedpayment.processing or update
Network othersystem_network_othersoftFallback/retry may be triedTemporary network issueretry/fallback trace
Network ECONNRESETsystem_network_econnresetsoftFallback/retry may be triedTemporary network issueretry/fallback trace
Max retry exceededsystem_candidate_processor_max_retry_exceededsoftNew route/status checkRetry attempts completedfailed/updated event
No provider config for plansystem_no_provider_strategy_available_for_routing_planhardFix routing/provider configNo suitable provider found (API error code; not a product step called "strategy execution")failed event
System unknownsystem_unknownsoft or hardCareful retry/status checkTransaction status is being verifiedupdated or failed

Best Practices

  • Test success, hard decline, soft decline, timeout, and 3DS failed scenarios separately.
  • Base retry decisions on type and normalized error, not raw messages.
  • Use idempotency keys.
  • Make webhook handler resilient to duplicate delivery.
  • Trace fallback attempts.
  • Separate user messages from technical provider messages.

Common Mistakes

  • Showing the same UI message for every decline result.
  • Auto-retrying with the same card after hard decline.
  • Creating a new payment immediately after timeout, assuming payment failed.
  • Treating webhook duplicate delivery as an error.
  • Not testing production provider error mapping in sandbox.

On this page