@dodopayments/opencode-plugin 0.6.0 → 0.6.2
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/README.md +1 -0
- package/opencode-plugin/README.md +1 -1
- package/package.json +1 -1
- package/skills/better-auth-integration/SKILL.md +12 -3
- package/skills/billing-sdk/SKILL.md +15 -167
- package/skills/billing-sdk/references/pricing-page.md +197 -0
- package/skills/checkout-integration/SKILL.md +31 -460
- package/skills/checkout-integration/references/overlay-checkout.md +148 -0
- package/skills/checkout-integration/references/server-examples.md +317 -0
- package/skills/credit-based-billing/SKILL.md +11 -158
- package/skills/credit-based-billing/references/ai-token-example.md +108 -0
- package/skills/credit-based-billing/references/credit-webhooks.md +51 -0
- 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 +64 -421
- package/skills/framework-adapters/references/astro.md +52 -0
- package/skills/framework-adapters/references/bun.md +45 -0
- package/skills/framework-adapters/references/convex.md +91 -0
- package/skills/framework-adapters/references/express.md +72 -0
- package/skills/framework-adapters/references/fastify.md +54 -0
- package/skills/framework-adapters/references/hono.md +63 -0
- package/skills/framework-adapters/references/nuxt.md +53 -0
- package/skills/framework-adapters/references/remix.md +65 -0
- package/skills/framework-adapters/references/sveltekit.md +49 -0
- package/skills/framework-adapters/references/tanstack-start.md +31 -0
- package/skills/license-keys/SKILL.md +117 -367
- package/skills/license-keys/references/cli-tool.md +143 -0
- package/skills/license-keys/references/desktop-app.md +170 -0
- package/skills/localized-pricing/SKILL.md +51 -29
- package/skills/mobile-checkout/SKILL.md +72 -310
- package/skills/mobile-checkout/references/android.md +69 -0
- package/skills/mobile-checkout/references/flutter.md +97 -0
- package/skills/mobile-checkout/references/ios.md +66 -0
- package/skills/mobile-checkout/references/react-native.md +100 -0
- package/skills/product-catalog-management/SKILL.md +39 -16
- package/skills/refunds-and-disputes/SKILL.md +91 -71
- package/skills/subscription-integration/SKILL.md +43 -64
- package/skills/subscription-integration/references/webhook-events.md +82 -0
- package/skills/testing-and-go-live/SKILL.md +22 -15
- package/skills/usage-based-billing/SKILL.md +16 -122
- package/skills/usage-based-billing/references/instrumentation.md +119 -0
- package/skills/webhook-integration/SKILL.md +31 -263
- package/skills/webhook-integration/references/common-mistakes.md +146 -0
- package/skills/webhook-integration/references/event-catalog.md +103 -0
- package/skills/webhook-integration/references/local-testing.md +42 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Framework Adapters: Convex
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/convex`
|
|
5
|
+
|
|
6
|
+
Convex uses a component-based architecture. Register the component in `convex.config.ts`:
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
// convex/convex.config.ts
|
|
10
|
+
import { defineApp } from "convex/server";
|
|
11
|
+
import dodopayments from "@dodopayments/convex/convex.config";
|
|
12
|
+
|
|
13
|
+
const app = defineApp();
|
|
14
|
+
app.use(dodopayments);
|
|
15
|
+
export default app;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run `npx convex dev` once to generate `components.dodopayments`. `identify` needs an app-owned lookup from the signed-in user to their Dodo customer ID. This assumes a `customers` table with `authId` and `dodoCustomerId` fields and a `by_auth_id` index; adapt it to your schema:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// convex/customers.ts
|
|
22
|
+
import { internalQuery } from "./_generated/server";
|
|
23
|
+
import { v } from "convex/values";
|
|
24
|
+
|
|
25
|
+
export const getByAuthId = internalQuery({
|
|
26
|
+
args: { authId: v.string() },
|
|
27
|
+
handler: async (ctx, { authId }) =>
|
|
28
|
+
ctx.db
|
|
29
|
+
.query("customers")
|
|
30
|
+
.withIndex("by_auth_id", (q) => q.eq("authId", authId))
|
|
31
|
+
.unique(),
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then create a `DodoPayments` client in a local module and export its API methods:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// convex/dodo.ts
|
|
39
|
+
import { DodoPayments } from "@dodopayments/convex";
|
|
40
|
+
import { components, internal } from "./_generated/api";
|
|
41
|
+
|
|
42
|
+
const apiKey = process.env.DODO_PAYMENTS_API_KEY;
|
|
43
|
+
if (!apiKey) throw new Error("DODO_PAYMENTS_API_KEY is not set");
|
|
44
|
+
|
|
45
|
+
export const dodo = new DodoPayments(components.dodopayments, {
|
|
46
|
+
// Maps the signed-in Convex user to a Dodo customer; used by customerPortal.
|
|
47
|
+
identify: async (ctx) => {
|
|
48
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
49
|
+
if (!identity) return null;
|
|
50
|
+
const customer = await ctx.runQuery(internal.customers.getByAuthId, {
|
|
51
|
+
authId: identity.subject,
|
|
52
|
+
});
|
|
53
|
+
return customer ? { dodoCustomerId: customer.dodoCustomerId } : null;
|
|
54
|
+
},
|
|
55
|
+
apiKey,
|
|
56
|
+
environment: process.env.DODO_PAYMENTS_ENVIRONMENT === "live_mode" ? "live_mode" : "test_mode",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const { checkout, customerPortal } = dodo.api();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Call `checkout` from an **action** (mutations cannot make network calls), passing the checkout session fields in `payload`. The client sends a plan slug; the server maps it to a product ID and owns the return URL, so a caller cannot pick an arbitrary product or redirect target:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// convex/payments.ts
|
|
66
|
+
import { action } from "./_generated/server";
|
|
67
|
+
import { v } from "convex/values";
|
|
68
|
+
import { checkout } from "./dodo";
|
|
69
|
+
|
|
70
|
+
// Public plan slug -> Dodo product ID. Never accept a product_id from the client.
|
|
71
|
+
const PRODUCT_IDS: Readonly<Record<string, string>> = {
|
|
72
|
+
starter: "pdt_starter_monthly",
|
|
73
|
+
pro: "pdt_pro_monthly",
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const createCheckout = action({
|
|
77
|
+
args: { plan: v.string() },
|
|
78
|
+
handler: async (ctx, { plan }) => {
|
|
79
|
+
if (!Object.hasOwn(PRODUCT_IDS, plan)) throw new Error(`Unknown plan: ${plan}`);
|
|
80
|
+
// Returns { checkout_url }
|
|
81
|
+
return await checkout(ctx, {
|
|
82
|
+
payload: {
|
|
83
|
+
product_cart: [{ product_id: PRODUCT_IDS[plan], quantity: 1 }],
|
|
84
|
+
return_url: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Convex only supports session checkout, not static or dynamic modes.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Framework Adapters: Express
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/express`
|
|
5
|
+
|
|
6
|
+
### Checkout
|
|
7
|
+
|
|
8
|
+
The Express adapter names its checkout export `checkoutHandler` in lowercase, unlike every other adapter. `import { Checkout } from "@dodopayments/express"` does not resolve.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import express from "express";
|
|
12
|
+
import { checkoutHandler } from "@dodopayments/express";
|
|
13
|
+
import { dodoEnvironment } from "./lib/dodo-env";
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
// Required before the POST route: the session/dynamic handlers read req.body.
|
|
17
|
+
app.use(express.json());
|
|
18
|
+
|
|
19
|
+
app.get("/api/checkout", checkoutHandler({
|
|
20
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
21
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
22
|
+
environment: dodoEnvironment,
|
|
23
|
+
type: "static",
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
app.post("/api/checkout", checkoutHandler({
|
|
27
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
28
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
29
|
+
environment: dodoEnvironment,
|
|
30
|
+
type: "session",
|
|
31
|
+
}));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Customer Portal
|
|
35
|
+
|
|
36
|
+
The adapter's `CustomerPortal` reads `customer_id` from `req.query`, which you cannot safely rewrite in Express 5. Behind your auth middleware, call the SDK directly with the session's customer ID instead:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import DodoPayments from "dodopayments";
|
|
40
|
+
import { dodoEnvironment } from "./lib/dodo-env";
|
|
41
|
+
|
|
42
|
+
const client = new DodoPayments({
|
|
43
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
44
|
+
environment: dodoEnvironment,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// requireAuth = your session middleware; it must set req.user.
|
|
48
|
+
app.get("/api/customer-portal", requireAuth, async (req, res) => {
|
|
49
|
+
const customerId = req.user?.dodoCustomerId; // from the session, never from req.query
|
|
50
|
+
if (!customerId) return res.status(401).send("Unauthorized");
|
|
51
|
+
const session = await client.customers.customerPortal.create(customerId);
|
|
52
|
+
res.redirect(session.link);
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Webhooks
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { Webhooks } from "@dodopayments/express";
|
|
60
|
+
|
|
61
|
+
// The Express handler verifies the signature against the PARSED req.body, so it
|
|
62
|
+
// needs express.json() (skip this line if already registered). Do NOT use express.raw() here - the
|
|
63
|
+
// handler would see a Buffer and reject every request.
|
|
64
|
+
app.use(express.json());
|
|
65
|
+
|
|
66
|
+
app.post("/api/webhook", Webhooks({
|
|
67
|
+
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
|
|
68
|
+
onPayload: async (payload) => {
|
|
69
|
+
console.log("Webhook:", payload.type);
|
|
70
|
+
},
|
|
71
|
+
}));
|
|
72
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Framework Adapters: Fastify
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/fastify`
|
|
5
|
+
|
|
6
|
+
Fastify requires a string body parser to preserve the raw body for webhook verification.
|
|
7
|
+
|
|
8
|
+
### Checkout
|
|
9
|
+
|
|
10
|
+
`Checkout(config)` returns an object with `getHandler` and `postHandler`, not a single callable. Build it once and mount each method, rather than calling the result.
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import Fastify from "fastify";
|
|
14
|
+
import { Checkout } from "@dodopayments/fastify";
|
|
15
|
+
import { dodoEnvironment } from "./lib/dodo-env";
|
|
16
|
+
|
|
17
|
+
const fastify = Fastify();
|
|
18
|
+
|
|
19
|
+
const staticCheckout = Checkout({
|
|
20
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
21
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
22
|
+
environment: dodoEnvironment,
|
|
23
|
+
type: "static",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const sessionCheckout = Checkout({
|
|
27
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
28
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
29
|
+
environment: dodoEnvironment,
|
|
30
|
+
type: "session",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
fastify.get("/api/checkout", staticCheckout.getHandler);
|
|
34
|
+
fastify.post("/api/checkout", sessionCheckout.postHandler);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Webhooks
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { Webhooks } from "@dodopayments/fastify";
|
|
41
|
+
|
|
42
|
+
fastify.addContentTypeParser(
|
|
43
|
+
"application/json",
|
|
44
|
+
{ parseAs: "string" },
|
|
45
|
+
(req, body, done) => done(null, body)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
fastify.post("/api/webhook", Webhooks({
|
|
49
|
+
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
|
|
50
|
+
onPayload: async (payload) => {
|
|
51
|
+
console.log("Webhook:", payload.type);
|
|
52
|
+
},
|
|
53
|
+
}));
|
|
54
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Framework Adapters: Hono
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/hono`
|
|
5
|
+
|
|
6
|
+
### Checkout
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { Hono } from "hono";
|
|
10
|
+
import { Checkout } from "@dodopayments/hono";
|
|
11
|
+
import { dodoEnvironment } from "./lib/dodo-env";
|
|
12
|
+
|
|
13
|
+
const app = new Hono();
|
|
14
|
+
|
|
15
|
+
app.get("/api/checkout", Checkout({
|
|
16
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
17
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
18
|
+
environment: dodoEnvironment,
|
|
19
|
+
type: "static",
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
app.post("/api/checkout", Checkout({
|
|
23
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
24
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
25
|
+
environment: dodoEnvironment,
|
|
26
|
+
type: "session",
|
|
27
|
+
}));
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Customer Portal
|
|
31
|
+
|
|
32
|
+
The adapter's `CustomerPortal` reads `c.req.query("customer_id")`. Behind your auth middleware, call the SDK directly with the session's customer ID instead:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import DodoPayments from "dodopayments";
|
|
36
|
+
import { dodoEnvironment } from "./lib/dodo-env";
|
|
37
|
+
|
|
38
|
+
const client = new DodoPayments({
|
|
39
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
40
|
+
environment: dodoEnvironment,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Assumes your auth middleware ran first and stored the user on the context.
|
|
44
|
+
app.get("/api/customer-portal", async (c) => {
|
|
45
|
+
const customerId = c.get("user")?.dodoCustomerId; // from the session, never from the query
|
|
46
|
+
if (!customerId) return c.text("Unauthorized", 401);
|
|
47
|
+
const session = await client.customers.customerPortal.create(customerId);
|
|
48
|
+
return c.redirect(session.link);
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Webhooks
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Webhooks } from "@dodopayments/hono";
|
|
56
|
+
|
|
57
|
+
app.post("/api/webhook", Webhooks({
|
|
58
|
+
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
|
|
59
|
+
onPayload: async (payload) => {
|
|
60
|
+
console.log("Webhook:", payload.type);
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Framework Adapters: Nuxt
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/nuxt`
|
|
5
|
+
|
|
6
|
+
Add the module to `nuxt.config.ts` and configure runtime variables:
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
// nuxt.config.ts
|
|
10
|
+
export default defineNuxtConfig({
|
|
11
|
+
modules: ["@dodopayments/nuxt"],
|
|
12
|
+
runtimeConfig: {
|
|
13
|
+
private: {
|
|
14
|
+
bearerToken: process.env.NUXT_PRIVATE_BEARER_TOKEN,
|
|
15
|
+
webhookKey: process.env.NUXT_PRIVATE_WEBHOOK_KEY,
|
|
16
|
+
environment: process.env.NUXT_PRIVATE_ENVIRONMENT,
|
|
17
|
+
returnUrl: process.env.NUXT_PRIVATE_RETURN_URL,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Checkout
|
|
24
|
+
|
|
25
|
+
The Nuxt module registers its handlers with `addServerImportsDir`, so `checkoutHandler`, `customerPortalHandler`, and `Webhooks` are **auto-imported** inside `server/`. Do not import them from `@dodopayments/nuxt` — that entry point exports only the Nuxt module itself, and a named import from it will not resolve.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// server/routes/api/checkout.ts
|
|
29
|
+
// checkoutHandler and useRuntimeConfig are auto-imported by the module.
|
|
30
|
+
const config = useRuntimeConfig();
|
|
31
|
+
|
|
32
|
+
export default checkoutHandler({
|
|
33
|
+
bearerToken: config.private.bearerToken,
|
|
34
|
+
returnUrl: config.private.returnUrl,
|
|
35
|
+
environment: config.private.environment,
|
|
36
|
+
type: "session",
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Webhooks
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// server/routes/api/webhook.ts
|
|
44
|
+
// Webhooks and useRuntimeConfig are auto-imported by the module.
|
|
45
|
+
const config = useRuntimeConfig();
|
|
46
|
+
|
|
47
|
+
export default Webhooks({
|
|
48
|
+
webhookKey: config.private.webhookKey,
|
|
49
|
+
onPayload: async (payload) => {
|
|
50
|
+
console.log("Webhook:", payload.type);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Framework Adapters: Remix
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/remix`
|
|
5
|
+
|
|
6
|
+
### Checkout
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
// app/routes/api.checkout.tsx
|
|
10
|
+
import { Checkout } from "@dodopayments/remix";
|
|
11
|
+
import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node";
|
|
12
|
+
import { dodoEnvironment } from "~/lib/dodo-env";
|
|
13
|
+
|
|
14
|
+
const checkoutHandler = Checkout({
|
|
15
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
16
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
17
|
+
environment: dodoEnvironment,
|
|
18
|
+
type: "session",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const loader = ({ request }: LoaderFunctionArgs) => checkoutHandler(request);
|
|
22
|
+
export const action = ({ request }: ActionFunctionArgs) => checkoutHandler(request);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Customer Portal
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// app/routes/api.customer-portal.tsx
|
|
29
|
+
import { CustomerPortal } from "@dodopayments/remix";
|
|
30
|
+
import type { LoaderFunctionArgs } from "@remix-run/node";
|
|
31
|
+
import { dodoEnvironment } from "~/lib/dodo-env";
|
|
32
|
+
// your auth helper
|
|
33
|
+
import { getSessionUser } from "~/lib/auth.server";
|
|
34
|
+
|
|
35
|
+
const portalHandler = CustomerPortal({
|
|
36
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
37
|
+
environment: dodoEnvironment,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
41
|
+
const user = await getSessionUser(request);
|
|
42
|
+
if (!user?.dodoCustomerId) return new Response("Unauthorized", { status: 401 });
|
|
43
|
+
|
|
44
|
+
// Overwrite any client-supplied ?customer_id= with the signed-in user's ID.
|
|
45
|
+
const url = new URL(request.url);
|
|
46
|
+
url.searchParams.set("customer_id", user.dodoCustomerId);
|
|
47
|
+
return portalHandler(new Request(url, request));
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Webhooks
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// app/routes/api.webhook.tsx
|
|
55
|
+
import { Webhooks } from "@dodopayments/remix";
|
|
56
|
+
import type { ActionFunctionArgs } from "@remix-run/node";
|
|
57
|
+
|
|
58
|
+
export const action = ({ request }: ActionFunctionArgs) =>
|
|
59
|
+
Webhooks({
|
|
60
|
+
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
|
|
61
|
+
onPayload: async (payload) => {
|
|
62
|
+
console.log("Webhook:", payload.type);
|
|
63
|
+
},
|
|
64
|
+
})(request);
|
|
65
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Framework Adapters: SvelteKit
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/sveltekit`
|
|
5
|
+
**Route placement:** `src/routes/api/checkout/+server.ts`, `src/routes/api/customer-portal/+server.ts`, `src/routes/api/webhook/+server.ts`
|
|
6
|
+
|
|
7
|
+
### Checkout
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// src/routes/api/checkout/+server.ts
|
|
11
|
+
import { Checkout } from "@dodopayments/sveltekit";
|
|
12
|
+
import { DODO_PAYMENTS_API_KEY, DODO_PAYMENTS_RETURN_URL, DODO_PAYMENTS_ENVIRONMENT } from "$env/static/private";
|
|
13
|
+
|
|
14
|
+
const dodoEnvironment = DODO_PAYMENTS_ENVIRONMENT === "live_mode" ? "live_mode" : "test_mode";
|
|
15
|
+
|
|
16
|
+
// Checkout() returns { GET, POST }, not a handler. Export GET from a "static"
|
|
17
|
+
// handler - the GET of a "session"/"dynamic" handler returns 400.
|
|
18
|
+
const staticCheckout = Checkout({
|
|
19
|
+
bearerToken: DODO_PAYMENTS_API_KEY,
|
|
20
|
+
returnUrl: DODO_PAYMENTS_RETURN_URL,
|
|
21
|
+
environment: dodoEnvironment,
|
|
22
|
+
type: "static",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const sessionCheckout = Checkout({
|
|
26
|
+
bearerToken: DODO_PAYMENTS_API_KEY,
|
|
27
|
+
returnUrl: DODO_PAYMENTS_RETURN_URL,
|
|
28
|
+
environment: dodoEnvironment,
|
|
29
|
+
type: "session",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const GET = staticCheckout.GET;
|
|
33
|
+
export const POST = sessionCheckout.POST;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Webhooks
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// src/routes/api/webhook/+server.ts
|
|
40
|
+
import { Webhooks } from "@dodopayments/sveltekit";
|
|
41
|
+
import { DODO_PAYMENTS_WEBHOOK_KEY } from "$env/static/private";
|
|
42
|
+
|
|
43
|
+
export const POST = Webhooks({
|
|
44
|
+
webhookKey: DODO_PAYMENTS_WEBHOOK_KEY,
|
|
45
|
+
onPayload: async (payload) => {
|
|
46
|
+
console.log("Webhook:", payload.type);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Framework Adapters: TanStack Start
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Package:** `@dodopayments/tanstack`
|
|
5
|
+
|
|
6
|
+
### Checkout
|
|
7
|
+
|
|
8
|
+
`Checkout(config)` returns a plain `(request: Request) => Promise<Response>`, so export it directly as the route's method handler. The adapter's own documented usage is `export const GET = Checkout(config)`.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
// src/routes/api/checkout.ts
|
|
12
|
+
import { Checkout } from "@dodopayments/tanstack";
|
|
13
|
+
// src/lib/dodo-env.ts
|
|
14
|
+
import { dodoEnvironment } from "../../lib/dodo-env";
|
|
15
|
+
|
|
16
|
+
export const GET = Checkout({
|
|
17
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
18
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
19
|
+
environment: dodoEnvironment,
|
|
20
|
+
type: "static",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const POST = Checkout({
|
|
24
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
25
|
+
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
|
|
26
|
+
environment: dodoEnvironment,
|
|
27
|
+
type: "session",
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
TanStack Start's server-route definition API has changed across releases (`createServerFileRoute` was removed). Wrap these exports in whatever route helper your installed version provides; the adapter handlers themselves are unaffected.
|