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.
-
The SFRA controller
placeOrdervalidates the basket and calculates totals. -
It creates a Dw Order via
COHelpers.createOrder. -
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:
- Retrieves the payment processor via
PaymentMgr.getPaymentMethod. - Constructs a dynamic hook name:
app.payment.processor.<processorId>. - Executes the hook's
Authorizemethod 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:
- Sets the payment status to PAID.
- Sets the export status to READY.
- Sets the confirmation status to CONFIRMED.
- 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.jscreates a Salesforce Commerce Cloud order and invokesauthorizationHelper.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.failOrderto cancel the transaction. - Capture is handled asynchronously by the
CAPTURE.jswebhook handler, which can restore previously cancelled orders to Paid status usingOrderMgr.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →