paddle-checkout-accelerator 2.5.0 → 2.7.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.
@@ -118,6 +118,331 @@ function appendEnvSafe(values) {
118
118
  }
119
119
  }
120
120
 
121
+ function readEnvFile() {
122
+ const envPath =
123
+ path.join(cwd, ".env.local");
124
+
125
+ if (!fs.existsSync(envPath)) {
126
+ return null;
127
+ }
128
+
129
+ return fs.readFileSync(envPath, "utf8");
130
+ }
131
+
132
+ function hasEnvValue(env, key) {
133
+ if (!env) return false;
134
+
135
+ const line =
136
+ env
137
+ .split("\n")
138
+ .find((entry) =>
139
+ entry.startsWith(`${key}=`)
140
+ );
141
+
142
+ if (!line) return false;
143
+
144
+ const value =
145
+ line.slice(key.length + 1).trim();
146
+
147
+ return value.length > 0;
148
+ }
149
+
150
+ function runDoctor() {
151
+ const env =
152
+ readEnvFile();
153
+
154
+ const checks = [
155
+ {
156
+ label: ".env.local exists",
157
+ pass: Boolean(env),
158
+ },
159
+ {
160
+ label: "PADDLE_API_KEY set",
161
+ pass: hasEnvValue(env, "PADDLE_API_KEY"),
162
+ },
163
+ {
164
+ label: "PADDLE_WEBHOOK_SECRET set",
165
+ pass: hasEnvValue(env, "PADDLE_WEBHOOK_SECRET"),
166
+ },
167
+ {
168
+ label: "NEXT_PUBLIC_PADDLE_CLIENT_TOKEN set",
169
+ pass: hasEnvValue(env, "NEXT_PUBLIC_PADDLE_CLIENT_TOKEN"),
170
+ },
171
+ {
172
+ label: "billing config exists",
173
+ pass: exists("src/lib/billing.ts"),
174
+ },
175
+ {
176
+ label: "webhook route exists",
177
+ pass: exists("src/app/api/paddle/webhook/route.ts"),
178
+ },
179
+ {
180
+ label: "portal route exists",
181
+ pass: exists("src/app/api/paddle/portal-session/route.ts"),
182
+ },
183
+ {
184
+ label: "refresh route exists",
185
+ pass: exists("src/app/api/paddle/refresh-subscription/route.ts"),
186
+ },
187
+ {
188
+ label: "repair route exists",
189
+ pass: exists("src/app/api/paddle/repair-by-email/route.ts"),
190
+ },
191
+ {
192
+ label: "pricing page exists",
193
+ pass: exists("src/app/billing/pricing/page.tsx"),
194
+ },
195
+ {
196
+ label: "billing dashboard exists",
197
+ pass: exists("src/app/billing/dashboard/page.tsx"),
198
+ },
199
+ {
200
+ label: "customer repair page exists",
201
+ pass: exists("src/app/billing/customer-repair/page.tsx"),
202
+ },
203
+ ];
204
+
205
+ console.log("");
206
+ console.log("Paddle Checkout Accelerator Doctor");
207
+ console.log("");
208
+
209
+ let failed = 0;
210
+
211
+ for (const check of checks) {
212
+ if (check.pass) {
213
+ console.log(`✅ ${check.label}`);
214
+ } else {
215
+ failed += 1;
216
+ console.log(`❌ ${check.label}`);
217
+ }
218
+ }
219
+
220
+ console.log("");
221
+
222
+ if (failed === 0) {
223
+ console.log("All checks passed.");
224
+ process.exit(0);
225
+ }
226
+
227
+ console.log(`${failed} check(s) failed.`);
228
+ console.log("");
229
+ console.log("Fix the missing items above, then run:");
230
+ console.log(" npx paddle-checkout-accelerator doctor
231
+ npx paddle-checkout-accelerator verify");
232
+
233
+ process.exit(1);
234
+ }
235
+
236
+ function parseEnv(env) {
237
+ const values = {};
238
+
239
+ if (!env) return values;
240
+
241
+ for (const line of env.split("\n")) {
242
+ if (!line || line.trim().startsWith("#")) {
243
+ continue;
244
+ }
245
+
246
+ const index = line.indexOf("=");
247
+
248
+ if (index === -1) {
249
+ continue;
250
+ }
251
+
252
+ const key = line.slice(0, index).trim();
253
+ const value = line.slice(index + 1).trim();
254
+
255
+ values[key] = value;
256
+ }
257
+
258
+ return values;
259
+ }
260
+
261
+ async function paddleRequest(pathname, apiKey) {
262
+ const response = await fetch(
263
+ `https://api.paddle.com${pathname}`,
264
+ {
265
+ headers: {
266
+ Authorization: `Bearer ${apiKey}`,
267
+ },
268
+ }
269
+ );
270
+
271
+ return response;
272
+ }
273
+
274
+ async function runVerify() {
275
+ const env =
276
+ readEnvFile();
277
+
278
+ const values =
279
+ parseEnv(env);
280
+
281
+ const apiKey =
282
+ values.PADDLE_API_KEY;
283
+
284
+ const checks = [];
285
+
286
+ function add(label, pass, details = "") {
287
+ checks.push({
288
+ label,
289
+ pass,
290
+ details,
291
+ });
292
+ }
293
+
294
+ add(
295
+ ".env.local exists",
296
+ Boolean(env)
297
+ );
298
+
299
+ add(
300
+ "PADDLE_API_KEY present",
301
+ Boolean(apiKey)
302
+ );
303
+
304
+ add(
305
+ "PADDLE_WEBHOOK_SECRET present",
306
+ Boolean(values.PADDLE_WEBHOOK_SECRET)
307
+ );
308
+
309
+ add(
310
+ "NEXT_PUBLIC_PADDLE_CLIENT_TOKEN present",
311
+ Boolean(values.NEXT_PUBLIC_PADDLE_CLIENT_TOKEN)
312
+ );
313
+
314
+ add(
315
+ "billing config exists",
316
+ exists("src/lib/billing.ts")
317
+ );
318
+
319
+ add(
320
+ "webhook route exists",
321
+ exists("src/app/api/paddle/webhook/route.ts")
322
+ );
323
+
324
+ if (apiKey) {
325
+ try {
326
+ const productsResponse =
327
+ await paddleRequest(
328
+ "/products",
329
+ apiKey
330
+ );
331
+
332
+ add(
333
+ "Paddle API reachable",
334
+ productsResponse.ok,
335
+ productsResponse.ok
336
+ ? ""
337
+ : `status ${productsResponse.status}`
338
+ );
339
+
340
+ if (productsResponse.ok) {
341
+ const products =
342
+ await productsResponse.json();
343
+
344
+ const count =
345
+ Array.isArray(products?.data)
346
+ ? products.data.length
347
+ : 0;
348
+
349
+ add(
350
+ "Paddle products found",
351
+ count > 0,
352
+ `${count} product(s)`
353
+ );
354
+ }
355
+
356
+ const pricesResponse =
357
+ await paddleRequest(
358
+ "/prices",
359
+ apiKey
360
+ );
361
+
362
+ if (pricesResponse.ok) {
363
+ const prices =
364
+ await pricesResponse.json();
365
+
366
+ const count =
367
+ Array.isArray(prices?.data)
368
+ ? prices.data.length
369
+ : 0;
370
+
371
+ add(
372
+ "Paddle prices found",
373
+ count > 0,
374
+ `${count} price(s)`
375
+ );
376
+ } else {
377
+ add(
378
+ "Paddle prices found",
379
+ false,
380
+ `status ${pricesResponse.status}`
381
+ );
382
+ }
383
+ } catch (error) {
384
+ add(
385
+ "Paddle API reachable",
386
+ false,
387
+ error instanceof Error
388
+ ? error.message
389
+ : "unknown error"
390
+ );
391
+ }
392
+ } else {
393
+ add(
394
+ "Paddle API reachable",
395
+ false,
396
+ "missing PADDLE_API_KEY"
397
+ );
398
+
399
+ add(
400
+ "Paddle products found",
401
+ false,
402
+ "missing PADDLE_API_KEY"
403
+ );
404
+
405
+ add(
406
+ "Paddle prices found",
407
+ false,
408
+ "missing PADDLE_API_KEY"
409
+ );
410
+ }
411
+
412
+ console.log("");
413
+ console.log("Paddle Checkout Accelerator Verify");
414
+ console.log("");
415
+
416
+ let passed = 0;
417
+
418
+ for (const check of checks) {
419
+ if (check.pass) {
420
+ passed += 1;
421
+ console.log(
422
+ `✅ ${check.label}${check.details ? ` — ${check.details}` : ""}`
423
+ );
424
+ } else {
425
+ console.log(
426
+ `❌ ${check.label}${check.details ? ` — ${check.details}` : ""}`
427
+ );
428
+ }
429
+ }
430
+
431
+ const score =
432
+ Math.round(
433
+ (passed / checks.length) * 100
434
+ );
435
+
436
+ console.log("");
437
+ console.log(`Readiness Score: ${score}%`);
438
+
439
+ if (score < 100) {
440
+ process.exit(1);
441
+ }
442
+
443
+ process.exit(0);
444
+ }
445
+
121
446
  async function getConfig() {
122
447
  const detectedAdapter =
123
448
  detectAdapter();
@@ -647,11 +972,21 @@ Usage:
647
972
  npx paddle-checkout-accelerator init
648
973
  npx paddle-checkout-accelerator init --interactive
649
974
  npx paddle-checkout-accelerator init --force
975
+ npx paddle-checkout-accelerator doctor
976
+ npx paddle-checkout-accelerator verify
650
977
  npx paddle-checkout-accelerator init --minimal
651
978
  `);
652
979
  process.exit(0);
653
980
  }
654
981
 
982
+ if (command === "doctor") {
983
+ runDoctor();
984
+ }
985
+
986
+ if (command === "verify") {
987
+ await runVerify();
988
+ }
989
+
655
990
  if (command !== "init") {
656
991
  console.error(
657
992
  `Unknown command: ${command}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paddle-checkout-accelerator",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "scripts": {
5
5
  "dev": "next dev",
6
6
  "build": "next build",