Adyen Salesforce Commerce Cloud Payment Authorization and Capture Flow Explained

The Adyen Salesforce Commerce Cloud cartridge creates an order at checkout, immediately authorizes payment instruments via HookMgr hooks, and later reconciles order status through Adyen webhooks that can restore cancelled orders to Paid status upon successful capture.

The payment authorization and capture flow in the Adyen Salesforce Commerce Cloud integration orchestrates real-time payment validation and asynchronous settlement reconciliation. This article examines the complete transaction lifecycle within the adyen/adyen-salesforce-commerce-cloud repository, tracing how checkout controllers, authorization helpers, and webhook handlers collaborate to manage payment state.

Checkout and Order Creation

The flow begins when a shopper proceeds to checkout in a Salesforce Commerce Cloud storefront.

  1. The SFRA controller placeOrder validates the basket and calculates totals.

  2. It creates a Dw Order via COHelpers.createOrder.

  3. Immediately after creation, the order is passed to the Adyen-specific helper:

// Inside placeOrder.js
var handlePaymentResult = adyenHelpers.handlePayments(order);

Source: src/cartridges/int_adyen_SFRA/cartridge/controllers/middlewares/checkout_services/placeOrder.js

Payment Authorization Process

Delegating to handlePayments

The handlePayments function in authorizationHelper.js acts as the entry point for payment processing. It validates the order total and delegates to getPayments if payment instruments exist.

function handlePayments(order) {
  if (order.totalNetPrice === 0.0) {
    return {};
  }
  if (order.paymentInstruments.length) {
    return getPayments(order);
  }
  Transaction.wrap(() => {
    OrderMgr.failOrder(order, true);
  });
  return { error: true };
}

Source: src/cartridges/int_adyen_SFRA/cartridge/adyen/utils/authorizationHelper.js

Authorizing Each Payment Instrument

The getPayments function iterates over every PaymentInstrument attached to the order. For each instrument, it:

  1. Retrieves the payment processor via PaymentMgr.getPaymentMethod.
  2. Constructs a dynamic hook name: app.payment.processor.<processorId>.
  3. Executes the hook's Authorize method via HookMgr.
const hookName = `app.payment.processor.${pProcessor.ID.toLowerCase()}`;
const customAuthorizeHook = () =>
  HookMgr.callHook(hookName, 'Authorize', order, pInstrument, pProcessor);
return HookMgr.hasHook(hookName) ? customAuthorizeHook()
                                  : HookMgr.callHook('app.payment.processor.default', 'Authorize');

Source: src/cartridges/int_adyen_SFRA/cartridge/adyen/utils/getPayments.js

If authorization succeeds, the PSP reference is stored on the instrument's transaction. If authorizationResult.error is truthy, the order is immediately failed via OrderMgr.failOrder.

Custom Authorization Hooks

Merchants can implement custom authorization logic by creating a hook file named app.payment.processor.<processorId>.js. When present, getPayments automatically invokes this hook instead of the default processor, enabling bespoke validation or routing logic for specific payment methods.

Post-Authorization Order Placement

When handlePayments returns without error, the placeOrder controller continues:

  • If the shopper used Adyen gift cards, the main payment instrument amount is adjusted and additional gift-card instruments are created.
  • The controller returns a success JSON response, allowing the shopper to proceed to order confirmation.

If any error occurs during authorization, the order is failed, basket data is cleared, and the shopper is redirected to a payment-error page.

Payment Capture via Webhook

Handling the CAPTURE Event

When Adyen settles the payment (after 3-D Secure or offline capture), it sends a CAPTURE webhook to the Salesforce Commerce Cloud instance. The webhook is routed to the handle function in CAPTURE.js.

function handle({ order, customObj }) {
  if (isWebhookSuccessful(customObj) &&
      order.status.value === Order.ORDER_STATUS_CANCELLED) {
    order.setPaymentStatus(Order.PAYMENT_STATUS_PAID);
    order.setExportStatus(Order.EXPORT_STATUS_READY);
    order.setConfirmationStatus(Order.CONFIRMATION_STATUS_CONFIRMED);
    OrderMgr.undoCancelOrder(order);
    AdyenLogs.info_log(
      `Undo failed capture, Order ${order.orderNo} updated to status PAID.`,
    );
  }
}

Source: src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/CAPTURE.js

Restoring Cancelled Orders

A critical feature of the capture flow is order restoration. If an order was previously CANCELLED (often due to a failed capture attempt or fraud review), the CAPTURE handler:

  1. Sets the payment status to PAID.
  2. Sets the export status to READY.
  3. Sets the confirmation status to CONFIRMED.
  4. Calls OrderMgr.undoCancelOrder(order) to restore the order to active status.

This ensures that delayed captures or retry scenarios do not result in lost revenue due to stuck order states.

Key Source Files

Purpose File Path
Checkout controller that creates the order and starts authorization src/cartridges/int_adyen_SFRA/cartridge/controllers/middlewares/checkout_services/placeOrder.js
Helper that delegates to getPayments src/cartridges/int_adyen_SFRA/cartridge/adyen/utils/authorizationHelper.js
Core authorization logic per payment instrument src/cartridges/int_adyen_SFRA/cartridge/adyen/utils/getPayments.js
Webhook handler for successful authorisation src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/AUTHORISATION.js
Webhook handler for capture events src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/CAPTURE.js
Utility that validates webhook success src/cartridges/int_adyen_SFRA/cartridge/utils/webhookUtils.js
Logging helper used throughout the flow src/cartridges/int_adyen_SFRA/cartridge/adyen/logs/adyenCustomLogs.js

Summary

  • The payment authorization and capture flow begins when placeOrder.js creates a Salesforce Commerce Cloud order and invokes authorizationHelper.handlePayments.
  • Authorization occurs via getPayments.js, which iterates over payment instruments and executes custom or default HookMgr hooks to validate payment with Adyen.
  • Successful authorization stores the PSP reference and marks the order as Paid; failures trigger OrderMgr.failOrder to cancel the transaction.
  • Capture is handled asynchronously by the CAPTURE.js webhook handler, which can restore previously cancelled orders to Paid status using OrderMgr.undoCancelOrder.

Frequently Asked Questions

What happens if payment authorization fails during checkout?

If the Authorize hook returns an error or the Adyen API declines the payment, getPayments.js detects the authorizationResult.error flag and immediately calls OrderMgr.failOrder(order, true) within a transaction wrapper. This cancels the order, clears the basket, and redirects the shopper to a payment-error page.

How does the CAPTURE webhook handle previously cancelled orders?

The CAPTURE.js handler checks if order.status.value === Order.ORDER_STATUS_CANCELLED. When true, it executes OrderMgr.undoCancelOrder(order) to restore the order, then sets PAYMENT_STATUS_PAID, EXPORT_STATUS_READY, and CONFIRMATION_STATUS_CONFIRMED. This ensures delayed captures or retry scenarios do not leave orders in a terminal cancelled state.

Can I implement custom authorization logic for specific payment methods?

Yes. The getPayments.js utility dynamically constructs a hook name using app.payment.processor.${pProcessor.ID.toLowerCase()}. If you create a custom hook file matching this naming convention (e.g., app.payment.processor.mycustom.js), the cartridge will invoke your Authorize function instead of the default processor, allowing bespoke validation or routing logic.

What is the difference between the AUTHORISATION and CAPTURE webhooks?

The AUTHORISATION webhook (AUTHORISATION.js) fires immediately after Adyen approves the payment, handling duplicate callbacks, partial payments, and fraud statuses to finalize the order as Paid. The CAPTURE webhook (CAPTURE.js) fires later when funds are actually settled, primarily handling order restoration for previously cancelled orders and ensuring the final payment status reflects successful settlement.

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 →