---
title: Native Mobile App Tests
slug: extensions/native-mobile-app-tests
docTags: 
createdAt: 2025-12-14T14:23:41.918Z
---

# Native Mobile App Testing

## App Testing Overview

If you have native applications for iOS or Android, we recommend integrating our mobile SDKs in order to ensure accurate decisions for this order segment. We have dedicated sections outlining the recommended integrations for [iOS](https://docs.forter.com/mobile-sdks/ios-sdk-overview) and [Android](https://docs.forter.com/mobile-sdks/android-sdk-overview) .

## Step 1: Retrieve the `forterMobileUID`

Once you've installed the relevant Forter mobile SDKs, make sure that the SDK is generating a unique device identifier. This identifier should be specific to the device (such as the [IDFV](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor)) for iOS and [Device Identifier for Android](https://developer.android.com/training/articles/user-data-ids) and it should persist across multiple user sessions.

When a customer makes a purchase from the mobile app, the unique Identifier should **also** be included as the *forterMobileUID* value in the `connectionInformation` object of the [Order API](https://docs.forter.com/reference/order-v3). Since the Order API call is server-to-server, this value, along with the customer's IP address and userAgent data should be retrieved from your HTTPS request headers and passed to the SFCC cartridge.

## Step 2: Update OrderType

Make sure that the `orderType` field in the `ForterOrder.ds` file is updated to the `"MOBILE"` enum value.

### Step 2.2 Mobile App Order Mapping

The below code represents a mapped mobile app order for an SFRA Adyen integration. Please note that the following fields in the `connectionInformation` object have been updated:

- **orderType** - This should always be "MOBILE" for transactions placed on an app
- **forterMobileUID** - This should correspond to the ID that is passed to the mobile SDK and included in the backend order request. It **replaces** the `forterTokenCookie` value for mobile orders
- **customerIP** - This should be the IP that corresponds to the end user's device. NOT the IP of your server.

Below is an example of how the connectionInformation object should appear for a native mobile app order:

```java
{
  "orderId": "Example_order_fdse0rr489",
  "orderType": "MOBILE",
  "timeSentToForter": 1415287568000,
  "checkoutTime": 1415273168,
  "connectionInformation": {
    "customerIP": "107.57.208.5",
    "userAgent": "testStore 18.4.1 rv:184100001 (iPhone; iOS 12.4.1; en_US; iPhone10,3)",
    "forterMobileUID": "6A9798AG16FF41F7B6B9878DD488ADD5", // pass unique Identifier here
    "mobileAppVersion": "18.4.1"
  },
  "totalAmount": {
    "amountUSD": "99.95"
  }
}
```

:::ExpandableHeading
### Sample Mobile Order

```javascript
var ForterLogger = require("int_forter/cartridge/scripts/lib/forter/ForterLogger.ds");
/**
 * ForterOrder class is the DTO object for request.
 *
 * To include this script use:
 * var ForterOrder = require("int_forter/cartridge/scripts/lib/forter/dto/ForterOrder.ds");
 */
function ForterOrder(args) {

    var log          = new ForterLogger("ForterOrder.ds"),
        order        = args.Order,
        site         = dw.system.Site.getCurrent(),
        paymentInstruments = order.getPaymentInstruments(),
        a       = null,
        authResponse = null,
        shipment     = null;
    
    for each (var paymentInstrument in paymentInstruments) {
        if (paymentInstrument.paymentMethod == 'CREDIT_CARD') {
            a = paymentInstrument;
            authResponse = args.CreditCardAuthResponse;
        } else if (paymentInstrument.paymentMethod == 'PayPal' || paymentInstrument.paymentMethod == 'ApplePay') {
            a = paymentInstrument;
            authResponse = {}; // not necessary as this info can be retreived from a  instrument
        } else if (paymentInstrument.paymentMethod == 'GIFT_CERTIFICATE') { // optional if this is an accepted a  method in your store
        	a = paymentInstrument;
        	authResponse = args.GCRedemptionTransaction;
        }
    }

    forterOrderType = function {
      for each (var type in OrderType) {
        if (orderType.type == 'SFCC storeFront web') {
            return "WEB";
        } else if (orderType.type == 'iOS version mobile' || orderType.type == 'Android linux mobile') {
            return "MOBILE"
       }
    }

    // General parameters
    this.orderId               = order.originalOrderNo;   // Required
    this.orderType             = order.forterOrderType;   // "MOBILE" for app and "WEB" for web orders,
    this.timeSentToForter      = (new Date()).getTime();          // Required
    this.checkoutTime          = new Number((order.creationDate.getTime()/1000).toFixed());    // Required //must be seconds, not milliseconds
    this.connectionInformation = new ForterConnectionInformation(order);    // Required

    // Calculate totals
    this.totalAmount                     = {};                                // Required
    this.totalAmount.amountLocalCurrency = order.totalGrossPrice.value.toFixed(2);
    this.totalAmount.currency            = order.totalGrossPrice.currencyCode;
    this.amountUSD                       = order.totalGrossPrice.value
    

    //Calculate totals for discounts
    var orderTotal    = order.getMerchandizeTotalPrice(),
        adjOrderTotal = order.getAdjustedMerchandizeTotalPrice(),
        shipTotal     = order.getShippingTotalPrice(),
        adjShipTotal  = order.getAdjustedShippingTotalPrice(),
        total         = orderTotal.add(shipTotal),
        adjTotal      = adjOrderTotal.add(adjShipTotal);

    // Discounts
    var discountPrice = 0;
    var couponName = '';
    var couponType = '';
    if (!order.getCouponLineItems().isEmpty()) {
        var coupons     = order.getCouponLineItems();
        var couponNames = [];

        for each (let coup in coupons) {
            couponNames.push(coup.getCouponCode());
            if (!coup.getPriceAdjustments().isEmpty()) {
                var coupAdjustments = coup.getPriceAdjustments();
                for each (let coupAdj in coupAdjustments) {
                    discountPrice += coupAdj.priceValue;
                }
            }
        }

        couponName = couponNames.join(",");
        discountPrice = discountPrice*(-1);

        if (discountPrice > 0) {
            this.totalDiscount = {};                                                // Optional
            this.totalDiscount.couponCodeUsed = couponName.substring(0,20);         // Required
            this.totalDiscount.couponDiscountAmount = {};                            // Required
            this.totalDiscount.couponDiscountAmount.amountLocalCurrency = discountPrice.toFixed(2);
            this.totalDiscount.couponDiscountAmount.currency = order.currencyCode;
            this.totalDiscount.discountType = "COUPON";                                // Required
        }
    }

    // Customer's details
    this.accountOwner = new ForterCustomer(order);

    // Cart items (regular product)
    this.cartItems = [];                                                    // Required
    for each (var pli in order.productLineItems) {
        this.cartItems.push(new ForterCartItem(pli));
    }
    
    // Cart items (gift certificate)
    for each (var gcli in order.giftCertificateLineItems) {
        this.cartItems.push(new ForterCartItem(gcli));
    }

    // Payments
    this.a  = [];                                                        // Required
    this.a .push(new ForterPayment(order, authResponse, a , log));

    // Delivery and Recipient (shipping information)
    if (order.shipments.length > 0) {
        shipment = order.shipments[0];
        
        this.primaryDeliveryDetails = {};
        this.primaryDeliveryDetails.deliveryPrice = {};
        this.primaryDeliveryDetails.deliveryMethod = (!empty(shipment.getShippingMethod()) && !empty(shipment.getShippingMethod().getDisplayName())) ? shipment.getShippingMethod().getDisplayName() : ""; //"BY AIR";
        
        var deliveryType = "PHYSICAL"; //default value
        if (order.productLineItems.size() > 0 && order.giftCertificateLineItems.size() == 0) {
            deliveryType = "PHYSICAL"; //if real products only
        } else if (order.productLineItems.size() == 0 && order.giftCertificateLineItems.size() > 0) {
            deliveryType = "DIGITAL";  //if gift certificates only
            this.primaryDeliveryDetails.deliveryMethod = "email";
        } else if (order.productLineItems.size() > 0 && order.giftCertificateLineItems.size() > 0) {
            deliveryType = "HYBRID";  //if gift certificates and real products
        }
        this.primaryDeliveryDetails.deliveryType                      = deliveryType;
        
        this.primaryDeliveryDetails.deliveryPrice.amountLocalCurrency = shipment.adjustedShippingTotalPrice.value.toFixed(2);
        this.primaryDeliveryDetails.deliveryPrice.currency            = shipment.adjustedShippingTotalPrice.currencyCode.toString();
  
            if (shipment.productLineItems.size() > 0) {
                
                this.primaryRecipient = {};                                                // Optional
                this.primaryRecipient.personalDetails = {};

                this.primaryRecipient.personalDetails.firstName = shipment.shippingAddress.firstName; //from the shipping address
                this.primaryRecipient.personalDetails.lastName = shipment.shippingAddress.lastName;  //from the shipping address
    
                this.primaryRecipient.address = {};
                this.primaryRecipient.address.address1 = shipment.shippingAddress.address1;
                this.primaryRecipient.address.address2 = !empty(shipment.shippingAddress.address2) ? shipment.shippingAddress.address2 : "";
                this.primaryRecipient.address.zip = shipment.shippingAddress.postalCode;
                this.primaryRecipient.address.city = shipment.shippingAddress.city;
                this.primaryRecipient.address.region = shipment.shippingAddress.stateCode;
                this.primaryRecipient.address.country = shipment.shippingAddress.countryCode.value.toUpperCase();
    
                this.primaryRecipient.phone = [];
                if (!empty(shipment.shippingAddress.phone)){
                    this.primaryRecipient.phone.push(new ForterPhone(shipment.shippingAddress.phone));
                }
                
            } else if (shipment.giftCertificateLineItems.size() > 0) {
                this.primaryRecipient = {};                                                // Optional
                this.primaryRecipient.personalDetails = {};
                
                this.primaryRecipient.personalDetails.fullName = shipment.giftCertificateLineItems[0].recipientName; //from the gift form
                this.primaryRecipient.personalDetails.email = shipment.giftCertificateLineItems[0].recipientEmail;   //from the gift form
            }
    
            if (shipment.gift == true) {
                this.primaryRecipient.comments = {};
                this.primaryRecipient.comments.messageToBeneficiary = shipment.giftMessage ? shipment.giftMessage : "";
            }
    }

    function ForterConnectionInformation(order) {

        this.customerIP = request.httpRemoteAddress;                    // Required
        this.userAgent  = request.httpUserAgent;

        var orderType = order.orderTypeRawRequest;

        if request.httpCookies { // if web or mobile browser order
            for (var i = 0; i < request.httpCookies.cookieCount; i++) {
                if (request.httpCookies[i].name == "forterToken") {
                    this.forterTokenCookie = request.httpCookies[i].value;  
                } else {
                    this.forterTokenCookie = "";
                }
            }
        } else if (orderType.type == 'iOS version mobile' || orderType.type == 'Android linux mobile') { // if app order
            if (request.http.mUID) {
                this.forterMobileUID = request.http.mUID;
            } else {
                this.forterMobileUID = "";
            }

            this.mobileAppVersion = request.http.versionHeaders
        }


        this.userAgent = request.httpUserAgent;                          // Required
        this.forterTokenCookie = "";                                     // Conditional
        for (var i = 0; i < request.httpCookies.cookieCount; i++) {
            if (request.httpCookies[i].name == "forterToken") {
                this.forterTokenCookie = request.httpCookies[i].value;
            }
        }
    }
}

function ForterCustomer(order) {

    if (order.customer.profile != null) {
        this.firstName = order.customer.profile.firstName;
        this.lastName = order.customer.profile.lastName;
        this.email = order.customer.profile.email;
        this.accountId = order.customer.ID;
        this.created = new Number((order.customer.profile.getCreationDate().getTime()/1000).toFixed());

        var query = "customerNo = {0} AND paymentStatus = {1}";
        var allOrders : SeekableIterator = dw.order.OrderMgr.searchOrders(query, "creationDate desc", order.customer.profile.customerNo, 2);
        this.pastOrdersCount = new Number(allOrders.count);

    } else {
        this.firstName = order.billingAddress.firstName;
        this.lastName = order.billingAddress.lastName;
        this.email = order.customerEmail;
    }
}

function ForterApplePay(auth, ap) {
// where var ap is the raw applepay response data from your payment gateway 
    var processorResponseCode = "",
        processorResponseText = "";

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

    this.nameOnCard = ap.creditCardHolder;
    this.cardBrand = ap.creditCardType;
    this.token = ap.token;
    this.lastFourDigits = ap.creditCardNumberLastDigits; //optional
    this.expirationMonth = creditCardExpMonth;
    this.expirationYear = cc.creditCardExpirationYear.toString();

    this.verificationResults = {};
    this.verificationResults.avsFullResult = auth.avs.toString(); // optional
    this.verificationResults.cvvResult = auth.cvd_code.toString(); // optional
    this.verificationResults.authorizationCode = auth.authorization_num.toString();
    
    processorResponseCode = auth.exact_resp_code.toString();
    processorResponseText = auth.exact_message.toString();

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

function ForterCartItem(item) {
    
    this.basicItemData = {};
    
    if (item instanceof dw.order.ProductLineItem) {
        this.basicItemData.productId = item.productID;     //Optional
        this.basicItemData.name      = item.productName;   //Required
        this.basicItemData.quantity  = item.quantityValue; //Required
    
        var product = item.getProduct();
        if (product.getCategories().isEmpty() && !empty(product.getVariationModel())) {
            product = product.getVariationModel().getMaster();
        }
        this.basicItemData.category = empty(product) ? "" : product.getCategories()[0].getDisplayName();
        this.basicItemData.type     = "TANGIBLE";                                            // Add if type is available. Change according to the actual item type
    
        //if any adjustements
        this.basicItemData.price = {};
        this.basicItemData.price.amountLocalCurrency = item.adjustedPrice.value.toFixed(2);
        this.basicItemData.price.currency = item.adjustedPrice.currencyCode.toString();
    }
    
    if (item instanceof dw.order.GiftCertificateLineItem) {
        this.basicItemData.name     = item.lineItemText;                                   //Required
        this.basicItemData.quantity = 1;                                                   //Required (set 1 by default for a gift cert?)
    
        this.basicItemData.type = "NON_TANGIBLE";                                          // Add if type is available. Change according to the actual item type
        this.deliveryDetails = {};
        this.deliveryDetails.deliveryType = "DIGITAL";
        this.deliveryDetails.deliveryMethod = "email";
    
        this.basicItemData.price = {};
        this.basicItemData.price.amountLocalCurrency = item.price.value.toFixed(2);
        this.basicItemData.price.currency = item.price.currencyCode.toString();
        
        
        this.beneficiaries = [];  
        this.beneficiaries.push(new ForterBeneficiaryDetailsFromGiftCard(item));
    }
}

module.exports = ForterOrder;
```
:::

## Step 3: Testing

### Normal QA email address

Place a phone order in your sandbox environment using any valid test email address. The transaction should show in the [portal transactions tab](https://portal.forter.com/transactions) and include a mobile phone icon next to the order number.

![](https://api.archbee.com/api/optimize/dYhsqkppjaUNAqFmds_Ny-o4caS21ku95oL5d0N32Zu-20250724-184334.png)

### Declined by Forter

Place a purchase with email address `decline@forter.com` using a valid test card credit card or payment method. This email address will always generate a "decline" decision in Forter's sandbox environment and will enable you to test your SFCC cartridge flow for a declined mobile transaction. Once you've placed this declined order, please check your cartridge business manager and review and (potentially update) your `processorResponse.json` file to either void funds and cancel declined mobile app orders.

### Approved by Forter

Create an order using `approve@forter.com` and a valid test card for the transaction. This will generate an "approve" decision and the transaction should show in the[ portal transactions tab](https://portal.forter.com/transactions) and include a mobile icon next to the order number. Make sure to review and update your `processorResponse.json` file to capture the funds and proceed with this order in your OMS.
