---
title: SFRA
slug: sfra
createdAt: 2025-07-30T21:22:24.109Z
---

# SFRA プレ認証統合

以下の指示は、SFCC ストアフロントの SFRA バージョンを使用しているすべてのストアフロントのための Forter のプレ認証統合に使用されるべきです。

:::BlockQuote
**注意** SFRA ベースのウェブサイトの場合、**CheckoutServices.js** ファイルには Forter プレ認証を呼び出すためのコメント付きコードが含まれています。プレ認証フローでは、このコードは **コメント解除** される必要があります。そうしないと、`CheckoutServices.js` ファイル内の **PlaceOrder** エンドポイントが置き換えられません。
:::

## CheckoutServices.js ファイルを更新する

Forter カートリッジ内の `CheckoutServices.js` ファイルに移動します。このファイルへの正確なパスは:
**Cartridges** > **int\_forter\_sfra** > **cartridge** > **controllers** > **CheckoutService.js**.

このファイルを開き、**コメント解除**する必要があるコードを *AFTER* `var order = COHelpers.createOrder(currentBasket);` 関数の後に、*BEFORE* `var handlePaymentResult = COHelpers.handlePayments(order, order.orderNo);` 関数の前に配置します。

```javascript
    var orderNumber = order.getCurrentOrderNo(); var argOrderValidate = {
        orderNumber : orderNumber,
        orderValidateAttemptInput : 1, 
        authorizationStep: “PRE_AUTHORIZATION”
    },

    forterCall = require(‘*/cartridge/scripts/pipelets/forter/forterValidate’), 
    forterDecision = forterCall.validateOrder(argOrderValidate);

    // in case if no response from Forter, try to call one more time
    if (forterDecision.result === false && forterDecision.orderValidateAttemptInput == 2) {
        var argOrderValidate = {
            orderNumber : orderNumber, orderValidateAttemptInput : 2,
            authorizationStep: “PRE_AUTHORIZATION” 
        },
        forterCall = require(‘*/cartridge/scripts/pipelets/forter/forterValidate’),
        forterDecision = forterCall.validateOrder(argOrderValidate); 
    }
    if (forterDecision.JsonResponseOutput.processorAction == ‘void’) { 
        Transaction.wrap(function () { OrderMgr.failOrder(order, true); }); 
        if (!empty(forterDecision.PlaceOrderError)) {
            res.json({
                error : true,
                errorMessage : forterDecision.PlaceOrderError.code 
            });
        } else { 
            res.json({
                error : true,
                errorMessage : Resource.msg(‘error.technical’, ‘checkout’, null) 
            });
        }
    this.emit(‘route:Complete’, req, res); 
    return;
    }
```

以下に含まれるスクリーンショットと完全なコード CheckoutServices.js ファイルは、この追加コードが **CheckoutServices.js** ファイル内のどこに配置されるべきかを示しています。

![](https://api.archbee.com/api/optimize/dYhsqkppjaUNAqFmds_Ny-fKnIX38CgjLzjgth-xLTf-20250724-184341.png)

:::ExpandableHeading
### Full CheckoutServices.js file with Pre-auth

```javascript
'use strict';

var server = require('server');
server.extend(module.superModule);

var COHelpers      = require('*/cartridge/scripts/checkout/checkoutHelpers');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
var ForterConfig   = require('int_forter_mfra/cartridge/scripts/lib/forter/ForterConfig.ds').ForterConfig;

server.replace('PlaceOrder', server.middleware.https, function (req, res, next) {
    var BasketMgr = require('dw/order/BasketMgr');
    var HookMgr = require('dw/system/HookMgr');
    var OrderMgr = require('dw/order/OrderMgr');
    var Resource = require('dw/web/Resource');
    var Transaction = require('dw/system/Transaction');
    var URLUtils = require('dw/web/URLUtils');
    var basketCalculationHelpers = require('*/cartridge/scripts/helpers/basketCalculationHelpers');

    var currentBasket = BasketMgr.getCurrentBasket();

    if (!currentBasket) {
        res.json({
            error: true,
            cartError: true,
            fieldErrors: [],
            serverErrors: [],
            redirectUrl: URLUtils.url('Cart-Show').toString()
        });
        return next();
    }

    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();
    }

    var validationBasketStatus = HookMgr.callHook(
        'app.validate.basket',
        'validateBasket',
        currentBasket,
        false
    );

    if (validationBasketStatus.error) {
        res.json({
            error: true,
            errorMessage: validationBasketStatus.message
        });
        return next();
    }

    // Check to make sure there is a shipping address
    if (currentBasket.defaultShipment.shippingAddress === null) {
        res.json({
            error: true,
            errorStage: {
                stage: 'shipping',
                step: 'address'
            },
            errorMessage: Resource.msg('error.no.shipping.address', 'checkout', null)
        });
        return next();
    }

    // Check to make sure billing address exists
    if (!currentBasket.billingAddress) {
        res.json({
            error: true,
            errorStage: {
                stage: 'payment',
                step: 'billingAddress'
            },
            errorMessage: Resource.msg('error.no.billing.address', 'checkout', null)
        });
        return next();
    }

    // Calculate the basket
    Transaction.wrap(function () {
        basketCalculationHelpers.calculateTotals(currentBasket);
    });

    // Re-validates existing payment instruments
    var validPayment = COHelpers.validatePayment(req, currentBasket);
    if (validPayment.error) {
        res.json({
            error: true,
            errorStage: {
                stage: 'payment',
                step: 'paymentInstrument'
            },
            errorMessage: Resource.msg('error.payment.not.valid', 'checkout', null)
        });
        return next();
    }

    // Re-calculate the payments.
    var calculatedPaymentTransactionTotal = COHelpers.calculatePaymentTransaction(currentBasket);
    if (calculatedPaymentTransactionTotal.error) {
        res.json({
            error: true,
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });
        return next();
    }

    // Creates a new order.
    var order = COHelpers.createOrder(currentBasket);
    if (!order) {
        res.json({
            error: true,
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });
        return next();
    }
    // add or uncomment the new pre-auth code here:
    var argOrderValidate = {
                Order: order,
                orderValidateAttemptInput: 1
            },
            forterController = require('int_forter/cartridge/controllers/ForterValidate'),
            forterDecision   = forterController.ValidateOrder(argOrderValidate);
        // in case if no response from Forter, try to call one more time
        if (forterDecision.result === false && forterDecision.orderValidateAttemptInput == 2) {
            var argOrderValidate = {
                    Order: order,
                    orderValidateAttemptInput: 2
                },
                forterController = require('int_forter/cartridge/controllers/ForterValidate'),
                forterDecision   = forterController.ValidateOrder(argOrderValidate);
        }
        
        if (forterDecision.JsonResponseOutput.processorAction == 'void') {
            if (!empty(forterDecision.PlaceOrderError)) {
                return {error : true, forterErrorCode : forterDecision.PlaceOrderError};
            } else {
                return {error : true};
            }
        }  
        // end of pre-auth code

    var handlePaymentResult = COHelpers.handlePayments(order, order.orderNo);     // Handles payment authorization
    if (handlePaymentResult.error) {
        if (ForterConfig.forterShowDeclinedPage == true && !empty(ForterConfig.forterCustomDeclineMessage)) {
            res.json({
                error: true,
                errorMessage: ForterConfig.forterCustomDeclineMessage
            });
        } else {
            res.json({
                error: true,
                errorMessage: Resource.msg('error.technical', 'checkout', null)
            });
        }
        return next();
    }
    
    var fraudDetectionStatus = HookMgr.callHook('app.fraud.detection', 'fraudDetection', currentBasket);
    if (fraudDetectionStatus.status === 'fail') {
        Transaction.wrap(function () { OrderMgr.failOrder(order); });

        // fraud detection failed
        req.session.privacyCache.set('fraudDetectionStatus', true);

        res.json({
            error: true,
            cartError: true,
            redirectUrl: URLUtils.url('Error-ErrorCode', 'err', fraudDetectionStatus.errorCode).toString(),
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });

        return next();
    }

    // Places the order
    var placeOrderResult = COHelpers.placeOrder(order, fraudDetectionStatus);
    if (placeOrderResult.error) {
        res.json({
            error: true,
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });
        return next();
    }

    COHelpers.sendConfirmationEmail(order, req.locale.id);

    // Reset usingMultiShip after successful Order placement
    req.session.privacyCache.set('usingMultiShipping', false);

    // TODO: Exposing a direct route to an Order, without at least encoding the orderID
    //  is a serious PII violation.  It enables looking up every customers orders, one at a
    //  time.
    res.json({
        error: false,
        orderID: order.orderNo,
        orderToken: order.orderToken,
        continueUrl: URLUtils.url('Order-Confirm').toString()
    });

    return next();
});


module.exports = server.exports();
```
:::

## オプション 1: 事前認証および事後認証の決定

Forterからの認証後の決定を受け取るために、以下の手順に従ってください。このフローでは、支払いゲートウェイに電話する前に、バイナリの承認/拒否の決定を受け取ります *prior* あなたのゲートウェイからの認証応答を受け取った後に。

### 1. 認証後の検証呼び出しのためのマッピングを含める

CheckoutServices.jsファイルのコードのコメントを外した後、forterOrder.jsファイルのマッピングをカスタマイズする必要があります (**cartridges** > **int\_forter\_sfra** > **cartridge** > **scripts** > **lib** > **forter** > **dto** > **forterOrder.js**) で、ゲートウェイからの認証応答をForterへの認証後の呼び出しに含める必要があります。正確なマッピングは、`ForterCreditCard(auth, cc)`関数内の`verificationResults`オブジェクトにあります。

サンプル forterOrder.js マッピング

```javascript

    function ForterCreditCard(auth, cc) {
    var processorResponseCode = '';
    var processorResponseText = '';
    var creditCardExpMonth;

    // format the expiration month. from 1 to 01, etc.
    if (cc.creditCardExpirationMonth.toString().length === 1) {
        creditCardExpMonth = '0' + cc.creditCardExpirationMonth.toString();
    } else {
        creditCardExpMonth = cc.creditCardExpirationMonth.toString();
    }

    this.nameOnCard = cc.creditCardHolder;
    this.cardBrand = cc.creditCardType;

    if (cc.creditCardNumber.substring(0, 6).indexOf('*') > -1) {
        this.bin = session.forms.billing.creditCardFields.cardNumber.value.substring(0, 6); // session.forms.billing.paymentMethods.creditCard.number.value.substring(0, 6);
    } else {
        this.bin = cc.creditCardNumber.substring(0, 6);
    }

    this.lastFourDigits = cc.creditCardNumberLastDigits;
    this.expirationMonth = creditCardExpMonth;
    this.expirationYear = cc.creditCardExpirationYear.toString();

    this.verificationResults = {};
    this.verificationResults.avsFullResult = auth ? auth.avsResultCode.toString() : '';     // must be adjusted according to the payment gateway used
    this.verificationResults.cvvResult = auth ? auth.cvvResultCode.toString() : '';         // must be adjusted according to the payment gateway used
    this.verificationResults.authorizationCode = auth ? auth.authCode.toString() : '';      // must be adjusted according to the payment gateway used

    this.paymentGatewayData = {};
    this.paymentGatewayData.gatewayName = 'AUTHORIZE.NET';                  // must be adjusted according to the payment gateway used
    this.paymentGatewayData.gatewayTransactionId = auth ? auth.transId.toString() : ''; // must be adjusted according to the payment gateway used

    if (auth && auth.errors.errorCode.length > 0) {                           // must be adjusted according to the payment gateway used
        processorResponseCode = auth.errors.errorCode.toString();     // must be adjusted according to the payment gateway used
        processorResponseText = auth.errors.errorText.toString();     // must be adjusted according to the payment gateway used
    } else if (auth && auth.messages.code.length > 0) {                       // must be adjusted according to the payment gateway used
        processorResponseCode = auth.messages.code.toString();        // must be adjusted according to the payment gateway used
        processorResponseText = auth.messages.description.toString(); // must be adjusted according to the payment gateway used
    }

    this.verificationResults.processorResponseCode = processorResponseCode;
    this.verificationResults.processorResponseText = processorResponseText;
}
```

### 2. ビジネスマネージャーを更新する\*\*

- 上記に追加したコードは、あなたの **CheckoutServices.js** ファイルが、決済ゲートウェイへの認証呼び出しが行われる **前** に、ストアフロントが注文情報をForterエンドポイントに送信できるようにします。
- Forterビジネスマネージャー拡張機能を更新して、更新された事前認証フローに対してカスタマイズされたエラーメッセージを表示するようにしてください。

## オプション2: 事後認証注文ステータスによる事前認証決定

この構成では、あなたは事前認証のForter決定のみを受け取ります（*事前*に決済ゲートウェイを呼び出す）そして、ゲートウェイからの認証応答は、Forterへのその後の注文ステータス呼び出しに含まれます。

### 1. 注文ステータスを更新する

以下のAuthorize.netスクリプトの例は、クレジットカード処理を扱うスクリプトを更新して、Forterへの関連する事後認証注文ステータス呼び出しを含める方法を示しています。デフォルトの2つの注文ステータスは次のとおりです:

- **"処理中"**（ゲートウェイが支払いを承認したとき）
- **"商人によってキャンセルされた"**（ゲートウェイ/プロセッサが支払いを承認していないとき）

以下のコード例は、すべてのSFRAおよびSG事前認証/ステータス実装に適用され、完全な `postAuthOrderStatusUpdate()` 関数はforterValidate.jsファイルにあります（**cartridges** > **int\_forter\_sfra** > **cartridge** > **scripts** > **pipelets** > **forter** > **forterValidate.js**)

:::BlockQuote
注文状況の呼び出しは **直ちに** 支払いが承認された後に行うべきです
:::

```javascript

    var argOrderUpdate = { 
        orderNumber: orderNumber,
        updateAttempt: 1 
    },
    forterCall = require('*/cartridge/scripts/pipelets/forter/forterValidate'),
    forterDecision = forterCall.postAuthOrderStatusUpdate(argOrderUpdate, "PROCESSING");

    if (forterDecision.result === false && forterDecision.updateAttempt == 2) { 
        forterDecision.updateAttempt = 2; 
        forterCall.postAuthOrderStatusUpdate(argOrderUpdate, "PROCESSING");
    }
```

### 2. ForterUpdate.jsファイルをカスタマイズする

事前承認コード `CheckoutServices.js` ファイルのコメントを解除した後、`forterUpdate.js` ファイルをカスタマイズして、あなたのゲートウェイの承認応答をForterへのステータス呼び出しに含めます。`forterUpdate.js` ファイルの正確なパスは **cartridges** > **int\_forter\_sfra** > **cartridge** > **scripts** > **lib** > **forter** > **dto** > **forterUpdate.js**)です。 `ForterPayment(payment, order)` 関数をカスタマイズして、特定のゲートウェイ承認応答をマッピングします。

:::ExpandableHeading
### Example ForterCreditCard() auth function

```javascript

    function ForterCreditCard(auth, cc) {
        var processorResponseCode = '';
        var processorResponseText = '';
        var creditCardExpMonth;

        // format the expiration month. from 1 to 01, etc.
        if (cc.creditCardExpirationMonth.toString().length === 1) {
            creditCardExpMonth = '0' + cc.creditCardExpirationMonth.toString();
        } else {
            creditCardExpMonth = cc.creditCardExpirationMonth.toString();
        }

        this.nameOnCard = cc.creditCardHolder;
        this.cardBrand = cc.creditCardType;

        if (cc.creditCardNumber.substring(0, 6).indexOf('*') > -1) {
            this.bin = session.forms.billing.creditCardFields.cardNumber.value.substring(0, 6); // session.forms.billing.paymentMethods.creditCard.number.value.substring(0, 6);
        } else {
            this.bin = cc.creditCardNumber.substring(0, 6);
        }

        this.lastFourDigits = cc.creditCardNumberLastDigits;
        this.expirationMonth = creditCardExpMonth;
        this.expirationYear = cc.creditCardExpirationYear.toString();

        this.verificationResults = {};
        this.verificationResults.avsFullResult = auth ? auth.avsResultCode.toString() : '';     // must be adjusted according to the payment gateway used
        this.verificationResults.cvvResult = auth ? auth.cvvResultCode.toString() : '';         // must be adjusted according to the payment gateway used
        this.verificationResults.authorizationCode = auth ? auth.authCode.toString() : '';      // must be adjusted according to the payment gateway used

        this.paymentGatewayData = {};
        this.paymentGatewayData.gatewayName = 'AUTHORIZE.NET';                  // must be adjusted according to the payment gateway used
        this.paymentGatewayData.gatewayTransactionId = auth ? auth.transId.toString() : ''; // must be adjusted according to the payment gateway used

        if (auth && auth.errors.errorCode.length > 0) {                           // must be adjusted according to the payment gateway used
            processorResponseCode = auth.errors.errorCode.toString();     // must be adjusted according to the payment gateway used
            processorResponseText = auth.errors.errorText.toString();     // must be adjusted according to the payment gateway used
        } else if (auth && auth.messages.code.length > 0) {                       // must be adjusted according to the payment gateway used
            processorResponseCode = auth.messages.code.toString();        // must be adjusted according to the payment gateway used
            processorResponseText = auth.messages.description.toString(); // must be adjusted according to the payment gateway used
        }

        this.verificationResults.processorResponseCode = processorResponseCode;
        this.verificationResults.processorResponseText = processorResponseText;
    }

    var billingAddress = order.billingAddress;

    this.billingDetails = {};
    this.billingDetails.personalDetails = {};
    this.billingDetails.personalDetails.firstName = billingAddress.firstName;
    this.billingDetails.personalDetails.lastName = billingAddress.lastName;

    if (billingAddress.phone) {
        this.billingDetails.phone = [];
        this.billingDetails.phone.push(new ForterPhone(billingAddress.phone));
    }

    this.amount = {
        amountLocalCurrency: order.totalGrossPrice.value.toFixed(2),
        currency: order.totalGrossPrice.currencyCode
    };

    if (payment) {
        if (payment.paymentMethod === 'CREDIT_CARD') {
            this.creditCard = new ForterCreditCard(authResponse, payment);
        }
    } else {
        log.error('No payment method information for order: ' + order.originalOrderNo);
    }
}
```
:::

### 3. ビジネスマネージャーを更新する

上記に追加したコードは、あなたの **CheckoutServices.js** ファイルが、決済ゲートウェイへの認証呼び出しが行われる **前に**、注文情報をForterエンドポイントに送信できるようにします。

また、あなたは **Forterビジネスマネージャー** **拡張機能** を更新して、この更新された事前認証フローのためのカスタマイズされたエラーメッセージを表示する必要があります。
