@dodopayments/opencode-plugin 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/.mcp.json +22 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/opencode-plugin/README.md +42 -0
- package/opencode-plugin/index.js +30 -0
- package/opencode.json +28 -0
- package/package.json +53 -0
- package/skills/best-practices/SKILL.md +315 -0
- package/skills/billing-sdk/SKILL.md +501 -0
- package/skills/checkout-integration/SKILL.md +599 -0
- package/skills/credit-based-billing/SKILL.md +942 -0
- package/skills/license-keys/SKILL.md +591 -0
- package/skills/subscription-integration/SKILL.md +571 -0
- package/skills/usage-based-billing/SKILL.md +680 -0
- package/skills/webhook-integration/SKILL.md +529 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: checkout-integration
|
|
3
|
+
description: Guide for creating checkout sessions and payment flows with Dodo Payments - one-time, subscriptions, and overlay checkout.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Dodo Payments Checkout Integration
|
|
7
|
+
|
|
8
|
+
**Reference: [docs.dodopayments.com/developer-resources/integration-guide](https://docs.dodopayments.com/developer-resources/integration-guide)**
|
|
9
|
+
|
|
10
|
+
Create seamless payment experiences with hosted checkout pages or overlay checkout modals.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Checkout Methods
|
|
15
|
+
|
|
16
|
+
| Method | Best For | Integration |
|
|
17
|
+
|--------|----------|-------------|
|
|
18
|
+
| **Hosted Checkout** | Simple integration, full-page redirect | Server-side SDK |
|
|
19
|
+
| **Overlay Checkout** | Seamless UX, stays on your site | JavaScript SDK |
|
|
20
|
+
| **Payment Links** | No-code, shareable links | Dashboard |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Hosted Checkout
|
|
25
|
+
|
|
26
|
+
### Basic Implementation
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import DodoPayments from 'dodopayments';
|
|
30
|
+
|
|
31
|
+
const client = new DodoPayments({
|
|
32
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Create checkout session
|
|
36
|
+
const session = await client.checkoutSessions.create({
|
|
37
|
+
product_cart: [
|
|
38
|
+
{ product_id: 'prod_xxxxx', quantity: 1 }
|
|
39
|
+
],
|
|
40
|
+
customer: {
|
|
41
|
+
email: 'customer@example.com',
|
|
42
|
+
name: 'John Doe',
|
|
43
|
+
},
|
|
44
|
+
return_url: 'https://yoursite.com/checkout/success',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Redirect customer to checkout
|
|
48
|
+
// session.checkout_url
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### With Multiple Products
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const session = await client.checkoutSessions.create({
|
|
55
|
+
product_cart: [
|
|
56
|
+
{ product_id: 'prod_item_1', quantity: 2 },
|
|
57
|
+
{ product_id: 'prod_item_2', quantity: 1 },
|
|
58
|
+
],
|
|
59
|
+
customer: {
|
|
60
|
+
email: 'customer@example.com',
|
|
61
|
+
},
|
|
62
|
+
return_url: 'https://yoursite.com/success',
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### With Customer ID (Existing Customer)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const session = await client.checkoutSessions.create({
|
|
70
|
+
product_cart: [
|
|
71
|
+
{ product_id: 'prod_xxxxx', quantity: 1 }
|
|
72
|
+
],
|
|
73
|
+
customer_id: 'cust_existing_customer',
|
|
74
|
+
return_url: 'https://yoursite.com/success',
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### With Metadata
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const session = await client.checkoutSessions.create({
|
|
82
|
+
product_cart: [
|
|
83
|
+
{ product_id: 'prod_xxxxx', quantity: 1 }
|
|
84
|
+
],
|
|
85
|
+
customer: {
|
|
86
|
+
email: 'customer@example.com',
|
|
87
|
+
},
|
|
88
|
+
metadata: {
|
|
89
|
+
order_id: 'order_12345',
|
|
90
|
+
referral_code: 'FRIEND20',
|
|
91
|
+
user_id: 'internal_user_id',
|
|
92
|
+
},
|
|
93
|
+
return_url: 'https://yoursite.com/success',
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Next.js Implementation
|
|
100
|
+
|
|
101
|
+
### API Route
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// app/api/checkout/route.ts
|
|
105
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
106
|
+
import DodoPayments from 'dodopayments';
|
|
107
|
+
|
|
108
|
+
const client = new DodoPayments({
|
|
109
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export async function POST(req: NextRequest) {
|
|
113
|
+
try {
|
|
114
|
+
const { productId, quantity = 1, email, name, metadata } = await req.json();
|
|
115
|
+
|
|
116
|
+
if (!productId || !email) {
|
|
117
|
+
return NextResponse.json(
|
|
118
|
+
{ error: 'Missing required fields' },
|
|
119
|
+
{ status: 400 }
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const session = await client.checkoutSessions.create({
|
|
124
|
+
product_cart: [{ product_id: productId, quantity }],
|
|
125
|
+
customer: { email, name },
|
|
126
|
+
metadata,
|
|
127
|
+
return_url: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success`,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return NextResponse.json({
|
|
131
|
+
checkoutUrl: session.checkout_url,
|
|
132
|
+
sessionId: session.checkout_session_id,
|
|
133
|
+
});
|
|
134
|
+
} catch (error: any) {
|
|
135
|
+
console.error('Checkout error:', error);
|
|
136
|
+
return NextResponse.json(
|
|
137
|
+
{ error: error.message || 'Failed to create checkout' },
|
|
138
|
+
{ status: 500 }
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Client Component
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// components/CheckoutButton.tsx
|
|
148
|
+
'use client';
|
|
149
|
+
|
|
150
|
+
import { useState } from 'react';
|
|
151
|
+
|
|
152
|
+
interface CheckoutButtonProps {
|
|
153
|
+
productId: string;
|
|
154
|
+
email: string;
|
|
155
|
+
name?: string;
|
|
156
|
+
children: React.ReactNode;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function CheckoutButton({ productId, email, name, children }: CheckoutButtonProps) {
|
|
160
|
+
const [loading, setLoading] = useState(false);
|
|
161
|
+
|
|
162
|
+
const handleCheckout = async () => {
|
|
163
|
+
setLoading(true);
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const response = await fetch('/api/checkout', {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: { 'Content-Type': 'application/json' },
|
|
169
|
+
body: JSON.stringify({ productId, email, name }),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const data = await response.json();
|
|
173
|
+
|
|
174
|
+
if (data.checkoutUrl) {
|
|
175
|
+
window.location.href = data.checkoutUrl;
|
|
176
|
+
} else {
|
|
177
|
+
throw new Error(data.error || 'Failed to create checkout');
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('Checkout error:', error);
|
|
181
|
+
alert('Failed to start checkout. Please try again.');
|
|
182
|
+
} finally {
|
|
183
|
+
setLoading(false);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<button onClick={handleCheckout} disabled={loading}>
|
|
189
|
+
{loading ? 'Loading...' : children}
|
|
190
|
+
</button>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Success Page
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// app/checkout/success/page.tsx
|
|
199
|
+
import { Suspense } from 'react';
|
|
200
|
+
|
|
201
|
+
function SuccessContent() {
|
|
202
|
+
return (
|
|
203
|
+
<div className="text-center py-20">
|
|
204
|
+
<h1 className="text-3xl font-bold">Payment Successful!</h1>
|
|
205
|
+
<p className="mt-4 text-gray-600">
|
|
206
|
+
Thank you for your purchase. You will receive a confirmation email shortly.
|
|
207
|
+
</p>
|
|
208
|
+
<a href="/" className="mt-8 inline-block text-blue-600 hover:underline">
|
|
209
|
+
Return to Home
|
|
210
|
+
</a>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export default function SuccessPage() {
|
|
216
|
+
return (
|
|
217
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
218
|
+
<SuccessContent />
|
|
219
|
+
</Suspense>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Overlay Checkout
|
|
227
|
+
|
|
228
|
+
Embed checkout directly on your page without redirects.
|
|
229
|
+
|
|
230
|
+
### Installation
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
npm install @dodopayments/checkout
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Basic Usage
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { DodoCheckout } from '@dodopayments/checkout';
|
|
240
|
+
|
|
241
|
+
// Initialize
|
|
242
|
+
const checkout = new DodoCheckout({
|
|
243
|
+
apiKey: 'your_publishable_key',
|
|
244
|
+
environment: 'live', // or 'test'
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Open overlay
|
|
248
|
+
checkout.open({
|
|
249
|
+
productId: 'prod_xxxxx',
|
|
250
|
+
customer: {
|
|
251
|
+
email: 'customer@example.com',
|
|
252
|
+
},
|
|
253
|
+
onSuccess: (result) => {
|
|
254
|
+
console.log('Payment successful:', result);
|
|
255
|
+
// Handle success
|
|
256
|
+
},
|
|
257
|
+
onClose: () => {
|
|
258
|
+
console.log('Checkout closed');
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### React Component
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// components/OverlayCheckout.tsx
|
|
267
|
+
'use client';
|
|
268
|
+
|
|
269
|
+
import { useEffect, useRef } from 'react';
|
|
270
|
+
import { DodoCheckout } from '@dodopayments/checkout';
|
|
271
|
+
|
|
272
|
+
interface OverlayCheckoutProps {
|
|
273
|
+
productId: string;
|
|
274
|
+
email: string;
|
|
275
|
+
onSuccess?: (result: any) => void;
|
|
276
|
+
children: React.ReactNode;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function OverlayCheckout({
|
|
280
|
+
productId,
|
|
281
|
+
email,
|
|
282
|
+
onSuccess,
|
|
283
|
+
children
|
|
284
|
+
}: OverlayCheckoutProps) {
|
|
285
|
+
const checkoutRef = useRef<DodoCheckout | null>(null);
|
|
286
|
+
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
checkoutRef.current = new DodoCheckout({
|
|
289
|
+
apiKey: process.env.NEXT_PUBLIC_DODO_PUBLISHABLE_KEY!,
|
|
290
|
+
environment: process.env.NODE_ENV === 'production' ? 'live' : 'test',
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return () => {
|
|
294
|
+
checkoutRef.current?.close();
|
|
295
|
+
};
|
|
296
|
+
}, []);
|
|
297
|
+
|
|
298
|
+
const handleClick = () => {
|
|
299
|
+
checkoutRef.current?.open({
|
|
300
|
+
productId,
|
|
301
|
+
customer: { email },
|
|
302
|
+
onSuccess: (result) => {
|
|
303
|
+
onSuccess?.(result);
|
|
304
|
+
// Optionally redirect
|
|
305
|
+
window.location.href = '/checkout/success';
|
|
306
|
+
},
|
|
307
|
+
onClose: () => {
|
|
308
|
+
console.log('Checkout closed');
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<button onClick={handleClick}>
|
|
315
|
+
{children}
|
|
316
|
+
</button>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Customization
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
checkout.open({
|
|
325
|
+
productId: 'prod_xxxxx',
|
|
326
|
+
customer: { email: 'customer@example.com' },
|
|
327
|
+
theme: {
|
|
328
|
+
primaryColor: '#0066FF',
|
|
329
|
+
backgroundColor: '#FFFFFF',
|
|
330
|
+
fontFamily: 'Inter, sans-serif',
|
|
331
|
+
},
|
|
332
|
+
locale: 'en',
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Express.js Implementation
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import express from 'express';
|
|
342
|
+
import DodoPayments from 'dodopayments';
|
|
343
|
+
|
|
344
|
+
const app = express();
|
|
345
|
+
app.use(express.json());
|
|
346
|
+
|
|
347
|
+
const client = new DodoPayments({
|
|
348
|
+
bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
app.post('/api/create-checkout', async (req, res) => {
|
|
352
|
+
try {
|
|
353
|
+
const { productId, email, name, quantity = 1 } = req.body;
|
|
354
|
+
|
|
355
|
+
const session = await client.checkoutSessions.create({
|
|
356
|
+
product_cart: [{ product_id: productId, quantity }],
|
|
357
|
+
customer: { email, name },
|
|
358
|
+
return_url: `${process.env.APP_URL}/success`,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
res.json({ checkoutUrl: session.checkout_url });
|
|
362
|
+
} catch (error: any) {
|
|
363
|
+
res.status(500).json({ error: error.message });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Success page route
|
|
368
|
+
app.get('/success', (req, res) => {
|
|
369
|
+
res.send('Payment successful!');
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Python Implementation
|
|
376
|
+
|
|
377
|
+
### FastAPI
|
|
378
|
+
|
|
379
|
+
```python
|
|
380
|
+
from fastapi import FastAPI, HTTPException
|
|
381
|
+
from pydantic import BaseModel
|
|
382
|
+
from dodopayments import DodoPayments
|
|
383
|
+
import os
|
|
384
|
+
|
|
385
|
+
app = FastAPI()
|
|
386
|
+
client = DodoPayments(bearer_token=os.environ["DODO_PAYMENTS_API_KEY"])
|
|
387
|
+
|
|
388
|
+
class CheckoutRequest(BaseModel):
|
|
389
|
+
product_id: str
|
|
390
|
+
email: str
|
|
391
|
+
name: str = None
|
|
392
|
+
quantity: int = 1
|
|
393
|
+
|
|
394
|
+
@app.post("/api/checkout")
|
|
395
|
+
async def create_checkout(request: CheckoutRequest):
|
|
396
|
+
try:
|
|
397
|
+
session = client.checkout_sessions.create(
|
|
398
|
+
product_cart=[{
|
|
399
|
+
"product_id": request.product_id,
|
|
400
|
+
"quantity": request.quantity
|
|
401
|
+
}],
|
|
402
|
+
customer={
|
|
403
|
+
"email": request.email,
|
|
404
|
+
"name": request.name
|
|
405
|
+
},
|
|
406
|
+
return_url=f"{os.environ['APP_URL']}/success"
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
return {"checkout_url": session.checkout_url}
|
|
410
|
+
except Exception as e:
|
|
411
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Flask
|
|
415
|
+
|
|
416
|
+
```python
|
|
417
|
+
from flask import Flask, request, jsonify
|
|
418
|
+
from dodopayments import DodoPayments
|
|
419
|
+
import os
|
|
420
|
+
|
|
421
|
+
app = Flask(__name__)
|
|
422
|
+
client = DodoPayments(bearer_token=os.environ["DODO_PAYMENTS_API_KEY"])
|
|
423
|
+
|
|
424
|
+
@app.route('/api/checkout', methods=['POST'])
|
|
425
|
+
def create_checkout():
|
|
426
|
+
data = request.json
|
|
427
|
+
|
|
428
|
+
session = client.checkout_sessions.create(
|
|
429
|
+
product_cart=[{
|
|
430
|
+
"product_id": data['product_id'],
|
|
431
|
+
"quantity": data.get('quantity', 1)
|
|
432
|
+
}],
|
|
433
|
+
customer={
|
|
434
|
+
"email": data['email'],
|
|
435
|
+
"name": data.get('name')
|
|
436
|
+
},
|
|
437
|
+
return_url=f"{os.environ['APP_URL']}/success"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
return jsonify({"checkout_url": session.checkout_url})
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Go Implementation
|
|
446
|
+
|
|
447
|
+
```go
|
|
448
|
+
package main
|
|
449
|
+
|
|
450
|
+
import (
|
|
451
|
+
"encoding/json"
|
|
452
|
+
"net/http"
|
|
453
|
+
"os"
|
|
454
|
+
|
|
455
|
+
"github.com/dodopayments/dodopayments-go"
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
var client = dodopayments.NewClient(
|
|
459
|
+
option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
type CheckoutRequest struct {
|
|
463
|
+
ProductID string `json:"product_id"`
|
|
464
|
+
Email string `json:"email"`
|
|
465
|
+
Name string `json:"name"`
|
|
466
|
+
Quantity int `json:"quantity"`
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
func createCheckout(w http.ResponseWriter, r *http.Request) {
|
|
470
|
+
var req CheckoutRequest
|
|
471
|
+
json.NewDecoder(r.Body).Decode(&req)
|
|
472
|
+
|
|
473
|
+
if req.Quantity == 0 {
|
|
474
|
+
req.Quantity = 1
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
session, err := client.CheckoutSessions.Create(r.Context(), &dodopayments.CheckoutSessionCreateParams{
|
|
478
|
+
ProductCart: []dodopayments.CartItem{
|
|
479
|
+
{ProductID: req.ProductID, Quantity: req.Quantity},
|
|
480
|
+
},
|
|
481
|
+
Customer: &dodopayments.Customer{
|
|
482
|
+
Email: req.Email,
|
|
483
|
+
Name: req.Name,
|
|
484
|
+
},
|
|
485
|
+
ReturnURL: os.Getenv("APP_URL") + "/success",
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
if err != nil {
|
|
489
|
+
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
490
|
+
return
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
json.NewEncoder(w).Encode(map[string]string{
|
|
494
|
+
"checkout_url": session.CheckoutURL,
|
|
495
|
+
})
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Handling Success
|
|
502
|
+
|
|
503
|
+
### Query Parameters
|
|
504
|
+
The return URL receives these query parameters:
|
|
505
|
+
- `status=success` - Payment completed
|
|
506
|
+
- `session_id` - Checkout session ID
|
|
507
|
+
|
|
508
|
+
### Verify Payment Server-Side
|
|
509
|
+
Don't rely solely on the redirect. Always verify via webhook:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
// Webhook handler confirms payment
|
|
513
|
+
app.post('/webhook', async (req, res) => {
|
|
514
|
+
const event = req.body;
|
|
515
|
+
|
|
516
|
+
if (event.type === 'payment.succeeded') {
|
|
517
|
+
// This is the source of truth
|
|
518
|
+
await fulfillOrder(event.data);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
res.json({ received: true });
|
|
522
|
+
});
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Advanced Options
|
|
528
|
+
|
|
529
|
+
### Prefill Customer Info
|
|
530
|
+
```typescript
|
|
531
|
+
const session = await client.checkoutSessions.create({
|
|
532
|
+
product_cart: [{ product_id: 'prod_xxxxx', quantity: 1 }],
|
|
533
|
+
customer: {
|
|
534
|
+
email: 'customer@example.com',
|
|
535
|
+
name: 'John Doe',
|
|
536
|
+
phone: '+1234567890',
|
|
537
|
+
address: {
|
|
538
|
+
line1: '123 Main St',
|
|
539
|
+
city: 'San Francisco',
|
|
540
|
+
state: 'CA',
|
|
541
|
+
postal_code: '94105',
|
|
542
|
+
country: 'US',
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
return_url: 'https://yoursite.com/success',
|
|
546
|
+
});
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Custom Success/Cancel URLs
|
|
550
|
+
```typescript
|
|
551
|
+
const session = await client.checkoutSessions.create({
|
|
552
|
+
product_cart: [{ product_id: 'prod_xxxxx', quantity: 1 }],
|
|
553
|
+
customer: { email: 'customer@example.com' },
|
|
554
|
+
return_url: 'https://yoursite.com/checkout/success?session_id={CHECKOUT_SESSION_ID}',
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Subscription with Trial
|
|
559
|
+
```typescript
|
|
560
|
+
const session = await client.checkoutSessions.create({
|
|
561
|
+
product_cart: [{ product_id: 'prod_subscription', quantity: 1 }],
|
|
562
|
+
subscription_data: {
|
|
563
|
+
trial_period_days: 14,
|
|
564
|
+
},
|
|
565
|
+
customer: { email: 'customer@example.com' },
|
|
566
|
+
return_url: 'https://yoursite.com/success',
|
|
567
|
+
});
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Error Handling
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
try {
|
|
576
|
+
const session = await client.checkoutSessions.create({...});
|
|
577
|
+
} catch (error: any) {
|
|
578
|
+
if (error.status === 400) {
|
|
579
|
+
// Invalid parameters
|
|
580
|
+
console.error('Invalid request:', error.message);
|
|
581
|
+
} else if (error.status === 401) {
|
|
582
|
+
// Invalid API key
|
|
583
|
+
console.error('Authentication failed');
|
|
584
|
+
} else if (error.status === 404) {
|
|
585
|
+
// Product not found
|
|
586
|
+
console.error('Product not found');
|
|
587
|
+
} else {
|
|
588
|
+
console.error('Checkout error:', error);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## Resources
|
|
596
|
+
|
|
597
|
+
- [Integration Guide](https://docs.dodopayments.com/developer-resources/integration-guide)
|
|
598
|
+
- [Overlay Checkout](https://docs.dodopayments.com/developer-resources/overlay-checkout)
|
|
599
|
+
- [API Reference](https://docs.dodopayments.com/api-reference/checkout-sessions)
|