# How to Handle Manual Payment Reviews and Fraud Detection in Adyen Salesforce Commerce Cloud

> Learn how to handle manual payment reviews and fraud detection with Adyen Salesforce Commerce Cloud. Automate risk checks and manage merchant decisions seamlessly.

- Repository: [Adyen/adyen-salesforce-commerce-cloud](https://github.com/adyen/adyen-salesforce-commerce-cloud)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/AUTHORISATION.js) (lines 25-44) inspects the `additionalData.fraudResultType` field to identify transactions flagged for manual review:

```javascript
// 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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/src/cartridges/int_adyen_webhooks/cartridge/utils/constants.js) at line 23:

```javascript
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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/MANUAL_REVIEW_ACCEPT.js), this handler executes when a merchant approves a flagged transaction:

```javascript
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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/src/cartridges/int_adyen_webhooks/cartridge/eventHandlers/MANUAL_REVIEW_REJECT.js), this handler processes rejection decisions:

```javascript
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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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):

```javascript
// 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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/src/cartridges/app_adyen_SFRA/cartridge/scripts/hooks/fraudDetection.js):

```javascript
'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.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/AUTHORISATION.js) webhook handler (lines 25-44) detects `FRAUD_STATUS_AMBER` from 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.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/MANUAL_REVIEW_ACCEPT.js) and [`MANUAL_REVIEW_REJECT.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/MANUAL_REVIEW_REJECT.js) handlers finalize orders based on merchant decisions, invoking `handleSuccessfulAuthorisation` for accepts or `OrderMgr.failOrder` for rejects.
- **Pre-Order Protection**: The [`placeOrder.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/placeOrder.js) controller (lines 56-66, 200-207) executes the `app.fraud.detection` hook before payment authorization, enabling custom risk rules to fail orders via `Transaction.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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/placeOrder.js) during checkout (lines 200-207). Merchants can implement custom logic in [`scripts/hooks/fraudDetection.js`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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`](https://github.com/adyen/adyen-salesforce-commerce-cloud/blob/main/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.