push
Browse files- src/app.ts +30 -4
- src/controllers/walletController.ts +10 -12
src/app.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import express, { Application } from 'express';
|
| 2 |
import cors from 'cors';
|
|
|
|
| 3 |
import swaggerUi from 'swagger-ui-express';
|
| 4 |
import { swaggerSpec } from './docs/swagger';
|
| 5 |
|
|
@@ -66,17 +67,42 @@ app.use('/api/chat', chatRoutes);
|
|
| 66 |
app.use('/api/subscriptions', subscriptionRoutes);
|
| 67 |
app.use('/api/wallet', walletRoutes);
|
| 68 |
|
| 69 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
app.use((req, res) => {
|
| 71 |
-
// Only return JSON for API routes, otherwise just 404
|
| 72 |
if (req.path.startsWith('/api')) {
|
| 73 |
res.status(404).json({ error: 'Route not found' });
|
| 74 |
-
} else {
|
| 75 |
res.status(404).send('Not found');
|
| 76 |
}
|
| 77 |
});
|
| 78 |
|
| 79 |
-
// Simplified error handler
|
| 80 |
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
| 81 |
if (process.env.NODE_ENV === 'development') {
|
| 82 |
console.error('Error:', err.message);
|
|
|
|
| 1 |
import express, { Application } from 'express';
|
| 2 |
import cors from 'cors';
|
| 3 |
+
import path from 'path';
|
| 4 |
import swaggerUi from 'swagger-ui-express';
|
| 5 |
import { swaggerSpec } from './docs/swagger';
|
| 6 |
|
|
|
|
| 67 |
app.use('/api/subscriptions', subscriptionRoutes);
|
| 68 |
app.use('/api/wallet', walletRoutes);
|
| 69 |
|
| 70 |
+
// Serve frontend static files if they exist
|
| 71 |
+
const frontendPath = path.join(__dirname, '../../zurri-mock-frontend/dist');
|
| 72 |
+
let frontendExists = false;
|
| 73 |
+
try {
|
| 74 |
+
const fs = require('fs');
|
| 75 |
+
if (fs.existsSync(frontendPath) && fs.existsSync(path.join(frontendPath, 'index.html'))) {
|
| 76 |
+
app.use(express.static(frontendPath));
|
| 77 |
+
frontendExists = true;
|
| 78 |
+
}
|
| 79 |
+
} catch (error) {
|
| 80 |
+
// Frontend not available, continue without it
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
// Serve React app for all non-API routes (SPA routing)
|
| 84 |
+
// This must come after all API routes but before 404 handler
|
| 85 |
+
if (frontendExists) {
|
| 86 |
+
app.get('*', (req, res, next) => {
|
| 87 |
+
// Skip if it's an API, docs, health, or root route
|
| 88 |
+
if (req.path.startsWith('/api') || req.path.startsWith('/docs') || req.path.startsWith('/health') || req.path === '/') {
|
| 89 |
+
return next();
|
| 90 |
+
}
|
| 91 |
+
// Serve React app for all other routes (dashboard, wallet, etc.)
|
| 92 |
+
res.sendFile(path.join(frontendPath, 'index.html'));
|
| 93 |
+
});
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// 404 handler - only for API routes or if frontend doesn't exist
|
| 97 |
app.use((req, res) => {
|
|
|
|
| 98 |
if (req.path.startsWith('/api')) {
|
| 99 |
res.status(404).json({ error: 'Route not found' });
|
| 100 |
+
} else if (!frontendExists) {
|
| 101 |
res.status(404).send('Not found');
|
| 102 |
}
|
| 103 |
});
|
| 104 |
|
| 105 |
+
// Simplified error handler (must be last)
|
| 106 |
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
| 107 |
if (process.env.NODE_ENV === 'development') {
|
| 108 |
console.error('Error:', err.message);
|
src/controllers/walletController.ts
CHANGED
|
@@ -62,12 +62,10 @@ export class WalletController {
|
|
| 62 |
const paymentReference = `wallet_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
| 63 |
|
| 64 |
// Paystack callback should point to backend endpoint, which then redirects to frontend
|
| 65 |
-
//
|
| 66 |
-
|
| 67 |
const backendUrl = process.env.PAYSTACK_CALLBACK_URL
|
| 68 |
-
||
|
| 69 |
-
? `https://nexusbert-zurri.hf.space/api/wallet/callback`
|
| 70 |
-
: `${process.env.API_BASE_URL || process.env.BACKEND_URL || 'http://localhost:7860'}/api/wallet/callback`);
|
| 71 |
|
| 72 |
const paymentData = await getPaystackService().initializeTransaction({
|
| 73 |
email: user.email,
|
|
@@ -289,7 +287,7 @@ export class WalletController {
|
|
| 289 |
|
| 290 |
if (!reference) {
|
| 291 |
// Redirect to frontend error page
|
| 292 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 293 |
return res.redirect(`${frontendUrl}/payment/failed?error=missing_reference`);
|
| 294 |
}
|
| 295 |
|
|
@@ -299,7 +297,7 @@ export class WalletController {
|
|
| 299 |
|
| 300 |
// Check if transaction was successful
|
| 301 |
if (verification.status !== 'success') {
|
| 302 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 303 |
return res.redirect(
|
| 304 |
`${frontendUrl}/payment/failed?reference=${reference}&status=${verification.status}&message=${encodeURIComponent(verification.gatewayResponse || 'Payment failed')}`
|
| 305 |
);
|
|
@@ -308,7 +306,7 @@ export class WalletController {
|
|
| 308 |
// Extract userId from metadata
|
| 309 |
const userId = verification.metadata?.userId;
|
| 310 |
if (!userId) {
|
| 311 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 312 |
return res.redirect(
|
| 313 |
`${frontendUrl}/payment/failed?error=missing_user_id&reference=${reference}`
|
| 314 |
);
|
|
@@ -318,7 +316,7 @@ export class WalletController {
|
|
| 318 |
const existingTx = await walletService.getTransactionByReference(reference);
|
| 319 |
if (existingTx && existingTx.status === TransactionStatus.COMPLETED) {
|
| 320 |
// Already processed, redirect to success
|
| 321 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 322 |
const wallet = await walletService.getOrCreateWallet(userId);
|
| 323 |
return res.redirect(
|
| 324 |
`${frontendUrl}/payment/success?reference=${reference}&already_processed=true&balance=${Number(wallet.balance)}`
|
|
@@ -344,20 +342,20 @@ export class WalletController {
|
|
| 344 |
const wallet = await walletService.getOrCreateWallet(userId);
|
| 345 |
|
| 346 |
// Redirect to frontend success page with details
|
| 347 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 348 |
return res.redirect(
|
| 349 |
`${frontendUrl}/payment/success?reference=${reference}&points=${points}&balance=${Number(wallet.balance)}&amount=${amountInNaira}`
|
| 350 |
);
|
| 351 |
} catch (verifyError: any) {
|
| 352 |
console.error('Payment verification error in callback:', verifyError);
|
| 353 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 354 |
return res.redirect(
|
| 355 |
`${frontendUrl}/payment/failed?reference=${reference}&error=${encodeURIComponent(verifyError.message || 'Verification failed')}`
|
| 356 |
);
|
| 357 |
}
|
| 358 |
} catch (error: any) {
|
| 359 |
console.error('Payment callback error:', error);
|
| 360 |
-
const frontendUrl = process.env.FRONTEND_URL || '
|
| 361 |
return res.redirect(`${frontendUrl}/payment/failed?error=${encodeURIComponent(error.message || 'Unknown error')}`);
|
| 362 |
}
|
| 363 |
}
|
|
|
|
| 62 |
const paymentReference = `wallet_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
| 63 |
|
| 64 |
// Paystack callback should point to backend endpoint, which then redirects to frontend
|
| 65 |
+
// Use environment variable or detect hosted URL
|
| 66 |
+
const baseUrl = process.env.API_BASE_URL || process.env.BACKEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 67 |
const backendUrl = process.env.PAYSTACK_CALLBACK_URL
|
| 68 |
+
|| `${baseUrl}/api/wallet/callback`;
|
|
|
|
|
|
|
| 69 |
|
| 70 |
const paymentData = await getPaystackService().initializeTransaction({
|
| 71 |
email: user.email,
|
|
|
|
| 287 |
|
| 288 |
if (!reference) {
|
| 289 |
// Redirect to frontend error page
|
| 290 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 291 |
return res.redirect(`${frontendUrl}/payment/failed?error=missing_reference`);
|
| 292 |
}
|
| 293 |
|
|
|
|
| 297 |
|
| 298 |
// Check if transaction was successful
|
| 299 |
if (verification.status !== 'success') {
|
| 300 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 301 |
return res.redirect(
|
| 302 |
`${frontendUrl}/payment/failed?reference=${reference}&status=${verification.status}&message=${encodeURIComponent(verification.gatewayResponse || 'Payment failed')}`
|
| 303 |
);
|
|
|
|
| 306 |
// Extract userId from metadata
|
| 307 |
const userId = verification.metadata?.userId;
|
| 308 |
if (!userId) {
|
| 309 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 310 |
return res.redirect(
|
| 311 |
`${frontendUrl}/payment/failed?error=missing_user_id&reference=${reference}`
|
| 312 |
);
|
|
|
|
| 316 |
const existingTx = await walletService.getTransactionByReference(reference);
|
| 317 |
if (existingTx && existingTx.status === TransactionStatus.COMPLETED) {
|
| 318 |
// Already processed, redirect to success
|
| 319 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 320 |
const wallet = await walletService.getOrCreateWallet(userId);
|
| 321 |
return res.redirect(
|
| 322 |
`${frontendUrl}/payment/success?reference=${reference}&already_processed=true&balance=${Number(wallet.balance)}`
|
|
|
|
| 342 |
const wallet = await walletService.getOrCreateWallet(userId);
|
| 343 |
|
| 344 |
// Redirect to frontend success page with details
|
| 345 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 346 |
return res.redirect(
|
| 347 |
`${frontendUrl}/payment/success?reference=${reference}&points=${points}&balance=${Number(wallet.balance)}&amount=${amountInNaira}`
|
| 348 |
);
|
| 349 |
} catch (verifyError: any) {
|
| 350 |
console.error('Payment verification error in callback:', verifyError);
|
| 351 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 352 |
return res.redirect(
|
| 353 |
`${frontendUrl}/payment/failed?reference=${reference}&error=${encodeURIComponent(verifyError.message || 'Verification failed')}`
|
| 354 |
);
|
| 355 |
}
|
| 356 |
} catch (error: any) {
|
| 357 |
console.error('Payment callback error:', error);
|
| 358 |
+
const frontendUrl = process.env.FRONTEND_URL || 'https://nexusbert-zurri.hf.space';
|
| 359 |
return res.redirect(`${frontendUrl}/payment/failed?error=${encodeURIComponent(error.message || 'Unknown error')}`);
|
| 360 |
}
|
| 361 |
}
|