Skip to content
Back to catalog

Payments with Dodo Payments

Connect Dodo Payments to Appwrite to sell one-time products, store paid orders in Appwrite Databases, and hand checkout and global tax to a Merchant of Record.

Dodo Payments is a Merchant of Record for SaaS, AI, and digital products. It sells your product to customers in 220+ countries as the legal seller, so payment processing, sales tax and VAT, fraud screening, and chargebacks are handled for you. Its hosted checkout supports cards, wallets, bank debits, and other local payment methods, and shows prices in local currencies.

How does the integration work?

You can use a pre-built Appwrite Function template to sell a one-time product with Dodo Payments, such as a course, an e-book, a lifetime license, or a pack of credits. The function creates a Dodo Payments checkout session for the signed-in Appwrite user. When the payment succeeds, it stores the order as a row in an Appwrite table that only the buyer can read.

The function verifies every webhook signature and looks the payment up in Dodo Payments before storing anything, so it never trusts the webhook payload. It also ships a demo page so you can test the full flow before writing any code.

How to implement

To implement the Dodo Payments payments integration, there are several steps you must complete:

Step 1: Set up Dodo Payments

First, sign up for Dodo Payments and keep the dashboard in Test Mode while you build. Test mode and live mode have separate API keys, products, and webhooks.

Head to Products in the sidebar and click Add Product. Choose One time as the pricing type, fill in the name and price, and copy the Product Id from the product list.

Dodo Payments products
Dodo Payments products

Next, head to Developer > API Keys and click Create New Key. Give the key a name and grant Checkout Sessions write access and Payments read access. Click Create Key and copy the key. Dodo Payments shows it only once.

Create a Dodo Payments API key
Create a Dodo Payments API key

Step 2: Create the Appwrite Function

For this step, you must create an account on Appwrite Cloud or self-host Appwrite if you haven't already. The Dodo Payments templates are available on Appwrite Cloud and in self-hosted Appwrite 2.6.0 and later. If you self-host Appwrite, there are additional setup steps to use Appwrite Function templates.

Head over to the Appwrite Console, navigate to the Functions page, click the Templates tab, and search for the Payments with Dodo Payments function template. Click Create from template.

Payments with Dodo Payments template
Payments with Dodo Payments template

In the Domain section, choose the subdomain your function will run on. Copy the full URL, for example https://<FUNCTION_DOMAIN>.fra.appwrite.run. You need it for the webhook in the next step, so keep this page open.

Function domain
Function domain

Step 3: Add the webhook endpoint

In a new tab, go to Developer > Webhooks in the Dodo Payments dashboard and click Add endpoint. Set the Endpoint URL to your function domain followed by /webhook:

Plain text
https://<FUNCTION_DOMAIN>.fra.appwrite.run/webhook

Under Subscribed events, select payment.succeeded. An endpoint with no events selected receives every event type, so always pick the event.

Add a Dodo Payments webhook endpoint
Add a Dodo Payments webhook endpoint

Click Create endpoint. On the endpoint's page, reveal the Signing secret and copy it. The function uses it to verify that webhooks come from Dodo Payments.

Step 4: Configure the variables and deploy

Back in the Appwrite Console, fill in the Template variables:

VariableValue
DODO_PAYMENTS_API_KEYThe API key from step 1
DODO_PAYMENTS_WEBHOOK_KEYThe signing secret from step 3
DODO_PAYMENTS_PRODUCT_IDThe product ID from step 1
DODO_PAYMENTS_ENVIRONMENTOptional. test_mode (default) or live_mode
APPWRITE_DATABASE_IDOptional. The database for orders, orders by default
APPWRITE_TABLE_IDOptional. The table for orders, orders by default

The function creates the database and table when the first order comes in.

Template variables
Template variables

Choose Connect your repository to clone the template into a new Git repository you can edit later, or Connect later to deploy it right away. Then click Create and deploy.

You don't need to create an Appwrite API key. The function talks to Appwrite with an ephemeral API key limited to the scopes the template asks for: databases.write, tables.read, tables.write, and rows.write.

Step 5: Test the function

The demo page runs on your function domain and calls Appwrite from the browser, so Appwrite needs to accept requests from that hostname. Open Apps in the project sidebar and click Add app. Choose Web, then No framework, and enter your function domain as the Hostname.

Register the function domain as a web app
Register the function domain as a web app

Then, open your function domain in a browser to load the demo page. Click Register as anonymous, then Create checkout with Dodo Payments. The function creates a checkout session and redirects you to the Dodo Payments checkout. Pay with the Dodo Payments test card 4242 4242 4242 4242, any future expiry date, and any CVC.

Dodo Payments checkout
Dodo Payments checkout

After the payment, Dodo Payments sends you back to the demo page, which lists the new order.

Payments demo with a paid order
Payments demo with a paid order

You can visit the Databases page in your Appwrite project, open the orders database, and open the orders table to find the row. The row ID is the Dodo Payments payment ID, so a repeated webhook can't store the same order twice. Only the user who paid can read it.

Paid orders in Appwrite Databases
Paid orders in Appwrite Databases

How the function works

The function exposes four routes on its domain:

RoutePurpose
GET /Serves the demo page, where a user can register, start a checkout, and see their paid orders
POST /checkoutCreates a checkout session for the product in DODO_PAYMENTS_PRODUCT_ID and redirects to it
GET /successVerifies the payment when the user returns from checkout, then redirects them to your success URL
POST /webhookHandles the payment.succeeded event

When a signed-in user starts a checkout, the function stores their Appwrite user ID in the checkout session's metadata, and Dodo Payments carries it through to the payment. That is how each order is tied back to the user who paid.

Dodo Payments signs every webhook following the Standard Webhooks specification. The function verifies the signature on every request, then looks the payment up in Dodo Payments instead of trusting the webhook payload. The /success route repeats the same check when Dodo Payments sends the user back after checkout, so the user gets access even if the webhook is slow.

Start a checkout from your app

To start a checkout from your own app, call the function's /checkout route as the signed-in user. Appwrite passes the caller's ID to the function in the x-appwrite-user-id header, and the function responds with a redirect to the checkout:

JavaScript
import { Client, Functions, ExecutionMethod } from 'appwrite';
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('<PROJECT_ID>');
const functions = new Functions(client);
const execution = await functions.createExecution({
functionId: '<FUNCTION_ID>',
body: JSON.stringify({
successUrl: 'https://example.com/thanks',
failureUrl: 'https://example.com/pricing',
verifyUrl: 'https://<FUNCTION_DOMAIN>/success',
}),
async: false,
xpath: '/checkout',
method: ExecutionMethod.POST,
headers: { 'Content-Type': 'application/json' },
});
const redirect = execution.responseHeaders.find((header) => header.name === 'location');
window.location.href = redirect.value;

Your app can then list the user's orders from the orders table with the client SDK, and the row permissions make sure each user only sees their own.

Before you go live

The demo flow is built for testing. Before you charge real customers:

  • Create a live API key, product, and webhook endpoint in Live Mode, update the variables, and set DODO_PAYMENTS_ENVIRONMENT to live_mode.
  • Check successUrl, failureUrl, and verifyUrl against your own domains in POST /checkout, so the function can't be used to redirect people to other sites.
  • Replace the demo's anonymous sessions with the sign-in method your app uses.
  • Mark the API key and signing secret as secret variables so nobody can read them back from the Console.

Read more about Dodo Payments and Appwrite Functions

If you would like to learn more about Dodo Payments and Appwrite Functions, we have some resources that you should visit:

Technology Partner Program

Building on Appwrite?

Teams and developers who ship integrations, tools, or services on Appwrite can join our partner program for verified catalog placement, co-marketing, training, and engineering support.