@dodopayments/opencode-plugin 0.3.3 → 0.5.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.
@@ -0,0 +1,335 @@
1
+ ---
2
+ name: testing-and-go-live
3
+ description: Guide for test-mode payment scenarios, renewal simulation, local webhook delivery tests, test and live catalog migration, and production launch checks.
4
+ ---
5
+
6
+ # Testing and Go-Live
7
+
8
+ This skill covers test mode, test payment methods, webhook testing, and the production launch checklist. Use it when building payment flows that need verification before going live, or when preparing to switch from test to production.
9
+
10
+ ## When to use this skill
11
+
12
+ - You need to test card success/decline scenarios, subscription renewals, or regional payment methods.
13
+ - You're setting up webhook testing locally or in a staging environment.
14
+ - You're preparing to launch to production and need a go-live checklist.
15
+ - You want to verify that test and live environments are properly isolated.
16
+ - You're copying products from test to production.
17
+
18
+ ## Core concepts
19
+
20
+ **Test vs. live mode:** Dodo Payments maintains two completely separate environments. Each has its own base URL, API keys, webhooks, products, customers, and transactions. Data does not carry over between them.
21
+
22
+ - **Test mode:** `https://test.dodopayments.com`
23
+ - **Live mode:** `https://live.dodopayments.com`
24
+
25
+ Each environment requires separate API keys issued from the dashboard. The SDK defaults to `live_mode` if you don't specify `environment`, which is a real footgun in development.
26
+
27
+ **Test payment methods:** Dodo documents specific card numbers, VPAs, and payment method scenarios for testing. These only work in test mode.
28
+
29
+ **Webhook testing:** The CLI provides commands to listen for webhooks locally and trigger test events without making real payments.
30
+
31
+ ---
32
+
33
+ ## Setting up test mode
34
+
35
+ Always explicitly set `environment: 'test_mode'` during development. The default is `live_mode`.
36
+
37
+ ```typescript
38
+ import DodoPayments from 'dodopayments';
39
+
40
+ const client = new DodoPayments({
41
+ bearerToken: process.env.DODO_PAYMENTS_API_KEY,
42
+ environment: 'test_mode', // REQUIRED — defaults to 'live_mode'
43
+ });
44
+ ```
45
+
46
+ ```python
47
+ from dodopayments import DodoPayments
48
+
49
+ client = DodoPayments(
50
+ bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
51
+ environment="test_mode", # defaults to "live_mode"
52
+ )
53
+ ```
54
+
55
+ ```go
56
+ client := dodopayments.NewClient(
57
+ option.WithBearerToken("dodo_test_..."),
58
+ option.WithEnvironmentTestMode(), // defaults to WithEnvironmentLiveMode()
59
+ )
60
+ ```
61
+
62
+ Get your test API key from the Dodo dashboard. Test keys are prefixed `dodo_test_`.
63
+
64
+ ---
65
+
66
+ ## Test payment methods
67
+
68
+ ### Card success and decline scenarios
69
+
70
+ Use these card numbers in test mode. The expiry can be any future date; the CVC can be any three digits.
71
+
72
+ | Scenario | Card Number | Result |
73
+ |---|---|---|
74
+ | Success | `4242 4242 4242 4242` | Payment succeeds |
75
+ | Decline | `4000 0000 0000 0002` | Payment declined |
76
+ | Decline (insufficient funds) | `4000 0000 0000 9995` | Insufficient funds error |
77
+ | Decline (lost card) | `4000 0000 0000 9987` | Lost card error |
78
+
79
+ ### Subscription renewal failure
80
+
81
+ To test subscription renewal failures, use this card:
82
+
83
+ | Card Number | Behavior |
84
+ |---|---|
85
+ | `4000 0000 0000 0069` | Renewal fails on next billing date |
86
+
87
+ ### UPI (India)
88
+
89
+ Test UPI success and failure with these VPAs:
90
+
91
+ | VPA | Result |
92
+ |---|---|
93
+ | `success@okhdfcbank` | Payment succeeds |
94
+ | `failure@okhdfcbank` | Payment fails |
95
+
96
+ ### BNPL, wallets, and regional methods
97
+
98
+ BNPL, Apple Pay, Google Pay, and European payment methods (Giropay, iDEAL, Bancontact) are available in test mode. Exact test scenarios vary by method. Refer to the [Testing Process](https://docs.dodopayments.com/miscellaneous/testing-process) documentation for the current test matrix.
99
+
100
+ ---
101
+
102
+ ## Testing subscription renewals
103
+
104
+ To force a subscription to renew on a specific date without waiting, update the `next_billing_date`:
105
+
106
+ ```typescript
107
+ await client.subscriptions.update('sub_123', {
108
+ next_billing_date: '2026-05-03T00:00:00Z',
109
+ });
110
+ ```
111
+
112
+ ```python
113
+ client.subscriptions.update(
114
+ 'sub_123',
115
+ next_billing_date='2026-05-03T00:00:00Z'
116
+ )
117
+ ```
118
+
119
+ ```go
120
+ // NextBillingDate is param.Field[time.Time], not a string. Passing a string
121
+ // literal compiles to param.Field[string] and will not type-check.
122
+ client.Subscriptions.Update(ctx, "sub_123", dodopayments.SubscriptionUpdateParams{
123
+ NextBillingDate: dodopayments.F(time.Date(2026, time.May, 3, 0, 0, 0, 0, time.UTC)),
124
+ })
125
+ ```
126
+
127
+ This immediately schedules the renewal. If you used the subscription-renewal-failure card, the failed charge emits `payment.failed` and the affected subscription enters `on_hold`, which emits `subscription.on_hold`.
128
+
129
+ ---
130
+
131
+ ## Webhook testing
132
+
133
+ The CLI ships separately from the SDKs:
134
+
135
+ ```bash
136
+ npm install -g dodopayments-cli
137
+ dodo login
138
+ ```
139
+
140
+ `dodo login` opens the API Keys page, accepts a pasted key, and asks whether it is Test Mode or Live Mode. One key of each can stay authenticated at the same time.
141
+
142
+ ### Listen for webhooks locally
143
+
144
+ ```bash
145
+ dodo wh listen http://localhost:3000/webhook
146
+ ```
147
+
148
+ This forwards real test-mode events to your local URL over an outbound relay. It is **not** a tunnel: nothing listens publicly and no URL is printed for you to paste into the dashboard. If you need a registerable HTTPS URL, use ngrok instead — that flow is described in the `webhook-integration` skill.
149
+
150
+ Two constraints to know before running it:
151
+
152
+ - **A Test Mode key is required.** Live Mode keys are not supported by the listen flow.
153
+ - **The URL argument is required in direct mode.** Bare `dodo wh listen` only works inside the interactive TUI as `/wh listen`, which opens a wizard.
154
+
155
+ Events arriving this way carry valid signatures, so verify them normally with `webhooks.unwrap()`.
156
+
157
+ ### Trigger mock webhook events
158
+
159
+ ```bash
160
+ dodo wh trigger payment.success http://localhost:3000/webhook
161
+ ```
162
+
163
+ Sends a realistic mock payload for a chosen event. It runs offline and works even while logged out — which is the tell for the part that matters: **these payloads are unsigned.** `unwrap()` rejects them because there is no valid signature to verify.
164
+
165
+ Use `unsafeUnwrap()` for triggered events only, and never on an endpoint that also receives real traffic. Event names are `<category>.<event>`, for example `payment.success`, `subscription.active`, or `dispute.opened`.
166
+
167
+ As with `listen`, both arguments are required in direct mode; `/wh trigger` inside the TUI opens a wizard instead.
168
+
169
+ ### Webhook signature verification
170
+
171
+ Covered in the `webhook-integration` skill. Verify every webhook that arrives over the network, in test mode as well as live. The single exception is the unsigned payloads produced by `dodo wh trigger` above.
172
+
173
+ ---
174
+
175
+ ## Copying products from test to live
176
+
177
+ Products are environment-specific. You cannot reuse a test product ID in production. Instead, recreate the product in live mode or use the dashboard to export and reimport.
178
+
179
+ **Manual approach:**
180
+
181
+ 1. Note the product details (name, price, tax category, etc.) from test mode.
182
+ 2. Create a new product in live mode with the same configuration.
183
+ 3. Update your code to use the new live product ID.
184
+
185
+ **Programmatic approach:**
186
+
187
+ Fetch the product from test mode, extract its configuration, and create it in live mode:
188
+
189
+ ```typescript
190
+ // In test mode
191
+ const testClient = new DodoPayments({
192
+ bearerToken: process.env.DODO_PAYMENTS_API_KEY,
193
+ environment: 'test_mode',
194
+ });
195
+ const testProduct = await testClient.products.retrieve('pdt_test_123');
196
+
197
+ // In live mode
198
+ const liveClient = new DodoPayments({
199
+ bearerToken: process.env.DODO_PAYMENTS_API_KEY,
200
+ environment: 'live_mode',
201
+ });
202
+ const liveProduct = await liveClient.products.create({
203
+ name: testProduct.name,
204
+ price: testProduct.price,
205
+ tax_category: testProduct.tax_category,
206
+ // ... other fields
207
+ });
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Go-live checklist
213
+
214
+ Before switching to production, verify each item:
215
+
216
+ ### Business and compliance
217
+
218
+ - [ ] KYC (Know Your Customer) verification is complete in the Dodo dashboard.
219
+ - [ ] Business verification is approved.
220
+ - [ ] Your business details are correct in the dashboard.
221
+
222
+ ### API and keys
223
+
224
+ - [ ] You have live API keys from the dashboard (prefixed `dodo_live_`).
225
+ - [ ] You've rotated or revoked any test keys that were accidentally committed.
226
+ - [ ] Your code uses `environment: 'live_mode'` (or omits it, since live is the default).
227
+
228
+ ### Webhooks
229
+
230
+ - [ ] Your webhook endpoint is publicly accessible over HTTPS.
231
+ - [ ] You've updated the webhook URL in the dashboard to your production domain.
232
+ - [ ] You've updated the webhook secret in your environment variables.
233
+ - [ ] Your webhook handler verifies signatures using `client.webhooks.unwrap()` or the `standardwebhooks` package.
234
+ - [ ] You've tested the webhook flow end-to-end in production (or a staging environment with live keys).
235
+
236
+ ### Products and checkout
237
+
238
+ - [ ] All products are created in live mode (not copied from test).
239
+ - [ ] Your checkout code uses live product IDs.
240
+ - [ ] Return URLs point to your production domain.
241
+ - [ ] Tax categories are correct for your products.
242
+
243
+ ### Monitoring and support
244
+
245
+ - [ ] You have monitoring in place for payment failures, webhook delivery, and API errors.
246
+ - [ ] You know how to access the Dodo dashboard to view transactions and disputes.
247
+ - [ ] You have a plan for handling refunds and chargebacks.
248
+
249
+ ---
250
+
251
+ ## Common mistakes
252
+
253
+ ### Leaving `environment` unset
254
+
255
+ The SDK defaults to `live_mode`. If you forget to set `environment: 'test_mode'` during development, your test code will hit production and create real charges.
256
+
257
+ ```typescript
258
+ // WRONG — this hits live mode
259
+ const client = new DodoPayments({
260
+ bearerToken: process.env.DODO_PAYMENTS_API_KEY,
261
+ });
262
+
263
+ // CORRECT
264
+ const client = new DodoPayments({
265
+ bearerToken: process.env.DODO_PAYMENTS_API_KEY,
266
+ environment: 'test_mode',
267
+ });
268
+ ```
269
+
270
+ ### Reusing test product IDs in production
271
+
272
+ Test and live environments are completely separate. A product ID from test mode does not exist in live mode. Always create new products in live mode or use the dashboard to migrate them.
273
+
274
+ ```typescript
275
+ // WRONG — pdt_test_123 does not exist in live mode
276
+ const session = await liveClient.checkoutSessions.create({
277
+ product_cart: [{ product_id: 'pdt_test_123', quantity: 1 }],
278
+ });
279
+
280
+ // CORRECT — use a live product ID
281
+ const session = await liveClient.checkoutSessions.create({
282
+ product_cart: [{ product_id: 'pdt_live_456', quantity: 1 }],
283
+ });
284
+ ```
285
+
286
+ ### Forgetting to reconfigure webhooks for production
287
+
288
+ Test and live modes have separate webhook endpoints and secrets. If you copy your test webhook configuration to production without updating the URL and secret, webhooks will fail or go to the wrong place.
289
+
290
+ - [ ] Update `DODO_PAYMENTS_WEBHOOK_KEY` in production environment variables.
291
+ - [ ] Update the webhook URL in the dashboard to your production domain.
292
+ - [ ] Test webhook delivery in production.
293
+
294
+ ### Not verifying webhook signatures in production
295
+
296
+ Always verify webhook signatures using `client.webhooks.unwrap()` or the `standardwebhooks` package. Never trust a webhook without verification, even if it came from Dodo.
297
+
298
+ ```typescript
299
+ // WRONG — no verification
300
+ app.post('/webhook', (req, res) => {
301
+ const event = req.body;
302
+ handleEvent(event);
303
+ res.json({ received: true });
304
+ });
305
+
306
+ // CORRECT
307
+ app.post('/webhook', async (req, res) => {
308
+ try {
309
+ const unwrapped = client.webhooks.unwrap(req.body.toString(), {
310
+ headers: {
311
+ 'webhook-id': req.headers['webhook-id'] as string,
312
+ 'webhook-signature': req.headers['webhook-signature'] as string,
313
+ 'webhook-timestamp': req.headers['webhook-timestamp'] as string,
314
+ },
315
+ });
316
+ handleEvent(unwrapped);
317
+ res.json({ received: true });
318
+ } catch (error) {
319
+ res.status(401).json({ error: 'Invalid signature' });
320
+ }
321
+ });
322
+ ```
323
+
324
+ ### Mixing test and live keys
325
+
326
+ Test keys (prefixed `dodo_test_`) only work with `https://test.dodopayments.com`. Live keys are issued per mode from the dashboard. Using a test key with live mode will fail with an authentication error.
327
+
328
+ ---
329
+
330
+ ## Resources
331
+
332
+ - [Testing Process](https://docs.dodopayments.com/miscellaneous/testing-process)
333
+ - [Test vs. Live Mode](https://docs.dodopayments.com/miscellaneous/test-mode-vs-live-mode)
334
+ - [Webhook Integration](https://docs.dodopayments.com/developer-resources/webhooks/intents/introduction)
335
+ - [Dodo CLI](https://docs.dodopayments.com/developer-resources/cli)