Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor SaaS Template and Add Paystack Subscription Setup #385

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
87baced
Refactor SaaS Template
farajabien Nov 12, 2024
a131176
Merge pull request #1 from farajabien/dev
farajabien Nov 12, 2024
08aa440
Update README and links to reflect new Supabase SaaS starter repository
farajabien Nov 12, 2024
dfe3dfb
feat: PAYSTACK SUBSCRIPTION SETUP 1.0
farajabien Nov 13, 2024
445f944
Merge pull request #2 from farajabien/dev
farajabien Nov 13, 2024
8d47fa3
chore: remove deprecated node-fetch and related types from pnpm-lock.…
farajabien Nov 13, 2024
50ecef4
Merge pull request #3 from farajabien/dev
farajabien Nov 13, 2024
9be8096
feat: implement subscription cancellation and customer portal functio…
farajabien Nov 13, 2024
9c0584d
Merge pull request #4 from farajabien/dev
farajabien Nov 13, 2024
99c7a79
feat: move CustomerPortalForm to a new location and update subscripti…
farajabien Nov 13, 2024
e23e47b
Merge pull request #5 from farajabien/dev
farajabien Nov 13, 2024
c32ba15
feat: remove Stripe integration and update CustomerPortalForm for Pay…
farajabien Nov 13, 2024
631f3a0
feat: enhance customer retrieval in Paystack checkout with error hand…
farajabien Nov 13, 2024
8633799
chore: remove Stripe integration from pnpm-lock.yaml
farajabien Nov 13, 2024
df7ccdd
Merge branch 'main' into dev
farajabien Nov 13, 2024
36fb030
Merge branch 'main' into dev
farajabien Nov 13, 2024
f8b59b5
feat: implement account management and subscription features, removin…
farajabien Nov 13, 2024
cf01b3b
feat: update GitHub Actions workflow for syncing Paystack plans
farajabien Nov 13, 2024
87b373d
feat: enhance subscription management and update GitHub Actions workflow
farajabien Nov 14, 2024
76b9e72
feat: refactor Paystack integration to use Supabase admin client for …
farajabien Nov 14, 2024
d7790ff
feat: add Next.js configuration and Paystack callback API for transac…
farajabien Nov 14, 2024
d3fcf94
feat: implement account update API and enhance account form for user …
farajabien Nov 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ NEXT_PUBLIC_SITE_URL="http://localhost:3000"
# These environment variables are used for Supabase Local Dev
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"
NEXT_PUBLIC_SUPABASE_URL="http://127.0.0.1:54321"
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImN6dXVlY2VkcnN1Z2l6eHVnYW9xIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTczMTQyNDI0NCwiZXhwIjoyMDQ3MDAwMjQ0fQ.IxJYmnMPpAsoPB_i-e2mghfk8jEAoqHCPsIS9wVhkb8

# Get these from Stripe dashboard
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
Expand Down
72 changes: 72 additions & 0 deletions .github/workflows/sync-plans.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Sync Paystack Plans

on:
schedule:
- cron: '0 0 * * *' # Runs daily at midnight
workflow_dispatch: # Allows manual triggering
push:
branches:
- main
paths:
- 'scripts/sync-plans.ts'

jobs:
sync-plans:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'

- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_ENV

- name: Setup pnpm cache
uses: actions/cache@v3
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

- name: Create .env file
run: |
echo "NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" >> .env
echo "SUPABASE_SERVICE_ROLE_KEY=${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}" >> .env
echo "PAYSTACK_SECRET_KEY=${{ secrets.PAYSTACK_SECRET_KEY }}" >> .env
echo "NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY=${{ secrets.NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY }}" >> .env

- name: Verify environment variables
run: |
if [ -z "${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" ]; then
echo "Error: NEXT_PUBLIC_SUPABASE_URL is not set"
exit 1
fi
if [ -z "${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}" ]; then
echo "Error: SUPABASE_SERVICE_ROLE_KEY is not set"
exit 1
fi
if [ -z "${{ secrets.PAYSTACK_SECRET_KEY }}" ]; then
echo "Error: PAYSTACK_SECRET_KEY is not set"
exit 1
fi

- name: Sync Paystack plans
run: pnpm paystack:sync-plans
231 changes: 231 additions & 0 deletions ImplementationGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# Next.js Subscription Payments with Paystack - Implementation Guide

## 1. Initial Setup

### 1.1 Clone and Install

```bash
# Clone the repository
git clone https://github.com/vercel/nextjs-subscription-payments
cd nextjs-subscription-payments

# Install dependencies
pnpm install

# Create environment files
cp .env.example .env.local
```

### 1.2 Set Up Supabase

1. Create a new project at supabase.com
2. Get your project URL and anon key
3. Update `.env.local`:

```env
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
```

### 1.3 Set Up Paystack

1. Create account at paystack.com
2. Get your test API keys
3. Add to `.env.local`:

```env
PAYSTACK_SECRET_KEY=sk_test_xxxxx
NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY=pk_test_xxxxx
```

## 2. Database Setup

### 2.1 Run Migrations

```sql
-- Execute in Supabase SQL Editor
-- Create necessary enums
CREATE TYPE pricing_type AS ENUM ('one_time', 'recurring');
CREATE TYPE pricing_plan_interval AS ENUM ('day', 'week', 'month', 'year');
CREATE TYPE subscription_status AS ENUM (
'trialing', 'active', 'canceled', 'incomplete',
'incomplete_expired', 'past_due', 'unpaid', 'paused'
);

-- Create tables
CREATE TABLE products (
id text PRIMARY KEY,
active boolean,
name text,
description text,
image text,
metadata jsonb
);

CREATE TABLE prices (
id text PRIMARY KEY,
product_id text REFERENCES products(id),
active boolean,
description text,
unit_amount bigint,
currency text CHECK (char_length(currency) = 3),
type pricing_type,
interval pricing_plan_interval,
interval_count integer,
trial_period_days integer,
metadata jsonb
);

CREATE TABLE customers (
id uuid REFERENCES auth.users PRIMARY KEY,
paystack_customer_id text
);

CREATE TABLE subscriptions (
id text PRIMARY KEY,
user_id uuid REFERENCES auth.users NOT NULL,
status subscription_status,
metadata jsonb,
price_id text REFERENCES prices(id),
quantity integer,
cancel_at_period_end boolean,
created timestamp with time zone DEFAULT timezone('utc'::text, now()),
current_period_start timestamp with time zone DEFAULT timezone('utc'::text, now()),
current_period_end timestamp with time zone DEFAULT timezone('utc'::text, now()),
ended_at timestamp with time zone DEFAULT timezone('utc'::text, now()),
cancel_at timestamp with time zone DEFAULT timezone('utc'::text, now()),
canceled_at timestamp with time zone DEFAULT timezone('utc'::text, now()),
trial_start timestamp with time zone DEFAULT timezone('utc'::text, now()),
trial_end timestamp with time zone DEFAULT timezone('utc'::text, now())
);
```

### 2.2 Set Up Row Level Security (RLS)

```sql
-- Enable RLS
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
ALTER TABLE prices ENABLE ROW LEVEL SECURITY;
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;

-- Create policies
CREATE POLICY "Allow public read-only access" ON products FOR SELECT USING (true);
CREATE POLICY "Allow public read-only access" ON prices FOR SELECT USING (true);
CREATE POLICY "Can only view own subscription data" ON subscriptions FOR SELECT USING (auth.uid() = user_id);
```

## 3. Initialize Paystack Integration

### 3.1 Create Plans in Paystack

1. Run the plan creation script:

```bash
pnpm paystack:create-plans
```

### 3.2 Set Up Webhook

1. Set up ngrok for local testing:

```bash
ngrok http 3000
```

2. Configure webhook URL in Paystack dashboard:

```
https://your-domain/api/webhooks
```

3. Select events to listen for:

- subscription.create
- subscription.disable
- subscription.enable
- plan.create
- plan.update
- charge.success

## 4. Set Up GitHub Actions

### 4.1 Add Secrets

Add these secrets to your GitHub repository:

- NEXT_PUBLIC_SUPABASE_URL
- SUPABASE_SERVICE_ROLE_KEY
- PAYSTACK_SECRET_KEY
- NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY

### 4.2 Create Workflow

Create `.github/workflows/sync-plans.yml` for automatic plan syncing.

## 5. Deploy

### 5.1 Deploy to Vercel

```bash
vercel deploy
```

### 5.2 Add Environment Variables

Add all environment variables to your Vercel project.

### 5.3 Update Webhook URL

Update Paystack webhook URL to your production domain.

## 6. Testing

### 6.1 Test Subscription Flow

1. Create a test account
2. Subscribe to a plan
3. Verify webhook receives events
4. Check database for subscription record

### 6.2 Test Plan Sync

1. Create a new plan in Paystack dashboard
2. Wait for webhook or trigger manual sync
3. Verify plan appears in database

## 7. Monitoring

### 7.1 Set Up Logging

1. Add logging to webhook handler
2. Monitor webhook events in Vercel logs
3. Set up error notifications

### 7.2 Regular Maintenance

1. Run plan sync daily via GitHub Actions
2. Monitor webhook health
3. Check for failed payments
4. Verify subscription statuses

## Common Issues and Solutions

1. **Webhook Errors**

- Check signature validation
- Verify environment variables
- Check Paystack webhook logs

2. **Subscription Issues**

- Verify customer creation
- Check payment authorization
- Validate webhook events

3. **Database Sync Issues**

- Check Supabase connection
- Verify RLS policies
- Monitor sync script logs
Loading