Skip to content

The purchase webhooks let your application confirm purchase events and automatically trigger the PurchaseCompleted interaction, which pays out the reward to the ambassador and the buyer when a campaign matches. Behind the scenes, Frak records each purchase’s finality status in an anonymized, tamper-evident log (each entry is a hash of the purchaseId, userId, and status, no personal data included), so you can always verify whether a purchase was confirmed. You don’t need to understand that mechanism to integrate: send the webhook, and Frak handles the rest.

We provide three types of webhooks to handle purchase tracking:

  1. Shopify Webhook
  2. WooCommerce Webhook
  3. Custom Webhook

The Shopify webhook should be set up to trigger on order updates, allowing the backend to validate purchases and record their status. It is provisioned automatically by the official Frak Shopify app during onboarding. See the Shopify setup guide for details.

Similar to the Shopify webhook, the WooCommerce webhook is provisioned automatically by the Frak WordPress plugin once the webhook secret is pasted in. Follow the WordPress integration guide to properly set it up.

The Custom webhook is designed for applications that use a custom eCommerce solution or need more flexibility. This webhook requires specific headers and a payload to register the purchase event.

The specific endpoint URL for each webhook type is visible in the business dashboard under the Purchase Tracker section of your merchant.

  • x-hmac-sha256: A HMAC SHA-256 digest of the entire request body. The HMAC is computed using the secret available in your business dashboard.
  • x-test: Indicates if the webhook call is for a test order. This is useful for development environments.

The body of the Custom webhook should be a JSON object that follows the CustomWebhookDto type:

type CustomWebhookDto = Readonly<{
/**
* Id of the order on your side (external order id)
*/
id: string;
/**
* Id of the customer on your side (external customer id)
*/
customerId: string;
/**
* Status of the order
*/
status: "pending" | "confirmed" | "cancelled" | "refunded";
/**
* Custom token for this order
* - Should be the same one as the one exposed to the end user and submitted through the `listenForPurchase` API method
*/
token: string;
/**
* Currency code (ISO 4217)
* - Optional, could be empty
* - Recommended for the UX in the members space
*/
currency?: string;
/**
* The total price of the order, in the currency provided
* - Optional, could be empty
* - Recommended for the UX in the members space
*/
totalPrice?: string;
/**
* All the items in the order
* - Optional
* - Will be used in the wallet members space, to display additional data to the end user, e.g. which product they referee bought the most
*/
items?: {
/**
* The product id on your side
*/
productId: string;
/**
* The quantity of the product in the order
*/
quantity: number;
/**
* The price of the product
*/
price: string;
/**
* An internal name for the product, used in url slug
*/
name: string;
/**
* The displayable title of the product
*/
title: string;
/**
* The SKU of the product, if you have one
* - Optional
* - Used for SKU-based campaign product-scope matching
*/
sku?: string;
/**
* A potential image URL for the product
*/
image?: string;
}[];
}>;

Here is a few custom webhook calls in different languages:

import crypto from 'crypto';
type Purchase = {
id: string;
customerId: string;
status: "pending" | "confirmed" | "cancelled" | "refunded";
token: string;
currency?: string;
totalPrice?: string;
items?: {
productId: string;
quantity: number;
price: string;
name: string;
title: string;
sku?: string;
image?: string;
}[];
}
async function sendPurchaseWebhook(
purchase: Purchase
): Promise<void> {
const body = JSON.stringify(purchase);
const hmac = await crypto
.createHmac('sha256', process.env.FRAK_PURCHASE_WEBHOOK_SECRET)
.update(body)
.digest('hex');
await fetch(process.env.FRAK_PURCHASE_WEBHOOK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-hmac-sha256': hmac,
'x-test': process.env.NODE_ENV === 'production' ? 'false' : 'true'
},
body
});
}
// Usage example
const purchase = {
id: 'order_123',
customerId: 'cust_456',
status: 'confirmed',
token: 'purchase_token_789',
currency: 'USD',
totalPrice: '99.99',
items: [{
productId: 'prod_001',
quantity: 1,
price: '99.99',
name: 'premium-subscription',
title: 'Premium Subscription',
sku: 'SKU-PREMIUM-001'
}]
};
sendPurchaseWebhook(purchase)
.then(response => console.log('Webhook sent successfully:', response))
.catch(error => console.error('Error sending webhook:', error));

Each webhook request should include a valid HMAC signature of the entire body payload, ensuring the request’s integrity and authenticity. The secret used to generate this HMAC signature is available in your business dashboard.

All webhook types expect a JSON response indicating whether the request was successfully processed.

  • Ensure that the id, customerId, status, and token fields are consistent between the client and the server to ensure successful tracking.
  • The webhooks automatically record the purchase’s finality status, anonymized, so a refund or cancellation cancels the reward rather than paying it out.
  • You can find the specific webhook URL for your merchant in the business dashboard, under the Purchase Tracker section of your merchant.