Skip to content

Commit e77890e

Browse files
committed
stripe integrated. testing phase in progress.
1 parent 87e4756 commit e77890e

File tree

8 files changed

+899
-684
lines changed

8 files changed

+899
-684
lines changed

bin/sync-stripe-test-env.js

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Stripe Test Environment Setup Script
5+
*
6+
* This script creates products and prices in your Stripe test environment and
7+
* updates the .env file with the new price IDs.
8+
*
9+
* Usage:
10+
* node bin/sync-stripe-test-env.js
11+
*/
12+
13+
import Stripe from 'stripe';
14+
import dotenv from 'dotenv-flow';
15+
import fs from 'fs';
16+
import path from 'path';
17+
import { fileURLToPath } from 'url';
18+
19+
// Get the directory name
20+
const __filename = fileURLToPath(import.meta.url);
21+
const __dirname = path.dirname(__filename);
22+
23+
// Load environment variables
24+
dotenv.config({ path: path.join(__dirname, '..') });
25+
26+
// Configuration
27+
const PRODUCTS = [
28+
{
29+
name: 'PDF Service Subscription',
30+
description: 'Subscription for PDF conversion service',
31+
prices: [
32+
{
33+
nickname: 'Monthly Subscription',
34+
unit_amount: 500, // $5.00
35+
currency: 'usd',
36+
recurring: {
37+
interval: 'month'
38+
},
39+
metadata: {
40+
plan: 'monthly'
41+
}
42+
},
43+
{
44+
nickname: 'Yearly Subscription',
45+
unit_amount: 3000, // $30.00
46+
currency: 'usd',
47+
recurring: {
48+
interval: 'year'
49+
},
50+
metadata: {
51+
plan: 'yearly'
52+
}
53+
}
54+
]
55+
}
56+
];
57+
58+
// Initialize Stripe
59+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
60+
apiVersion: '2023-10-16',
61+
});
62+
63+
async function main() {
64+
console.log('Starting Stripe test environment setup...');
65+
66+
// Create/update products and prices
67+
let monthlyPriceId = null;
68+
let yearlyPriceId = null;
69+
70+
for (const productConfig of PRODUCTS) {
71+
// Check if product exists
72+
let product;
73+
const existingProducts = await stripe.products.list({
74+
active: true,
75+
});
76+
77+
const existingProduct = existingProducts.data.find(p => p.name === productConfig.name);
78+
79+
if (existingProduct) {
80+
console.log(`Product '${productConfig.name}' already exists with ID: ${existingProduct.id}`);
81+
product = existingProduct;
82+
} else {
83+
console.log(`Creating product: ${productConfig.name}`);
84+
product = await stripe.products.create({
85+
name: productConfig.name,
86+
description: productConfig.description,
87+
active: true,
88+
});
89+
console.log(`Created product with ID: ${product.id}`);
90+
}
91+
92+
// Process prices for this product
93+
for (const priceConfig of productConfig.prices) {
94+
// Check for existing prices with the same nickname
95+
const existingPrices = await stripe.prices.list({
96+
active: true,
97+
product: product.id,
98+
});
99+
100+
const existingPrice = existingPrices.data.find(p => p.nickname === priceConfig.nickname);
101+
102+
if (existingPrice) {
103+
console.log(`Price '${priceConfig.nickname}' already exists with ID: ${existingPrice.id}`);
104+
105+
// Store the price IDs
106+
if (priceConfig.metadata.plan === 'monthly') {
107+
monthlyPriceId = existingPrice.id;
108+
} else if (priceConfig.metadata.plan === 'yearly') {
109+
yearlyPriceId = existingPrice.id;
110+
}
111+
} else {
112+
console.log(`Creating price: ${priceConfig.nickname}`);
113+
const newPrice = await stripe.prices.create({
114+
product: product.id,
115+
nickname: priceConfig.nickname,
116+
unit_amount: priceConfig.unit_amount,
117+
currency: priceConfig.currency,
118+
recurring: priceConfig.recurring,
119+
metadata: priceConfig.metadata,
120+
});
121+
122+
console.log(`Created price with ID: ${newPrice.id}`);
123+
124+
// Store the price IDs
125+
if (priceConfig.metadata.plan === 'monthly') {
126+
monthlyPriceId = newPrice.id;
127+
} else if (priceConfig.metadata.plan === 'yearly') {
128+
yearlyPriceId = newPrice.id;
129+
}
130+
}
131+
}
132+
}
133+
134+
// Update .env file with the new price IDs if they exist
135+
if (monthlyPriceId || yearlyPriceId) {
136+
console.log('Updating .env file with new price IDs...');
137+
138+
const envFilePath = path.join(__dirname, '..', '.env');
139+
let envContent = fs.readFileSync(envFilePath, 'utf8');
140+
141+
if (monthlyPriceId) {
142+
// Check if STRIPE_MONTHLY_PRICE_ID already exists in .env
143+
if (envContent.includes('STRIPE_MONTHLY_PRICE_ID=')) {
144+
// Replace existing value
145+
envContent = envContent.replace(
146+
/STRIPE_MONTHLY_PRICE_ID=.*/g,
147+
`STRIPE_MONTHLY_PRICE_ID=${monthlyPriceId}`
148+
);
149+
} else {
150+
// Add new variable
151+
envContent += `\nSTRIPE_MONTHLY_PRICE_ID=${monthlyPriceId}`;
152+
}
153+
console.log(`Set STRIPE_MONTHLY_PRICE_ID=${monthlyPriceId}`);
154+
}
155+
156+
if (yearlyPriceId) {
157+
// Check if STRIPE_YEARLY_PRICE_ID already exists in .env
158+
if (envContent.includes('STRIPE_YEARLY_PRICE_ID=')) {
159+
// Replace existing value
160+
envContent = envContent.replace(
161+
/STRIPE_YEARLY_PRICE_ID=.*/g,
162+
`STRIPE_YEARLY_PRICE_ID=${yearlyPriceId}`
163+
);
164+
} else {
165+
// Add new variable
166+
envContent += `\nSTRIPE_YEARLY_PRICE_ID=${yearlyPriceId}`;
167+
}
168+
console.log(`Set STRIPE_YEARLY_PRICE_ID=${yearlyPriceId}`);
169+
}
170+
171+
// Write updated content back to .env file
172+
fs.writeFileSync(envFilePath, envContent);
173+
174+
console.log('.env file updated successfully!');
175+
}
176+
177+
console.log('Stripe test environment setup complete!');
178+
}
179+
180+
// Run the main function
181+
main().catch(error => {
182+
console.error('Error:', error);
183+
process.exit(1);
184+
});

public/js/page-initializers.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,17 +331,33 @@ export function initRegisterPage() {
331331
throw new Error(checkoutData.error);
332332
}
333333

334-
if (!checkoutData.checkout_url) {
335-
throw new Error('No checkout URL returned from server');
334+
// Handle different response formats from the server
335+
// The server might return either {checkout_url} or a full session object with {id, url}
336+
const checkoutUrl = checkoutData.checkout_url || checkoutData.url;
337+
if (!checkoutUrl) {
338+
console.error('Invalid checkout response:', checkoutData);
339+
throw new Error('No checkout URL in the server response');
336340
}
337341

338342
// Store only temporary registration data before redirecting
339343
localStorage.setItem('temp_registration_email', email);
340344
localStorage.setItem('temp_registration_plan', selectedPlan);
345+
localStorage.setItem('temp_client_id', checkoutData.id || checkoutData.session_id || `temp_${Date.now()}`);
341346

342-
// Redirect directly to Stripe Checkout
343-
console.log('Redirecting to Stripe Checkout:', checkoutData.checkout_url);
344-
window.location.href = checkoutData.checkout_url;
347+
// Add timeout protection for the redirect
348+
console.log('Redirecting to Stripe Checkout:', checkoutUrl);
349+
350+
// Use a small timeout to ensure localStorage is updated before redirect
351+
setTimeout(() => {
352+
try {
353+
window.location.href = checkoutUrl;
354+
} catch (redirectError) {
355+
console.error('Redirect error:', redirectError);
356+
alert('Could not redirect to payment page. Please try again or contact support.');
357+
submitButton.textContent = originalButtonText;
358+
submitButton.disabled = false;
359+
}
360+
}, 100);
345361
return;
346362
} catch (error) {
347363
console.error('Error creating Stripe checkout session:', error);

src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import { WebSocketServer } from 'ws';
99

1010
import { registerRoutes } from './routes/index.js';
1111
import { errorHandler } from './middleware/error-handler.js';
12-
import { enableHttpDebugging } from './utils/http-debug.js';
12+
import { enableSafeHttpDebugging } from './utils/safe-http-debug.js';
1313

1414
// Enable HTTP debugging for detailed request/response logging
1515
console.log('Enabling HTTP debugging for detailed request/response logging');
16-
enableHttpDebugging();
16+
enableSafeHttpDebugging();
1717

1818
// Load environment variables
1919
dotenvFlow.config();

src/routes/auth.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,27 @@ export async function registerHandler(c) {
3636
// Continue with registration attempt even if check fails
3737
}
3838

39+
// First, explicitly check if user exists using a different method
40+
let existingUser;
41+
try {
42+
const { data, error } = await supabase.auth.admin.listUsers();
43+
if (!error && data && data.users) {
44+
existingUser = data.users.find(user => user.email === email);
45+
}
46+
} catch (listError) {
47+
console.warn(`Could not check user list: ${listError.message}`);
48+
}
49+
50+
// If user already exists, return success directly as this isn't an error case
51+
if (existingUser) {
52+
console.log(`User already exists, returning success: ${email}`);
53+
return c.json({
54+
success: true,
55+
message: 'User registered successfully',
56+
email
57+
});
58+
}
59+
3960
// Register user with Supabase using the admin API for better reliability
4061
try {
4162
const { data: adminAuthData, error: adminAuthError } = await supabase.auth.admin.createUser({
@@ -49,6 +70,16 @@ export async function registerHandler(c) {
4970
});
5071

5172
if (adminAuthError) {
73+
// Special handling for email_exists error - this is still a success case
74+
if (adminAuthError.code === 'email_exists') {
75+
console.log(`Admin API failed due to email exists, returning success: ${email}`);
76+
return c.json({
77+
success: true,
78+
message: 'User registered successfully',
79+
email
80+
});
81+
}
82+
5283
console.error('Admin user creation error:', adminAuthError);
5384
throw adminAuthError;
5485
}

0 commit comments

Comments
 (0)