@dodopayments/convex 0.1.0 → 0.2.0
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 +123 -58
- package/dist/client/index.d.ts +0 -1
- package/dist/index.cjs +174 -148
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +18 -3
- package/dist/index.js +174 -148
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Dodo Payments Convex Component
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/js/@dodopayments%2Fconvex.svg)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This component is the official way to integrate the Dodo Payments in your Convex project.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
- Checkout Session: Integrate Dodo Payments Checkout with a couple of lines code.
|
|
10
|
+
|
|
11
|
+
- Customer Portal: Allow customers to manage subscriptions and details
|
|
12
|
+
|
|
13
|
+
- Webhooks: Handle webhooks efficiently. Just write your business logic and handle the events you want. Webhook verification is taken care by us
|
|
8
14
|
|
|
9
15
|
Detailed documentation can be found at [Dodo Payments Convex Component](https://docs.dodopayments.com/developer-resources/convex-component)
|
|
10
16
|
|
|
@@ -16,6 +22,7 @@ npm install @dodopayments/convex
|
|
|
16
22
|
|
|
17
23
|
## Quick Start
|
|
18
24
|
|
|
25
|
+
|
|
19
26
|
### 1. Add Component to Convex Config
|
|
20
27
|
|
|
21
28
|
```typescript
|
|
@@ -30,8 +37,15 @@ export default app;
|
|
|
30
37
|
|
|
31
38
|
### 2. Set Up Environment Variables
|
|
32
39
|
|
|
40
|
+
Create a [Dodo Payments](https://dodopayments.com) account and get the API_KEY and WEBHOOK_SECRET
|
|
41
|
+
|
|
33
42
|
Add your environment variables in the Convex dashboard (**Settings** → **Environment Variables**). You can open the dashboard by running:
|
|
34
43
|
|
|
44
|
+
```env
|
|
45
|
+
DODO_PAYMENTS_API_KEY=your-dodo-payments-api-key
|
|
46
|
+
DODO_PAYMENTS_ENVIRONMENT=test_mode
|
|
47
|
+
DODO_PAYMENTS_WEBHOOK_SECRET=your-webhook-secret (if using webhooks)
|
|
48
|
+
```
|
|
35
49
|
```sh
|
|
36
50
|
npx convex dashboard
|
|
37
51
|
```
|
|
@@ -44,37 +58,57 @@ Set the following variables:
|
|
|
44
58
|
|
|
45
59
|
> **Note:** Convex backend functions only read environment variables set in the Convex dashboard. `.env` files are ignored. Always set secrets in the Convex dashboard for both production and development.
|
|
46
60
|
|
|
47
|
-
### 3. Create
|
|
61
|
+
### 3. Create Internal Query
|
|
62
|
+
|
|
63
|
+
First, create an internal query to fetch users from your database. This will be used in the payment functions to identify customers.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// convex/users.ts
|
|
67
|
+
import { internalQuery } from "./_generated/server";
|
|
68
|
+
import { v } from "convex/values";
|
|
69
|
+
|
|
70
|
+
// Internal query to fetch user by auth ID
|
|
71
|
+
export const getByAuthId = internalQuery({
|
|
72
|
+
args: { authId: v.string() },
|
|
73
|
+
handler: async (ctx, { authId }) => {
|
|
74
|
+
return await ctx.db
|
|
75
|
+
.query("users")
|
|
76
|
+
.withIndex("by_auth_id", (q) => q.eq("authId", authId))
|
|
77
|
+
.first();
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. Create Payment Functions
|
|
48
83
|
|
|
49
84
|
```typescript
|
|
50
85
|
// convex/dodo.ts
|
|
51
86
|
import { DodoPayments } from "@dodopayments/convex";
|
|
52
87
|
import { components } from "./_generated/api";
|
|
88
|
+
import { internal } from "./_generated/api";
|
|
53
89
|
|
|
54
90
|
export const dodo = new DodoPayments(components.dodopayments, {
|
|
55
91
|
// This function maps your Convex user to a Dodo Payments customer
|
|
56
|
-
// Customize it based on your authentication provider and
|
|
92
|
+
// Customize it based on your authentication provider and user database
|
|
93
|
+
|
|
57
94
|
identify: async (ctx) => {
|
|
58
95
|
const identity = await ctx.auth.getUserIdentity();
|
|
59
96
|
if (!identity) {
|
|
60
97
|
return null; // User is not logged in
|
|
61
98
|
}
|
|
62
99
|
|
|
63
|
-
//
|
|
64
|
-
const user = await ctx.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
email: user.email,
|
|
76
|
-
},
|
|
77
|
-
};
|
|
100
|
+
// Use ctx.runQuery() to lookup user from your database
|
|
101
|
+
const user = await ctx.runQuery(internal.users.getByAuthId, {
|
|
102
|
+
authId: identity.subject,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!user) {
|
|
106
|
+
return null; // User not found in database
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
dodoCustomerId: user.dodoCustomerId, // Replace user.dodoCustomerId with your field storing Dodo Payments customer ID
|
|
111
|
+
};
|
|
78
112
|
},
|
|
79
113
|
apiKey: process.env.DODO_PAYMENTS_API_KEY!,
|
|
80
114
|
environment: process.env.DODO_PAYMENTS_ENVIRONMENT as "test_mode" | "live_mode",
|
|
@@ -84,7 +118,7 @@ export const dodo = new DodoPayments(components.dodopayments, {
|
|
|
84
118
|
export const { checkout, customerPortal } = dodo.api();
|
|
85
119
|
```
|
|
86
120
|
|
|
87
|
-
###
|
|
121
|
+
### 5. Set Up Webhooks (Optional)
|
|
88
122
|
|
|
89
123
|
For handling Dodo Payments webhooks, create a file `convex/http.ts`:
|
|
90
124
|
|
|
@@ -92,6 +126,7 @@ For handling Dodo Payments webhooks, create a file `convex/http.ts`:
|
|
|
92
126
|
// convex/http.ts
|
|
93
127
|
import { httpRouter } from "convex/server";
|
|
94
128
|
import { createDodoWebhookHandler } from "@dodopayments/convex";
|
|
129
|
+
import { internal } from "./_generated/api";
|
|
95
130
|
|
|
96
131
|
const http = httpRouter();
|
|
97
132
|
|
|
@@ -99,13 +134,24 @@ http.route({
|
|
|
99
134
|
path: "/dodopayments-webhook",
|
|
100
135
|
method: "POST",
|
|
101
136
|
handler: createDodoWebhookHandler({
|
|
102
|
-
onPaymentSucceeded: async (payload) => {
|
|
103
|
-
console.log("Payment succeeded:", payload.data.payment_id);
|
|
104
|
-
//
|
|
137
|
+
onPaymentSucceeded: async (ctx, payload) => {
|
|
138
|
+
console.log("Payment succeeded:", payload.data.payment_id);
|
|
139
|
+
// Update order status in your database
|
|
140
|
+
await ctx.runMutation(internal.orders.markAsPaid, {
|
|
141
|
+
orderId: payload.data.metadata.orderId,
|
|
142
|
+
paymentId: payload.data.payment_id,
|
|
143
|
+
amount: payload.data.amount,
|
|
144
|
+
});
|
|
105
145
|
},
|
|
106
|
-
|
|
146
|
+
|
|
147
|
+
onSubscriptionActive: async (ctx, payload) => {
|
|
107
148
|
console.log("Subscription activated:", payload.data.subscription_id);
|
|
108
|
-
//
|
|
149
|
+
// Create or update subscription record in your database
|
|
150
|
+
await ctx.runMutation(internal.subscriptions.createOrUpdate, {
|
|
151
|
+
subscriptionId: payload.data.subscription_id,
|
|
152
|
+
customerId: payload.data.customer_id,
|
|
153
|
+
status: "active",
|
|
154
|
+
});
|
|
109
155
|
},
|
|
110
156
|
// Add other event handlers as needed
|
|
111
157
|
}),
|
|
@@ -114,11 +160,13 @@ http.route({
|
|
|
114
160
|
export default http;
|
|
115
161
|
```
|
|
116
162
|
|
|
163
|
+
**Important:** All webhook handlers receive the Convex `ActionCtx` as the first parameter, allowing you to use `ctx.runQuery()` and `ctx.runMutation()` to interact with your database.
|
|
164
|
+
|
|
117
165
|
Add your webhook secret in the Convex dashboard (**Settings** → **Environment Variables**):
|
|
118
166
|
|
|
119
167
|
- `DODO_PAYMENTS_WEBHOOK_SECRET=your-webhook-secret`
|
|
120
168
|
|
|
121
|
-
###
|
|
169
|
+
### 6. Define Payment Actions
|
|
122
170
|
|
|
123
171
|
```typescript
|
|
124
172
|
// convex/payments.ts
|
|
@@ -149,7 +197,7 @@ export const createCheckout = action({
|
|
|
149
197
|
});
|
|
150
198
|
```
|
|
151
199
|
|
|
152
|
-
###
|
|
200
|
+
### 7. Frontend Usage
|
|
153
201
|
|
|
154
202
|
```tsx
|
|
155
203
|
import { useAction } from "convex/react";
|
|
@@ -220,44 +268,51 @@ Then add the required environment variables (e.g., DODO_PAYMENTS_API_KEY, DODO_P
|
|
|
220
268
|
DODO_PAYMENTS_API_KEY=your-api-key
|
|
221
269
|
DODO_PAYMENTS_ENVIRONMENT=test_mode
|
|
222
270
|
|
|
223
|
-
Step 3: Create
|
|
271
|
+
Step 3: Create an internal query to fetch users from your database.
|
|
272
|
+
|
|
273
|
+
// convex/users.ts
|
|
274
|
+
import { internalQuery } from "./_generated/server";
|
|
275
|
+
import { v } from "convex/values";
|
|
276
|
+
|
|
277
|
+
// Internal query to fetch user by auth ID
|
|
278
|
+
export const getByAuthId = internalQuery({
|
|
279
|
+
args: { authId: v.string() },
|
|
280
|
+
handler: async (ctx, { authId }) => {
|
|
281
|
+
return await ctx.db
|
|
282
|
+
.query("users")
|
|
283
|
+
.withIndex("by_auth_id", (q) => q.eq("authId", authId))
|
|
284
|
+
.first();
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
Step 4: Create your payment functions file.
|
|
224
289
|
|
|
225
290
|
// convex/dodo.ts
|
|
226
291
|
import { DodoPayments } from "@dodopayments/convex";
|
|
227
292
|
import { components } from "./_generated/api";
|
|
293
|
+
import { internal } from "./_generated/api";
|
|
228
294
|
|
|
229
295
|
export const dodo = new DodoPayments(components.dodopayments, {
|
|
230
296
|
// This function maps your Convex user to a Dodo Payments customer
|
|
231
|
-
// Customize it based on your authentication provider
|
|
297
|
+
// Customize it based on your authentication provider and user database
|
|
298
|
+
|
|
232
299
|
identify: async (ctx) => {
|
|
233
300
|
const identity = await ctx.auth.getUserIdentity();
|
|
234
301
|
if (!identity) {
|
|
235
302
|
return null; // User is not logged in
|
|
236
303
|
}
|
|
237
304
|
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
name: identity.name,
|
|
243
|
-
email: identity.email,
|
|
244
|
-
},
|
|
245
|
-
};
|
|
305
|
+
// Use ctx.runQuery() to lookup user from your database
|
|
306
|
+
const user = await ctx.runQuery(internal.users.getByAuthId, {
|
|
307
|
+
authId: identity.subject,
|
|
308
|
+
});
|
|
246
309
|
|
|
247
|
-
// Lookup user from your database
|
|
248
|
-
const user = await ctx.db.query("users")
|
|
249
|
-
.withIndex("by_auth_id", (q) => q.eq("authId", identity.subject))
|
|
250
|
-
.first();
|
|
251
310
|
if (!user) {
|
|
252
311
|
return null; // User not found in database
|
|
253
312
|
}
|
|
254
|
-
|
|
313
|
+
|
|
255
314
|
return {
|
|
256
|
-
dodoCustomerId: user.dodoCustomerId, //
|
|
257
|
-
customerData: {
|
|
258
|
-
name: user.name,
|
|
259
|
-
email: user.email,
|
|
260
|
-
},
|
|
315
|
+
dodoCustomerId: user.dodoCustomerId, // Replace user.dodoCustomerId with your field storing Dodo Payments customer ID
|
|
261
316
|
};
|
|
262
317
|
},
|
|
263
318
|
apiKey: process.env.DODO_PAYMENTS_API_KEY!,
|
|
@@ -267,7 +322,7 @@ export const dodo = new DodoPayments(components.dodopayments, {
|
|
|
267
322
|
// Export the API methods for use in your app
|
|
268
323
|
export const { checkout, customerPortal } = dodo.api();
|
|
269
324
|
|
|
270
|
-
Step
|
|
325
|
+
Step 5: Create actions that use the checkout function.
|
|
271
326
|
|
|
272
327
|
// convex/payments.ts
|
|
273
328
|
import { action } from "./_generated/server";
|
|
@@ -297,7 +352,7 @@ export const createCheckout = action({
|
|
|
297
352
|
},
|
|
298
353
|
});
|
|
299
354
|
|
|
300
|
-
Step
|
|
355
|
+
Step 6: Use in your frontend.
|
|
301
356
|
|
|
302
357
|
// Your frontend component
|
|
303
358
|
import { useAction } from "convex/react";
|
|
@@ -332,9 +387,9 @@ Purpose: This function allows customers to manage their subscriptions and paymen
|
|
|
332
387
|
|
|
333
388
|
Integration Steps:
|
|
334
389
|
|
|
335
|
-
Follow Steps 1-
|
|
390
|
+
Follow Steps 1-4 from the Checkout Function section, then:
|
|
336
391
|
|
|
337
|
-
Step
|
|
392
|
+
Step 5: Create a customer portal action.
|
|
338
393
|
|
|
339
394
|
// convex/payments.ts (add to existing file)
|
|
340
395
|
import { action } from "./_generated/server";
|
|
@@ -350,7 +405,7 @@ export const getCustomerPortal = action({
|
|
|
350
405
|
},
|
|
351
406
|
});
|
|
352
407
|
|
|
353
|
-
Step
|
|
408
|
+
Step 6: Use in your frontend.
|
|
354
409
|
|
|
355
410
|
// Your frontend component
|
|
356
411
|
import { useAction } from "convex/react";
|
|
@@ -394,6 +449,7 @@ Step 2: Create a file `convex/http.ts`:
|
|
|
394
449
|
// convex/http.ts
|
|
395
450
|
import { httpRouter } from "convex/server";
|
|
396
451
|
import { createDodoWebhookHandler } from "@dodopayments/convex";
|
|
452
|
+
import { internal } from "./_generated/api";
|
|
397
453
|
|
|
398
454
|
const http = httpRouter();
|
|
399
455
|
|
|
@@ -401,13 +457,22 @@ http.route({
|
|
|
401
457
|
path: "/dodopayments-webhook",
|
|
402
458
|
method: "POST",
|
|
403
459
|
handler: createDodoWebhookHandler({
|
|
404
|
-
onPaymentSucceeded: async (payload) => {
|
|
460
|
+
onPaymentSucceeded: async (ctx, payload) => {
|
|
405
461
|
console.log("Payment succeeded:", payload.data.payment_id);
|
|
406
|
-
//
|
|
462
|
+
// Update order status in your database
|
|
463
|
+
await ctx.runMutation(internal.orders.markAsPaid, {
|
|
464
|
+
orderId: payload.data.metadata.orderId,
|
|
465
|
+
paymentId: payload.data.payment_id,
|
|
466
|
+
});
|
|
407
467
|
},
|
|
408
|
-
|
|
468
|
+
|
|
469
|
+
onSubscriptionActive: async (ctx, payload) => {
|
|
409
470
|
console.log("Subscription activated:", payload.data.subscription_id);
|
|
410
|
-
//
|
|
471
|
+
// Use ctx to create or update subscription records
|
|
472
|
+
await ctx.runMutation(internal.subscriptions.createOrUpdate, {
|
|
473
|
+
subscriptionId: payload.data.subscription_id,
|
|
474
|
+
customerId: payload.data.customer_id,
|
|
475
|
+
});
|
|
411
476
|
},
|
|
412
477
|
// Add other event handlers as needed
|
|
413
478
|
}),
|
package/dist/client/index.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ export interface DodoPaymentsComponent {
|
|
|
12
12
|
export type DodoPaymentsClientConfig = {
|
|
13
13
|
identify: (ctx: GenericActionCtx<any>) => Promise<{
|
|
14
14
|
dodoCustomerId: string;
|
|
15
|
-
customerData?: any;
|
|
16
15
|
} | null>;
|
|
17
16
|
apiKey: string;
|
|
18
17
|
environment: "test_mode" | "live_mode";
|