@dodopayments/opencode-plugin 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +4 -14
- package/README.md +34 -19
- package/mcp.json +13 -0
- package/opencode-plugin/README.md +3 -3
- package/opencode-plugin/index.js +9 -7
- package/package.json +3 -2
- package/skills/better-auth-integration/SKILL.md +12 -3
- package/skills/billing-sdk/SKILL.md +66 -33
- package/skills/checkout-integration/SKILL.md +26 -20
- package/skills/credit-based-billing/SKILL.md +7 -8
- package/skills/customer-management/SKILL.md +25 -17
- package/skills/discounts-and-promotions/SKILL.md +20 -17
- package/skills/dodo-best-practices/SKILL.md +18 -5
- package/skills/dodo-mcp-usage/SKILL.md +152 -0
- package/skills/framework-adapters/SKILL.md +167 -35
- package/skills/license-keys/SKILL.md +142 -97
- package/skills/localized-pricing/SKILL.md +51 -29
- package/skills/mobile-checkout/SKILL.md +168 -118
- package/skills/product-catalog-management/SKILL.md +39 -16
- package/skills/refunds-and-disputes/SKILL.md +91 -71
- package/skills/subscription-integration/SKILL.md +62 -9
- package/skills/testing-and-go-live/SKILL.md +22 -15
- package/skills/usage-based-billing/SKILL.md +18 -15
- package/skills/webhook-integration/SKILL.md +27 -10
package/.mcp.json
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"dodopayments-api": {
|
|
4
|
-
"type": "
|
|
5
|
-
"
|
|
6
|
-
"args": [
|
|
7
|
-
"-y",
|
|
8
|
-
"mcp-remote@latest",
|
|
9
|
-
"https://mcp.dodopayments.com/mcp"
|
|
10
|
-
],
|
|
4
|
+
"type": "http",
|
|
5
|
+
"url": "https://mcp.dodopayments.com/mcp",
|
|
11
6
|
"enabled": true
|
|
12
7
|
},
|
|
13
8
|
"dodo-knowledge": {
|
|
14
|
-
"type": "
|
|
15
|
-
"
|
|
16
|
-
"args": [
|
|
17
|
-
"-y",
|
|
18
|
-
"mcp-remote@latest",
|
|
19
|
-
"https://knowledge.dodopayments.com/mcp"
|
|
20
|
-
],
|
|
9
|
+
"type": "http",
|
|
10
|
+
"url": "https://knowledge.dodopayments.com/mcp",
|
|
21
11
|
"enabled": true
|
|
22
12
|
}
|
|
23
13
|
}
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@dodopayments/opencode-plugin)
|
|
6
6
|
[](https://discord.gg/bYqAp4ayYh)
|
|
7
7
|
|
|
8
|
-
The official Dodo Payments plugin for AI coding agents. Installs
|
|
8
|
+
The official Dodo Payments plugin for AI coding agents. Installs the Dodo Payments integration skills and two MCP servers across **Claude Code**, **Codex CLI**, **Cursor**, **VS Code / GitHub Copilot**, **Grok Build**, **Meta Muse Code**, **Gemini CLI**, **Antigravity**, **Junie**, **OpenCode**, and more, from a single source of truth.
|
|
9
9
|
|
|
10
10
|
This plugin conforms to the [Agent Plugins 1.0.0](https://agent-plugins.org/specification) specification: a root [`plugin.json`](./plugin.json), skills as immediate children of [`skills/`](./skills), and MCP servers in [`mcp.json`](./mcp.json). Clients with native Agent Plugins support load it directly; the provider-specific manifests in this repo are generated compatibility shims for clients that do not.
|
|
11
11
|
|
|
@@ -13,10 +13,28 @@ This plugin conforms to the [Agent Plugins 1.0.0](https://agent-plugins.org/spec
|
|
|
13
13
|
|
|
14
14
|
- **Dodo Payments API MCP server** - Live API access (payments, subscriptions, customers, products, refunds, licenses, usage). Authenticates via browser OAuth, no local credentials required.
|
|
15
15
|
- **Dodo Knowledge MCP server** - No credentials. Semantic search over the current Dodo Payments documentation.
|
|
16
|
-
- **
|
|
16
|
+
- **Agent skills** - Integration guides (listed [below](#included-skills)), written as `SKILL.md` files with YAML frontmatter. Your agent loads the relevant skill on its own when a task calls for it.
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
|
+
| Client | Skills | MCP | How |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| Claude Code | ✅ | ✅ | [Marketplace](#claude-code) |
|
|
23
|
+
| Codex CLI / ChatGPT | ✅ | ✅ | [Marketplace](#codex-cli) |
|
|
24
|
+
| Cursor (and Grok Bot) | ✅ | ✅ | [Clone](#cursor) |
|
|
25
|
+
| VS Code / GitHub Copilot | ✅ | ✅ | [Plugins view](#vs-code--github-copilot) · [Copilot CLI](./docs/clients/more-clients.md) |
|
|
26
|
+
| Grok Build | ✅ | ✅ | [`grok plugin install`](./docs/clients/grok.md) |
|
|
27
|
+
| grok.com, xAI API | — | ✅ | [Connector / API tool](./docs/clients/grok.md) |
|
|
28
|
+
| Meta Muse Code | ✅ | ✅ | [`muse plugins install`](./docs/clients/muse.md) |
|
|
29
|
+
| Gemini CLI | ✅ | ✅ | [Extension](#gemini-cli) |
|
|
30
|
+
| Google Antigravity | ✅ | ✅ | [`agy plugin install`](./docs/clients/antigravity-and-junie.md#antigravity) |
|
|
31
|
+
| JetBrains Junie | ✅ | ✅ | [Extension](./docs/clients/antigravity-and-junie.md#junie) |
|
|
32
|
+
| Kiro | ✅ | ✅ | [Power](#kiro) |
|
|
33
|
+
| OpenCode | ✅ | ✅ | [npm](#opencode) |
|
|
34
|
+
| Qwen Code, Devin, Goose, Factory Droid, Augment, OpenHands, OpenClaw, Hermes | ✅ | ✅ | [More clients](./docs/clients/more-clients.md) |
|
|
35
|
+
| Cline, Kilo, Zed, Warp, Amp, Continue, Kimi, … | ✅ | ✅ | [`npx skills` + `npx add-mcp`](./docs/clients/more-clients.md#skills-and-mcp-separately) |
|
|
36
|
+
| Claude.ai, ChatGPT, Perplexity, Mistral | — | ✅ | [Custom connector](./docs/clients/more-clients.md#mcp-only-assistants) |
|
|
37
|
+
|
|
20
38
|
### Claude Code
|
|
21
39
|
|
|
22
40
|
```bash
|
|
@@ -75,16 +93,13 @@ git clone https://github.com/dodopayments/dodo-agent-plugin.git
|
|
|
75
93
|
|
|
76
94
|
Point Kiro at the cloned folder. Skills load from `skills/`, MCP servers from `mcp.json`, and Kiro-specific presentation comes from the `dev.kiro` extension namespace in `plugin.json`.
|
|
77
95
|
|
|
78
|
-
### Gemini CLI
|
|
79
|
-
|
|
80
|
-
Gemini CLI has no agent-skill primitive, so **only the two MCP servers are available** - the seventeen skills are not. `dodo-knowledge` still covers a good share of what the skills provide, and it stays current automatically.
|
|
96
|
+
### Gemini CLI
|
|
81
97
|
|
|
82
98
|
```bash
|
|
83
|
-
|
|
84
|
-
~/.gemini/extensions/dodopayments
|
|
99
|
+
gemini extensions install https://github.com/dodopayments/dodo-agent-plugin
|
|
85
100
|
```
|
|
86
101
|
|
|
87
|
-
|
|
102
|
+
`gemini-extension.json` at the repo root is the manifest, and Gemini auto-discovers the `skills/` directory next to it. Verified on Gemini CLI 0.63.0: `gemini skills list` shows every skill and `gemini mcp list` shows both servers.
|
|
88
103
|
|
|
89
104
|
### VS Code / GitHub Copilot
|
|
90
105
|
|
|
@@ -94,7 +109,7 @@ git clone https://github.com/dodopayments/dodo-agent-plugin.git
|
|
|
94
109
|
|
|
95
110
|
Then open the Chat view, go to **Plugins**, and add the cloned folder. Skills load from `skills/`, and both MCP servers load from `.mcp.json`.
|
|
96
111
|
|
|
97
|
-
> VS Code 1.125.1 does not key off the Agent Plugins `$schema` - the string appears nowhere in its bundle. Its loader picks a manifest by probing, in order, `.plugin/plugin.json`, then `.claude-plugin/plugin.json`, then a root `plugin.json`, and defaults MCP to `.mcp.json` rather than `mcp.json`. Because this repo ships a generated `.claude-plugin/plugin.json`, VS Code loads it through that branch. Everything works -
|
|
112
|
+
> VS Code 1.125.1 does not key off the Agent Plugins `$schema` - the string appears nowhere in its bundle. Its loader picks a manifest by probing, in order, `.plugin/plugin.json`, then `.claude-plugin/plugin.json`, then a root `plugin.json`, and defaults MCP to `.mcp.json` rather than `mcp.json`. Because this repo ships a generated `.claude-plugin/plugin.json`, VS Code loads it through that branch. Everything works - all skills and both MCP servers - via the compatibility manifests rather than the spec ones.
|
|
98
113
|
|
|
99
114
|
### OpenCode
|
|
100
115
|
|
|
@@ -107,7 +122,7 @@ OpenCode distributes via npm. Add the plugin to your `opencode.json`:
|
|
|
107
122
|
}
|
|
108
123
|
```
|
|
109
124
|
|
|
110
|
-
Restart OpenCode. Both MCP servers (`dodopayments-api`, `dodo-knowledge`) are registered automatically via the plugin's `config` hook. No manual `mcp` block required.
|
|
125
|
+
Restart OpenCode. Both MCP servers (`dodopayments-api`, `dodo-knowledge`) are registered automatically via the plugin's `config` hook as native remote servers. No manual `mcp` block required. Run `opencode mcp auth dodopayments-api` to sign in to the API server.
|
|
111
126
|
|
|
112
127
|
**Skills need the package installed locally plus one extra line.** OpenCode does not scan installed packages for skills, so point it at the package's `skills/` directory yourself. `skills.paths` entries resolve against the project directory, so the package must be present in the project's `node_modules` - OpenCode's own plugin cache is not the same location:
|
|
113
128
|
|
|
@@ -127,7 +142,7 @@ npm install --save-dev @dodopayments/opencode-plugin
|
|
|
127
142
|
|
|
128
143
|
An absolute path works too, and avoids the local-install requirement.
|
|
129
144
|
|
|
130
|
-
Verify with `opencode run "List every skill available to you by name."` - you should see
|
|
145
|
+
Verify with `opencode run "List every skill available to you by name."` - you should see every skill listed under [Included Skills](#included-skills). A skills path that does not exist is ignored silently, so check rather than assume.
|
|
131
146
|
|
|
132
147
|
> Versions before 0.5.0 documented these skills as auto-discovered. They were not: nothing in OpenCode scans an installed package, so OpenCode users had MCP servers but no skills. Setting `config.skills` from the plugin's `config` hook does not fix this either - the skill index is built before `config` hooks run, so it never registers anything.
|
|
133
148
|
|
|
@@ -160,6 +175,7 @@ If you prefer the local stdio API server with your own API key instead of the de
|
|
|
160
175
|
| `dodo-best-practices` | SDK setup, environments, API keys, and the canonical checkout-to-webhook architecture |
|
|
161
176
|
| `framework-adapters` | Official `@dodopayments/*` handlers for Next.js, Express, Hono, Astro, Remix, SvelteKit, Nuxt, Fastify, TanStack, Bun, Convex |
|
|
162
177
|
| `testing-and-go-live` | Test mode, test payment methods, webhook testing, production launch checklist |
|
|
178
|
+
| `dodo-mcp-usage` | When to use the documentation vs API MCP servers, test-mode safety rules, auth, troubleshooting |
|
|
163
179
|
|
|
164
180
|
**Accepting payments**
|
|
165
181
|
|
|
@@ -209,7 +225,7 @@ Skills source: [`dodopayments/skills`](https://github.com/dodopayments/skills),
|
|
|
209
225
|
| `dodopayments-api` | Live API access (payments, subscriptions, customers, products, refunds, licenses, usage) | OAuth (browser) |
|
|
210
226
|
| `dodo-knowledge` | Semantic search over the Dodo Payments documentation | None |
|
|
211
227
|
|
|
212
|
-
Both servers speak Streamable HTTP
|
|
228
|
+
Both servers speak Streamable HTTP over HTTPS, and every manifest dials them natively - no `npx mcp-remote` bridge and no Node dependency. The canonical `mcp.json` uses the spec's `type: "streamable-http"`; the generated `.mcp.json` (Claude Code, Cursor, VS Code, Grok Build, Devin, Goose, Droid) uses the same endpoints spelled `type: "http"`, the name that dialect uses; Muse, Antigravity, Junie and Gemini get their own spellings from the generator.
|
|
213
229
|
|
|
214
230
|
## Configure (optional, Claude Code)
|
|
215
231
|
|
|
@@ -269,9 +285,8 @@ These clients load MCPs from the static `.mcp.json` shipped with the plugin. To
|
|
|
269
285
|
{
|
|
270
286
|
"mcpServers": {
|
|
271
287
|
"dodopayments-api": {
|
|
272
|
-
"type": "
|
|
273
|
-
"
|
|
274
|
-
"args": ["-y", "mcp-remote@latest", "https://mcp.dodopayments.com/mcp"],
|
|
288
|
+
"type": "http",
|
|
289
|
+
"url": "https://mcp.dodopayments.com/mcp",
|
|
275
290
|
"enabled": false
|
|
276
291
|
}
|
|
277
292
|
}
|
|
@@ -327,9 +342,9 @@ npm run verify # generated artifacts in sync + Agent Plugins conformance
|
|
|
327
342
|
|---|---|
|
|
328
343
|
| `plugin.json` | **Canonical.** Agent Plugins v1.0.0 manifest and the version source of truth |
|
|
329
344
|
| `mcp.json` | **Canonical.** Agent Plugins v1.0.0 MCP config |
|
|
330
|
-
| `skills/` | **Canonical.**
|
|
345
|
+
| `skills/` | **Canonical.** Skills, vendored as real files; the declared set lives in `.skills-source.json` |
|
|
331
346
|
| `overlays/*.json` | Hand-authored provider extras the closed spec schema cannot express |
|
|
332
|
-
| `.claude-plugin/`, `.cursor-plugin/`, `.agents/`, `.mcp.json`, `plugins/dodopayments/` | **Generated.** Do not hand-edit - run `npm run build` |
|
|
347
|
+
| `.claude-plugin/`, `.cursor-plugin/`, `.codex-plugin/`, `.muse-plugin/`, `.agents/`, `.mcp.json`, `gemini-extension.json`, `plugins/dodopayments/`, `providers/` | **Generated.** Do not hand-edit - run `npm run build` |
|
|
333
348
|
| `scripts/build.mjs` | The single generator (`--check` for drift) |
|
|
334
349
|
| `scripts/conformance.mjs` | Agent Plugins conformance validator |
|
|
335
350
|
| `.skills-source.json` | Upstream provenance for the vendored skills |
|
|
@@ -343,7 +358,7 @@ The repo is configured to publish the OpenCode npm package on every GitHub Relea
|
|
|
343
358
|
**One-time setup (already done for this repo):**
|
|
344
359
|
|
|
345
360
|
- npm scope `@dodopayments` exists and is owned by Dodo Payments.
|
|
346
|
-
-
|
|
361
|
+
- npm Trusted Publishing (OIDC) is configured for `@dodopayments/opencode-plugin` against `publish-opencode.yml`. No npm token is stored in the repo.
|
|
347
362
|
|
|
348
363
|
**Release workflow:**
|
|
349
364
|
|
|
@@ -358,7 +373,7 @@ The repo is configured to publish the OpenCode npm package on every GitHub Relea
|
|
|
358
373
|
|
|
359
374
|
**CI checks:**
|
|
360
375
|
|
|
361
|
-
- `Verify` runs on every pull request and push to `main`: artifact drift, Agent Plugins conformance, live JSON Schema validation,
|
|
376
|
+
- `Verify` runs on every pull request and push to `main`: artifact drift, Agent Plugins conformance, live JSON Schema validation, an "every declared skill, zero symlinks" assertion, per-client manifest checks (Muse, Antigravity, Junie, Gemini), and an npm payload check.
|
|
362
377
|
- The release workflow re-runs the same gates before publishing.
|
|
363
378
|
|
|
364
379
|
## Resources
|
package/mcp.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"dodopayments-api": {
|
|
5
|
+
"type": "streamable-http",
|
|
6
|
+
"url": "https://mcp.dodopayments.com/mcp"
|
|
7
|
+
},
|
|
8
|
+
"dodo-knowledge": {
|
|
9
|
+
"type": "streamable-http",
|
|
10
|
+
"url": "https://knowledge.dodopayments.com/mcp"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @dodopayments/opencode-plugin
|
|
2
2
|
|
|
3
|
-
Official OpenCode plugin for Dodo Payments. Ships
|
|
3
|
+
Official OpenCode plugin for Dodo Payments. Ships the Dodo Payments integration skills and auto-registers two MCP servers.
|
|
4
4
|
|
|
5
5
|
This package is part of [`dodopayments/dodo-agent-plugin`](https://github.com/dodopayments/dodo-agent-plugin) - the universal Dodo Payments plugin that also installs into Claude Code, Codex, and Cursor.
|
|
6
6
|
|
|
@@ -25,7 +25,7 @@ npm install --save-dev @dodopayments/opencode-plugin
|
|
|
25
25
|
Restart OpenCode.
|
|
26
26
|
|
|
27
27
|
- Both MCP servers are registered automatically via the plugin's `config` hook.
|
|
28
|
-
- The `skills.paths` entry is what makes the
|
|
28
|
+
- The `skills.paths` entry is what makes the skills visible - OpenCode does not scan installed packages for skills. The path resolves against the **project** directory, not OpenCode's plugin cache, which is why the local install above is required. An absolute path works too and avoids that requirement.
|
|
29
29
|
- The plugin module exports only its default function. OpenCode's loader throws `Plugin export is not a function` on any non-function named export, skips the plugin, and swallows the error - leaving you with no MCP servers. Verified on OpenCode 1.18.15.
|
|
30
30
|
- The first call to `dodopayments-api` opens a browser for OAuth. `dodo-knowledge` needs no auth.
|
|
31
31
|
|
|
@@ -37,7 +37,7 @@ opencode run "List every skill available to you by name."
|
|
|
37
37
|
|
|
38
38
|
## What you get
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Agent skills (auto-loaded when relevant): `dodo-best-practices`, `dodo-mcp-usage`, `framework-adapters`, `testing-and-go-live`, `checkout-integration`, `subscription-integration`, `mobile-checkout`, `webhook-integration`, `credit-based-billing`, `usage-based-billing`, `license-keys`, `product-catalog-management`, `discounts-and-promotions`, `localized-pricing`, `customer-management`, `refunds-and-disputes`, `billing-sdk`, `better-auth-integration`.
|
|
41
41
|
|
|
42
42
|
Two MCP servers (registered automatically):
|
|
43
43
|
|
package/opencode-plugin/index.js
CHANGED
|
@@ -30,19 +30,21 @@
|
|
|
30
30
|
//
|
|
31
31
|
// The endpoint URLs below MUST match the canonical mcp.json. This file is
|
|
32
32
|
// hand-written rather than generated - OpenCode's config shape is its own
|
|
33
|
-
// (`type: "
|
|
33
|
+
// (`type: "remote"`, `url`) - so `build.mjs --check` cannot cover
|
|
34
34
|
// it. `scripts/conformance.mjs` asserts the URL sets are identical instead.
|
|
35
|
-
//
|
|
36
|
-
//
|
|
35
|
+
// `type: "remote"` is OpenCode's native Streamable HTTP transport; OpenCode
|
|
36
|
+
// runs OAuth discovery itself. Verified on 1.18.34 from a project-level
|
|
37
|
+
// opencode.json with these exact entries: dodo-knowledge connects, and
|
|
38
|
+
// dodopayments-api reports "needs authentication" (`opencode mcp auth`).
|
|
37
39
|
const DODO_MCP_SERVERS = {
|
|
38
40
|
"dodopayments-api": {
|
|
39
|
-
type: "
|
|
40
|
-
|
|
41
|
+
type: "remote",
|
|
42
|
+
url: "https://mcp.dodopayments.com/mcp",
|
|
41
43
|
enabled: true,
|
|
42
44
|
},
|
|
43
45
|
"dodo-knowledge": {
|
|
44
|
-
type: "
|
|
45
|
-
|
|
46
|
+
type: "remote",
|
|
47
|
+
url: "https://knowledge.dodopayments.com/mcp",
|
|
46
48
|
enabled: true,
|
|
47
49
|
},
|
|
48
50
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dodopayments/opencode-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Dodo Payments skills and MCP
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Dodo Payments agent skills and MCP servers for OpenCode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "opencode-plugin/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"opencode-plugin/",
|
|
12
12
|
"skills/",
|
|
13
|
+
"mcp.json",
|
|
13
14
|
".mcp.json",
|
|
14
15
|
"LICENSE"
|
|
15
16
|
],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: better-auth-integration
|
|
3
|
-
description:
|
|
3
|
+
description: Dodo Payments plugin for Better Auth (@dodopayments/better-auth) covering customer sync on signup, authenticated checkout, customer portal, usage ingestion, and verified webhooks. Use when an app uses Better Auth and needs the dodopayments, dodopaymentsClient, checkout, portal, usage, or webhooks plugins; for other auth stacks use framework-adapters.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Better Auth Integration
|
|
@@ -18,9 +18,11 @@ Use `@dodopayments/better-auth` to synchronize Better Auth users with Dodo Payme
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npm install @dodopayments/better-auth dodopayments better-auth
|
|
21
|
+
npm install @dodopayments/better-auth dodopayments better-auth zod
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
Requires Better Auth 1.4 or a later 1.x release.
|
|
25
|
+
|
|
24
26
|
The package exports the lowercase server functions `dodopayments`, `checkout`, `portal`, `usage`, and `webhooks`. Its client entry point exports `dodopaymentsClient`.
|
|
25
27
|
|
|
26
28
|
## Server setup
|
|
@@ -76,6 +78,13 @@ export const auth = betterAuth({
|
|
|
76
78
|
|
|
77
79
|
`products` maps an application slug to a Dodo product ID. `authenticatedUsersOnly: true` requires a Better Auth session for checkout. `getCustomerParams` adds application metadata when customer synchronization runs.
|
|
78
80
|
|
|
81
|
+
The plugin adds a `dodoCustomerId` field to the Better Auth `user` table. After adding the plugin, update your database schema with the [Better Auth CLI](https://www.better-auth.com/docs/concepts/cli):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx auth generate # generate the ORM schema or SQL migration, then apply it yourself
|
|
85
|
+
npx auth migrate # or apply it directly (built-in Kysely adapter only)
|
|
86
|
+
```
|
|
87
|
+
|
|
79
88
|
## Browser client setup
|
|
80
89
|
|
|
81
90
|
```typescript
|
|
@@ -170,7 +179,7 @@ if (error) {
|
|
|
170
179
|
}
|
|
171
180
|
```
|
|
172
181
|
|
|
173
|
-
The plugin derives `customer_id` from the authenticated session. `event_id` is the event's idempotency key; reuse it when retrying the same event.
|
|
182
|
+
Both `usage.ingest` and `usage.meters.list` require a signed-in user with a verified email address. The plugin derives `customer_id` from the authenticated session. `event_id` is the event's idempotency key; reuse it when retrying the same event.
|
|
174
183
|
|
|
175
184
|
## Webhook endpoint
|
|
176
185
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: billing-sdk
|
|
3
|
-
description:
|
|
3
|
+
description: BillingSDK, the open-source React and shadcn component library for Dodo Payments billing UI. Use when building pricing tables, pricing pages, account billing pages, usage meters, credit balances, invoice history, or cancel and upgrade UI in React or Next.js, or running @billingsdk/cli init; keep Dodo API calls server-side.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# BillingSDK
|
|
@@ -75,8 +75,10 @@ From the current official registry, grouped by purpose:
|
|
|
75
75
|
| Payments | `payment-details`, `payment-details-two`, `payment-method-selector`, `payment-card`, `payment-failure` |
|
|
76
76
|
| Promotion and trials | `banner`, `limited-offer-dialog`, `trial-expiry-card` |
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
Most blocks have a published usage example and props table on their page at
|
|
79
|
+
[billingsdk.com/docs/components](https://billingsdk.com/docs/components) (for example `subscription-management`
|
|
80
|
+
and `billing-settings`); `pricing-table-one` is reproduced below. The generated file's props type is still
|
|
81
|
+
authoritative for the version you installed, so read it after running `add`.
|
|
80
82
|
|
|
81
83
|
## Server client
|
|
82
84
|
|
|
@@ -103,44 +105,75 @@ are `https://test.dodopayments.com` and `https://live.dodopayments.com`.
|
|
|
103
105
|
|
|
104
106
|
### 1. Plan catalogue
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
`pricing-table-one` takes BillingSDK's `Plan[]`, not an arbitrary shape. Adding the component generates
|
|
109
|
+
`lib/billingsdk-config.ts`, which exports the `Plan` interface and a sample `plans` array — edit that array
|
|
110
|
+
rather than defining your own type. Prices are display **strings** (`monthlyPrice`, `yearlyPrice`) and
|
|
111
|
+
`features` are objects. The amount actually charged comes from the Dodo product catalogue (always in the
|
|
112
|
+
smallest currency unit), so treat this file as a mirror of the dashboard and reconcile it when you change a
|
|
113
|
+
price.
|
|
110
114
|
|
|
111
115
|
```typescript
|
|
112
|
-
// lib/
|
|
113
|
-
export type PlanId = 'starter' | 'pro';
|
|
114
|
-
|
|
116
|
+
// lib/billingsdk-config.ts (generated by `add pricing-table-one`; edit the plans)
|
|
115
117
|
export interface Plan {
|
|
116
|
-
id:
|
|
118
|
+
id: string;
|
|
117
119
|
title: string;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
description: string;
|
|
121
|
+
highlight?: boolean;
|
|
122
|
+
type?: 'monthly' | 'yearly';
|
|
123
|
+
currency?: string;
|
|
124
|
+
monthlyPrice: string;
|
|
125
|
+
yearlyPrice: string;
|
|
126
|
+
buttonText: string;
|
|
127
|
+
badge?: string;
|
|
128
|
+
features: {
|
|
129
|
+
name: string;
|
|
130
|
+
icon: string;
|
|
131
|
+
iconColor?: string;
|
|
132
|
+
}[];
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
export const
|
|
135
|
+
export const plans: Plan[] = [
|
|
125
136
|
{
|
|
126
137
|
id: 'starter',
|
|
127
138
|
title: 'Starter',
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
description: 'For individuals getting started.',
|
|
140
|
+
currency: '$',
|
|
141
|
+
monthlyPrice: '9',
|
|
142
|
+
yearlyPrice: '90',
|
|
143
|
+
buttonText: 'Choose Starter',
|
|
144
|
+
features: [
|
|
145
|
+
{ name: '100 requests', icon: 'check' },
|
|
146
|
+
{ name: 'Basic support', icon: 'check' },
|
|
147
|
+
{ name: '1 project', icon: 'check' },
|
|
148
|
+
],
|
|
132
149
|
},
|
|
133
150
|
{
|
|
134
151
|
id: 'pro',
|
|
135
152
|
title: 'Pro',
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
153
|
+
description: 'For teams running in production.',
|
|
154
|
+
currency: '$',
|
|
155
|
+
monthlyPrice: '29',
|
|
156
|
+
yearlyPrice: '290',
|
|
157
|
+
buttonText: 'Choose Pro',
|
|
158
|
+
badge: 'Most popular',
|
|
159
|
+
highlight: true,
|
|
160
|
+
features: [
|
|
161
|
+
{ name: 'Unlimited requests', icon: 'check' },
|
|
162
|
+
{ name: 'Priority support', icon: 'check' },
|
|
163
|
+
{ name: '10 projects', icon: 'check' },
|
|
164
|
+
],
|
|
140
165
|
},
|
|
141
166
|
];
|
|
142
167
|
```
|
|
143
168
|
|
|
169
|
+
The generated file is the contract for your installed version; if its `Plan` differs from the above, follow
|
|
170
|
+
the file. `onPlanSelect` receives only the plan `id`, so the ids here are the slugs the server maps below.
|
|
171
|
+
|
|
172
|
+
This example sells **monthly plans only**: `yearlyPrice` is required by the `Plan` type and is displayed,
|
|
173
|
+
but the server map below has only monthly products. To sell yearly too, create the yearly products in Dodo,
|
|
174
|
+
track the table's billing-period toggle in your own state (it is not passed to `onPlanSelect`), send it with
|
|
175
|
+
the plan id, and key the server map by plan and period (for example `pro:yearly`).
|
|
176
|
+
|
|
144
177
|
### 2. Product id map — server only
|
|
145
178
|
|
|
146
179
|
Never let the browser choose an arbitrary `product_id`. Map the public plan slug to a `pdt_` id on the
|
|
@@ -149,16 +182,16 @@ server and reject anything else.
|
|
|
149
182
|
```typescript
|
|
150
183
|
// lib/plan-products.server.ts
|
|
151
184
|
import 'server-only';
|
|
152
|
-
import type { PlanId } from './plans';
|
|
153
185
|
|
|
154
|
-
|
|
186
|
+
// Keys must match the `id`s in lib/billingsdk-config.ts.
|
|
187
|
+
const PRODUCT_IDS: Readonly<Record<string, string>> = {
|
|
155
188
|
starter: 'pdt_starter_monthly',
|
|
156
189
|
pro: 'pdt_pro_monthly',
|
|
157
190
|
};
|
|
158
191
|
|
|
159
192
|
export function productIdForPlan(value: unknown): string | null {
|
|
160
|
-
if (typeof value !== 'string') return null;
|
|
161
|
-
return PRODUCT_IDS[value
|
|
193
|
+
if (typeof value !== 'string' || !Object.hasOwn(PRODUCT_IDS, value)) return null;
|
|
194
|
+
return PRODUCT_IDS[value];
|
|
162
195
|
}
|
|
163
196
|
```
|
|
164
197
|
|
|
@@ -220,7 +253,7 @@ integrations. Checkout parameters are covered in depth in the `checkout-integrat
|
|
|
220
253
|
|
|
221
254
|
import { useState } from 'react';
|
|
222
255
|
import { PricingTableOne } from '@/components/billingsdk/pricing-table-one';
|
|
223
|
-
import {
|
|
256
|
+
import { plans } from '@/lib/billingsdk-config';
|
|
224
257
|
|
|
225
258
|
export function PricingPlans() {
|
|
226
259
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -249,7 +282,7 @@ export function PricingPlans() {
|
|
|
249
282
|
return (
|
|
250
283
|
<>
|
|
251
284
|
<PricingTableOne
|
|
252
|
-
plans={
|
|
285
|
+
plans={plans}
|
|
253
286
|
title="Choose your plan"
|
|
254
287
|
description="Select the plan that works best for you"
|
|
255
288
|
onPlanSelect={handlePlanSelect}
|
|
@@ -458,14 +491,14 @@ signature verification is covered in the `webhook-integration` skill.
|
|
|
458
491
|
**Accepting a raw `product_id` from the request body.** A caller can then check out against any product in
|
|
459
492
|
your catalogue, including internal or discounted ones. Map an opaque plan slug to a `pdt_` id server-side.
|
|
460
493
|
|
|
461
|
-
**Hardcoding prices in the client.** `
|
|
494
|
+
**Hardcoding prices in the client.** `plans[].monthlyPrice` / `yearlyPrice` are display text. When you change a price in the Dodo
|
|
462
495
|
dashboard, the checkout charges the new amount while the pricing page keeps advertising the old one.
|
|
463
|
-
Reconcile `lib/
|
|
496
|
+
Reconcile `lib/billingsdk-config.ts` with the dashboard as part of any pricing change.
|
|
464
497
|
|
|
465
498
|
**`npm install billingsdk`.** The root repository package is private. Install through
|
|
466
499
|
`@billingsdk/cli` or the shadcn registry instead.
|
|
467
500
|
|
|
468
|
-
**Guessing prop names.**
|
|
501
|
+
**Guessing prop names.** Check the block's page on billingsdk.com/docs/components, then open the
|
|
469
502
|
generated file under `components/billingsdk/` and read the exported props type before wiring anything else.
|
|
470
503
|
|
|
471
504
|
**Using `DODO_PAYMENTS_WEBHOOK_SECRET`.** The SDK reads `DODO_PAYMENTS_WEBHOOK_KEY` for the `webhookKey`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: checkout-integration
|
|
3
|
-
description:
|
|
3
|
+
description: Dodo Payments checkout via hosted Checkout Sessions (client.checkoutSessions.create), static payment links, and overlay or inline checkout for one-time and subscription products. Use when building a pay button, checkout page, trial signup, custom fields, discount codes at checkout, or return_url redirects; use subscription-integration for post-checkout lifecycle.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Dodo Payments Checkout Integration
|
|
@@ -38,7 +38,7 @@ Dodo Payments offers three ways to collect payment:
|
|
|
38
38
|
|
|
39
39
|
**Checkout Session:** A single-use session that generates a hosted checkout URL. Expires after 24 hours (or 15 minutes if `confirm=true`).
|
|
40
40
|
|
|
41
|
-
**Return URL:** Where the customer lands after payment.
|
|
41
|
+
**Return URL:** Where the customer lands after payment. Dodo appends `payment_id` (one-time) or `subscription_id` (subscription), `status`, and when available `license_key` and `email`. It is a UI hint only, never proof of payment.
|
|
42
42
|
|
|
43
43
|
**Entitlement on webhook:** The browser redirect is not the source of truth. Always verify payment via webhook before granting access. See `webhook-integration` skill for verification.
|
|
44
44
|
|
|
@@ -637,31 +637,27 @@ async function createCheckoutUrl(planId: PlanId, quantity = 1): Promise<string>
|
|
|
637
637
|
return data.checkoutUrl;
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
640
|
+
// Call ONCE when the app loads (e.g. in your root component or entry point),
|
|
641
|
+
// not before every checkout. Pick the display type your page uses.
|
|
642
|
+
export function initCheckout(displayType: 'overlay' | 'inline'): void {
|
|
643
643
|
DodoPayments.Initialize({
|
|
644
644
|
mode: 'test', // or 'live'
|
|
645
|
-
displayType
|
|
645
|
+
displayType,
|
|
646
646
|
onEvent: (event) => {
|
|
647
647
|
console.log('Checkout event:', event);
|
|
648
648
|
}
|
|
649
649
|
});
|
|
650
|
+
}
|
|
650
651
|
|
|
652
|
+
// Requires initCheckout('overlay') to have run at app load.
|
|
653
|
+
export async function openOverlayCheckout(planId: PlanId): Promise<void> {
|
|
654
|
+
const checkoutUrl = await createCheckoutUrl(planId);
|
|
651
655
|
DodoPayments.Checkout.open({ checkoutUrl });
|
|
652
656
|
}
|
|
653
657
|
|
|
658
|
+
// Requires initCheckout('inline') to have run at app load.
|
|
654
659
|
export async function openInlineCheckout(planId: PlanId): Promise<void> {
|
|
655
660
|
const checkoutUrl = await createCheckoutUrl(planId);
|
|
656
|
-
|
|
657
|
-
DodoPayments.Initialize({
|
|
658
|
-
mode: 'test', // or 'live'
|
|
659
|
-
displayType: 'inline',
|
|
660
|
-
onEvent: (event) => {
|
|
661
|
-
console.log('Checkout event:', event);
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
|
|
665
661
|
DodoPayments.Checkout.open({
|
|
666
662
|
checkoutUrl,
|
|
667
663
|
elementId: 'dodo-inline-checkout'
|
|
@@ -685,6 +681,8 @@ No-code shareable links. No server-side API call needed.
|
|
|
685
681
|
https://checkout.dodopayments.com/buy/{productid}
|
|
686
682
|
```
|
|
687
683
|
|
|
684
|
+
In test mode the host is `test.checkout.dodopayments.com` (for example `https://test.checkout.dodopayments.com/buy/{productid}`); a test-mode product does not exist on the live host.
|
|
685
|
+
|
|
688
686
|
Example:
|
|
689
687
|
```text
|
|
690
688
|
https://checkout.dodopayments.com/buy/pdt_example
|
|
@@ -789,12 +787,20 @@ const session = await client.checkoutSessions.create({
|
|
|
789
787
|
After payment, the customer is redirected to your `return_url` with query parameters:
|
|
790
788
|
|
|
791
789
|
```text
|
|
792
|
-
|
|
790
|
+
# One-time payment
|
|
791
|
+
https://yoursite.com/success?payment_id=pay_xxx&status=succeeded&email=customer%40example.com
|
|
792
|
+
|
|
793
|
+
# Subscription (with license keys)
|
|
794
|
+
https://yoursite.com/success?subscription_id=sub_xxx&status=active&license_key=LK-001,LK-002&email=customer%40example.com
|
|
793
795
|
```
|
|
794
796
|
|
|
795
797
|
Query parameters:
|
|
796
|
-
- `
|
|
797
|
-
- `
|
|
798
|
+
- `payment_id` (one-time payments) or `subscription_id` (subscriptions)
|
|
799
|
+
- `status`: `succeeded` (one-time) or `active` (subscription) on success; `failed` if declined; `processing` (or a `requires_*` value) if it settles later; `expired` if the session expired
|
|
800
|
+
- `license_key`: present when the product issues license keys (comma-separated if several)
|
|
801
|
+
- `email`: present when the customer has an email on record
|
|
802
|
+
|
|
803
|
+
There is no `session_id` parameter. Treat `processing`/missing status as "unknown" and wait for the webhook.
|
|
798
804
|
|
|
799
805
|
### Verify Payment Server-Side
|
|
800
806
|
|
|
@@ -818,8 +824,8 @@ Do not use `client.payments.create()` or `client.subscriptions.create()` for new
|
|
|
818
824
|
**3. Forgetting the `environment` flag**
|
|
819
825
|
The default is `live_mode`. Always set `environment: 'test_mode'` during development to avoid charging real cards.
|
|
820
826
|
|
|
821
|
-
**4.
|
|
822
|
-
|
|
827
|
+
**4. Using the deprecated `discount_code`**
|
|
828
|
+
Use `discount_codes` (an array). The singular `discount_code` string is deprecated (still accepted for backward compatibility) and cannot be combined with `discount_codes` in the same request.
|
|
823
829
|
|
|
824
830
|
**5. Amounts in wrong unit**
|
|
825
831
|
All amounts are in the smallest currency unit (cents for USD). $10 is `1000`, not `10`.
|