Terraform Registry
Example Repository
Overview
The Dodo Payments Terraform provider keeps your pricing in configuration files in your Git repository. You define products, prices, meters, credits, discounts, entitlements, and webhook endpoints in code. A pricing change then goes through the same steps as a code change: a pull request, a reviewed plan, and an apply from CI. This gives your pricing four properties that dashboard edits do not have:- Review before release.
terraform planshows each product, price, and discount that a change creates, updates, or archives. A teammate approves the plan before customers see the new price. - Version history. Each pricing change is a commit with an author, a reviewer, and a reason. To undo a change, revert the commit and apply again.
- One catalog in each mode. The same configuration builds your catalog in test mode and in live mode. You test a pricing change end to end in test mode, then apply the same commit to live mode.
- Drift detection. A scheduled
terraform planfinds changes that someone made in the dashboard, so the repository stays the source of truth.
What You Can Manage
Each catalog object has a resource. Each resource also has a data source for one object, a data source for a list of objects, and a list resource forterraform query.
The provider also has these Terraform objects:
- The ephemeral resource
dodopayments_webhook_secretreads the signing secret of a webhook endpoint without a copy in the state. - The actions
dodopayments_product_unarchive,dodopayments_addon_unarchive,dodopayments_meter_unarchive,dodopayments_product_collection_unarchive, anddodopayments_credit_entitlement_undeleterestore an archived or deleted object.
Getting Started
You need Terraform 1.12 or later and a test mode API key. Create the key in the dashboard under Developer → API Keys while test mode is on.1
Configure the Provider
Create a file named
versions.tf:versions.tf
2
Set the API Key
The provider reads the API key from the
DODO_PAYMENTS_API_KEY environment variable:3
Define a Product
Create a file named For the meaning of the subscription period, see Subscriptions.
products.tf. Prices are in the smallest currency unit, so 4900 is $49.00:products.tf
4
Plan and Apply
Download the provider, review the plan, and create the product:
terraform apply prints the new product ID. The product is also in the dashboard, in test mode.Modeling a Pricing Catalog
A real catalog has tiers, billing cycles, and rules between them. In Terraform, you write the pricing model once as data and generate the objects from it. To change a price, you change one number, and the plan shows each object that the change affects. This example builds three tiers in two billing cycles, puts them in one product collection, and adds a launch discount for the annual plans:catalog.tf
- One source for each price. The discount and the collection refer to the products, not to product IDs. When you add a tier, the collection and the discount get the new plan in the same apply.
- Pricing rules as checks. The
preconditionstops the plan when an annual price does not save at least 10%. Usepreconditionblocks andcheckblocks for each rule that your pricing must keep. - Reviewable changes. When you change
pro.monthlyfrom4900to5900, the plan shows one change tododopayments_product.plan["pro_monthly"]before anything changes.
Reviewing Pricing Changes With GitOps
With GitOps, the main branch of your repository is the record of your live catalog. Nobody edits pricing in the dashboard. Each change is a pull request, and CI applies the change after the merge. A GitOps setup for the provider has these parts:
This GitHub Actions job applies the catalog after a merge to the main branch:
.github/workflows/apply.yml
concurrency group makes a second apply wait for the first one. The job also needs credentials for your state backend. The example repository uses Workload Identity Federation, so CI has no long-lived cloud key. It also has the plan, drift, and end-to-end workflows.
Promoting Changes From Test Mode to Live Mode
Each API key works in one mode, and object IDs are different in each mode. Keep one state for each mode, and refer to resources instead of writing IDs in the configuration. Then the same configuration builds the same catalog in both modes. Set the mode with a variable or with theDODO_PAYMENTS_ENVIRONMENT environment variable:
1
Apply in Test Mode
Apply the commit with the test mode API key and the test mode state.
2
Test the Change
Create a checkout for the changed products in test mode. Use the test cards to complete a purchase.
3
Apply in Live Mode
Apply the same commit with the live mode API key and the live mode state.
Detecting Dashboard Changes
A change in the dashboard does not update your configuration. The nextterraform apply reverts each dashboard change to an argument that your configuration sets. To find these changes before an apply does, run a plan on a schedule:
0 when the catalog matches the configuration and 2 when it does not. The example repository runs this command each night. When the result is 2, it opens an issue with the summary of the plan. When a later run returns 0, it closes the issue.
To keep a dashboard change, copy it into the configuration in a pull request. To discard it, run the apply workflow again.
Importing Existing Products
You can bring objects that you created in the dashboard under Terraform management. Import an object with animport block and its ID:
terraform query lists the objects in your account and writes the configuration for you. Create a file with the .tfquery.hcl extension:
catalog.tfquery.hcl
resource block and an import block for each product:
terraform plan, and confirm that the plan imports the objects with no other changes.
Keeping the Webhook Secret Out of State
The ephemeral resourcedodopayments_webhook_secret reads the signing secret of a webhook endpoint during a run. Terraform does not write an ephemeral value to the plan or the state. Pass the secret to a write-only argument, such as the secret data of a secret manager:
Deleting a Resource
When you remove a resource from the configuration, Terraform destroys it. The provider archives some objects and deletes others. An action can restore some of them. The table shows what happens to each object.
To prevent an accidental destroy of a product that customers subscribe to, add
prevent_destroy to its lifecycle block.