Payment Capture and Capture Delay Handling in Adyen Salesforce Commerce Cloud
The Adyen SFCC integration supports both immediate capture (Sale) and delayed capture (Authorize-then-Capture) models, processing final capture states via CAPTURE webhooks while handling intermediate delays through PENDING and RECEIVED status checks.
When processing transactions in the adyen/adyen-salesforce-commerce-cloud repository, the cartridge implements a robust two-step workflow for payment capture and handling capture delays. This integration allows merchants to choose between instant fund transfer at checkout or deferred capture via asynchronous webhooks, ensuring flexibility for different payment method requirements and risk management strategies.
The Two-Step Capture Workflow
The Adyen cartridge separates payment processing into distinct authorization and capture phases. During checkout, the shopper's payment method is first authorized, followed by a separate capture operation that transfers funds to the merchant account. This architecture supports both synchronous and asynchronous capture models depending on the payment method configuration stored in AdyenConfigs.
Immediate vs Delayed Capture Models
The integration implements two distinct capture strategies controlled by the setPaymentTransactionType logic in adyenHelper.js.
Immediate Capture (Sale Flow)
Immediate capture processes the payment instantly during order creation. When configured, the system sets the transaction type to dw.order.PaymentTransaction.TYPE_CAPTURE immediately upon authorization, triggering automatic fund transfer without waiting for a separate webhook.
This behavior applies to payment methods listed in the Adyen Sale Payment Methods configuration. The setPaymentTransactionType function checks if the current payment method exists in this list before wrapping the transaction update:
// Immediate capture – set transaction type to Capture
if (salePaymentMethods.includes(paymentMethodType)) {
Transaction.wrap(() => {
paymentInstrument.getPaymentTransaction()
.setType(dw.order.PaymentTransaction.TYPE_CAPTURE);
});
}
Delayed Capture (Authorize-then-Capture)
Delayed capture holds the authorized amount until a later trigger, typically when the order ships. This model relies on Adyen webhooks to signal when the capture actually occurs, allowing for intermediate states and failure handling. The transaction type remains unset or defaults to authorization-only until the CAPTURE webhook arrives.
Processing Capture Webhooks in SFCC
For delayed captures, the system processes Adyen webhooks through dedicated event handlers that update order statuses and manage exceptions. The webhook processor first creates a custom object storing the payload, then routes to specific handlers based on the eventCode.
CAPTURE Webhook Handler
Located in eventHandlers/CAPTURE.js, this handler processes successful capture notifications. It validates the webhook via webhookUtils.isWebhookSuccessful and updates the order status to PAID. Notably, if a previous capture attempt failed and the order was cancelled, the handler calls OrderMgr.undoCancelOrder to restore the order before finalizing status fields:
// Delayed capture – CAPTURE webhook handler
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.`);
}
}
module.exports = { handle };
CAPTURE_FAILED Webhook Handler
The eventHandlers/CAPTURE_FAILED.js handler manages unsuccessful capture attempts. Upon receiving a failure notification, it prevents order fulfillment by updating the payment status and cancelling the order:
// Capture failure – CAPTURE_FAILED webhook handler
function handle({ order, customObj }) {
if (isWebhookSuccessful(customObj)) {
order.setPaymentStatus(Order.PAYMENT_STATUS_NOTPAID);
order.trackOrderChange('Capture failed, cancelling order');
OrderMgr.cancelOrder(order);
}
AdyenLogs.info_log(`Capture failed for order ${order.orderNo}`);
}
module.exports = { handle };
Both handlers rely on webhookUtils.isWebhookSuccessful to guard against malformed payloads before processing state changes.
Handling Intermediate Result Codes and Capture Delays
Adyen may return intermediate result codes (PENDING, RECEIVED) when capture is not immediately final. The cartridge treats these as delayed captures, keeping the order in a pending state until definitive confirmation arrives.
The isIntermediateResultCode function in adyenHelper.js (lines 908-918) checks the payment transaction's authCode field. When these intermediate statuses are detected, the system waits for subsequent CAPTURE webhooks rather than finalizing the order immediately. This ensures that payment capture delays are safely reconciled when the final result code arrives, preventing premature order processing or cancellation.
Configuring Capture Settings
Merchants configure the capture model through the Business Manager interface. The lpmSettings.isml template renders the "Payment methods without separate capture" configuration field, which stores method types in AdyenConfigs:
<label class="form-title mb-0" for="saleFlow">Payment methods without separate capture</label>
<input type="text" name="AdyenSalePaymentMethods" id="saleFlow"
placeholder="e.g. ideal, sepadirectdebit, paypal"
value="${AdyenConfigs.getAdyenSalePaymentMethods().join(', ')}">
The setPaymentTransactionType function consults this configuration to determine whether to trigger immediate capture or await webhook confirmation for each transaction.
Summary
- The Adyen SFCC cartridge supports immediate capture (Sale) and delayed capture (Authorize-then-Capture) models via
setPaymentTransactionTypeinadyenHelper.js. - CAPTURE webhooks finalize delayed orders by setting payment status to PAID and undoing previous cancellations when necessary.
- CAPTURE_FAILED webhooks automatically cancel orders and mark them as NOT PAID to prevent fulfillment failures.
- Intermediate result codes (
PENDING,RECEIVED) trigger a waiting state, ensuring capture delays are handled safely before order finalization. - Configuration occurs through the Adyen Sale Payment Methods list in Business Manager, stored in
AdyenConfigsand processed during transaction creation.
Frequently Asked Questions
How does the Adyen SFCC cartridge handle capture delays?
The cartridge handles capture delays by checking for intermediate result codes (PENDING, RECEIVED) using isIntermediateResultCode in adyenHelper.js. When detected, the order remains in a pending state until Adyen sends a definitive CAPTURE webhook, at which point eventHandlers/CAPTURE.js finalizes the order status to PAID and prepares it for export.
What is the difference between immediate and delayed capture in Adyen SFCC?
Immediate capture (Sale) sets PaymentTransaction.TYPE_CAPTURE during order creation in adyenHelper.js, capturing funds instantly. Delayed capture authorizes the payment but waits for a CAPTURE webhook to transfer funds, utilizing eventHandlers/CAPTURE.js to process the final transaction state asynchronously and allowing for shipping-triggered captures.
How does the system recover from a failed capture attempt?
If a capture fails, Adyen sends a CAPTURE_FAILED webhook processed by eventHandlers/CAPTURE_FAILED.js. This handler cancels the order via OrderMgr.cancelOrder and sets the payment status to NOT PAID. If a subsequent capture succeeds, the CAPTURE handler calls OrderMgr.undoCancelOrder to restore the cancelled order and mark it as PAID, ensuring no duplicate charges occur.
Where is the capture mode configured in the Adyen SFCC integration?
Merchants configure capture modes in Business Manager through the lpmSettings.isml template. The "Payment methods without separate capture" field stores method types (like paypal or ideal) in AdyenConfigs, which setPaymentTransactionType references to determine whether to use immediate capture or await webhook confirmation.
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 →