| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| v:2.0:hmac_validation [2019/07/02 23:37] – [Example: Building a link string in PHP] marija | v:2.0:hmac_validation [2024/12/13 22:56] (current) – [Do You Need FoxyCart's HMAC Product Verification?] foxybrett |
|---|
| |
| ==== Do You Need FoxyCart's HMAC Product Verification? ===== | ==== Do You Need FoxyCart's HMAC Product Verification? ===== |
| It really depends on a few things, including your order volume, your order processing flow, the nature of your products, and your cost to implement versus the potential payoff. That said, if you're worried about price spoofing we strongly recommend implementing it. If you only have a few products then it's only a little extra work up front. If you have many products then it can be added to your CMS and handled automatically by your site, in the background. | |
| |
| | It depends. If you have low order volume, allow customer-set pricing (like for donations), or otherwise would catch any funny business, you might not need it. We do generally recommend turning it on, especially if you're using a more powerful or flexible CMS where you could implement it automatically. |
| |
| | That said, there are other options that might make more sense: |
| |
| | * The [[./pre_payment_webhook|pre-payment webhook]] can be used to validate carts before you accept payment. This can also be used to verify availability, perform an anti-fraud check, or other custom validation. |
| | * The [[./webhooks|transaction webhook]] can be used to validate things after the transaction's done. |
| | * [[https://github.com/Foxy/foxy-cloudflare-addtocart-signing|Cloudflare workers]] can automatically sign your page. This is a bit more complicated to set up, but will handle things automatically for you. |
| |
| | Foxy gives you a lot of flexibility, so just [[https://foxy.io/contact|reach out to us]] if you're confused. We're here to help. |
| | ===== Language and Site Independent Solutions ===== |
| |
| | Regardless of how you've built your site, you can use our Cloudflare Worker script, along with Cloudflare, to automatically protect your site. More info is here: |
| | |
| | * [[https://github.com/Foxy/foxy-cloudflare-addtocart-signing|Foxy Cloudflare Worker: Add-to-cart Signer]] |
| | |
| | Please let us know if you run into trouble getting that set up. |
| |
| ===== Language Specific Examples & Helper Files ===== | ===== Language Specific Examples & Helper Files ===== |
| While you certainly can build your validation functionality from scratch, it may be a boon to use an existing library. Currently we only have official code for PHP but if you're interested in porting the PHP version to another language please [[https://forum.foxycart.com/|let us know]] and we'd be happy to provide assistance. | While you certainly can build your validation functionality from scratch, it may be a boon to use an existing library. Currently we only have official code for PHP but if you're interested in porting the PHP version to another language please [[https://forum.foxycart.com/|let us know]] and we'd be happy to provide assistance. |
| | |
| | ==== Webflow: Pre-Payment Webhook ==== |
| | If you're using Webflow, you can set up a serverless function on Netlify.com to verify your product prices against your Webflow product collection. This is slightly technical, but hopefully will be more straightforward than it looks, and will generally be free unless you're doing really heavy volume. Setting it up starts with a single click. |
| | |
| | * [[https://github.com/Foxy/foxy-node-netlify-functions|Netlify Serverless Functions for the Foxy.io API]] |
| | * [[https://github.com/Foxy/foxy-node-netlify-functions/tree/main/src/functions/datastore-integration-webflow|Webflow pre-payment webhook details]] |
| | |
| | Let us know if you need help getting it set up. |
| | |
| |
| ==== PHP: Automatic Validation with Minimal Effort ==== | ==== PHP: Automatic Validation with Minimal Effort ==== |
| <code php>function get_verification($var_name, $var_value, $var_code, $var_parent_code = "", $for_value = false) { | <code php>function get_verification($var_name, $var_value, $var_code, $var_parent_code = "", $for_value = false) { |
| $api_key = "your_api_key_here"; | $api_key = "your_api_key_here"; |
| $encodingval = htmlspecialchars($var_code . $var_parent_code . $var_name . $var_value); | $encodingval = htmlspecialchars($var_code . $var_parent_code . $var_name . $var_value, ENT_COMPAT); |
| $label = ($for_value) ? $var_value : $var_name; | $label = ($for_value) ? $var_value : $var_name; |
| return $label . '||' . hash_hmac('sha256', $encodingval, $api_key) . ($var_value === "--OPEN--" ? "||open" : ""); | return $label . '||' . hash_hmac('sha256', $encodingval, $api_key) . ($var_value === "--OPEN--" ? "||open" : ""); |
| </code> | </code> |
| |
| With this function, for each product you want to hash, you need to provide the ''name'', ''value'', and ''code'' to it. The ''name'' and ''value'' arguments are the name and value pair of the particular product attribute you're hashing, and the ''code'' argument is the product's code value. This function will return a hash based on the arguments provided - and return something that would look like this: | With this function, for each product you want to hash, you need to provide the ''name'', ''value'', and ''code'' to it. The ''name'' and ''value'' arguments are the name and value pair of the particular product attribute you're hashing, and the ''code'' argument is the product's code value. **Note the value you pass off to the //get_verification// function should still be the actual value, not a url encoded version.** |
| | |
| | This function will return a hash based on the arguments provided - and return something that would look like this: |
| |
| <code>mycode||of02h1340wef9neq230sfnwr4340adn7z</code> | <code>mycode||of02h1340wef9neq230sfnwr4340adn7z</code> |
| <input type="hidden" name="<?php echo get_verification('price', '1.99', 'warmups'); ?>" value="1.99" /> | <input type="hidden" name="<?php echo get_verification('price', '1.99', 'warmups'); ?>" value="1.99" /> |
| </code> | </code> |
| | |
| | <WRAP center round tip 90%> |
| | **Where should the signature be placed in a form element?** Note that the signature can be appended to either the ''name'' or the ''value'' of an input. Either will work just fine, although for radio inputs and select dropdowns, the signature //must// be appended to the ''value'' as the ''name'' is the same for all options of the field. See [[:#a_basic_overview|the implementation details]] for more information on that. |
| | </WRAP> |
| |
| In a link in it's vanilla state would look like this: | In a link in it's vanilla state would look like this: |
| $atc .= "&" . get_verification("price", "1.99", "warmups") . "=1.99"; | $atc .= "&" . get_verification("price", "1.99", "warmups") . "=1.99"; |
| </code> | </code> |
| | |
| |
| **Bundled Products** | **Bundled Products** |
| |
| ====Bundled Products==== | ====Bundled Products==== |
| Because the child product in a bundled product setup often has a different price than if the product were added by itself, we require that the product code used to hash the child product contains both the parent code and the child code in the format {childcode}{parentcode}. So if you are adding a T-shirt with the code "shirt" and a child product with the code "poster", the child product fields would be hashed with the code "postershirt". The parent product would be hashed like normal with the code "shirt". | As the child product in a bundled product setup often has a different price than if the product were added by itself, we require that the product code used to hash all attributes of the child product contains both the parent code and the child code in the format ''{childcode}{parentcode}'' (but without the curly brackets). |
| | |
| | For example, if you are adding a T-shirt with the code ''shirt'' and a child product with the code ''poster'', the child product attributes would be hashed with the code ''postershirt'', instead of just ''poster''. The parent product's attributes would be hashed like normal with the code ''shirt''. |
| ==== An Example ==== | ==== An Example ==== |
| |
| <code php>$atc .= "?" . get_verification("name", "Black & White T-shirt", "abc123") . "=" . urlencode("Black & White T-shirt");</code> | <code php>$atc .= "?" . get_verification("name", "Black & White T-shirt", "abc123") . "=" . urlencode("Black & White T-shirt");</code> |
| |
| While we pass the string to the hashing function with spaces or special characters included (''get_verification("name", "Black & White T-shirt", "abc123")''), leaving that in the actual URL would break it both because of the spaces and the ampersand. If you're using PHP, we recommend running the value through PHP's ''urlencode()'' function to convert any spaces and special characters to URL friendly equivalents. Note the value you pass off to the hashing function should still be the actual value, not a url encoded version. If you're using a different programming language, you'll need to find an equivalent function within it, or manually convert special characters yourself. | While we pass the string to the hashing function with spaces or special characters included (''get_verification("name", "Black & White T-shirt", "abc123")''), leaving that in the actual URL would break it both because of the spaces and the ampersand. If you're using PHP, we recommend running the value through PHP's ''urlencode()'' function to convert any spaces and special characters to URL friendly equivalents.** Note the value you pass off to the hashing function should still be the actual value, not a url encoded version.** If you're using a different programming language, you'll need to find an equivalent function within it, or manually convert special characters yourself. |
| |
| There are also some other special considerations when it comes to spaces and other special characters - please see the 'Important Notes' section below. | There are also some other special considerations when it comes to spaces and other special characters - please see the 'Important Notes' section below. |