How to Handle Payment Errors and Display Relevant Error Messages in Adyen SFCC

The Adyen Salesforce Commerce Cloud integration handles payment errors by detecting failures at validation, API, or webhook layers, logging incidents via AdyenLogs.error_log, generating localized messages through Resource.msg, and returning structured JSON for storefront responses or rendered pages for server-to-server notifications.

The adyen/adyen-salesforce-commerce-cloud repository implements a consistent five-step pattern to handle payment errors and display relevant error messages across checkout flows and asynchronous notifications. This architecture ensures shoppers receive clear, localized feedback while maintaining comprehensive server-side logging for merchant support teams. Whether handling zero-authorization card validation failures, checkout form validation, or webhook processing issues, the integration propagates error states through standardized schemas.

Error Handling Architecture Overview

The integration follows a uniform pattern across three primary entry points: payment instrument saving, order placement, and webhook notifications. The flow consists of detecting the error through validation logic or API responses, logging the incident via AdyenLogs.error_log, creating a user-friendly message localized through Resource.msg, returning the message as JSON to the SFCC front-end or as a rendered page for webhooks, and marking the request as failed using the setErrorType helper when necessary.

Handling Payment Instrument Save Errors

When shoppers save payment instruments during client-side card entry, the savePayment.js middleware validates zero-authorization responses before persisting data.

Zero-Authorization Validation Failures

In src/cartridges/int_adyen_SFRA/cartridge/controllers/middlewares/payment_instruments/savePayment.js, the cartridge calls adyenZeroAuth.zeroAuthPayment after creating a temporary payment instrument. If the result contains error: true or an invalid resultCode (not matching approved constants like AUTHORISED or IDENTIFYSHOPPER), the system rolls back the transaction and returns a localized error message.

// src/cartridges/int_adyen_SFRA/cartridge/controllers/middlewares/payment_instruments/savePayment.js
if (zeroAuthResult.error || !containsValidResultCode(zeroAuthResult)) {
    Transaction.rollback();
    res.json({
        success: false,
        error: [
            Resource.msg('error.card.information.error', 'creditCard', null),
        ],
    });
    return this.emit('route:Complete', req, res);
}

Any unexpected exceptions within the try…catch blocks trigger AdyenLogs.error_log to capture stack traces for debugging.

Managing Checkout and Order Placement Errors

The placeOrder.js service handles validation errors (missing shipping addresses, billing information, or fraud detection flags) and payment authorization failures through distinct response patterns.

Validation Error Responses

For checkout validation failures, the controller returns JSON objects containing error: true, a localized errorMessage, and an optional errorStage object indicating which checkout step failed.

// src/cartridges/int_adyen_SFRA/cartridge/controllers/middlewares/checkout_services/placeOrder.js
if (currentBasket.defaultShipment.shippingAddress === null) {
    res.json({
        error: true,
        errorStage: { stage: 'shipping', step: 'address' },
        errorMessage: Resource.msg('error.no.shipping.address', 'checkout', null)
    });
    return next();
}

Payment Authorization Failures

After order creation, adyenHelpers.handlePayments(order) processes the authorization. When this returns an error, the cartridge cancels partial payments, clears basket data, and redirects the shopper to the payment stage with a query parameter containing the error message.

if (handlePaymentResult.error) {
    res.json({
        error: true,
        cartError: true,
        redirectUrl: URLUtils.url(
            'Checkout-Begin',
            'stage',
            'payment',
            'paymentError',
            Resource.msg('error.payment.not.valid', 'checkout', null)
        ).toString()
    });
    this.emit('route:Complete', req, res);
    return;
}

Uncaught exceptions in this flow trigger AdyenLogs.error_log followed by setErrorType to redirect to a generic error page.

Processing Webhook Notification Errors

The notify.js controller in the webhooks cartridge handles server-to-server communication asynchronously, rendering error pages rather than returning JSON.

HMAC and Authentication Failures

When HMAC validation fails or authentication headers are missing, the controller returns a 403 status code and renders a generic error view.

// src/cartridges/int_adyen_webhooks/cartridge/notify.js
if (!status || !isHmacValid) {
    res.status(403).render('/adyen/error');
    return {};
}

Business Logic Error Rendering

For valid requests that fail business logic (such as invalid status transitions), the system renders /notifyError and injects the Adyen-provided error message directly into the view context.

if (notificationResult.success) {
    Transaction.commit();
    res.render('/notify');
} else {
    res.status(403).render('/notifyError', {
        errorMessage: notificationResult.errorMessage,
    });
    Transaction.rollback();
}

All unhandled exceptions bubble to an outer catch block where AdyenLogs.error_log records the stack trace and setErrorType redirects to a safe error URL.

Centralized Error Utilities

The integration relies on several shared utilities to maintain consistent error handling across cartridges:

Summary

  • Detection: Errors are caught at validation layers, API responses (zero-auth, payment authorization), or webhook processing stages.
  • Logging: AdyenLogs.error_log captures full stack traces in savePayment.js, placeOrder.js, and notify.js for merchant support teams.
  • Localization: User-facing messages use Resource.msg with keys like error.card.information.error and error.payment.not.valid.
  • Response Formats: Client-side errors return JSON with error and errorMessage properties; webhook errors render /adyen/error or /notifyError templates.
  • Transaction Safety: Failed operations trigger Transaction.rollback() before error responses are sent to prevent partial data commits.

Frequently Asked Questions

How does the Adyen SFCC cartridge localize error messages for shoppers?

The cartridge uses Salesforce Commerce Cloud's Resource.msg() function to pull translated strings from resource bundles (.properties files). For example, Resource.msg('error.card.information.error', 'creditCard', null) retrieves the appropriate language string based on the current locale session, ensuring shoppers see error messages in their preferred language.

What is the difference between JSON error responses and rendered error pages in this integration?

JSON responses with error: true and errorMessage properties are used for storefront checkout flows (savePayment.js and placeOrder.js) to allow JavaScript front-ends to display inline errors. Rendered error pages (/adyen/error or /notifyError) are used for server-to-server webhook notifications (notify.js) where no browser session exists, returning HTTP 403 status codes and HTML views instead.

Where are payment errors logged in the Adyen SFCC integration?

Payment errors are logged via AdyenLogs.error_log, implemented in src/cartridges/int_adyen_SFRA/cartridge/logs/adyenCustomLogs.js. This utility is called in catch blocks across savePayment.js, placeOrder.js, and notify.js to capture stack traces and contextual data for merchant technical support teams without exposing sensitive details to shoppers.

How does the integration handle webhook HMAC validation failures?

When HMAC validation fails in src/cartridges/int_adyen_webhooks/cartridge/notify.js, the controller immediately returns a 403 HTTP status code and renders the /adyen/error template. This prevents processing of potentially fraudulent notifications while providing a clear error response to Adyen's servers, adhering to secure webhook handling practices.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →