paddle-checkout-accelerator 2.1.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.
Files changed (88) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +388 -0
  3. package/dist/package/index.d.ts +28 -0
  4. package/dist/package/index.js +28 -0
  5. package/dist/src/components/paddle/BillingHistory.d.ts +1 -0
  6. package/dist/src/components/paddle/BillingHistory.js +17 -0
  7. package/dist/src/components/paddle/CustomerPortal.d.ts +1 -0
  8. package/dist/src/components/paddle/CustomerPortal.js +5 -0
  9. package/dist/src/components/paddle/CustomerPortalButton.d.ts +1 -0
  10. package/dist/src/components/paddle/CustomerPortalButton.js +31 -0
  11. package/dist/src/components/paddle/InlineCheckout.d.ts +5 -0
  12. package/dist/src/components/paddle/InlineCheckout.js +13 -0
  13. package/dist/src/components/paddle/PricingTable.d.ts +1 -0
  14. package/dist/src/components/paddle/PricingTable.js +27 -0
  15. package/dist/src/components/paddle/SubscriptionCard.d.ts +6 -0
  16. package/dist/src/components/paddle/SubscriptionCard.js +5 -0
  17. package/dist/src/components/paddle/TrialBanner.d.ts +5 -0
  18. package/dist/src/components/paddle/TrialBanner.js +5 -0
  19. package/dist/src/components/paddle/UpgradeModal.d.ts +11 -0
  20. package/dist/src/components/paddle/UpgradeModal.js +35 -0
  21. package/dist/src/components/paddle/UsageMeter.d.ts +6 -0
  22. package/dist/src/components/paddle/UsageMeter.js +8 -0
  23. package/dist/src/components/paddle/gates/SubscriptionGate.d.ts +7 -0
  24. package/dist/src/components/paddle/gates/SubscriptionGate.js +8 -0
  25. package/dist/src/lib/billing/adapters/index.d.ts +3 -0
  26. package/dist/src/lib/billing/adapters/index.js +3 -0
  27. package/dist/src/lib/billing/adapters/memory.d.ts +2 -0
  28. package/dist/src/lib/billing/adapters/memory.js +41 -0
  29. package/dist/src/lib/billing/adapters/prisma/index.d.ts +28 -0
  30. package/dist/src/lib/billing/adapters/prisma/index.js +80 -0
  31. package/dist/src/lib/billing/adapters/types.d.ts +13 -0
  32. package/dist/src/lib/billing/adapters/types.js +1 -0
  33. package/dist/src/lib/billing/configure.d.ts +6 -0
  34. package/dist/src/lib/billing/configure.js +13 -0
  35. package/dist/src/lib/billing/customer-repair.d.ts +4 -0
  36. package/dist/src/lib/billing/customer-repair.js +19 -0
  37. package/dist/src/lib/billing/demo-seed.d.ts +1 -0
  38. package/dist/src/lib/billing/demo-seed.js +13 -0
  39. package/dist/src/lib/billing/entitlements.d.ts +3 -0
  40. package/dist/src/lib/billing/entitlements.js +16 -0
  41. package/dist/src/lib/billing/events.d.ts +12 -0
  42. package/dist/src/lib/billing/events.js +20 -0
  43. package/dist/src/lib/billing/plans.d.ts +9 -0
  44. package/dist/src/lib/billing/plans.js +41 -0
  45. package/dist/src/lib/billing/protection.d.ts +6 -0
  46. package/dist/src/lib/billing/protection.js +16 -0
  47. package/dist/src/lib/billing/refresh.d.ts +1 -0
  48. package/dist/src/lib/billing/refresh.js +32 -0
  49. package/dist/src/lib/billing/subscriptions.d.ts +16 -0
  50. package/dist/src/lib/billing/subscriptions.js +36 -0
  51. package/dist/src/lib/billing/teams.d.ts +42 -0
  52. package/dist/src/lib/billing/teams.js +104 -0
  53. package/dist/src/lib/billing/usage.d.ts +17 -0
  54. package/dist/src/lib/billing/usage.js +40 -0
  55. package/dist/src/lib/billing/webhook-sync.d.ts +26 -0
  56. package/dist/src/lib/billing/webhook-sync.js +39 -0
  57. package/dist/src/lib/paddle/api.d.ts +1 -0
  58. package/dist/src/lib/paddle/api.js +21 -0
  59. package/dist/src/lib/paddle/client.d.ts +1 -0
  60. package/dist/src/lib/paddle/client.js +13 -0
  61. package/dist/src/lib/paddle/customers.d.ts +15 -0
  62. package/dist/src/lib/paddle/customers.js +14 -0
  63. package/dist/src/lib/paddle/events.d.ts +10 -0
  64. package/dist/src/lib/paddle/events.js +11 -0
  65. package/dist/src/lib/paddle/hooks.d.ts +4 -0
  66. package/dist/src/lib/paddle/hooks.js +11 -0
  67. package/dist/src/lib/paddle/portal.d.ts +8 -0
  68. package/dist/src/lib/paddle/portal.js +28 -0
  69. package/dist/src/lib/paddle/subscriptions.d.ts +20 -0
  70. package/dist/src/lib/paddle/subscriptions.js +10 -0
  71. package/dist/src/lib/paddle/types.d.ts +8 -0
  72. package/dist/src/lib/paddle/types.js +1 -0
  73. package/dist/src/lib/paddle/webhook.d.ts +1 -0
  74. package/dist/src/lib/paddle/webhook.js +8 -0
  75. package/dist/src/lib/utils.d.ts +2 -0
  76. package/dist/src/lib/utils.js +5 -0
  77. package/docs/agency-use-case.md +19 -0
  78. package/docs/quickstart.md +24 -0
  79. package/docs/recipes/prisma/adapter.example.txt +70 -0
  80. package/docs/recipes/prisma/client.example.txt +4 -0
  81. package/docs/recipes/prisma/schema.prisma +30 -0
  82. package/package.json +59 -0
  83. package/recipes/README.md +20 -0
  84. package/recipes/nextjs/app/billing.ts +14 -0
  85. package/recipes/nextjs/custom-adapter.ts +26 -0
  86. package/recipes/nextjs/protected-api-route.ts +27 -0
  87. package/recipes/nextjs/server-page-gate.tsx +18 -0
  88. package/recipes/nextjs/webhook-route.ts +23 -0
@@ -0,0 +1,40 @@
1
+ import { memoryBillingAdapter, } from "./adapters";
2
+ import { plans } from "./plans";
3
+ import { getSubscription } from "./subscriptions";
4
+ let adapter = memoryBillingAdapter;
5
+ export function setUsageAdapter(nextAdapter) {
6
+ adapter = nextAdapter;
7
+ }
8
+ function currentPeriod() {
9
+ const now = new Date();
10
+ return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
11
+ }
12
+ export async function getUsage(userId, key) {
13
+ return adapter.getUsage(userId, key, currentPeriod());
14
+ }
15
+ export async function getUsageLimit(userId) {
16
+ const subscription = await getSubscription(userId);
17
+ return plans[subscription.plan].monthlyLimit;
18
+ }
19
+ export async function canUse(userId, key) {
20
+ const [used, limit] = await Promise.all([
21
+ getUsage(userId, key),
22
+ getUsageLimit(userId),
23
+ ]);
24
+ return {
25
+ allowed: used < limit,
26
+ used,
27
+ limit,
28
+ remaining: Math.max(0, limit - used),
29
+ };
30
+ }
31
+ export async function incrementUsage(userId, key, amount = 1) {
32
+ return adapter.incrementUsage(userId, key, currentPeriod(), amount);
33
+ }
34
+ export async function requireUsage(userId, key) {
35
+ const usage = await canUse(userId, key);
36
+ if (!usage.allowed) {
37
+ throw new Error("Usage limit reached");
38
+ }
39
+ return usage;
40
+ }
@@ -0,0 +1,26 @@
1
+ import type { PlanId } from "./plans";
2
+ interface PaddleEvent {
3
+ event_type?: string;
4
+ data?: {
5
+ id?: string;
6
+ status?: string;
7
+ customer_id?: string;
8
+ custom_data?: {
9
+ userId?: string;
10
+ plan?: PlanId;
11
+ };
12
+ current_billing_period?: {
13
+ ends_at?: string;
14
+ };
15
+ };
16
+ }
17
+ export declare function syncPaddleEvent(event: PaddleEvent): Promise<{
18
+ synced: boolean;
19
+ reason: string;
20
+ type?: undefined;
21
+ } | {
22
+ synced: boolean;
23
+ type: string;
24
+ reason?: undefined;
25
+ }>;
26
+ export {};
@@ -0,0 +1,39 @@
1
+ import { upsertSubscription } from "./subscriptions";
2
+ export async function syncPaddleEvent(event) {
3
+ const data = event.data;
4
+ const userId = data?.custom_data?.userId;
5
+ if (!userId) {
6
+ return {
7
+ synced: false,
8
+ reason: "missing userId in custom_data",
9
+ };
10
+ }
11
+ const plan = data?.custom_data?.plan ?? "starter";
12
+ if (event.event_type?.startsWith("subscription.")) {
13
+ await upsertSubscription({
14
+ userId,
15
+ plan,
16
+ status: data?.status === "active"
17
+ ? "active"
18
+ : data?.status === "trialing"
19
+ ? "trialing"
20
+ : data?.status === "paused"
21
+ ? "paused"
22
+ : data?.status === "canceled"
23
+ ? "canceled"
24
+ : "none",
25
+ paddleCustomerId: data?.customer_id,
26
+ paddleSubscriptionId: data?.id,
27
+ currentPeriodEnd: data?.current_billing_period
28
+ ?.ends_at,
29
+ });
30
+ return {
31
+ synced: true,
32
+ type: "subscription",
33
+ };
34
+ }
35
+ return {
36
+ synced: false,
37
+ reason: "unhandled event type",
38
+ };
39
+ }
@@ -0,0 +1 @@
1
+ export declare function paddleApi<T>(path: string, options?: RequestInit): Promise<T>;
@@ -0,0 +1,21 @@
1
+ const PADDLE_API_BASE = process.env.PADDLE_API_BASE ??
2
+ "https://api.paddle.com";
3
+ export async function paddleApi(path, options = {}) {
4
+ const apiKey = process.env.PADDLE_API_KEY;
5
+ if (!apiKey) {
6
+ throw new Error("Missing PADDLE_API_KEY");
7
+ }
8
+ const response = await fetch(`${PADDLE_API_BASE}${path}`, {
9
+ ...options,
10
+ headers: {
11
+ Authorization: `Bearer ${apiKey}`,
12
+ "Content-Type": "application/json",
13
+ ...options.headers,
14
+ },
15
+ });
16
+ if (!response.ok) {
17
+ const text = await response.text();
18
+ throw new Error(`Paddle API failed: ${response.status} ${text}`);
19
+ }
20
+ return response.json();
21
+ }
@@ -0,0 +1 @@
1
+ export declare function getPaddle(): Promise<import("@paddle/paddle-js").Paddle | undefined>;
@@ -0,0 +1,13 @@
1
+ import { initializePaddle } from "@paddle/paddle-js";
2
+ let paddleInstance = null;
3
+ export async function getPaddle() {
4
+ if (paddleInstance) {
5
+ return paddleInstance;
6
+ }
7
+ paddleInstance =
8
+ await initializePaddle({
9
+ token: process.env
10
+ .NEXT_PUBLIC_PADDLE_CLIENT_TOKEN,
11
+ });
12
+ return paddleInstance;
13
+ }
@@ -0,0 +1,15 @@
1
+ export interface PaddleCustomer {
2
+ id: string;
3
+ email?: string;
4
+ name?: string;
5
+ status?: string;
6
+ }
7
+ export interface PaddleCustomerListResponse {
8
+ data?: PaddleCustomer[];
9
+ }
10
+ export interface PaddleCustomerResponse {
11
+ data?: PaddleCustomer;
12
+ }
13
+ export declare function fetchPaddleCustomer(customerId: string): Promise<PaddleCustomerResponse>;
14
+ export declare function findPaddleCustomersByEmail(email: string): Promise<PaddleCustomerListResponse>;
15
+ export declare function findFirstPaddleCustomerByEmail(email: string): Promise<PaddleCustomer | null>;
@@ -0,0 +1,14 @@
1
+ import { paddleApi } from "./api";
2
+ export async function fetchPaddleCustomer(customerId) {
3
+ return paddleApi(`/customers/${customerId}`);
4
+ }
5
+ export async function findPaddleCustomersByEmail(email) {
6
+ const params = new URLSearchParams({
7
+ email,
8
+ });
9
+ return paddleApi(`/customers?${params.toString()}`);
10
+ }
11
+ export async function findFirstPaddleCustomerByEmail(email) {
12
+ const response = await findPaddleCustomersByEmail(email);
13
+ return response.data?.[0] ?? null;
14
+ }
@@ -0,0 +1,10 @@
1
+ export declare enum PaddleEventType {
2
+ TransactionCompleted = "transaction.completed",
3
+ TransactionPaid = "transaction.paid",
4
+ SubscriptionCreated = "subscription.created",
5
+ SubscriptionActivated = "subscription.activated",
6
+ SubscriptionUpdated = "subscription.updated",
7
+ SubscriptionCanceled = "subscription.canceled",
8
+ SubscriptionPaused = "subscription.paused",
9
+ CustomerCreated = "customer.created"
10
+ }
@@ -0,0 +1,11 @@
1
+ export var PaddleEventType;
2
+ (function (PaddleEventType) {
3
+ PaddleEventType["TransactionCompleted"] = "transaction.completed";
4
+ PaddleEventType["TransactionPaid"] = "transaction.paid";
5
+ PaddleEventType["SubscriptionCreated"] = "subscription.created";
6
+ PaddleEventType["SubscriptionActivated"] = "subscription.activated";
7
+ PaddleEventType["SubscriptionUpdated"] = "subscription.updated";
8
+ PaddleEventType["SubscriptionCanceled"] = "subscription.canceled";
9
+ PaddleEventType["SubscriptionPaused"] = "subscription.paused";
10
+ PaddleEventType["CustomerCreated"] = "customer.created";
11
+ })(PaddleEventType || (PaddleEventType = {}));
@@ -0,0 +1,4 @@
1
+ export declare function openCheckout(items: {
2
+ priceId: string;
3
+ quantity: number;
4
+ }[]): Promise<void>;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { getPaddle } from "./client";
3
+ export async function openCheckout(items) {
4
+ const paddle = await getPaddle();
5
+ if (!paddle) {
6
+ throw new Error("Paddle failed to initialize");
7
+ }
8
+ paddle.Checkout.open({
9
+ items,
10
+ });
11
+ }
@@ -0,0 +1,8 @@
1
+ export interface CreatePortalSessionOptions {
2
+ customerId: string;
3
+ returnUrl?: string;
4
+ }
5
+ export interface PortalSessionResult {
6
+ url: string;
7
+ }
8
+ export declare function createCustomerPortalSession({ customerId, returnUrl, }: CreatePortalSessionOptions): Promise<PortalSessionResult>;
@@ -0,0 +1,28 @@
1
+ export async function createCustomerPortalSession({ customerId, returnUrl, }) {
2
+ const apiKey = process.env.PADDLE_API_KEY;
3
+ if (!apiKey) {
4
+ throw new Error("Missing PADDLE_API_KEY");
5
+ }
6
+ const response = await fetch("https://api.paddle.com/customer-portal-sessions", {
7
+ method: "POST",
8
+ headers: {
9
+ Authorization: `Bearer ${apiKey}`,
10
+ "Content-Type": "application/json",
11
+ },
12
+ body: JSON.stringify({
13
+ customer_id: customerId,
14
+ urls: returnUrl
15
+ ? { return_url: returnUrl }
16
+ : undefined,
17
+ }),
18
+ });
19
+ if (!response.ok) {
20
+ throw new Error("Failed to create Paddle customer portal session");
21
+ }
22
+ const data = (await response.json());
23
+ const url = data.data?.urls?.general;
24
+ if (!url) {
25
+ throw new Error("Paddle portal URL missing from response");
26
+ }
27
+ return { url };
28
+ }
@@ -0,0 +1,20 @@
1
+ export interface PaddleSubscription {
2
+ id: string;
3
+ status: string;
4
+ customer_id?: string;
5
+ current_billing_period?: {
6
+ ends_at?: string;
7
+ };
8
+ custom_data?: {
9
+ userId?: string;
10
+ plan?: "free" | "starter" | "pro" | "business";
11
+ };
12
+ }
13
+ export interface PaddleSubscriptionResponse {
14
+ data?: PaddleSubscription;
15
+ }
16
+ export interface PaddleSubscriptionListResponse {
17
+ data?: PaddleSubscription[];
18
+ }
19
+ export declare function fetchPaddleSubscription(subscriptionId: string): Promise<PaddleSubscriptionResponse>;
20
+ export declare function fetchPaddleSubscriptionsForCustomer(customerId: string): Promise<PaddleSubscriptionListResponse>;
@@ -0,0 +1,10 @@
1
+ import { paddleApi } from "./api";
2
+ export async function fetchPaddleSubscription(subscriptionId) {
3
+ return paddleApi(`/subscriptions/${subscriptionId}`);
4
+ }
5
+ export async function fetchPaddleSubscriptionsForCustomer(customerId) {
6
+ const params = new URLSearchParams({
7
+ customer_id: customerId,
8
+ });
9
+ return paddleApi(`/subscriptions?${params.toString()}`);
10
+ }
@@ -0,0 +1,8 @@
1
+ export interface CheckoutItem {
2
+ priceId: string;
3
+ quantity: number;
4
+ }
5
+ export interface OpenCheckoutOptions {
6
+ items: CheckoutItem[];
7
+ customerEmail?: string;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function verifyPaddleWebhook(rawBody: string, signature: string): Promise<unknown>;
@@ -0,0 +1,8 @@
1
+ import { Webhook } from "svix";
2
+ export async function verifyPaddleWebhook(rawBody, signature) {
3
+ const secret = process.env.PADDLE_WEBHOOK_SECRET;
4
+ const wh = new Webhook(secret);
5
+ return wh.verify(rawBody, {
6
+ "paddle-signature": signature,
7
+ });
8
+ }
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,19 @@
1
+ # Agency Use Case
2
+
3
+ Build SaaS billing systems faster.
4
+
5
+ ## Functions
6
+
7
+ requireSubscription()
8
+ hasFeature()
9
+ canUse()
10
+ incrementUsage()
11
+ syncPaddleEvent()
12
+
13
+ ## Ideal Clients
14
+
15
+ AI SaaS
16
+ Micro SaaS
17
+ Developer Tools
18
+ Membership Sites
19
+ API Products
@@ -0,0 +1,24 @@
1
+ # Paddle SaaS Billing Starter
2
+
3
+ Quickstart documentation.
4
+
5
+ ## Core Usage
6
+
7
+ import {
8
+ protectPlan,
9
+ protectFeature,
10
+ protectUsage
11
+ } from "@/lib/billing/protection";
12
+
13
+ await protectPlan(userId, "pro");
14
+ await protectFeature(userId, "api_access");
15
+ await protectUsage(userId, "ai_generation");
16
+
17
+ ## Webhook
18
+
19
+ /api/paddle/webhook
20
+
21
+ ## Environment
22
+
23
+ NEXT_PUBLIC_PADDLE_CLIENT_TOKEN=
24
+ PADDLE_WEBHOOK_SECRET=
@@ -0,0 +1,70 @@
1
+ import type { BillingAdapter } from "@/lib/billing/adapters";
2
+ import { prisma } from "./client.example";
3
+
4
+ export const prismaBillingAdapter: BillingAdapter = {
5
+ async getSubscription(userId) {
6
+ return prisma.subscription.findUnique({
7
+ where: { userId },
8
+ });
9
+ },
10
+
11
+ async upsertSubscription(record) {
12
+ return prisma.subscription.upsert({
13
+ where: {
14
+ userId: record.userId,
15
+ },
16
+ create: record,
17
+ update: record,
18
+ });
19
+ },
20
+
21
+ async getUsage(
22
+ userId,
23
+ key,
24
+ period
25
+ ) {
26
+ const usage =
27
+ await prisma.usageEvent.findUnique({
28
+ where: {
29
+ userId_key_period: {
30
+ userId,
31
+ key,
32
+ period,
33
+ },
34
+ },
35
+ });
36
+
37
+ return usage?.count ?? 0;
38
+ },
39
+
40
+ async incrementUsage(
41
+ userId,
42
+ key,
43
+ period,
44
+ amount
45
+ ) {
46
+ const usage =
47
+ await prisma.usageEvent.upsert({
48
+ where: {
49
+ userId_key_period: {
50
+ userId,
51
+ key,
52
+ period,
53
+ },
54
+ },
55
+ create: {
56
+ userId,
57
+ key,
58
+ period,
59
+ count: amount,
60
+ },
61
+ update: {
62
+ count: {
63
+ increment: amount,
64
+ },
65
+ },
66
+ });
67
+
68
+ return usage.count;
69
+ },
70
+ };
@@ -0,0 +1,4 @@
1
+ import { PrismaClient } from "@prisma/client";
2
+
3
+ export const prisma =
4
+ new PrismaClient();
@@ -0,0 +1,30 @@
1
+ model Subscription {
2
+ id String @id @default(cuid())
3
+ userId String @unique
4
+
5
+ plan String
6
+ status String
7
+
8
+ paddleCustomerId String?
9
+ paddleSubscriptionId String?
10
+
11
+ currentPeriodEnd DateTime?
12
+
13
+ createdAt DateTime @default(now())
14
+ updatedAt DateTime @updatedAt
15
+ }
16
+
17
+ model UsageEvent {
18
+ id String @id @default(cuid())
19
+
20
+ userId String
21
+ key String
22
+ period String
23
+
24
+ count Int @default(0)
25
+
26
+ createdAt DateTime @default(now())
27
+ updatedAt DateTime @updatedAt
28
+
29
+ @@unique([userId, key, period])
30
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "paddle-checkout-accelerator",
3
+ "version": "2.1.0",
4
+ "scripts": {
5
+ "dev": "next dev",
6
+ "build": "next build",
7
+ "start": "next start",
8
+ "lint": "eslint",
9
+ "test": "vitest run",
10
+ "build:package": "tsc -p tsconfig.package.json",
11
+ "prepack": "npm run build:package"
12
+ },
13
+ "dependencies": {
14
+ "@paddle/paddle-js": "^1.6.4",
15
+ "@radix-ui/react-dialog": "^1.1.16",
16
+ "@radix-ui/react-dropdown-menu": "^2.1.17",
17
+ "@radix-ui/react-scroll-area": "^1.2.11",
18
+ "@radix-ui/react-separator": "^1.1.9",
19
+ "@radix-ui/react-slot": "^1.2.5",
20
+ "@radix-ui/react-tabs": "^1.1.14",
21
+ "@radix-ui/react-tooltip": "^1.2.9",
22
+ "class-variance-authority": "^0.7.1",
23
+ "clsx": "^2.1.1",
24
+ "framer-motion": "^12.40.0",
25
+ "lucide-react": "^1.18.0",
26
+ "next": "16.2.9",
27
+ "react": "19.2.4",
28
+ "react-dom": "19.2.4",
29
+ "svix": "^1.95.2",
30
+ "tailwind-merge": "^3.6.0"
31
+ },
32
+ "devDependencies": {
33
+ "@tailwindcss/postcss": "^4",
34
+ "@types/node": "^20",
35
+ "@types/react": "^19",
36
+ "@types/react-dom": "^19",
37
+ "eslint": "^9",
38
+ "eslint-config-next": "16.2.9",
39
+ "tailwindcss": "^4",
40
+ "typescript": "^5",
41
+ "vitest": "^4.1.8"
42
+ },
43
+ "type": "module",
44
+ "main": "./dist/package/index.js",
45
+ "types": "./dist/package/index.d.ts",
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/package/index.d.ts",
49
+ "import": "./dist/package/index.js"
50
+ }
51
+ },
52
+ "files": [
53
+ "dist",
54
+ "README.md",
55
+ "LICENSE",
56
+ "docs",
57
+ "recipes"
58
+ ]
59
+ }
@@ -0,0 +1,20 @@
1
+ # Recipes
2
+
3
+ Copy-paste examples for real SaaS projects.
4
+
5
+ ## Next.js
6
+
7
+ - protected-api-route.ts
8
+ - server-page-gate.tsx
9
+ - webhook-route.ts
10
+ - custom-adapter.example.txt
11
+
12
+ These recipes help agencies integrate Paddle billing into client projects quickly.
13
+
14
+ ## Prisma
15
+
16
+ schema.prisma
17
+ adapter.example.txt
18
+ client.example.txt
19
+
20
+ Provides a production-ready adapter example.
@@ -0,0 +1,14 @@
1
+ import {
2
+ configureBilling,
3
+ createPrismaBillingAdapter,
4
+ } from "@namahlogisticspaddle-checkout-accelerator";
5
+
6
+ import { prisma } from "@/lib/prisma";
7
+
8
+ const adapter =
9
+ createPrismaBillingAdapter(prisma);
10
+
11
+ export const billing =
12
+ configureBilling({
13
+ adapter,
14
+ });
@@ -0,0 +1,26 @@
1
+ import type { BillingAdapter } from "@/lib/billing/adapters";
2
+
3
+ export const customBillingAdapter: BillingAdapter = {
4
+ async getSubscription(userId) {
5
+ // Replace with Prisma/Supabase query
6
+ console.log(userId);
7
+ return null;
8
+ },
9
+
10
+ async upsertSubscription(record) {
11
+ // Replace with Prisma/Supabase upsert
12
+ return record;
13
+ },
14
+
15
+ async getUsage(userId, key, period) {
16
+ // Replace with DB lookup
17
+ console.log(userId, key, period);
18
+ return 0;
19
+ },
20
+
21
+ async incrementUsage(userId, key, period, amount) {
22
+ // Replace with DB increment
23
+ console.log(userId, key, period, amount);
24
+ return amount;
25
+ },
26
+ };
@@ -0,0 +1,27 @@
1
+ import { NextResponse } from "next/server";
2
+ import { protectFeature, protectUsage } from "@/lib/billing/protection";
3
+
4
+ export async function POST() {
5
+ const userId = "replace-with-your-auth-user-id";
6
+
7
+ try {
8
+ await protectFeature(userId, "api_access");
9
+ await protectUsage(userId, "ai_generation");
10
+
11
+ return NextResponse.json({
12
+ success: true,
13
+ message: "Paid feature executed",
14
+ });
15
+ } catch (err) {
16
+ return NextResponse.json(
17
+ {
18
+ success: false,
19
+ error:
20
+ err instanceof Error
21
+ ? err.message
22
+ : "Access denied",
23
+ },
24
+ { status: 403 }
25
+ );
26
+ }
27
+ }
@@ -0,0 +1,18 @@
1
+ import { redirect } from "next/navigation";
2
+ import { requireSubscription } from "@/lib/billing/subscriptions";
3
+
4
+ export default async function ProDashboardPage() {
5
+ const userId = "replace-with-your-auth-user-id";
6
+
7
+ try {
8
+ await requireSubscription(userId, "pro");
9
+ } catch {
10
+ redirect("/upgrade");
11
+ }
12
+
13
+ return (
14
+ <main>
15
+ <h1>Pro Dashboard</h1>
16
+ </main>
17
+ );
18
+ }