Skip to content

Commit d6871c8

Browse files
feat: add Stripe webhook handling and update user subscriptions in Appwrite
1 parent 9c9d933 commit d6871c8

File tree

6 files changed

+422
-94
lines changed

6 files changed

+422
-94
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ NEXT_PUBLIC_STRIPE_PRICE_BYOK=price_...
1515
NEXT_PUBLIC_STRIPE_PRICE_DITECTREV=price_...
1616
NEXT_PUBLIC_STRIPE_PRICE_LOCAL=price_...
1717
STRIPE_SECRET_KEY=
18+
STRIPE_WEBHOOK_SECRET=
1819

1920
# AI Provider Keys (for Ditectrev premium service)
2021
DITECTREV_OPENAI_KEY=sk-...
Lines changed: 84 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,84 @@
1-
name: Azure Static Web Apps CI/CD
2-
3-
on:
4-
push:
5-
branches:
6-
- main
7-
- 'feature/*'
8-
pull_request:
9-
types: [opened, synchronize, reopened, closed]
10-
branches:
11-
- main
12-
- 'feature/*'
13-
14-
jobs:
15-
build_and_deploy_job:
16-
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
17-
runs-on: ubuntu-latest
18-
name: Build and Deploy Job
19-
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }}
20-
env:
21-
# Google Analytics
22-
NEXT_PUBLIC_GA_TRACKING_ID: ${{ secrets.NEXT_PUBLIC_GA_TRACKING_ID }}
23-
24-
# Appwrite Configuration (comes from environment secrets)
25-
NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}
26-
NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}
27-
NEXT_PUBLIC_APPWRITE_API_KEY: ${{ secrets.NEXT_PUBLIC_APPWRITE_API_KEY }}
28-
NEXT_PUBLIC_APPWRITE_DATABASE_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_DATABASE_ID }}
29-
NEXT_PUBLIC_APPWRITE_DATABASE_NAME: ${{ secrets.NEXT_PUBLIC_APPWRITE_DATABASE_NAME }}
30-
NEXT_PUBLIC_APPWRITE_COLLECTION_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_COLLECTION_ID }}
31-
32-
# Stripe Configuration
33-
# Note: Using NEXT_PUBLIC_ prefix is required for Azure Static Web Apps runtime access
34-
# This is safe because it's only used in server-side API routes, never in client components
35-
NEXT_PUBLIC_STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
36-
# Stripe Price IDs (environment variables, distinguished per environment)
37-
NEXT_PUBLIC_STRIPE_PRICE_ADS_FREE: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_ADS_FREE }}
38-
NEXT_PUBLIC_STRIPE_PRICE_LOCAL: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_LOCAL }}
39-
NEXT_PUBLIC_STRIPE_PRICE_BYOK: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_BYOK }}
40-
NEXT_PUBLIC_STRIPE_PRICE_DITECTREV: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_DITECTREV }}
41-
42-
# Azure Cosmos DB Configuration (comes from environment secrets)
43-
AZURE_COSMOSDB_ENDPOINT: ${{ secrets.AZURE_COSMOSDB_ENDPOINT }}
44-
AZURE_COSMOSDB_KEY: ${{ secrets.AZURE_COSMOSDB_KEY }}
45-
AZURE_COSMOSDB_DATABASE: ${{ secrets.AZURE_COSMOSDB_DATABASE }}
46-
steps:
47-
- uses: actions/checkout@v3
48-
with:
49-
submodules: true
50-
- name: Setup Node.js
51-
uses: actions/setup-node@v2
52-
with:
53-
node-version: "20.x"
54-
env:
55-
NODE_VERSION: "20.x"
56-
- name: Build And Deploy
57-
id: builddeploy
58-
uses: Azure/static-web-apps-deploy@v1
59-
with:
60-
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_KIND_PLANT_0E80E5803 }}
61-
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
62-
action: "upload"
63-
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
64-
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
65-
app_location: "/" # App source code path
66-
api_location: "" # Api source code path - optional
67-
output_location: "" # Built app content directory - optional
68-
app_build_command: "npm run build"
69-
api_build_command: "rm -rf ./node_modules/@next/swc-* && rm -rf ./.next/cache"
70-
production_branch: "main" # Only main branch goes to production (custom domain)
71-
###### End of Repository/Build Configurations ######
72-
73-
close_pull_request_job:
74-
if: github.event_name == 'pull_request' && github.event.action == 'closed'
75-
runs-on: ubuntu-latest
76-
name: Close Pull Request Job
77-
steps:
78-
- name: Close Pull Request
79-
id: closepullrequest
80-
uses: Azure/static-web-apps-deploy@v1
81-
with:
82-
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_KIND_PLANT_0E80E5803 }}
83-
action: "close"
1+
name: Azure Static Web Apps CI/CD
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- 'feature/*'
8+
pull_request:
9+
types: [opened, synchronize, reopened, closed]
10+
branches:
11+
- main
12+
- 'feature/*'
13+
14+
jobs:
15+
build_and_deploy_job:
16+
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
17+
runs-on: ubuntu-latest
18+
name: Build and Deploy Job
19+
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }}
20+
env:
21+
# Google Analytics
22+
NEXT_PUBLIC_GA_TRACKING_ID: ${{ secrets.NEXT_PUBLIC_GA_TRACKING_ID }}
23+
24+
# Appwrite Configuration (comes from environment secrets)
25+
NEXT_PUBLIC_APPWRITE_ENDPOINT: ${{ secrets.NEXT_PUBLIC_APPWRITE_ENDPOINT }}
26+
NEXT_PUBLIC_APPWRITE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_PROJECT_ID }}
27+
NEXT_PUBLIC_APPWRITE_API_KEY: ${{ secrets.NEXT_PUBLIC_APPWRITE_API_KEY }}
28+
NEXT_PUBLIC_APPWRITE_DATABASE_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_DATABASE_ID }}
29+
NEXT_PUBLIC_APPWRITE_DATABASE_NAME: ${{ secrets.NEXT_PUBLIC_APPWRITE_DATABASE_NAME }}
30+
NEXT_PUBLIC_APPWRITE_COLLECTION_ID: ${{ secrets.NEXT_PUBLIC_APPWRITE_COLLECTION_ID }}
31+
32+
# Stripe Configuration
33+
# Note: Using NEXT_PUBLIC_ prefix is required for Azure Static Web Apps runtime access
34+
# This is safe because it's only used in server-side API routes, never in client components
35+
NEXT_PUBLIC_STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
36+
NEXT_PUBLIC_STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
37+
# Stripe Price IDs (environment variables, distinguished per environment)
38+
NEXT_PUBLIC_STRIPE_PRICE_ADS_FREE: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_ADS_FREE }}
39+
NEXT_PUBLIC_STRIPE_PRICE_LOCAL: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_LOCAL }}
40+
NEXT_PUBLIC_STRIPE_PRICE_BYOK: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_BYOK }}
41+
NEXT_PUBLIC_STRIPE_PRICE_DITECTREV: ${{ vars.NEXT_PUBLIC_STRIPE_PRICE_DITECTREV }}
42+
43+
# Azure Cosmos DB Configuration (comes from environment secrets)
44+
AZURE_COSMOSDB_ENDPOINT: ${{ secrets.AZURE_COSMOSDB_ENDPOINT }}
45+
AZURE_COSMOSDB_KEY: ${{ secrets.AZURE_COSMOSDB_KEY }}
46+
AZURE_COSMOSDB_DATABASE: ${{ secrets.AZURE_COSMOSDB_DATABASE }}
47+
steps:
48+
- uses: actions/checkout@v3
49+
with:
50+
submodules: true
51+
- name: Setup Node.js
52+
uses: actions/setup-node@v2
53+
with:
54+
node-version: "20.x"
55+
env:
56+
NODE_VERSION: "20.x"
57+
- name: Build And Deploy
58+
id: builddeploy
59+
uses: Azure/static-web-apps-deploy@v1
60+
with:
61+
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_KIND_PLANT_0E80E5803 }}
62+
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
63+
action: "upload"
64+
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
65+
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
66+
app_location: "/" # App source code path
67+
api_location: "" # Api source code path - optional
68+
output_location: "" # Built app content directory - optional
69+
app_build_command: "npm run build"
70+
api_build_command: "rm -rf ./node_modules/@next/swc-* && rm -rf ./.next/cache"
71+
production_branch: "main" # Only main branch goes to production (custom domain)
72+
###### End of Repository/Build Configurations ######
73+
74+
close_pull_request_job:
75+
if: github.event_name == 'pull_request' && github.event.action == 'closed'
76+
runs-on: ubuntu-latest
77+
name: Close Pull Request Job
78+
steps:
79+
- name: Close Pull Request
80+
id: closepullrequest
81+
uses: Azure/static-web-apps-deploy@v1
82+
with:
83+
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_KIND_PLANT_0E80E5803 }}
84+
action: "close"

app/api/profile/route.ts

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,87 @@
11
import { NextRequest, NextResponse } from "next/server";
2+
import { Client, Databases } from "node-appwrite";
23

3-
// Mock user data - replace with your actual database
4-
const mockUser = {
5-
id: "user_123",
6-
7-
subscription: "free",
4+
// Initialize Appwrite client
5+
function getAppwriteClient() {
6+
const client = new Client()
7+
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || "")
8+
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || "")
9+
.setKey(process.env.NEXT_PUBLIC_APPWRITE_API_KEY || "");
10+
11+
return new Databases(client);
12+
}
13+
14+
// Default user data structure
15+
const defaultUser = {
16+
subscription: "free" as const,
817
apiKeys: {
918
openai: "",
1019
gemini: "",
1120
mistral: "",
1221
deepseek: "",
1322
},
1423
preferences: {
15-
explanationProvider: "ollama",
24+
explanationProvider: "ollama" as const,
1625
},
1726
};
1827

1928
export async function GET(request: NextRequest) {
2029
try {
21-
// TODO: Get user from authentication context
22-
// const user = await getCurrentUser(request);
30+
// TODO: Get user email from authentication context
31+
// For now, we'll try to get it from query params or use a default
32+
// In production, you should get this from your auth system
33+
const email =
34+
request.nextUrl.searchParams.get("email") || "[email protected]";
35+
36+
const DATABASE_ID = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID;
37+
const USERS_COLLECTION_ID = "users";
38+
39+
if (!DATABASE_ID) {
40+
console.warn("Appwrite not configured, returning default user");
41+
return NextResponse.json({
42+
id: "default",
43+
email,
44+
...defaultUser,
45+
});
46+
}
47+
48+
try {
49+
const databases = getAppwriteClient();
50+
const { Query } = await import("node-appwrite");
51+
52+
// Try to find user by email
53+
const users = await databases.listDocuments(
54+
DATABASE_ID,
55+
USERS_COLLECTION_ID,
56+
[Query.equal("email", email)],
57+
);
2358

24-
// For now, return mock data
25-
return NextResponse.json(mockUser);
59+
if (users.documents.length > 0) {
60+
const user = users.documents[0];
61+
return NextResponse.json({
62+
id: user.$id,
63+
email: user.email || email,
64+
subscription: user.subscription || defaultUser.subscription,
65+
apiKeys: user.apiKeys || defaultUser.apiKeys,
66+
preferences: user.preferences || defaultUser.preferences,
67+
});
68+
} else {
69+
// User doesn't exist yet, return default
70+
return NextResponse.json({
71+
id: "new",
72+
email,
73+
...defaultUser,
74+
});
75+
}
76+
} catch (dbError: any) {
77+
console.error("Database error:", dbError);
78+
// Return default user on error
79+
return NextResponse.json({
80+
id: "error",
81+
email,
82+
...defaultUser,
83+
});
84+
}
2685
} catch (error) {
2786
console.error("Error fetching profile:", error);
2887
return NextResponse.json(

app/api/stripe/create-checkout-session/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export async function POST(request: NextRequest) {
8686
cancel_url: `${baseUrl}/pricing?canceled=true`,
8787
allow_promotion_codes: true,
8888
billing_address_collection: "required",
89+
customer_email: request.headers.get("x-user-email") || undefined, // Get from auth header if available
8990
metadata: {
9091
priceId: priceId.trim(),
9192
},

0 commit comments

Comments
 (0)