upfynai-code 2.9.1 → 2.9.2
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.
- package/README.md +91 -66
- package/client/dist/api-docs.html +838 -0
- package/client/dist/assets/AppContent-BXZDeSIC.js +545 -0
- package/client/dist/assets/CanvasFullScreen-mnpCnLZ9.js +1 -0
- package/client/dist/assets/CanvasWorkspace-4CqmjAVQ.js +163 -0
- package/client/dist/assets/DashboardPanel-zFIFlw56.js +1 -0
- package/client/dist/assets/FileTree-B0c_GaB3.js +1 -0
- package/client/dist/assets/GitPanel-DUP4zVU4.js +2 -0
- package/client/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/client/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/client/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/client/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/client/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/client/dist/assets/LoginModal-BRycfsyD.js +13 -0
- package/client/dist/assets/MarkdownPreview-DHmk3qzu.js +1 -0
- package/client/dist/assets/MermaidBlock-BuBc_G-F.js +2 -0
- package/client/dist/assets/Onboarding-BcnaZZ0o.js +1 -0
- package/client/dist/assets/PreviewPanel-CqCa92Tf.js +32 -0
- package/client/dist/assets/SetupForm-S0g6u5yT.js +1 -0
- package/client/dist/assets/WorkflowsPanel-CouH9JDO.js +1 -0
- package/client/dist/assets/index-BFuqS0tY.css +1 -0
- package/client/dist/assets/index-CNDcVl2g.js +68 -0
- package/client/dist/assets/pdf-CE_K4jFx.js +12 -0
- package/client/dist/assets/vendor-canvas-BZV40eAE.css +1 -0
- package/client/dist/assets/vendor-canvas-D39yWul6.js +49 -0
- package/client/dist/assets/vendor-codemirror-CbtmxxaB.js +35 -0
- package/client/dist/assets/vendor-diff-DNQpbhrT.js +69 -0
- package/client/dist/assets/vendor-i18n-DCFGyhQR.js +1 -0
- package/client/dist/assets/vendor-icons-BaD0x9SL.js +711 -0
- package/client/dist/assets/vendor-markdown-CimbIo6Y.js +296 -0
- package/client/dist/assets/vendor-mermaid-CH7SGc99.js +2556 -0
- package/client/dist/assets/vendor-react-96lCPsRK.js +67 -0
- package/client/dist/assets/vendor-syntax-DuHI9Ok6.js +16 -0
- package/client/dist/assets/vendor-xterm-CZq1hqo1.js +66 -0
- package/client/dist/assets/vendor-xterm-qxJ8_QYu.css +32 -0
- package/client/dist/clear-cache.html +85 -0
- package/client/dist/convert-icons.md +53 -0
- package/client/dist/favicon.png +0 -0
- package/client/dist/favicon.svg +5 -0
- package/client/dist/generate-icons.js +49 -0
- package/client/dist/icons/claude-ai-icon.svg +1 -0
- package/client/dist/icons/codex-white.svg +3 -0
- package/client/dist/icons/codex.svg +3 -0
- package/client/dist/icons/cursor-white.svg +12 -0
- package/client/dist/icons/cursor.svg +1 -0
- package/client/dist/icons/icon-128x128.png +0 -0
- package/client/dist/icons/icon-128x128.svg +5 -0
- package/client/dist/icons/icon-144x144.png +0 -0
- package/client/dist/icons/icon-144x144.svg +5 -0
- package/client/dist/icons/icon-152x152.png +0 -0
- package/client/dist/icons/icon-152x152.svg +5 -0
- package/client/dist/icons/icon-192x192.png +0 -0
- package/client/dist/icons/icon-192x192.svg +5 -0
- package/client/dist/icons/icon-384x384.png +0 -0
- package/client/dist/icons/icon-384x384.svg +5 -0
- package/client/dist/icons/icon-512x512.png +0 -0
- package/client/dist/icons/icon-512x512.svg +5 -0
- package/client/dist/icons/icon-72x72.png +0 -0
- package/client/dist/icons/icon-72x72.svg +5 -0
- package/client/dist/icons/icon-96x96.png +0 -0
- package/client/dist/icons/icon-96x96.svg +5 -0
- package/client/dist/icons/icon-template.svg +5 -0
- package/client/dist/index.html +119 -0
- package/client/dist/logo-128.png +0 -0
- package/client/dist/logo-256.png +0 -0
- package/client/dist/logo-32.png +0 -0
- package/client/dist/logo-512.png +0 -0
- package/client/dist/logo-64.png +0 -0
- package/client/dist/logo.svg +14 -0
- package/client/dist/manifest.json +61 -0
- package/client/dist/mcp-docs.html +108 -0
- package/client/dist/offline.html +84 -0
- package/client/dist/screenshots/cli-selection.png +0 -0
- package/client/dist/screenshots/desktop-main.png +0 -0
- package/client/dist/screenshots/mobile-chat.png +0 -0
- package/client/dist/screenshots/tools-modal.png +0 -0
- package/client/dist/sw.js +82 -0
- package/commands/upfynai-connect.md +59 -0
- package/commands/upfynai-disconnect.md +31 -0
- package/commands/upfynai-doctor.md +99 -0
- package/commands/upfynai-export.md +49 -0
- package/commands/upfynai-local.md +82 -0
- package/commands/upfynai-status.md +75 -0
- package/commands/upfynai-stop.md +49 -0
- package/commands/upfynai-uninstall.md +58 -0
- package/commands/upfynai.md +69 -0
- package/package.json +143 -82
- package/scripts/build-client.js +17 -0
- package/scripts/fix-node-pty.js +67 -0
- package/scripts/install-commands.js +78 -0
- package/server/agent-loop.js +242 -0
- package/server/auto-compact.js +99 -0
- package/server/claude-sdk.js +797 -0
- package/server/cli-ui.js +785 -0
- package/server/cli.js +596 -0
- package/server/constants/config.js +31 -0
- package/server/cursor-cli.js +270 -0
- package/server/database/auth.db +0 -0
- package/server/database/db.js +1391 -0
- package/server/database/init.sql +70 -0
- package/server/index.js +3799 -0
- package/server/load-env.js +26 -0
- package/server/mcp-server.js +621 -0
- package/server/middleware/auth.js +176 -0
- package/server/middleware/relayHelpers.js +44 -0
- package/server/middleware/sandboxRouter.js +174 -0
- package/server/openai-codex.js +403 -0
- package/server/openrouter.js +137 -0
- package/server/projects.js +1807 -0
- package/server/provider-factory.js +174 -0
- package/server/relay-client.js +379 -0
- package/server/routes/agent.js +1226 -0
- package/server/routes/auth.js +554 -0
- package/server/routes/canvas.js +53 -0
- package/server/routes/cli-auth.js +263 -0
- package/server/routes/codex.js +396 -0
- package/server/routes/commands.js +707 -0
- package/server/routes/composio.js +176 -0
- package/server/routes/cursor.js +770 -0
- package/server/routes/dashboard.js +295 -0
- package/server/routes/git.js +1208 -0
- package/server/routes/keys.js +34 -0
- package/server/routes/mcp-utils.js +48 -0
- package/server/routes/mcp.js +661 -0
- package/server/routes/payments.js +227 -0
- package/server/routes/projects.js +655 -0
- package/server/routes/sessions.js +146 -0
- package/server/routes/settings.js +261 -0
- package/server/routes/taskmaster.js +1928 -0
- package/server/routes/user.js +106 -0
- package/server/routes/vapi-chat.js +624 -0
- package/server/routes/voice.js +235 -0
- package/server/routes/webhooks.js +166 -0
- package/server/routes/workflows.js +312 -0
- package/server/sandbox.js +120 -0
- package/server/services/composio.js +204 -0
- package/server/services/sessionRegistry.js +139 -0
- package/server/services/whisperService.js +84 -0
- package/server/services/workflowScheduler.js +206 -0
- package/server/tests/relay-flow.test.js +570 -0
- package/server/tests/sessions.test.js +259 -0
- package/server/utils/commandParser.js +303 -0
- package/server/utils/email.js +61 -0
- package/server/utils/gitConfig.js +24 -0
- package/server/utils/mcp-detector.js +198 -0
- package/server/utils/taskmaster-websocket.js +129 -0
- package/shared/integrationCatalog.d.ts +12 -0
- package/shared/integrationCatalog.js +172 -0
- package/shared/modelConstants.js +96 -0
- package/bin/cli.js +0 -97
- package/dist/agents/claude.js +0 -229
- package/dist/agents/codex.js +0 -48
- package/dist/agents/cursor.js +0 -48
- package/dist/agents/detect.js +0 -51
- package/dist/agents/exec.js +0 -31
- package/dist/agents/files.js +0 -105
- package/dist/agents/git.js +0 -18
- package/dist/agents/gitagent.js +0 -67
- package/dist/agents/index.js +0 -88
- package/dist/agents/shell.js +0 -38
- package/dist/agents/utils.js +0 -136
- package/scripts/postinstall.js +0 -9
- package/scripts/prepublish.js +0 -58
- package/src/animation.js +0 -228
- package/src/auth.js +0 -122
- package/src/config.js +0 -40
- package/src/connect.js +0 -416
- package/src/launch.js +0 -78
- package/src/mcp.js +0 -57
- package/src/permissions.js +0 -140
- package/src/persistent-shell.js +0 -261
- package/src/server.js +0 -54
- /package/{dist → shared}/gitagent/index.js +0 -0
- /package/{dist → shared}/gitagent/parser.js +0 -0
- /package/{dist → shared}/gitagent/prompt-builder.js +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import { subscriptionDb, paymentDb, userDb, PLAN_DURATIONS } from '../database/db.js';
|
|
4
|
+
|
|
5
|
+
const router = express.Router();
|
|
6
|
+
|
|
7
|
+
// Plan prices in paise (INR × 100) — source of truth
|
|
8
|
+
const PLAN_PRICES = {
|
|
9
|
+
monthly: { amount: 49900, original: 49900 }, // ₹499
|
|
10
|
+
'half-yearly': { amount: 249900, original: 249900 }, // ₹2,499
|
|
11
|
+
yearly: { amount: 49900, original: 499900 }, // ₹499 offer (original ₹4,999)
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Plan tier ordering — higher number = higher tier
|
|
15
|
+
const PLAN_TIERS = { monthly: 1, 'half-yearly': 2, yearly: 3 };
|
|
16
|
+
|
|
17
|
+
// Lazy-load Razorpay instance
|
|
18
|
+
let _razorpay = null;
|
|
19
|
+
async function getRazorpay() {
|
|
20
|
+
if (_razorpay) return _razorpay;
|
|
21
|
+
const keyId = process.env.RAZORPAY_KEY_ID?.trim();
|
|
22
|
+
const keySecret = process.env.RAZORPAY_KEY_SECRET?.trim();
|
|
23
|
+
if (!keyId || !keySecret) throw new Error('Razorpay credentials not configured');
|
|
24
|
+
const Razorpay = (await import('razorpay')).default;
|
|
25
|
+
_razorpay = new Razorpay({ key_id: keyId, key_secret: keySecret });
|
|
26
|
+
return _razorpay;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Create Order ───────────────────────────────────────────────────────────────
|
|
30
|
+
// Body: { planId, isDowngrade? }
|
|
31
|
+
// isDowngrade: if true, uses changePlan (cancels current sub + starts fresh)
|
|
32
|
+
// otherwise uses createSub (extends from current expiry for upgrades/renewals)
|
|
33
|
+
router.post('/create-order', async (req, res) => {
|
|
34
|
+
try {
|
|
35
|
+
const userId = req.user.id;
|
|
36
|
+
const { planId, isDowngrade } = req.body;
|
|
37
|
+
|
|
38
|
+
const plan = PLAN_PRICES[planId];
|
|
39
|
+
if (!planId || !plan) {
|
|
40
|
+
return res.status(400).json({ error: 'Invalid plan' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const amountPaise = plan.amount;
|
|
44
|
+
const razorpay = await getRazorpay();
|
|
45
|
+
|
|
46
|
+
const notes = { userId: String(userId), planId };
|
|
47
|
+
if (plan.original !== plan.amount) {
|
|
48
|
+
notes.originalPrice = String(plan.original);
|
|
49
|
+
notes.offerPrice = String(plan.amount);
|
|
50
|
+
}
|
|
51
|
+
if (isDowngrade) notes.isDowngrade = 'true';
|
|
52
|
+
|
|
53
|
+
const order = await razorpay.orders.create({
|
|
54
|
+
amount: amountPaise,
|
|
55
|
+
currency: 'INR',
|
|
56
|
+
receipt: `upfyn_${userId}_${planId}_${Date.now()}`,
|
|
57
|
+
notes,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Record pending payment
|
|
61
|
+
await paymentDb.createPayment(userId, planId, amountPaise, 'INR', order.id);
|
|
62
|
+
|
|
63
|
+
res.json({
|
|
64
|
+
orderId: order.id,
|
|
65
|
+
amount: amountPaise,
|
|
66
|
+
originalAmount: plan.original,
|
|
67
|
+
currency: 'INR',
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
res.status(500).json({ error: 'Could not create order' });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// ─── Verify Payment ─────────────────────────────────────────────────────────────
|
|
75
|
+
// Body: { orderId, paymentId, signature, planId, isDowngrade? }
|
|
76
|
+
router.post('/verify', async (req, res) => {
|
|
77
|
+
try {
|
|
78
|
+
const userId = req.user.id;
|
|
79
|
+
const { orderId, paymentId, signature, planId, isDowngrade } = req.body;
|
|
80
|
+
|
|
81
|
+
if (!orderId || !paymentId || !signature || !planId) {
|
|
82
|
+
return res.status(400).json({ error: 'Missing payment details' });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Verify Razorpay signature
|
|
86
|
+
const keySecret = process.env.RAZORPAY_KEY_SECRET?.trim();
|
|
87
|
+
if (!keySecret) return res.status(500).json({ error: 'Server payment config missing' });
|
|
88
|
+
|
|
89
|
+
const expectedSig = crypto
|
|
90
|
+
.createHmac('sha256', keySecret)
|
|
91
|
+
.update(`${orderId}|${paymentId}`)
|
|
92
|
+
.digest('hex');
|
|
93
|
+
|
|
94
|
+
if (expectedSig !== signature) {
|
|
95
|
+
await paymentDb.markFailed(orderId);
|
|
96
|
+
return res.status(400).json({ error: 'Invalid payment signature' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Look up the pending payment to get the amount
|
|
100
|
+
const payment = await paymentDb.getByOrderId(orderId);
|
|
101
|
+
if (!payment) {
|
|
102
|
+
return res.status(404).json({ error: 'Order not found' });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (payment.user_id !== userId) {
|
|
106
|
+
return res.status(403).json({ error: 'Order does not belong to this user' });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let sub;
|
|
110
|
+
if (isDowngrade) {
|
|
111
|
+
// Downgrade: cancel current sub and create new one starting now
|
|
112
|
+
sub = await subscriptionDb.changePlan(
|
|
113
|
+
userId, planId, payment.amount, payment.currency,
|
|
114
|
+
orderId, paymentId, signature
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
// Upgrade or new: extends from current expiry (or starts now if no active sub)
|
|
118
|
+
sub = await subscriptionDb.createSub(
|
|
119
|
+
userId, planId, payment.amount, payment.currency,
|
|
120
|
+
orderId, paymentId, signature
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Mark payment as paid
|
|
125
|
+
await paymentDb.markPaid(orderId, paymentId, signature, sub.id);
|
|
126
|
+
|
|
127
|
+
// Grant paid access
|
|
128
|
+
await userDb.setAccessOverride(userId, 'paid');
|
|
129
|
+
|
|
130
|
+
res.json({
|
|
131
|
+
success: true,
|
|
132
|
+
subscription: {
|
|
133
|
+
id: sub.id,
|
|
134
|
+
planId: sub.planId,
|
|
135
|
+
status: sub.status,
|
|
136
|
+
startsAt: sub.startsAt,
|
|
137
|
+
expiresAt: sub.expiresAt,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
} catch (error) {
|
|
141
|
+
res.status(500).json({ error: 'Payment verification failed' });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// ─── Get Current Subscription ───────────────────────────────────────────────────
|
|
146
|
+
router.get('/subscription', async (req, res) => {
|
|
147
|
+
try {
|
|
148
|
+
await subscriptionDb.expireOverdue();
|
|
149
|
+
const sub = await subscriptionDb.getActiveSub(req.user.id);
|
|
150
|
+
res.json({ subscription: sub || null });
|
|
151
|
+
} catch (error) {
|
|
152
|
+
res.status(500).json({ error: 'Could not fetch subscription' });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ─── Get Payment History ────────────────────────────────────────────────────────
|
|
157
|
+
router.get('/history', async (req, res) => {
|
|
158
|
+
try {
|
|
159
|
+
const payments = await paymentDb.getUserPayments(req.user.id);
|
|
160
|
+
const subscriptions = await subscriptionDb.getAllSubs(req.user.id);
|
|
161
|
+
res.json({ payments, subscriptions });
|
|
162
|
+
} catch (error) {
|
|
163
|
+
res.status(500).json({ error: 'Could not fetch payment history' });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// ─── Cancel Subscription ────────────────────────────────────────────────────────
|
|
168
|
+
router.post('/cancel', async (req, res) => {
|
|
169
|
+
try {
|
|
170
|
+
const sub = await subscriptionDb.getActiveSub(req.user.id);
|
|
171
|
+
if (!sub) {
|
|
172
|
+
return res.status(404).json({ error: 'No active subscription found' });
|
|
173
|
+
}
|
|
174
|
+
const cancelled = await subscriptionDb.cancelSub(req.user.id, sub.id);
|
|
175
|
+
res.json({ success: cancelled });
|
|
176
|
+
} catch (error) {
|
|
177
|
+
res.status(500).json({ error: 'Could not cancel subscription' });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ─── Invoice / Receipt Details ─────────────────────────────────────────────────
|
|
182
|
+
// Returns payment details for a given payment ID (for receipt / invoice view)
|
|
183
|
+
router.get('/invoice/:paymentId', async (req, res) => {
|
|
184
|
+
try {
|
|
185
|
+
const payments = await paymentDb.getUserPayments(req.user.id);
|
|
186
|
+
const payment = payments.find(p => String(p.id) === req.params.paymentId);
|
|
187
|
+
if (!payment) {
|
|
188
|
+
return res.status(404).json({ error: 'Payment not found' });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Fetch payment details from Razorpay if payment ID is available
|
|
192
|
+
let razorpayDetails = null;
|
|
193
|
+
if (payment.razorpay_payment_id) {
|
|
194
|
+
try {
|
|
195
|
+
const razorpay = await getRazorpay();
|
|
196
|
+
razorpayDetails = await razorpay.payments.fetch(payment.razorpay_payment_id);
|
|
197
|
+
} catch { /* Razorpay fetch failed — return local data only */ }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const planName = {
|
|
201
|
+
monthly: 'Monthly Plan',
|
|
202
|
+
'half-yearly': '6-Month Plan',
|
|
203
|
+
yearly: 'Annual Plan',
|
|
204
|
+
}[payment.plan_id] || payment.plan_id;
|
|
205
|
+
|
|
206
|
+
res.json({
|
|
207
|
+
id: payment.id,
|
|
208
|
+
planId: payment.plan_id,
|
|
209
|
+
planName,
|
|
210
|
+
amount: payment.amount,
|
|
211
|
+
currency: payment.currency || 'INR',
|
|
212
|
+
status: payment.status,
|
|
213
|
+
createdAt: payment.created_at,
|
|
214
|
+
razorpayOrderId: payment.razorpay_order_id || null,
|
|
215
|
+
razorpayPaymentId: payment.razorpay_payment_id || null,
|
|
216
|
+
// Razorpay payment details (method, email, contact, etc.)
|
|
217
|
+
method: razorpayDetails?.method || null,
|
|
218
|
+
email: razorpayDetails?.email || null,
|
|
219
|
+
contact: razorpayDetails?.contact || null,
|
|
220
|
+
description: razorpayDetails?.description || null,
|
|
221
|
+
});
|
|
222
|
+
} catch (error) {
|
|
223
|
+
res.status(500).json({ error: 'Could not fetch invoice details' });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export default router;
|