paddle-checkout-accelerator 2.4.0 → 2.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.
@@ -17,6 +17,9 @@ const interactive =
17
17
  args.has("--interactive") ||
18
18
  args.has("-i");
19
19
 
20
+ const minimal =
21
+ args.has("--minimal");
22
+
20
23
  function exists(filePath) {
21
24
  return fs.existsSync(
22
25
  path.join(cwd, filePath)
@@ -126,6 +129,7 @@ async function getConfig() {
126
129
  env: true,
127
130
  schema:
128
131
  detectedAdapter === "prisma",
132
+ ui: !minimal,
129
133
  };
130
134
  }
131
135
 
@@ -180,6 +184,15 @@ async function getConfig() {
180
184
  active: "yes",
181
185
  inactive: "no",
182
186
  },
187
+ {
188
+ type: "toggle",
189
+ name: "ui",
190
+ message:
191
+ "Generate billing UI pages?",
192
+ initial: true,
193
+ active: "yes",
194
+ inactive: "no",
195
+ },
183
196
  ]);
184
197
 
185
198
  if (!answers.adapter) {
@@ -289,6 +302,198 @@ model BillingEvent {
289
302
  );
290
303
  }
291
304
 
305
+ function writeUiPages() {
306
+ writeFileSafe(
307
+ "src/app/billing/pricing/page.tsx",
308
+ `import {
309
+ PricingTable,
310
+ } from "paddle-checkout-accelerator";
311
+
312
+ export default function BillingPricingPage() {
313
+ return (
314
+ <main className="mx-auto max-w-5xl p-10">
315
+ <h1 className="text-4xl font-bold">
316
+ Choose your plan
317
+ </h1>
318
+
319
+ <p className="mt-3 text-gray-500">
320
+ Upgrade your account with Paddle billing.
321
+ </p>
322
+
323
+ <div className="mt-8">
324
+ <PricingTable />
325
+ </div>
326
+ </main>
327
+ );
328
+ }
329
+ `
330
+ );
331
+
332
+ writeFileSafe(
333
+ "src/app/billing/dashboard/page.tsx",
334
+ `import {
335
+ BillingHistory,
336
+ CustomerPortalButton,
337
+ SubscriptionCard,
338
+ TrialBanner,
339
+ UsageMeter,
340
+ } from "paddle-checkout-accelerator";
341
+
342
+ export default function BillingDashboardPage() {
343
+ return (
344
+ <main className="mx-auto max-w-5xl space-y-6 p-10">
345
+ <TrialBanner daysRemaining={5} />
346
+
347
+ <SubscriptionCard
348
+ plan="Pro"
349
+ status="Active"
350
+ />
351
+
352
+ <UsageMeter
353
+ current={37}
354
+ limit={50}
355
+ />
356
+
357
+ <CustomerPortalButton />
358
+
359
+ <BillingHistory />
360
+ </main>
361
+ );
362
+ }
363
+ `
364
+ );
365
+
366
+ writeFileSafe(
367
+ "src/app/billing/customer-repair/page.tsx",
368
+ `"use client";
369
+
370
+ import { useState } from "react";
371
+
372
+ export default function CustomerRepairPage() {
373
+ const [email, setEmail] =
374
+ useState("");
375
+ const [result, setResult] =
376
+ useState("");
377
+
378
+ async function repair() {
379
+ const response =
380
+ await fetch(
381
+ "/api/paddle/repair-by-email",
382
+ {
383
+ method: "POST",
384
+ headers: {
385
+ "Content-Type":
386
+ "application/json",
387
+ },
388
+ body: JSON.stringify({
389
+ email,
390
+ }),
391
+ }
392
+ );
393
+
394
+ const data =
395
+ await response.json();
396
+
397
+ setResult(
398
+ JSON.stringify(data, null, 2)
399
+ );
400
+ }
401
+
402
+ return (
403
+ <main className="mx-auto max-w-3xl space-y-6 p-10">
404
+ <div>
405
+ <h1 className="text-4xl font-bold">
406
+ Repair Customer Access
407
+ </h1>
408
+
409
+ <p className="mt-3 text-gray-500">
410
+ Use this when a customer paid but their account did not unlock.
411
+ </p>
412
+ </div>
413
+
414
+ <input
415
+ value={email}
416
+ onChange={(event) =>
417
+ setEmail(event.target.value)
418
+ }
419
+ placeholder="customer@example.com"
420
+ className="w-full rounded-xl border p-4"
421
+ />
422
+
423
+ <button
424
+ onClick={repair}
425
+ className="rounded-xl bg-black px-6 py-3 text-white"
426
+ >
427
+ Repair Subscription
428
+ </button>
429
+
430
+ {result && (
431
+ <pre className="overflow-auto rounded-xl border bg-slate-50 p-5 text-sm">
432
+ {result}
433
+ </pre>
434
+ )}
435
+ </main>
436
+ );
437
+ }
438
+ `
439
+ );
440
+
441
+ writeFileSafe(
442
+ "src/app/billing/protected-api/page.tsx",
443
+ `"use client";
444
+
445
+ import { useState } from "react";
446
+
447
+ export default function ProtectedApiPage() {
448
+ const [result, setResult] =
449
+ useState("");
450
+
451
+ async function run() {
452
+ const response =
453
+ await fetch(
454
+ "/api/demo/protected-generation",
455
+ {
456
+ method: "POST",
457
+ }
458
+ );
459
+
460
+ const data =
461
+ await response.json();
462
+
463
+ setResult(
464
+ JSON.stringify(data, null, 2)
465
+ );
466
+ }
467
+
468
+ return (
469
+ <main className="mx-auto max-w-3xl space-y-6 p-10">
470
+ <h1 className="text-4xl font-bold">
471
+ Protected API Demo
472
+ </h1>
473
+
474
+ <p className="text-gray-500">
475
+ Test paid-route protection, feature checks, and usage limits.
476
+ </p>
477
+
478
+ <button
479
+ onClick={run}
480
+ className="rounded-xl bg-black px-6 py-3 text-white"
481
+ >
482
+ Run Protected Action
483
+ </button>
484
+
485
+ {result && (
486
+ <pre className="overflow-auto rounded-xl border bg-slate-50 p-5 text-sm">
487
+ {result}
488
+ </pre>
489
+ )}
490
+ </main>
491
+ );
492
+ }
493
+ `
494
+ );
495
+ }
496
+
292
497
  function writeRoutes() {
293
498
  writeFileSafe(
294
499
  "src/app/api/paddle/webhook/route.ts",
@@ -442,6 +647,7 @@ Usage:
442
647
  npx paddle-checkout-accelerator init
443
648
  npx paddle-checkout-accelerator init --interactive
444
649
  npx paddle-checkout-accelerator init --force
650
+ npx paddle-checkout-accelerator init --minimal
445
651
  `);
446
652
  process.exit(0);
447
653
  }
@@ -472,6 +678,10 @@ Usage:
472
678
  writeRoutes();
473
679
  }
474
680
 
681
+ if (config.ui) {
682
+ writeUiPages();
683
+ }
684
+
475
685
  if (config.env) {
476
686
  appendEnvSafe({
477
687
  NEXT_PUBLIC_PADDLE_CLIENT_TOKEN: "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paddle-checkout-accelerator",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "scripts": {
5
5
  "dev": "next dev",
6
6
  "build": "next build",