As a Make.com app developer, you’ve put in the time and effort to create custom apps that solve real-world problems and streamline workflows. Now, it’s time to take your apps to the next level by implementing a monetization strategy.
In this comprehensive guide, I’ll walk you through the process of monetizing your custom Make apps using a powerful combination of technologies: Cloudflare Workers, Supabase, and Lemon Squeezy.
These tools will enable you to create a secure and scalable monetization system that seamlessly integrates with your Make apps.
I’ll cover everything from setting up the necessary infrastructure to implementing key features and integrating the various components.this guide will provide you with the knowledge and steps you need to successfully monetize your custom apps.
Why Cloudflare Workers and Supabase?
Cloudflare
-
Free tier: 100,000 requests/day, 10 ms CPU time/invocation
-
Scalability: Cloudflare Workers can handle a high volume of requests and automatically scale to meet demand.
-
Low Latency: By running your code at the edge, Cloudflare Workers reduces latency and improves performance.
Supabase
- 500MB storage for the PostgreSQL database.
- Unlimited API requests.
- 5GB of bandwidth.
By leveraging Cloudflare Workers and Supabase, you can build a scalable and cost-effective monetization system for your Make apps.
Setting Up the Infrastructure
Supabase Setup
Supabase is an open-source alternative to Firebase that provides a scalable database and authentication services. To get started with Supabase, follow these steps:
- Create a Supabase account at https://supabase.com
- Once logged in, click on the "New Project" button to create a new project.
- Choose a unique name for your project and select a region for hosting.
- Wait for the project setup to complete.
Next, let’s create the “Order” table in your Supabase project. The “Order” table will store information about customer orders, including the product ID, email, and license key.
- Navigate to the “SQL Editor” section in your Supabase project dashboard.
- Click on the “New Query” button to create a new SQL query.
- Execute the following SQL statement to create the “Order” table:
CREATE TABLE "Order" (
id SERIAL PRIMARY KEY,
"productId" INTEGER NOT NULL,
"licenseKey" TEXT NULL,
email TEXT NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
The “Order” table has the following columns:
id
: The unique identifier for each order (primary key).productId
: The ID of the purchased product.email
: The email address of the customer.licenseKey
: The license key associated with the order.createdAt
: The timestamp indicating when the order was created
Cloudflare Workers Setup
Cloudflare Workers is a serverless computing platform that allows you to run code at the edge. It will be used to handle the user info endpoint and process Lemon Squeezy webhooks. In this guide, we’ll set up Cloudflare Workers using the Cloudflare CLI and write our worker code in TypeScript.
Prerequisites:
- Node.js installed on your machine
- Cloudflare account
Follow these steps to set up Cloudflare Workers:
1. Install the Cloudflare CLI by running the following command in your terminal:
npm install wrangler --save-dev
2. Log in to your Cloudflare account using the CLI:
npx wrangler login
This will open a browser window where you can log in to your Cloudflare account and grant the necessary permissions.
3. Create a new Cloudflare Workers project:
npx wrangler init monetization-worker
This command creates a new directory named monetization-worker
and initializes a new worker project.
4. Navigate to the project directory:
cd monetization-worker
- Open the
src/index.ts
file and replace the default code with the code provided in the next section.
Note: After creating the monetization worker for handling the user info endpoint, you need to create another worker for handling the Lemon Squeezy webhooks. Follow the same steps as before to create a new worker, but make sure to give it a different name (e.g.,
lemon-squeezy-webhook-handler
) and update the code accordingly.
Once you have created both workers, take note of their respective URLs. You will need to provide the URL of the user info worker in your Make app’s connection settings and the URL of the Lemon Squeezy webhook worker in the Lemon Squeezy webhook configuration.
Make sure to keep the worker URLs handy, as you will need them in the subsequent steps of the integration process.
- Configure the required environment variables for your Cloudflare workers using the Cloudflare CLI.
Run the following commands:
npx wrangler secret put SUPABASE_URL
npx wrangler secret put SUPABASE_ANON_KEY
npx wrangler secret put LEMON_SQUEEZY_SIGNING_SECRET
Each command will prompt you to enter the corresponding value for the environment variable.
Replace your_supabase_project_url
, your_supabase_anon_key
, and your_lemon_squeezy_signing_secret
with the actual values.
- Deploy your worker to Cloudflare by running the following command:
npx wrangler deploy
This command deploys your worker to Cloudflare and provides you with a unique URL where your worker is accessible.
That’s it! You have now set up Cloudflare Workers using the Cloudflare CLI and TypeScript.
Lemon Squeezy Setup
Lemon Squeezy is a payment processing platform that simplifies the process of selling digital products and managing subscriptions. It will be used to handle the payment flow and generate license keys for your custom apps. To set up Lemon Squeezy, follow these steps:
- Create a Lemon Squeezy account at https://lemonsqueezy.com/.
- Once logged in, click on the “Products” tab in the Lemon Squeezy dashboard.
- Click on the “Create Product” button to create a new product.
- Fill in the required details, such as the product name, price, and description.
- Customize the product settings according to your requirements.
Note: make sure to enable the option to generate license keys. This will allow Lemon Squeezy to automatically create license keys for each purchase, which will be stored in the Supabase database and used for license verification in your app.
Next, configure the webhook settings in Lemon Squeezy to send order and license key events to your Cloudflare worker.
- In the Lemon Squeezy dashboard, navigate to the “Settings” tab.
- Click on the “Webhooks” section.
- Enter the URL of your Cloudflare worker in the “Webhook URL” field.
- Generate a unique signing secret and copy it.
- Paste the signing secret into the
**LEMON_SQUEEZY_SIGNING_SECRET
** environment variable in your Cloudflare worker settings.
Implementing the User Info Endpoint
The user info endpoint is a critical component of the monetization system. It allows your Make app to verify the validity of a user’s license key and retrieve relevant information about the user’s order.
Here’s the Cloudflare worker index.ts code for the user info endpoint:
import { createClient, SupabaseClient } from '@supabase/supabase-js';
interface Env {
SUPABASE_URL: string;
SUPABASE_ANON_KEY: string;
}
interface UserInfoRequest {
email: string;
productId: number;
}
const createSupabaseClient = (env: Env): SupabaseClient => {
return createClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY);
};
const getUserInfo = async (request: Request, env: Env): Promise<Response> => {
// Request validation
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
try {
const { email, productId } = await request.json() as UserInfoRequest;
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return new Response(JSON.stringify({ error: 'Missing token' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
const supabase = createSupabaseClient(env);
// Retrieve the order based on the license key and product ID
const { data: orderData, error: orderError } = await supabase
.from('Order')
.select('*')
.eq('licenseKey', token)
.eq('productId', productId)
.single();
if (orderError) {
if (orderError.code === 'PGRST116') {
// Order not found for the given license key and product ID
return new Response(JSON.stringify({ error: 'Invalid token for the given product ID' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
// Handle other errors
return new Response(JSON.stringify({ error: 'Error retrieving order' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
const order = orderData;
// Check if the email matches the order's email
if (order.email !== email) {
return new Response(JSON.stringify({ error: 'Invalid email' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
// Return success response
return new Response(JSON.stringify({ valid: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
return new Response(JSON.stringify({ error: 'Internal server error' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return getUserInfo(request, env);
},
};
The user info endpoint performs the following steps:
- It validates the incoming request method to ensure it’s a POST request.
- It extracts the
email
andproductId
from the request payload. - It retrieves the
token
(license key) from theAuthorization
header. - It creates a Supabase client instance using the provided environment variables.
- It retrieves the order data from the Supabase “Order” table based on the provided
licenseKey
andproductId
. - It validates the retrieved order data:
- Checks if the order exists for the given
licenseKey
andproductId
. If not, returns an error response. - Checks if the provided
email
matches the order’s email. If not, returns an error response.
- If the order is valid, it returns a success response indicating that the license key is valid.
make sure to Configure the required environment variables .
Handling Lemon Squeezy Webhooks
Note: To handle Lemon Squeezy webhooks, you need to create another Cloudflare worker by following the same steps as creating the user info worker. Make sure to give it a different name, such as
lemon-squeezy-webhook-handler
, and update the code accordingly.
Lemon Squeezy sends webhooks to notify your application about important events, such as order creation and license key generation. The webhook handler in your Cloudflare worker is responsible for processing these events and updating the relevant data in your Supabase database.
Here’s the Cloudflare worker index.ts code for handling Lemon Squeezy webhooks:
import { createClient, SupabaseClient } from '@supabase/supabase-js';
import HmacSHA256 from 'crypto-js/hmac-sha256';
import Hex from 'crypto-js/enc-hex';
interface Env {
SUPABASE_URL: string;
SUPABASE_ANON_KEY: string;
LEMON_SQUEEZY_SIGNING_SECRET: string;
}
const createSupabaseClient = (env: Env): SupabaseClient => {
return createClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY);
};
const handleOrderCreated = async (data: any, env: Env) => {
const supabase = createSupabaseClient(env);
const { id, attributes } = data.data;
const { first_order_item, user_email } = attributes;
const { product_id } = first_order_item;
// Insert the order data into the Supabase table
const { error } = await supabase.from('Order').insert({
id,
productId: product_id,
email: user_email,
});
if (error) {
throw error;
}
};
const handleLicenseKeyCreated = async (data: any, env: Env) => {
const supabase = createSupabaseClient(env);
const { attributes } = data.data;
const { key, order_id } = attributes;
// Update the order with the license key
const { error } = await supabase
.from('Order')
.update({ licenseKey: key })
.eq('id', order_id);
if (error) {
throw error;
}
};
const verifySignature = (payload: string, signature: string | null, signingSecret: string): boolean => {
if (!signature) {
return false;
}
const expectedSignature = HmacSHA256(payload, signingSecret).toString(Hex);
return signature === expectedSignature;
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
try {
// Verify the webhook signature
const signature = request.headers.get('X-Signature');
const payload = await request.text();
if (!verifySignature(payload, signature, env.LEMON_SQUEEZY_SIGNING_SECRET)) {
return new Response('Unauthorized', { status: 401 });
}
const data = JSON.parse(payload);
if (data.meta.event_name === 'order_created') {
await handleOrderCreated(data, env);
} else if (data.meta.event_name === 'license_key_created') {
await handleLicenseKeyCreated(data, env);
}
return new Response('OK', { status: 200 });
} catch (error) {
return new Response('Internal Server Error', { status: 500 });
}
},
};
The webhook handler performs the following steps:
- It validates the incoming request method to ensure it’s a POST request.
- It verifies the webhook signature using the
verifySignature
function. If the signature is invalid, it returns an unauthorized response. - It parses the webhook payload and determines the event type.
- Based on the event type, it calls the appropriate handler function:
- For the
order_created
event, it extracts the relevant data from the payload and inserts the order data into the Supabase “Order” table. - For the
license_key_created
event, it extracts the license key and order ID from the payload and updates the corresponding order in the Supabase “Order” table with the license key.
- If the event is handled successfully, it returns a success response.
Integrating with Make.com
Setting Up the Connection
To integrate your monetization system with your app, you need to set up a connection. Follow these steps:
-
Open your Make app and navigate to the “Connections” section.
-
Click on the “Add Connection” button to create a new OAuth connection.
-
Add the following code to the connection communication:
{
"token": {
"condition": "{{if(data.expires, data.expires < addMinutes(now, 1), true)}}",
"url": "https://whoami.add-your-api.com",
"method": "POST",
"body": {
"email": "{{parameters.email}}",
"productId": "{{common.productId}}"
},
"headers": {
"Authorization": "Bearer {{parameters.token}}",
"Content-Type": "application/json"
},
"response": {
"error": {
"message": "[{{statusCode}}]: {{body.error}}"
}
}
},
"info": {
"url": "https://add-yourapi.dev",
"headers": {
"Authorization": "Bearer {{parameters.apiKey}}"
},
"response": {
"error": {
"message": "[{{statusCode}}] {{body.error}}"
},
"metadata": {
"type": "email",
"value": "{{parameters.email}}"
}
},
"log": {
"sanitize": ["request.headers.Authorization"]
}
}
}
- Update the
url
fields in the connection communication with your actual API endpoints:
-
Replace
https://whoami.add-your-api.com
with the URL of your user info endpoint hosted on Cloudflare Workers. -
Replace
https://add-yourapi.dev
with the URL of your API endpoint for retrieving additional user information.
- Update the
common
data with your product ID:
{
"productId": "YOUR_PRODUCT_ID"
}
Note: You can find your product ID in the Lemon Squeezy dashboard. Navigate to your product’s settings and copy the product ID.
That’s it! You have now set up the connection in your app to integrate with your monetization system.
The connection communication consists of two main parts:
-
The
token
object handles the user info endpoint request. It sends a POST request to your Cloudflare worker with the user’s email and product ID, along with the license token in theAuthorization
header. -
The
info
object handles retrieving additional user information from your API endpoint. It sends a request to your API with the user’s API key in theAuthorization
header.
The common
data object allows you to store and access common data across your app. In this case, you’ll store your product ID obtained from Lemon Squeezy.
With the connection set up, your Make app can now communicate with your monetization system, verifying user licenses and retrieving user information based on the provided credentials
Configuring Mappable Parameters
Mappable parameters allow you to define input fields in your app that users can fill in when setting up the connection .
To configure mappable parameters, follow these steps:
In your Make app, navigate to the “Parameters” section.
Click on the “Add Parameter” button to create a new parameter. Define the following parameters:
Here’s how the parameters.jsonc file should look:
[
{
"name": "apiKey",
"type": "text",
"label": "API Key",
"required": true,
"help": "Go to example.com and get your API key"
},
{
"name": "email",
"type": "text",
"label": "User Email",
"required": true,
"help": "The email address you used to sign up for the app"
},
{
"name": "token",
"type": "text",
"label": "License Token",
"required": true,
"help": "The license key received in the email"
}
]
Testing the Integration
To ensure that your monetization system is working correctly, it’s important to test the integration thoroughly. Follow these steps to create a test scenario:
Purchase a product on Lemon Squeezy:
Go to your Lemon Squeezy product page.
Complete the purchase process by providing the necessary information and making a test payment.
Receive the license key via email:
After a successful purchase, Lemon Squeezy will generate a license key and send it to the email address you provided.
Check your email inbox and locate the email containing the license key.
Use the license key in your Make app:
Open your Make app and navigate to the connection settings.
Enter the required information, including the API key, user email, and license token (license key).
Save the connection settings.
Verify the data flow:
Check the Supabase “Order” table:
Open your Supabase project and navigate to the “Order” table.
Verify that a new row has been created with the correct order information, including the product ID, email, and license key.
Check the Make.com app’s behavior:
Interact with your Make app and ensure that it behaves as expected based on the provided license key.
Verify that the app recognizes the license key as valid and allows access to the monetized features.
Common Errors
When integrating your monetization system with your app, you (end-user)may encounter some common errors. In this section, we’ll discuss these errors and provide troubleshooting steps to resolve them.
Invalid Token for the Given Product ID
If you encounter the error message “Invalid token for the given product ID,” it indicates that the provided license token is not valid for the specified product ID. Here are a few things to check:
-
Make sure that the license token provided in the
parameters.token
field of the connection is correct and matches the token assigned to the user. -
Verify that the
common.productId
value in your Make app matches the actual product ID for which the license token was generated. -
Check the Supabase “Order” table to ensure that there is a valid order record with the corresponding
licenseKey
andproductId
.
Invalid Email
If you see the error message “Invalid email,” it means that the email provided in the parameters.email
field of the connection does not match the email associated with the license token. To resolve this error:
- Ensure that the user is providing the correct email address associated with their license.
- Check the Supabase “Order” table to verify that the email in the order record matches the email provided in the connection.
Other Errors
If you encounter any other errors, such as “Missing token” or “Error retrieving order,” here are some general troubleshooting steps:
-
Double-check that all the required fields in the connection (e.g.,
parameters.email
,parameters.token
,common.productId
) are properly populated. -
Verify that the Cloudflare worker is deployed and accessible at the specified URL.
-
Check the Cloudflare worker logs for any error messages or stack traces that can help identify the issue.
-
Ensure that the Supabase connection details (URL and anon key) are correctly configured in the Cloudflare worker.
By following these best practices and prioritizing security, you can ensure that your monetization system is robust, reliable, and protected against potential threats.
Conclusion
Monetizing custom Make apps using Cloudflare, Supabase, and Lemon Squeezy provides a powerful and flexible solution for app developers. By leveraging these technologies, you can create a seamless monetization experience for your users while ensuring the security and scalability of your system.
If you need any assistance implementing this monetization system or have further questions, feel free to reach out to me at bilalmansouri.com.