How to Handle Manual Payment Reviews and Fraud Detection in Adyen Salesforce Commerce Cloud
The Adyen Salesforce Commerce Cloud integration automatically flags high-risk transactions for manual review when Adyen returns an AMBER fraud result, then processes merchant accept or reject decisions through dedicated webhook handlers while providing a pluggable fraud detection hook for pre-order risk checks.
The adyen/adyen-salesforce-commerce-cloud repository implements a two-layered security architecture to handle manual payment reviews and fraud detection. This approach leverages Adyen's risk engine to pause suspicious orders for human oversight while enabling merchants to inject custom fraud logic during the checkout flow before any payment authorization occurs.
Detecting Manual Review Triggers in Webhook Handlers
When Adyen's fraud engine identifies a transaction requiring human oversight, it returns the AMBER fraud result type. The integration detects this status in the AUTHORISATION webhook handler and halts automatic order placement until a merchant manually reviews the case in the Adyen Customer Area.
Identifying AMBER Status in AUTHORISATION.js
The webhook processor at src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/AUTHORISATION.js (lines 25-44) inspects the additionalData.fraudResultType field to identify transactions flagged for manual review:
// excerpt from lines 25-44
const fraudResultType = webhookData['additionalData.fraudResultType'];
// ...
if (fraudResultType === constants.FRAUD_STATUS_AMBER) {
order.trackOrderChange(
'Order sent for manual review in Adyen Customer Area',
);
} else {
// normal success path → place order, etc.
}
The constant FRAUD_STATUS_AMBER is defined in src/cartridges/int_adyen_webhooks/cartridge/utils/constants.js at line 23:
FRAUD_STATUS_AMBER: 'AMBER',
When this condition triggers, the order remains in CREATED status and webhook processing stops immediately. No order confirmation is sent to the shopper until the merchant intervenes in the Adyen dashboard.
Processing Accept and Reject Decisions
Once a merchant reviews the transaction, Adyen sends follow-up webhooks that the integration handles through specific event handlers in the int_adyen_webhooks cartridge.
MANUAL_REVIEW_ACCEPT.js
Located at src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/MANUAL_REVIEW_ACCEPT.js, this handler executes when a merchant approves a flagged transaction:
order.trackOrderChange(
'Manual review is accepted in Adyen Customer Area, placing the order',
);
handleSuccessfulAuthorisation(order, result);
This invokes handleSuccessfulAuthorisation to transition the order to PAID status, trigger OMS export, and send the order confirmation email.
MANUAL_REVIEW_REJECT.js
Located at src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/MANUAL_REVIEW_REJECT.js, this handler processes rejection decisions:
order.trackOrderChange(
'Manual review is not accepted in Adyen Customer Area, failing the order',
);
order.setPaymentStatus(Order.PAYMENT_STATUS_NOTPAID);
Transaction.wrap(() => {
OrderMgr.failOrder(order, false);
});
The order is explicitly set to NOTPAID and failed within a Transaction.wrap() block, ensuring atomic database updates while returning the shopper to the checkout error flow.
Implementing Pre-Order Fraud Detection
Beyond Adyen's post-authorisation risk checks, the integration supports custom fraud detection during checkout through a pluggable hook architecture that aborts suspicious transactions before payment processing begins.
The Fraud Detection Hook Architecture
The checkout controller at src/cartridges/int_adyen_SFRA/cartridge/controllers/middlewares/checkout_services/placeOrder.js implements fraud checking at two critical points (lines 56-66 and 200-207):
// Lines 56-66 – abort if a previous fraud check already failed
if (req.session.privacyCache.get('fraudDetectionStatus')) {
res.json({
error: true,
cartError: true,
redirectUrl: URLUtils.url('Error-ErrorCode', 'err', '01').toString(),
errorMessage: Resource.msg('error.technical', 'checkout', null)
});
return next();
}
// Lines 200-207 – invoke the hook for the current basket
var fraudDetectionStatus = hooksHelper(
'app.fraud.detection',
'fraudDetection',
currentBasket,
require('*/cartridge/scripts/hooks/fraudDetection').fraudDetection
);
if (fraudDetectionStatus.status === 'fail') {
Transaction.wrap(() => { OrderMgr.failOrder(order, true); });
req.session.privacyCache.set('fraudDetectionStatus', true);
// ...
}
The hooksHelper utility dynamically routes to the implementation specified in scripts/hooks/fraudDetection.js without requiring modifications to core controller logic.
Custom Fraud Logic Implementation
Merchants can override the default stub by creating src/cartridges/app_adyen_SFRA/cartridge/scripts/hooks/fraudDetection.js:
'use strict';
module.exports = {
fraudDetection: function (basket) {
// Example: block orders over $5,000
var threshold = 500000; // in minor units (e.g. cents)
if (basket.totalGrossPrice.value > threshold) {
return { status: 'fail', errorCode: '01' };
}
return { status: 'success' };
}
};
This implementation executes automatically during the placeOrder middleware flow, allowing validation of basket totals, shipping addresses, device fingerprints, or third-party risk service integrations.
Summary
- AMBER Detection: The
AUTHORISATION.jswebhook handler (lines 25-44) detectsFRAUD_STATUS_AMBERfrom Adyen's risk engine and pauses order placement by tracking the change and stopping processing, leaving orders in CREATED status. - Manual Review Handlers: Dedicated
MANUAL_REVIEW_ACCEPT.jsandMANUAL_REVIEW_REJECT.jshandlers finalize orders based on merchant decisions, invokinghandleSuccessfulAuthorisationfor accepts orOrderMgr.failOrderfor rejects. - Pre-Order Protection: The
placeOrder.jscontroller (lines 56-66, 200-207) executes theapp.fraud.detectionhook before payment authorization, enabling custom risk rules to fail orders viaTransaction.wrap(). - Transaction Safety: All order state changes execute within
Transaction.wrap()wrappers to ensure database consistency and prevent partial order creation.
Frequently Asked Questions
What triggers a manual payment review in the Adyen Salesforce Commerce Cloud integration?
Adyen's fraud risk engine returns an AMBER result type for transactions requiring human oversight. The AUTHORISATION.js webhook handler detects this via the additionalData.fraudResultType field and pauses the order by calling order.trackOrderChange(), leaving the order in CREATED status until the merchant accepts or rejects it in the Adyen Customer Area.
How does the integration handle accepted manual reviews?
When a merchant clicks Accept in the Adyen dashboard, Adyen sends a MANUAL_REVIEW_ACCEPT webhook. The MANUAL_REVIEW_ACCEPT.js handler executes handleSuccessfulAuthorisation(order, result), which transitions the order to PAID status, triggers OMS export, and sends the order confirmation email to the shopper.
Can merchants add custom fraud checks before the payment is processed?
Yes. The integration includes a pluggable app.fraud.detection hook invoked by placeOrder.js during checkout (lines 200-207). Merchants can implement custom logic in scripts/hooks/fraudDetection.js to validate basket values, shipping addresses, or device fingerprints, returning { status: 'fail' } to abort the order via OrderMgr.failOrder() before any payment authorization occurs.
What happens to the order when a manual review is rejected?
The MANUAL_REVIEW_REJECT.js handler sets the payment status to NOTPAID using order.setPaymentStatus(Order.PAYMENT_STATUS_NOTPAID), then fails the order within a transaction wrapper via OrderMgr.failOrder(order, false). This prevents order confirmation and returns the shopper to the checkout error page with a technical error message.
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 →