This is an old revision of the document!
Table of Contents
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:
- Set it to 60 to start with.
- Monitor your normal scores (you can see the score on completed transactions) and see what's in a “normal” range.
- 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% |
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:
- Enabled, Always. As it sounds, this will include reCAPTCHA on every checkout.
- 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.
Common use cases for the pre-checkout hook include:
- Final inventory checks for low-stock or one-of-a-kind products
- Custom validations to ensure that certain product configurations are present
- Integrations with custom validation or fraud-check services
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.
FoxyCart expects a response from your endpoint within 20 seconds of the request being sent - so you will need to ensure that any logic that your endpoint undertakes is completed quickly. Remember that as this request happens after the customer clicks to complete their transaction, any extended delays with your endpoint will mean they will also be waiting longer for a response to their transaction.
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);
Debugging Errors
If your pre-checkout hook endpoint fails to return a response, or returns a non-JSON response, a error will be added to your store's error log in the administration.