Documentation You are here: start » v » 2.0 » fighting-fraud

This is an old revision of the document!


Fraud Prevention in FoxyCart

minFraud in Foxy

Though we strongly recommend setting up any and all available anti-fraud prevention tools at the gateway level (and most gateways do have fraud controls available, though sometimes at an additional cost), FoxyCart does have integration with MaxMind's minFraud service. Click that link to get a feel for what minFraud is, or just understand that it looks at all the available data from the customer and transaction and provides a riskScore.

What to do with it?

You can enable minFraud in the “payment” page of your FoxyCart admin. Simply set it to any number greater than 0 to enable it. Any transaction with a riskScore higher than the number you enter will be declined.

Though every store and customer base will have different riskScore averages, MaxMind's general recommendation is to definitely reject anything with a riskScore of 60 or higher, and to screen anything with a riskScore between 4-59. FoxyCart defaults to minFraud off, so our recommendation is:

  1. Set it to 60 to start with.
  2. Monitor your normal scores (you can see the score on completed transactions) and see what's in a “normal” range.
  3. Adjust the score down to what's a comfortable upper-normal. For many of our users, that's as low as 4.

If you're already experiencing fraudulent orders, start at 15 or lower instead of 60, as above.

As a potential point of reference, MaxMind shares the following approximate distribution of riskScores across minFraud customers:

riskScore range Percent of orders in range
0.10 - 4.99 90%
5.00 - 9.99 5%
10.00 - 29.99 3%
30.00 - 99.99 2%

Source

Make the error message helpful

You can change the language for the error message displayed to the blocked customer in your store's “language” page, under “minfraud”. For example, you could include a phone number at which they would be able to further verify their identities.

Where is the score shown?

On any transaction that has a risk score of greater than 0, an entry for “Minfraud Score” will be shown within the transaction report within the administration.

There's no magic bullet to eliminate all fraud while allowing through all legitimate orders, but with your gateway's fraud filters and FoxyCart's minFraud integration you can get as close as possible.

Google's reCAPTCHA on the Foxy Checkout

Foxy's reCAPTCHA integration can be useful to preventing bots from aggressively scripting and pushing through transactions in an automated way. Note that reCAPTCHA is specifically to ensure that a human must be behind the request, but it has no opinion on whether that human is an honest person or a fraudster.

Foxy defaults to reCAPTCHA being off, but has 2 different options:

  1. Enabled, Always. As it sounds, this will include reCAPTCHA on every checkout.
  2. Enabled, Automatically as Needed. This will require reCAPTCHA for checkouts loaded by IP addresses that have triggered multiple errors in a preceding window of time. We attempt to set this so it would very rarely be shown to a legitimate customer, but would effectively make bot-based bulk fraud impossible. We don't publicize the exact thresholds, and may change them as needed.

Note that reCAPTCHA isn't required for API-based or UOE-based transactions.

Pre-Checkout Hooks

The Pre-Checkout Hook functionality allows for stores to apply custom validations for transactions prior to the request being set to the chosen payment method. A payload containing information about the current transaction is sent to the endpoint configured for this functionality, allowing the endpoint to respond to either approve or reject the transaction. If approved, the transaction will be sent on to the gateway to process, or if rejected the customer will be sent back to the checkout with a custom error message.

Enabling the Pre-Checkout Hook

To enable the pre-checkout hook, head to the “payment” setting page in your store's FoxyCart administration, ensuring that you've selected the payment set you're wanting to enable the hook for. Within the “Anti-Fraud Integrations” section, check the checkbox to “enable custom pre-checkout hook” and complete the settings below.

pre-checkout hook url
The URL for the endpoint script, which needs to be HTTPS, which will receive the JSON payload.
failure handling
If your endpoint script fails to load or respond correctly, use this setting to set whether the transaction should be automatically approved or rejected. If set to reject, a generic error message will be returned, which you can edit the error from the “language” section of your store's administration - look for the “error precheckout hook” language string within the “checkout” group.

Handling the request

When the customer attempts to complete their purchase, after the Google reCAPTCHA is validated (if active), a POST request is sent off to your custom post-checkout hook endpoint with a JSON payload representing the current cart. It follows the same structure as our Hypermedia API, as the majority of the data comes from there.

The following is an example of the JSON payload, showing two products (with one having live rates) and having a coupon added.

{
    "_links": {
    },
    "_embedded": {
        "fx:items": [
            {
                "_links": {
                },
                "_embedded": {
                    "fx:item_options": [
                        {
                            "_links": {
                            },
                            "name": "size",
                            "value": "medium",
                            "price_mod": 0,
                            "weight_mod": 0,
                            "date_created": null,
                            "date_modified": null
                        },
                        {
                            "_links": {
                            },
                            "name": "color",
                            "value": "red",
                            "price_mod": 0,
                            "weight_mod": 0,
                            "date_created": null,
                            "date_modified": null
                        }
                    ],
                    "fx:item_category": {
                        "_links": {
                        },
                        "admin_email_template_uri": "",
                        "customer_email_template_uri": "",
                        "code": "DEFAULT",
                        "name": "Default for all products",
                        "item_delivery_type": "flat_rate",
                        "max_downloads_per_customer": 3,
                        "max_downloads_time_period": 24,
                        "default_weight": 0,
                        "default_weight_unit": "LBS",
                        "default_length_unit": "IN",
                        "shipping_flat_rate_type": "per_item",
                        "shipping_flat_rate": 5,
                        "handling_fee_type": "none",
                        "handling_fee": 0,
                        "handling_fee_minimum": 0,
                        "handling_fee_percentage": 0,
                        "customs_value": 0,
                        "discount_type": "",
                        "discount_name": "",
                        "discount_details": "",
                        "send_customer_email": false,
                        "send_admin_email": false,
                        "admin_email": "",
                        "date_created": null,
                        "date_modified": null
                    }
                },
                "item_category_uri": "https://api.foxycart.com/item_categories/100",
                "name": "Example Product",
                "price": 15.99,
                "quantity": 1,
                "quantity_min": 0,
                "quantity_max": 0,
                "weight": 0,
                "code": "abc123",
                "parent_code": "",
                "discount_name": "",
                "discount_type": "",
                "discount_details": "",
                "subscription_frequency": "",
                "subscription_start_date": null,
                "subscription_next_transaction_date": null,
                "subscription_end_date": null,
                "is_future_line_item": false,
                "shipto": "Me",
                "url": "",
                "image": "",
                "length": 0,
                "width": 0,
                "height": 0,
                "expires": 0,
                "date_created": null,
                "date_modified": "2017-07-20T04:13:08-0700"
            },
            {
                "_links": {
                },
                "_embedded": {
                    "fx:item_category": {
                        "_links": {
                        },
                        "admin_email_template_uri": "",
                        "customer_email_template_uri": "",
                        "code": "live",
                        "name": "Live Rates",
                        "item_delivery_type": "shipped",
                        "max_downloads_per_customer": 3,
                        "max_downloads_time_period": 24,
                        "default_weight": 5,
                        "default_weight_unit": "LBS",
                        "default_length_unit": "IN",
                        "shipping_flat_rate_type": "per_order",
                        "shipping_flat_rate": 1,
                        "handling_fee_type": "none",
                        "handling_fee": 0,
                        "handling_fee_minimum": 0,
                        "handling_fee_percentage": 0,
                        "customs_value": 0,
                        "discount_type": "",
                        "discount_name": "",
                        "discount_details": "",
                        "send_customer_email": false,
                        "send_admin_email": false,
                        "admin_email": "",
                        "date_created": null,
                        "date_modified": null
                    }
                },
                "item_category_uri": "https://api.foxycart.com/item_categories/101",
                "name": "Another Product",
                "price": 20,
                "quantity": 1,
                "quantity_min": 0,
                "quantity_max": 0,
                "weight": 5,
                "code": "foo321",
                "parent_code": "",
                "discount_name": "",
                "discount_type": "",
                "discount_details": "",
                "subscription_frequency": "",
                "subscription_start_date": null,
                "subscription_next_transaction_date": null,
                "subscription_end_date": null,
                "is_future_line_item": false,
                "shipto": "Me",
                "url": "",
                "image": "",
                "length": 0,
                "width": 0,
                "height": 0,
                "expires": 0,
                "date_created": null,
                "date_modified": "2017-07-20T04:14:03-0700"
            }
        ],
        "fx:discounts": [
            {
                "code": "coupon",
                "amount": -3.6,
                "name": "Default Discount",
                "display": "-$3.60",
                "is_taxable": false,
                "is_future_discount": false
            }
        ],
        "fx:custom_fields": [
            {
                "name": "custom_note",
                "value": "Happy Birthday!",
                "is_hidden": 0
            }],
        "fx:shipment": {
            "address_name": "",
            "first_name": "John",
            "last_name": "Smith",
            "company": "",
            "address1": "Main Street",
            "address2": "",
            "city": "Saint Paul",
            "region": "MN",
            "postal_code": "55116",
            "country": "US",
            "origin_region": "TX",
            "origin_postal_code": "77018",
            "origin_country": "US",
            "shipping_service_id": 0,
            "shipping_service_description": "",
            "is_residential": false,
            "item_count": 2,
            "total_weight": 5,
            "total_customs_value": 0,
            "total_handling_fee": 0,
            "total_flat_rate_shipping": 5,
            "total_item_price": 35.99,
            "total_tax": 3.24,
            "total_shipping": 0,
            "total_price": 39.23
        },
        "fx:customer": {
            "id": "1512345",
            "first_name": "John",
            "last_name": "Smith",
            "email": "john@example.com",
            "tax_id": "",
            "is_anonymous": "0",
            "_embedded": {
                "fx:payments": [
                    {
                        "cc_type": "plastic",
                        "cc_number_masked": "xxxx xxxx xxxx 4242",
                        "cc_exp_month": "10",
                        "cc_exp_year": "2020",
                        "purchase_order": null
                    }
                ],
                "fx:default_billing_address": {
                    "country": "US",
                    "region": "MN",
                    "city": "Saint Paul",
                    "postal_code": "55116",
                    "address1": "Main Street",
                    "address2": "",
                    "company": "",
                    "full_name": "John Smith",
                    "first_name": "John",
                    "last_name": "Smith",
                    "phone": ""
                }
            }
        }
    },
    "customer_uri": "",
    "template_set_uri": "",
    "language": "",
    "locale_code": "en_US",
    "total_item_price": 35.99,
    "total_tax": 3.24,
    "total_shipping": 14.23,
    "total_future_shipping": 0,
    "total_order": 49.86,
    "date_created": null,
    "date_modified": "2017-07-20T04:12:25-0700"
}

Notes

  • The payload includes several _links arrays. These contain helpful URI's that could be used through the Hypermedia API if you're also making use of that. If not, these can be safely ignored.

Sending a response

In response, FoxyCart expects a JSON payload in the following format to be output on the page (prettified for display purposes):

Approve

{
  "ok": true,
  "details": ""
}

Reject

{
  "ok": false,
  "details": "Sorry, we're all out of Example Products. Please remove that item from your cart and try again."
}

Only a valid JSON object should be output to your custom endpoint. If you output other elements to the page that is not part of a valid JSON object, the hook will fail to process and the default handling will happen.

Example Endpoint

The following is an example PHP endpoint that could be used to handle the pre-checkout hook:

<?php
$rawPost = file_get_contents('php://input');
$cart_details = json_decode($rawPost, true);
 
$response = array(
    'ok' => true,
    'details' => ''
);
 
foreach($cart_details['_embedded']['fx:items'] as $item) {
    if ($item['name'] == 'Example Product') {
        $response['ok'] = false;
        $response['details'] = "Sorry, we're all out of Example Products. Please remove that item from your cart and try again.";
    }
}
 
header('Content-Type: application/json');
print json_encode($response);

Site Tools