
Building Static Ecommerce with Decap CMS and Foxycart
Static websites are fast, secure, and easy to maintain, which makes them a great choice for most projects, especially business websites. The problem is when you need ecommerce. Adding carts, checkout, and product options usually means giving up the simplicity of a static site and relying on fully locked systems like Shopify, WooCommerce, and Wix. For developers who don't use any builders or templates, this is where Foxycart and Decap CMS come in. Foxycart handles the checkout and security, while our custom Decap CMS collection gives you an easy way to manage products and content without needing a full database, server, or any builder system.
Below, I’ll show how to connect the two to build a simple ecommerce setup with Eleventy as the static site generator. Eleventy is simply my current SSG of choice, this will work with pure HTML/CSS and any other SSG like Astro. I'll go over how to read Foxycarts product builder, how to remake it, and how to add a layer of security to ensure checkout integrity. By the end, you’ll have a clean, fast ecommerce setup that feels like a real backend but runs entirely as a static site.
What is Foxycart and Decap CMS?
Before we get into the setup, it helps to understand the tools we’re working with and why they make sense for static websites.
Foxycart is a flexible ecommerce platform designed to integrate with your existing website instead of replacing it. Unlike systems like Shopify or WooCommerce that control both your store and your front end, Foxycart lets you build your site however you want and simply handle the secure checkout through them. You can build everything in static HTML, and Foxycart only steps in when it’s time to process a transaction. This is part of why I like it so much. It allows all of my clients to later get ecommerce at any time.
That’s also what makes it perfect for static sites. You get all the benefits of a fast, static front end while letting Foxycart handle the dynamic parts like payment security, taxes, and shipping. They even provide a clear cheatsheet and documentation that explain how product data is passed and verified at checkout.
For content management, we’ll use Decap CMS (previously known as Netlify CMS). Decap is a Git-based CMS, which means all your products and content live as Markdown files inside your repository. When you make an edit through the CMS, it just commits a new version of those files. That fits perfectly with static site generators like Eleventy since everything stays version-controlled, lightweight, and server-free.
A key change to note is how products work. In this setup, the products don’t live in Foxycart itself, While they handle the cart, user accounts, and all the other aspects of an ecommerce store, the products themselves live as Markdown files in Decap CMS. Foxycart is only used for checkout and cart handling, keeping everything else fully static and under your control.
Reading Foxycarts Product Builder
Before creating your product collection in Decap CMS, it’s important to understand how Foxycart structures its product data. Each product has a few required fields so keep these in mind when templating. Name, Price, and SKU are required to work correctly at checkout.
Foxycart has two admin dashboards, New Admin and Legacy Admin. For this tutorial, use the New Admin.
In case you haven’t already added the loader.js script from Foxycart to your website, this script is specific to your store and can be found under Settings → Website & Products → Foxy Loader.js.
In the same section, you’ll find example products: one for a single product link and another for a full product form. We’ll focus on the full form here because it’s easier to visualize and template, but the same approach works for the single-link version. Examples for the minicart and customer portal are provided in Foxycart but are not covered in this tutorial.
The Link & Form Builder, located at the bottom of the sidebar, lets you generate product forms with all required fields. Back in the Form Builder, you can fill in some example information, and you’ll see that Name, Price, and SKU are now required fields. These are the minimum values needed to create a valid product and form the foundation for the Decap CMS collection we’ll build next.
Example of the HTML output from the builder and products wiki.
<form action="https://YOURDOMAIN.foxycart.com/cart" method="post">
<input type="hidden" name="name" value="A great product" />
<input type="hidden" name="price" value="5.00" />
<input type="hidden" name="code" value="AGP-001" />
<input type="submit" value="Buy It Now!" />
</form>
Note You can entirely stop here and add products to your website manually by copy and pasting from the product builder. I didnt want to do that so I templated and used DecapCMS
Creating a Product Collection in Decap
Now that you understand the required fields from Foxycart, we can replicate the product structure inside Decap CMS. This will allow you to manage products as Markdown files and generate the forms dynamically in your Eleventy templates.
The three required fields from Foxycart: Name, Price, and SKU must be included in every product entry. You can also add additional fields like image, description, or custom options if needed for your store.
Here’s an example of how to define a simple product collection in Decap CMS. Add this to your config.yaml
:
collections:
- name: "products"
label: "Products"
folder: "content/products"
create: true
slug: "undefined"
fields:
- { label: "Name", name: "name", widget: "string" } # required
- { label: "Price", name: "price", widget: "number", value_type: "float" } # required
- { label: "SKU", name: "sku", widget: "string" } # required
Once your collection is set up, each product is stored as a Markdown file inside content/products
. Here’s an example of what a single product might look like:
---
name: "A Great Product"
price: 5.00
sku: "AGP-001"
---
Securing Products For Checkout
To prevent users from tampering with product data on your static site, Foxycart uses HMAC validation to verify form values at checkout. This section shows how to generate signed values for each product at build time using a Node.js script.
First, make sure you’ve enabled the store secret in Foxycart at Settings → General → Store Secrets. This secret key will be used to sign your product values.
Next, store the secret in your local environment by adding it to your .env
file:
FOXY_SECRET=your_store_secret_here
Also ensure the .env
file is ignored by Git so your secret isn’t exposed. If you’re deploying to a host like Netlify, store the secret there as well so the script works in both local and production builds.
How the Script Works
The script loops through every Markdown product in your SHOP_DIRECTORY
and generates signed values for the required Foxycart fields: name, price, and SKU.
- First, it reads the
.env
file to get theFOXY_SECRET
. If it’s missing, the script stops and warns you. - Then it reads all Markdown files in your products directory and parses them with
gray-matter
to access the frontmatter. - For each product, the script removes any existing signed values (
nameSigned
,priceSigned
,skuSigned
) to ensure a clean regeneration. - If the product has all required fields (
title
,price
, andsku
), the script uses a Foxy-style HMAC function to generate a signature for each field. The signature combines thesku
,name
, andvalue
and hashes it with your secret key. - The signed values are added back to the frontmatter with field names like
nameSigned
,priceSigned
, andskuSigned
. - Finally, the updated Markdown is written back to the file.
At the end, the script logs how many products were signed and how many were skipped because of missing fields.
This ensures that your static product forms include cryptographically signed values, so Foxycart can verify that prices and SKUs haven’t been tampered with at checkout.
With this in place, every product form generated from your Decap CMS collection is secure, even though the site itself is static.
// https://wiki.foxycart.com/v/2.0/hmac_validation
// This is inspired by the hmac docs from foxycart. They have a php implementation and cloudflare workers one.
// I wanted one that worked with eleventy to run at build time
// Created by Sighfy (Max) for Pyrobyte Web Solutions
// https://pyrobytewebsolutions.com/blog/static-ecommerce-with-foxycart/
const fs = require("fs");
const path = require("path");
const matter = require("gray-matter");
const crypto = require("crypto");
require("dotenv").config();
const shopDir = path.join(__dirname, "SHOP_DIRECTORY");
const secret = process.env.FOXY_SECRET;
if (!secret) {
console.error("!! FOXY_SECRET is missing in your .env file.");
process.exit(1);
}
// Foxy-style HMAC signature function
function getVerification({ name, value, sku }) {
const input = sku + parentCode + name + value;
const hash = crypto.createHmac("sha256", secret).update(input).digest("hex");
const suffix = value === "--OPEN--" ? "||open" : ""; // used for user editable fields
return `${name}||${hash}${suffix}`;
}
let signedCount = 0;
let skippedCount = 0;
fs.readdirSync(shopDir).forEach((file) => {
if (!file.endsWith(".md")) return;
const filePath = path.join(shopDir, file);
const content = fs.readFileSync(filePath, "utf8");
const parsed = matter(content);
const { title, price, sku } = parsed.data; // Add more items here
let updated = false;
// Remove all existing signed values to ensure clean regeneration
delete parsed.data.nameSigned;
delete parsed.data.priceSigned;
delete parsed.data.skuSigned;
if (title && price != null && sku) {
parsed.data.nameSigned = getVerification({ name: "name", value: title, sku });
parsed.data.priceSigned = getVerification({ name: "price", value: parseFloat(price).toFixed(2), sku,});
parsed.data.skuSigned = getVerification({ name: "sku", value: sku, sku });
// Sign more items here
signedCount++;
updated = true;
} else {
skippedCount++;
}
if (updated) {
const newContent = matter.stringify(parsed.content, parsed.data);
fs.writeFileSync(filePath, newContent, "utf8");
}
});
console.log(`> Signing Complete`);
console.log(`> Products signed: ${signedCount}`);
console.log(`> Skipped (missing title/price/sku): ${skippedCount}`);
The markdown file after going through the script should look something like this:
---
name: "A Great Product"
price: 5.00
sku: "AGP-001"
nameSigned: "name||f2d8a7c1e9b3f4c5d6e7f8a9b0c1d2e3f4g5h6i7j8k9l0m1n2o3p4q5r6s7t8u9"
priceSigned: "price||a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2"
skuSigned: "sku||z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0f9e8d7c6b5a4"
---
Dev Note: The script is designed to make it easy to add additional fields for signing. For example, if you want to include an image at checkout, you can add the image field to the config and update the script in the three relevant sections: reading the value from the Markdown file, deleting any existing signed value, and generating the new signature.
Run at Build
To make sure the signing script runs automatically during your build process, you can update your package.json
scripts so that the sign-products
script runs before the site is built. This ensures all products are signed and ready for deployment.
"scripts": {
"sign-products": "node scripts/sign-products.js",
"build": "npm run sign-products"
}
Building Product Templates in Eleventy
With your products created in Decap CMS, the next step is to display them on the site. Each product page will read the values from its Markdown file and use them to generate a Foxycart-compatible form at build time.
This is the same form shown earlier, now updated to use Eleventy template variables. When Eleventy builds the site, these variables are replaced with the product data and signed fields.
<form action="https://YOURDOMAIN.foxycart.com/cart" method="post">
<input type="hidden" name="{{ nameSigned }}" value="{{ name }}">
<input type="hidden" name="{{ priceSigned }}" value="{{ price }}">
<input type="hidden" name="{{ skuSigned }}" value="{{ sku }}">
<input type="submit" value="Buy It Now!" />
</form>
After the build, the final HTML will look like this:
<form action="https://YOURDOMAIN.foxycart.com/cart" method="post">
<input type="hidden" name="name||f2d8a7c1e9b3..." value="A Great Product">
<input type="hidden" name="price||a1b2c3d4e5..." value="5.00">
<input type="hidden" name="sku||z9y8x7w6v5..." value="AGP-001">
<input type="submit" value="Buy It Now!" />
</form>
That's really all there is to it to add simple ecommerce to your website. Each value is automatically filled in and securely signed. Eleventy takes care of the templating, and Foxycart confirms the signed values at checkout to make sure they haven’t been modified.
Wrap Up
That’s really all it takes to bring secure ecommerce into a static site. With Foxycart handling checkout and Decap CMS managing your products, you get a lightweight setup that feels dynamic without ever needing a full backend. Your products stay version-controlled and editable, while Eleventy (or any SSG you prefer) builds the final forms automatically with signed values for verification.
This approach keeps your site fast, secure, and easy to maintain. You can start small, add products or options as needed, and still keep everything running as a clean static build.
Note: Additional features such as multiple options, discounts, or cart customization are possible but will be covered in a future intermediate to advanced blog post.