Skip to main content

Introduction

Appwrite is an open-source backend platform with auth, databases, storage, and serverless functions. Appwrite publishes two Appwrite Functions templates for Dodo Payments. Each template deploys a function that opens a Dodo Payments checkout for a signed-in Appwrite user, receives Dodo Payments webhooks, and gives the user access after they pay. The two templates sell different product types:
Appwrite builds and maintains these templates. The source code and full endpoint reference are on GitHub: payments-with-dodo-payments and subscriptions-with-dodo-payments.

How It Works

Both templates follow the same flow:
  1. Your app calls the function’s checkout route as a signed-in user: /checkout for payments, /subscribe for subscriptions. Appwrite passes the user’s ID to the function in the x-appwrite-user-id header.
  2. The function creates a checkout session with the user ID in metadata.user_id, then redirects the user to the Dodo Payments checkout.
  3. After the payment, Dodo Payments sends a webhook to the function’s /webhook route. The function verifies the webhook signature, fetches the payment or subscription from the Dodo Payments API instead of trusting the payload, and grants access.
  4. If your app passes verifyUrl, Dodo Payments also returns the customer to the function’s /success route. That route grants access the same way, then redirects to your successUrl. Either the webhook or the redirect is enough to grant access, and running both is safe.

Prerequisites

Before you start, you need:
  1. An Appwrite project. The template wizard stores the function code in a Git repository, so you also connect a Git provider such as GitHub.
  2. A Dodo Payments product in test mode: a one-time product for the Payments template, or a subscription product for the Subscriptions template. Copy its product ID, which starts with pdt_.
  3. A test mode Dodo Payments API key with write access, which the function needs to create checkout sessions. Create one under Developer → API Keys. See Authentication.

Setting Up a Template

1

Create the Function From the Template

In the Appwrite Console, open your project and go to Functions → Templates. Search for Dodo Payments, select Payments with Dodo Payments or Subscriptions with Dodo Payments, and click Create function.
2

Choose a Runtime

Select a Node.js runtime. The templates need Node.js 20 or later.
3

Set the Variables

Fill in the function’s environment variables:You don’t create an Appwrite API key. The template sets the permission scopes the function needs, and Appwrite provides a key with those scopes on each execution.
4

Connect a Repository and Create the Function

Create a new repository or connect an existing one, keep the default branch settings, and click Create. Appwrite builds and deploys the function.
5

Copy the Function Domain

Open the function and go to its Domains tab. Copy the generated domain, which ends with .appwrite.run.
6

Add a Webhook Endpoint in Dodo Payments

In the Dodo Payments dashboard, go to Developer → Webhooks and click Add endpoint. In Endpoint URL, enter https://<function-domain>/webhook.Under Subscribed events, select payment.succeeded for the Payments template, or the subscription.* events for the Subscriptions template. An endpoint with no events selected receives every event type.Click Create endpoint, then copy the signing secret from the endpoint’s Overview tab.
7

Add the Signing Secret and Redeploy

In the Appwrite Console, open the function’s Settings tab and go to Environment variables. Set DODO_PAYMENTS_WEBHOOK_KEY to the signing secret, then redeploy the function. Appwrite applies variable changes only to new deployments.Until you redeploy, the function rejects webhooks with a 401 response, and Dodo Payments retries them.
8

Test a Purchase

Add the function domain as a web platform in your Appwrite project, so the demo page can call Appwrite without CORS errors. Then open https://<function-domain>/ in a browser. The function serves a demo page where you register as an anonymous user and start a checkout.Pay with the test card 4242 4242 4242 4242, any future expiry date such as 06/32, and CVV 123. For more test cards and scenarios, see Testing Process.
The demo page lists the paid order for the Payments template, or shows the user’s subscription status for the Subscriptions template.

Starting a Checkout From Your App

The demo page is for testing. In your own app, call the function through the Appwrite SDK while the user is signed in. Appwrite then adds the x-appwrite-user-id header, and the function responds with a 303 redirect to the Dodo Payments checkout. This example uses the Appwrite Web SDK, the same way the template’s demo page does:
checkout.js
The request body accepts these fields:

What Each Template Grants

Payments With Dodo Payments

The function handles payment.succeeded and ignores other events. It stores an order only when the payment’s status is succeeded and its product_cart includes DODO_PAYMENTS_PRODUCT_ID. On the first order, the function creates the orders database and table, with a userId and an orderId column. Each order row uses the Dodo Payments payment ID as its row ID, so a repeated webhook delivery doesn’t store the order twice. Only the customer who paid can read their row, so your app can list the signed-in user’s orders directly from the table. Dodo Payments sends payment.succeeded for every payment in your business, including subscription renewals. A subscription payment has no product_cart, so the Payments template ignores it, and you can run both templates against the same Dodo Payments business.

Subscriptions With Dodo Payments

The function handles every subscription.* event. Webhooks can arrive late, twice, or out of order, so the function fetches the subscription’s current status and applies that, whatever the event type: past_due is the grace period while Dodo Payments retries a failed renewal, so the customer keeps access until the subscription moves to on_hold or cancelled. The /success route only adds the label, because the customer can arrive there while the subscription is still pending. For the full lifecycle, see the Subscription Integration Guide. To gate features, check for subscriber in the user’s labels, or grant access to Appwrite resources with the label:subscriber role.

Going Live

Test mode and live mode have separate API keys, products, and webhook endpoints.
The function redirects customers to the successUrl, failureUrl, and verifyUrl values your app sends. Before you go live, edit the function’s src/main.js so that the /checkout handler (Payments template) or the /subscribe handler (Subscriptions template) accepts only URLs on your own domains. Otherwise anyone can use the function to redirect people to other sites.
To accept real payments:
  1. In live mode, create the product, an API key with write access, and a webhook endpoint at https://<function-domain>/webhook.
  2. In the function’s environment variables, set DODO_PAYMENTS_ENVIRONMENT to live_mode and replace the API key, product ID, and signing secret with the live values.
  3. Redeploy the function.
See Test Mode vs Live Mode.

Troubleshooting

  • Check that DODO_PAYMENTS_WEBHOOK_KEY matches the signing secret on the endpoint’s Overview tab.
  • Check that you redeployed the function after you changed the variable.
  • Check that the secret comes from the endpoint that sends the deliveries. Test mode and live mode endpoints have different signing secrets.
  • Check that the webhook endpoint subscribes to payment.succeeded.
  • Check that the customer bought the product in DODO_PAYMENTS_PRODUCT_ID.
  • Check that the checkout was created through the function. The function needs user_id in the payment’s metadata to know which Appwrite user to credit.
  • Open the function’s Executions tab in Appwrite for its logs, and the Logs tab under Developer → Webhooks for each delivery.
  • Check that the webhook endpoint subscribes to the subscription.* events.
  • Check that the checkout was created through the function, so the subscription has user_id in its metadata.
  • If the Appwrite user was deleted, the function logs an error and acknowledges the event without changing anything.
  • Check that the user is signed in when your app calls the function. Without a user, Appwrite doesn’t send x-appwrite-user-id.
  • Check that DODO_PAYMENTS_API_KEY is valid, has write access, and belongs to the mode in DODO_PAYMENTS_ENVIRONMENT.
  • Check that DODO_PAYMENTS_PRODUCT_ID is a product in that same mode.
Add the function domain as a web platform in your Appwrite project.

Appwrite Templates

Source code and endpoint reference for both Dodo Payments templates.

Webhooks

Endpoints, event types, signature verification, and retries.

Checkout Sessions

The checkout session fields the templates set, and the return URL parameters.

Subscription Integration Guide

Subscription statuses, renewals, and failed payment handling.
Last modified on October 24, 2026