We will assume you have a general understanding of programming. This guide is
intended for developers, and we expect you to have a good understanding of
REST APIs in general. If you’re not a developer, please skip this section.
We’ll make practical use of dynamic payments so that you can use them effectively in your applications. There are 8 steps required to make this work. We will use TypeScript and Node.js here, but the implementation can be ported to any language.
A practical use case for dynamic payments is, for example, if you have a Discord server and you want to grant a user a role. In this case, you can pass in:
fields[{input: "discord_username", display_value: "Discord Username"}]
during the payment creation. This will prompt for the Discord username in the checkout process. Once the payment is made, a payment.succeeded
webhook event is triggered. You can then receive the Discord username as discord_username
or fields[0].input
in the request body of the webhook and grant the user their role.
This has many practical applications, for example, a site selling dynamic courses or other customizable products or services.
Grab the secrets
Log in to your account and navigate to the “API Tokens” section. Click the “Create New Token” button. The generated API token will look similar to this: ey.xxxx
. Copy and securely store the API token.
Next in your shop, create a new webhook. The webhook secret will look like this: whsk_xxx
. Copy and securely store the webhook secret.
We are storing these two secrets as
PANDABASE_API_TOKEN=""
PANDABASE_WEBHOOK_SECRET=""
in our .env
file.
Prepare backend
Set up an Express server with TypeScript. First, initialize your project and install the necessary dependencies:
npm init
npm add express dotenv
npm add -D typescript @types/express @types/node ts-node
npx tsc --init
Create a src
folder and add an index.ts
file:
import express from "express";
import dotenv from "dotenv";
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Build webhook receiver
Add a route to receive webhooks and verify the signature:
import crypto from "crypto";
function validateSignature(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const signature = crypto
.createHmac("sha256", process.env.PANDABASE_WEBHOOK_SECRET!)
.update(JSON.stringify(req.body))
.digest("hex");
if (req.headers["x-pandabase-signature"] === signature) {
next();
} else {
res.status(401).send("Invalid signature");
}
}
app.post("/webhook", validateSignature, (req, res) => {
const event = req.body;
if (event.type === "payment.succeeded") {
console.log("Payment succeeded:", event.data);
}
res.sendStatus(200);
});
Generate payment session
Create a function to generate a payment session:
async function createPaymentSession(
amount: number,
username: string
): Promise<string> {
try {
const response = await fetch(
`https://api.pandabase.io/${process.env.PANDABASE_SHOP_ID}/shop_id/payments`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PANDABASE_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
dynamic: true,
amount,
currency: "USD",
description: "Premium access",
params: {
default_values: {
full_name: "",
email: "",
},
fields: [
{
type: "input",
name: "username",
display_name: "Your Username",
},
],
},
return_url: "https://your-site.com/success",
cancel_url: "https://your-site.com/cancel",
tax_enabled: true,
}),
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.payload.checkout_url;
} catch (error) {
console.error("Error creating payment session:", error);
throw error;
}
}
Send checkout link
Create an endpoint to initiate the payment process:
app.post("/create-payment", async (req, res) => {
try {
const { amount, username } = req.body;
const checkoutUrl = await createPaymentSession(amount, username);
res.json({ checkoutUrl });
} catch (error) {
res.status(500).json({ error: "Failed to create payment session" });
}
});
Monitor payment.success event
The webhook receiver we set up in Step 3 will handle the payment.succeeded
event. Make sure to implement the necessary logic in the webhook handler.
Grant app access
In the webhook handler, implement the logic to grant access based on the username:
app.post("/webhook", validateSignature, (req, res) => {
const event = req.body;
if (event.type === "payment.succeeded") {
const username = event.data.payment.fields[0].input;
const id = event.data.payment.id;
grantAccess(username, id);
}
res.sendStatus(200);
});
function grantAccess(username: string, id: string) {
console.log(`Granting access to user: ${username} | Payment ID: ${id}`);
}