Pre-Payment Webhook

The pre-payment hook functionality sends a request to an endpoint of your choosing right before Foxy would otherwise submit the payment to the payment gateway, and 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-payment 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-payment Hook

To enable the pre-payment 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-payment hook” and complete the settings below.

pre-payment 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 shipping 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",
    "customer_ip": "192.168.0.1",
    "ip_country": "United States",
    "session_name": "fcsid",
    "session_id": "hvcv28l8md0qc8qt5rrjh4qo85",
    "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 from 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 occur.

Example Endpoint

The following is an example PHP endpoint that could be used to handle the pre-payment 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-payment 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.

Site Tools