ft-scout 8.0.4 → 8.0.6
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/.firebase/hosting.d2Vi.cache +6 -6
- package/README.md +2 -2
- package/bin/src/commands/agent.d.ts.map +1 -1
- package/bin/src/commands/agent.js +6 -0
- package/bin/src/commands/agent.js.map +1 -1
- package/bin/src/commands/audit.d.ts.map +1 -1
- package/bin/src/commands/audit.js +8 -2
- package/bin/src/commands/audit.js.map +1 -1
- package/bin/src/commands/dashboard.d.ts.map +1 -1
- package/bin/src/commands/dashboard.js +9 -7
- package/bin/src/commands/dashboard.js.map +1 -1
- package/bin/src/commands/plan.d.ts.map +1 -0
- package/bin/src/commands/plan.js +70 -0
- package/bin/src/commands/plan.js.map +1 -0
- package/bin/src/commands/quota.d.ts.map +1 -1
- package/bin/src/commands/quota.js +4 -20
- package/bin/src/commands/quota.js.map +1 -1
- package/bin/src/commands/risky.d.ts.map +1 -1
- package/bin/src/commands/risky.js +8 -2
- package/bin/src/commands/risky.js.map +1 -1
- package/bin/src/index.js +17 -7
- package/bin/src/index.js.map +1 -1
- package/bin/src/server/server.d.ts.map +1 -1
- package/bin/src/server/server.js +125 -0
- package/bin/src/server/server.js.map +1 -1
- package/bin/src/utils/auth.d.ts.map +1 -1
- package/bin/src/utils/auth.js +382 -7
- package/bin/src/utils/auth.js.map +1 -1
- package/bin/src/utils/branding.js +1 -1
- package/bin/src/utils/payment.d.ts.map +1 -0
- package/bin/src/utils/payment.js +262 -0
- package/bin/src/utils/payment.js.map +1 -0
- package/bin/src/utils/razorpay.d.ts.map +1 -0
- package/bin/src/utils/razorpay.js +136 -0
- package/bin/src/utils/razorpay.js.map +1 -0
- package/firebase.json +15 -0
- package/package.json +3 -2
- package/web/app.js +366 -31
- package/web/styles.css +2 -2
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
import { loadAuthConfig, saveAuthConfig } from './auth.js';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
dotenv.config({ path: path.resolve(__dirname, '../../../.env'), quiet: true });
|
|
9
|
+
dotenv.config({ quiet: true });
|
|
10
|
+
export const PLAN_CONFIGS = {
|
|
11
|
+
free: {
|
|
12
|
+
id: 'free',
|
|
13
|
+
name: 'Free Community',
|
|
14
|
+
monthlyPrice: 0,
|
|
15
|
+
annualPrice: 0,
|
|
16
|
+
credits: 100,
|
|
17
|
+
description: 'Get started with codebase onboarding',
|
|
18
|
+
features: ['100 AI credits / month', 'Standard response time', '1 CLI session slot', 'Community support'],
|
|
19
|
+
},
|
|
20
|
+
pro: {
|
|
21
|
+
id: 'pro',
|
|
22
|
+
name: 'Pro Developer',
|
|
23
|
+
monthlyPrice: 19,
|
|
24
|
+
annualPrice: 15,
|
|
25
|
+
credits: 1000,
|
|
26
|
+
description: 'For active engineers navigating complex codebases',
|
|
27
|
+
features: ['1,000 AI credits / month', 'Fast-track reasoning engine', '5 concurrent CLI session slots', 'Priority support response'],
|
|
28
|
+
},
|
|
29
|
+
enterprise: {
|
|
30
|
+
id: 'enterprise',
|
|
31
|
+
name: 'Team & Enterprise',
|
|
32
|
+
monthlyPrice: 49,
|
|
33
|
+
annualPrice: 39,
|
|
34
|
+
credits: 10000,
|
|
35
|
+
description: 'Full organization control and dedicated scale',
|
|
36
|
+
features: ['10,000 AI credits / month', 'Continuous in-memory screen streaming HUD', 'Unlimited CLI session connections', 'Dedicated account SLA support'],
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
export function getPayPalBaseUrl() {
|
|
40
|
+
return process.env.PAYPAL_MODE === 'live'
|
|
41
|
+
? 'https://api-m.paypal.com'
|
|
42
|
+
: 'https://api-m.sandbox.paypal.com';
|
|
43
|
+
}
|
|
44
|
+
export async function getPayPalAccessToken() {
|
|
45
|
+
const clientId = process.env.PAYPAL_CLIENT_ID;
|
|
46
|
+
const clientSecret = process.env.PAYPAL_CLIENT_SECRET;
|
|
47
|
+
if (!clientId || !clientSecret) {
|
|
48
|
+
throw new Error('PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET must be configured in environment variables.');
|
|
49
|
+
}
|
|
50
|
+
const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
51
|
+
const baseUrl = getPayPalBaseUrl();
|
|
52
|
+
const response = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Authorization': `Basic ${auth}`,
|
|
56
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
57
|
+
},
|
|
58
|
+
body: 'grant_type=client_credentials',
|
|
59
|
+
});
|
|
60
|
+
const data = await safeJsonParse(response, 'PayPal OAuth token');
|
|
61
|
+
if (!response.ok || !data.access_token) {
|
|
62
|
+
throw new Error(`PayPal OAuth failed: ${data.error_description || data.error || 'Unable to retrieve access token'}`);
|
|
63
|
+
}
|
|
64
|
+
return data.access_token;
|
|
65
|
+
}
|
|
66
|
+
async function safeJsonParse(res, endpointLabel) {
|
|
67
|
+
const text = await res.text();
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(text);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
const preview = text.trim().replace(/\s+/g, ' ').slice(0, 150);
|
|
73
|
+
throw new Error(`PayPal API returned non-JSON response from ${endpointLabel} (HTTP ${res.status} ${res.statusText}): ${preview}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function getPaymentProvider() {
|
|
77
|
+
return 'paypal';
|
|
78
|
+
}
|
|
79
|
+
export function getPublicPaymentConfig() {
|
|
80
|
+
const isLive = process.env.PAYPAL_MODE === 'live';
|
|
81
|
+
const hasPayPalKeys = Boolean(process.env.PAYPAL_CLIENT_ID && process.env.PAYPAL_CLIENT_SECRET);
|
|
82
|
+
return {
|
|
83
|
+
provider: 'paypal',
|
|
84
|
+
isSandbox: !isLive || !hasPayPalKeys,
|
|
85
|
+
paypalClientId: process.env.PAYPAL_CLIENT_ID || undefined,
|
|
86
|
+
razorpayKeyId: process.env.RAZORPAY_KEY_ID || undefined,
|
|
87
|
+
plans: PLAN_CONFIGS,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates a checkout order strictly with PayPal.
|
|
92
|
+
*/
|
|
93
|
+
export async function createCheckoutSession(params) {
|
|
94
|
+
const tier = params.tier;
|
|
95
|
+
if (tier === 'free') {
|
|
96
|
+
throw new Error('Cannot create a payment session for the free tier.');
|
|
97
|
+
}
|
|
98
|
+
const plan = PLAN_CONFIGS[tier];
|
|
99
|
+
if (!plan) {
|
|
100
|
+
throw new Error(`Unknown tier: ${tier}`);
|
|
101
|
+
}
|
|
102
|
+
const billingCycle = params.billingCycle === 'annual' ? 'annual' : 'monthly';
|
|
103
|
+
const unitPrice = billingCycle === 'annual' ? plan.annualPrice * 12 : plan.monthlyPrice;
|
|
104
|
+
const DEFAULT_PLANS_URL = process.env.FRONTTERRAIN_DASHBOARD_URL
|
|
105
|
+
? `${process.env.FRONTTERRAIN_DASHBOARD_URL.replace(/\/$/, '')}/plans`
|
|
106
|
+
: 'https://scout.frontterrain.com/plans';
|
|
107
|
+
const accessToken = await getPayPalAccessToken();
|
|
108
|
+
const baseUrl = getPayPalBaseUrl();
|
|
109
|
+
const returnUrl = params.returnUrl || DEFAULT_PLANS_URL;
|
|
110
|
+
const successReturnUrl = `${returnUrl}${returnUrl.includes('?') ? '&' : '?'}success=true&tier=${tier}`;
|
|
111
|
+
const cancelReturnUrl = params.cancelUrl || `${returnUrl}${returnUrl.includes('?') ? '&' : '?'}canceled=true`;
|
|
112
|
+
const orderPayload = {
|
|
113
|
+
intent: 'CAPTURE',
|
|
114
|
+
purchase_units: [
|
|
115
|
+
{
|
|
116
|
+
reference_id: `scout_${tier}_${Date.now()}`,
|
|
117
|
+
description: `FrontTerrain Scout ${plan.name} (${billingCycle})`,
|
|
118
|
+
amount: {
|
|
119
|
+
currency_code: 'USD',
|
|
120
|
+
value: unitPrice.toFixed(2),
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
application_context: {
|
|
125
|
+
brand_name: 'FrontTerrain Scout',
|
|
126
|
+
landing_page: 'BILLING',
|
|
127
|
+
user_action: 'PAY_NOW',
|
|
128
|
+
return_url: successReturnUrl,
|
|
129
|
+
cancel_url: cancelReturnUrl,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
const orderRes = await fetch(`${baseUrl}/v2/checkout/orders`, {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: {
|
|
135
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify(orderPayload),
|
|
139
|
+
});
|
|
140
|
+
const orderData = await safeJsonParse(orderRes, 'PayPal create order');
|
|
141
|
+
if (!orderRes.ok || !orderData.id) {
|
|
142
|
+
throw new Error(`PayPal Order Creation Failed: ${orderData.message || JSON.stringify(orderData)}`);
|
|
143
|
+
}
|
|
144
|
+
const approveLink = orderData.links?.find((l) => l.rel === 'approve')?.href;
|
|
145
|
+
if (!approveLink) {
|
|
146
|
+
throw new Error('PayPal did not return an approval URL for this order.');
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
success: true,
|
|
150
|
+
provider: 'paypal',
|
|
151
|
+
sessionId: orderData.id,
|
|
152
|
+
orderId: orderData.id,
|
|
153
|
+
checkoutUrl: approveLink,
|
|
154
|
+
amount: unitPrice,
|
|
155
|
+
currency: 'USD',
|
|
156
|
+
tier,
|
|
157
|
+
billingCycle,
|
|
158
|
+
isSandbox: process.env.PAYPAL_MODE !== 'live',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Strictly verifies the PayPal transaction using PayPal REST API.
|
|
163
|
+
* Validates order status, currency, and paid amount against the requested plan.
|
|
164
|
+
* Absolutely no simulation or fake tokens allowed.
|
|
165
|
+
*/
|
|
166
|
+
export async function verifyAndApplyUpgrade(params) {
|
|
167
|
+
const targetOrderId = (params.orderId || params.token || params.sessionId || '').trim();
|
|
168
|
+
const tier = params.tier;
|
|
169
|
+
if (!targetOrderId) {
|
|
170
|
+
throw new Error('PayPal payment verification failed: Missing order ID or payment token.');
|
|
171
|
+
}
|
|
172
|
+
const plan = PLAN_CONFIGS[tier];
|
|
173
|
+
if (!plan || tier === 'free') {
|
|
174
|
+
throw new Error(`Invalid plan tier specified for upgrade: ${tier}`);
|
|
175
|
+
}
|
|
176
|
+
const billingCycle = params.billingCycle === 'annual' ? 'annual' : 'monthly';
|
|
177
|
+
const expectedPrice = billingCycle === 'annual' ? plan.annualPrice * 12 : plan.monthlyPrice;
|
|
178
|
+
// Strict verification against real PayPal API
|
|
179
|
+
const accessToken = await getPayPalAccessToken();
|
|
180
|
+
const baseUrl = getPayPalBaseUrl();
|
|
181
|
+
let orderData = null;
|
|
182
|
+
let transactionId = '';
|
|
183
|
+
// Step 1: Attempt to capture the PayPal order
|
|
184
|
+
const captureRes = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(targetOrderId)}/capture`, {
|
|
185
|
+
method: 'POST',
|
|
186
|
+
headers: {
|
|
187
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
188
|
+
'Content-Type': 'application/json',
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
const captureJson = await safeJsonParse(captureRes, 'PayPal capture order');
|
|
192
|
+
if (captureRes.ok && (captureJson.status === 'COMPLETED' || captureJson.status === 'SAVED')) {
|
|
193
|
+
orderData = captureJson;
|
|
194
|
+
const captureUnit = captureJson.purchase_units?.[0]?.payments?.captures?.[0];
|
|
195
|
+
transactionId = captureUnit?.id || captureJson.id;
|
|
196
|
+
}
|
|
197
|
+
else if (captureJson.status === 'COMPLETED' ||
|
|
198
|
+
captureJson.name === 'UNPROCESSABLE_ENTITY' ||
|
|
199
|
+
captureJson.details?.some((d) => d.issue === 'ORDER_ALREADY_CAPTURED')) {
|
|
200
|
+
// Step 2: If order was already captured, fetch order details to verify
|
|
201
|
+
const getRes = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(targetOrderId)}`, {
|
|
202
|
+
method: 'GET',
|
|
203
|
+
headers: {
|
|
204
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
205
|
+
'Content-Type': 'application/json',
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const getJson = await safeJsonParse(getRes, 'PayPal get order');
|
|
209
|
+
if (getRes.ok && getJson.status === 'COMPLETED') {
|
|
210
|
+
orderData = getJson;
|
|
211
|
+
const captureUnit = getJson.purchase_units?.[0]?.payments?.captures?.[0];
|
|
212
|
+
transactionId = captureUnit?.id || getJson.id;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
throw new Error(`PayPal Order Verification Failed: Order status is "${getJson.status || 'UNKNOWN'}" (must be COMPLETED).`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const errorMsg = captureJson.message || captureJson.details?.[0]?.description || JSON.stringify(captureJson);
|
|
220
|
+
throw new Error(`PayPal capture failed: ${errorMsg}`);
|
|
221
|
+
}
|
|
222
|
+
// Step 3: Strict verification of amount, currency, and purchase details
|
|
223
|
+
if (!orderData || orderData.status !== 'COMPLETED') {
|
|
224
|
+
throw new Error(`PayPal payment verification rejected: Order status is ${orderData?.status || 'INCOMPLETE'}.`);
|
|
225
|
+
}
|
|
226
|
+
const purchaseUnit = orderData.purchase_units?.[0];
|
|
227
|
+
const captureInfo = purchaseUnit?.payments?.captures?.[0];
|
|
228
|
+
const paidCurrency = captureInfo?.amount?.currency_code || purchaseUnit?.amount?.currency_code;
|
|
229
|
+
const paidAmount = parseFloat(captureInfo?.amount?.value || purchaseUnit?.amount?.value || '0');
|
|
230
|
+
if (paidCurrency !== 'USD') {
|
|
231
|
+
throw new Error(`PayPal payment verification rejected: Expected currency USD, received ${paidCurrency}.`);
|
|
232
|
+
}
|
|
233
|
+
// Allow a small 0.01 tolerance for floating point representations
|
|
234
|
+
if (paidAmount < expectedPrice - 0.01) {
|
|
235
|
+
throw new Error(`PayPal payment verification rejected: Paid amount ($${paidAmount.toFixed(2)}) does not match required plan price ($${expectedPrice.toFixed(2)}).`);
|
|
236
|
+
}
|
|
237
|
+
// Step 4: Apply verified upgrade to local auth config
|
|
238
|
+
const config = loadAuthConfig();
|
|
239
|
+
const now = new Date();
|
|
240
|
+
const renewalDate = new Date(now);
|
|
241
|
+
renewalDate.setMonth(renewalDate.getMonth() + (billingCycle === 'annual' ? 12 : 1));
|
|
242
|
+
const updatedConfig = {
|
|
243
|
+
...config,
|
|
244
|
+
currentTier: tier,
|
|
245
|
+
creditsTotal: plan.credits,
|
|
246
|
+
creditsUsed: 0, // Reset used credits upon verified purchase
|
|
247
|
+
resetCredits: true,
|
|
248
|
+
transactionId,
|
|
249
|
+
renewAt: renewalDate.toISOString(),
|
|
250
|
+
};
|
|
251
|
+
saveAuthConfig(updatedConfig);
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
tier,
|
|
255
|
+
creditsAdded: plan.credits,
|
|
256
|
+
totalCredits: plan.credits,
|
|
257
|
+
renewAt: renewalDate.toISOString(),
|
|
258
|
+
auth: updatedConfig,
|
|
259
|
+
transactionId: transactionId || targetOrderId,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=payment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.js","sourceRoot":"","sources":["payment.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAmB,MAAM,WAAW,CAAC;AAE5E,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/E,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAgB/B,MAAM,CAAC,MAAM,YAAY,GAAkC;IACvD,IAAI,EAAE;QACF,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,gBAAgB;QACtB,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,OAAO,EAAE,GAAG;QACZ,WAAW,EAAE,sCAAsC;QACnD,QAAQ,EAAE,CAAC,wBAAwB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,mBAAmB,CAAC;KAC5G;IACD,GAAG,EAAE;QACD,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,eAAe;QACrB,YAAY,EAAE,EAAE;QAChB,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,mDAAmD;QAChE,QAAQ,EAAE,CAAC,0BAA0B,EAAE,6BAA6B,EAAE,gCAAgC,EAAE,2BAA2B,CAAC;KACvI;IACD,UAAU,EAAE;QACR,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,mBAAmB;QACzB,YAAY,EAAE,EAAE;QAChB,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,+CAA+C;QAC5D,QAAQ,EAAE,CAAC,2BAA2B,EAAE,2CAA2C,EAAE,mCAAmC,EAAE,+BAA+B,CAAC;KAC7J;CACJ,CAAC;AAUF,MAAM,UAAU,gBAAgB;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,MAAM;QACrC,CAAC,CAAC,0BAA0B;QAC5B,CAAC,CAAC,kCAAkC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC9C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAEtD,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC9G,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IAEnC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,kBAAkB,EAAE;QACvD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,eAAe,EAAE,SAAS,IAAI,EAAE;YAChC,cAAc,EAAE,mCAAmC;SACtD;QACD,IAAI,EAAE,+BAA+B;KACxC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAQ,MAAM,aAAa,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IACtE,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,KAAK,IAAI,iCAAiC,EAAE,CAAC,CAAC;IACzH,CAAC;IAED,OAAO,IAAI,CAAC,YAAY,CAAC;AAC7B,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAa,EAAE,aAAqB;IAC7D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACL,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,8CAA8C,aAAa,UAAU,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,MAAM,OAAO,EAAE,CAAC,CAAC;IACtI,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kBAAkB;IAC9B,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,sBAAsB;IAClC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,MAAM,CAAC;IAClD,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAEhG,OAAO;QACH,QAAQ,EAAE,QAAQ;QAClB,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,aAAa;QACpC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,SAAS;QACzD,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS;QACvD,KAAK,EAAE,YAAY;KACtB,CAAC;AACN,CAAC;AAwBD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAA4B;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,MAAM,SAAS,GAAG,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IAExF,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B;QAC5D,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ;QACtE,CAAC,CAAC,sCAAsC,CAAC;IAE7C,MAAM,WAAW,GAAG,MAAM,oBAAoB,EAAE,CAAC;IACjD,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,iBAAiB,CAAC;IACxD,MAAM,gBAAgB,GAAG,GAAG,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,qBAAqB,IAAI,EAAE,CAAC;IACvG,MAAM,eAAe,GAAG,MAAM,CAAC,SAAS,IAAI,GAAG,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC;IAE9G,MAAM,YAAY,GAAG;QACjB,MAAM,EAAE,SAAS;QACjB,cAAc,EAAE;YACZ;gBACI,YAAY,EAAE,SAAS,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;gBAC3C,WAAW,EAAE,sBAAsB,IAAI,CAAC,IAAI,KAAK,YAAY,GAAG;gBAChE,MAAM,EAAE;oBACJ,aAAa,EAAE,KAAK;oBACpB,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;iBAC9B;aACJ;SACJ;QACD,mBAAmB,EAAE;YACjB,UAAU,EAAE,oBAAoB;YAChC,YAAY,EAAE,SAAS;YACvB,WAAW,EAAE,SAAS;YACtB,UAAU,EAAE,gBAAgB;YAC5B,UAAU,EAAE,eAAe;SAC9B;KACJ,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,qBAAqB,EAAE;QAC1D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,eAAe,EAAE,UAAU,WAAW,EAAE;YACxC,cAAc,EAAE,kBAAkB;SACrC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;KACrC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAQ,MAAM,aAAa,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IAC5E,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,iCAAiC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,EAAE,IAAI,CAAC;IACjF,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO;QACH,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,QAAQ;QAClB,SAAS,EAAE,SAAS,CAAC,EAAE;QACvB,OAAO,EAAE,SAAS,CAAC,EAAE;QACrB,WAAW,EAAE,WAAW;QACxB,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,KAAK;QACf,IAAI;QACJ,YAAY;QACZ,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,MAAM;KAChD,CAAC;AACN,CAAC;AAqBD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAA2B;IACnE,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAEzB,IAAI,CAAC,aAAa,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,MAAM,aAAa,GAAG,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IAE5F,8CAA8C;IAC9C,MAAM,WAAW,GAAG,MAAM,oBAAoB,EAAE,CAAC;IACjD,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IAEnC,IAAI,SAAS,GAAQ,IAAI,CAAC;IAC1B,IAAI,aAAa,GAAG,EAAE,CAAC;IAEvB,8CAA8C;IAC9C,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,uBAAuB,kBAAkB,CAAC,aAAa,CAAC,UAAU,EAAE;QACzG,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,eAAe,EAAE,UAAU,WAAW,EAAE;YACxC,cAAc,EAAE,kBAAkB;SACrC;KACJ,CAAC,CAAC;IAEH,MAAM,WAAW,GAAQ,MAAM,aAAa,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;IAEjF,IAAI,UAAU,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,OAAO,CAAC,EAAE,CAAC;QAC1F,SAAS,GAAG,WAAW,CAAC;QACxB,MAAM,WAAW,GAAG,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7E,aAAa,GAAG,WAAW,EAAE,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC;IACtD,CAAC;SAAM,IACH,WAAW,CAAC,MAAM,KAAK,WAAW;QAClC,WAAW,CAAC,IAAI,KAAK,sBAAsB;QAC3C,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,wBAAwB,CAAC,EAC7E,CAAC;QACC,uEAAuE;QACvE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,uBAAuB,kBAAkB,CAAC,aAAa,CAAC,EAAE,EAAE;YAC7F,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACL,eAAe,EAAE,UAAU,WAAW,EAAE;gBACxC,cAAc,EAAE,kBAAkB;aACrC;SACJ,CAAC,CAAC;QAEH,MAAM,OAAO,GAAQ,MAAM,aAAa,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAC9C,SAAS,GAAG,OAAO,CAAC;YACpB,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YACzE,aAAa,GAAG,WAAW,EAAE,EAAE,IAAI,OAAO,CAAC,EAAE,CAAC;QAClD,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,KAAK,CAAC,sDAAsD,OAAO,CAAC,MAAM,IAAI,SAAS,wBAAwB,CAAC,CAAC;QAC/H,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC7G,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,wEAAwE;IACxE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,yDAAyD,SAAS,EAAE,MAAM,IAAI,YAAY,GAAG,CAAC,CAAC;IACnH,CAAC;IAED,MAAM,YAAY,GAAG,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,WAAW,EAAE,MAAM,EAAE,aAAa,IAAI,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC;IAC/F,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;IAEhG,IAAI,YAAY,KAAK,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,yEAAyE,YAAY,GAAG,CAAC,CAAC;IAC9G,CAAC;IAED,kEAAkE;IAClE,IAAI,UAAU,GAAG,aAAa,GAAG,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,uDAAuD,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,0CAA0C,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxK,CAAC;IAED,sDAAsD;IACtD,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAClC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEpF,MAAM,aAAa,GAAe;QAC9B,GAAG,MAAM;QACT,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,IAAI,CAAC,OAAO;QAC1B,WAAW,EAAE,CAAC,EAAE,4CAA4C;QAC5D,YAAY,EAAE,IAAI;QAClB,aAAa;QACb,OAAO,EAAE,WAAW,CAAC,WAAW,EAAE;KACrC,CAAC;IAEF,cAAc,CAAC,aAAa,CAAC,CAAC;IAE9B,OAAO;QACH,OAAO,EAAE,IAAI;QACb,IAAI;QACJ,YAAY,EAAE,IAAI,CAAC,OAAO;QAC1B,YAAY,EAAE,IAAI,CAAC,OAAO;QAC1B,OAAO,EAAE,WAAW,CAAC,WAAW,EAAE;QAClC,IAAI,EAAE,aAAa;QACnB,aAAa,EAAE,aAAa,IAAI,aAAa;KAChD,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"razorpay.d.ts","sourceRoot":"","sources":["razorpay.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAGhC,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAI5E,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAc9C;AAED,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAqC/F;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CAyExF"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import Razorpay from 'razorpay';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { loadAuthConfig, saveAuthConfig } from './auth.js';
|
|
5
|
+
dotenv.config();
|
|
6
|
+
export function getRazorpayKeyId() {
|
|
7
|
+
return process.env.RAZORPAY_KEY_ID || '';
|
|
8
|
+
}
|
|
9
|
+
export function getRazorpayKeySecret() {
|
|
10
|
+
return process.env.RAZORPAY_KEY_SECRET || '';
|
|
11
|
+
}
|
|
12
|
+
export function getRazorpayInstance() {
|
|
13
|
+
const key_id = getRazorpayKeyId();
|
|
14
|
+
const key_secret = getRazorpayKeySecret();
|
|
15
|
+
if (!key_id || !key_secret) {
|
|
16
|
+
const err = new Error('Razorpay credentials missing: RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET must be configured in environment variables.');
|
|
17
|
+
err.statusCode = 401;
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
return new Razorpay({
|
|
21
|
+
key_id,
|
|
22
|
+
key_secret,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* STEP 1: BACKEND - Create Razorpay Order
|
|
27
|
+
* Endpoint: POST /api/create-order
|
|
28
|
+
* Validates amount >= 100 paise and creates order via Razorpay API.
|
|
29
|
+
*/
|
|
30
|
+
export async function createRazorpayOrder(params) {
|
|
31
|
+
const amount = Number(params.amount);
|
|
32
|
+
if (isNaN(amount) || amount < 100) {
|
|
33
|
+
const err = new Error('Validation Error: Minimum amount must be at least 100 paise.');
|
|
34
|
+
err.statusCode = 400;
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
const currency = (params.currency || 'INR').toUpperCase();
|
|
38
|
+
const receipt = params.receipt || `rcpt_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
39
|
+
const razorpay = getRazorpayInstance();
|
|
40
|
+
try {
|
|
41
|
+
const order = await razorpay.orders.create({
|
|
42
|
+
amount,
|
|
43
|
+
currency,
|
|
44
|
+
receipt,
|
|
45
|
+
notes: params.notes || {},
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
order_id: order.id,
|
|
49
|
+
amount: Number(order.amount),
|
|
50
|
+
currency: order.currency,
|
|
51
|
+
receipt: order.receipt || receipt,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err.statusCode === 401 || (err.error && err.error.code === 'BAD_REQUEST_ERROR' && err.error.description?.toLowerCase().includes('auth'))) {
|
|
56
|
+
const authErr = new Error(`Razorpay authentication failed: ${err.error?.description || err.message}`);
|
|
57
|
+
authErr.statusCode = 401;
|
|
58
|
+
throw authErr;
|
|
59
|
+
}
|
|
60
|
+
const apiErr = new Error(`Razorpay API Error: ${err.error?.description || err.message || 'Failed to create order'}`);
|
|
61
|
+
apiErr.statusCode = 500;
|
|
62
|
+
throw apiErr;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* STEP 3: BACKEND - Verify Signature
|
|
67
|
+
* Endpoint: POST /api/verify-payment
|
|
68
|
+
* Algorithm: HMAC-SHA256(order_id + "|" + payment_id, KEY_SECRET)
|
|
69
|
+
* Returns success only if generated signature strictly matches.
|
|
70
|
+
*/
|
|
71
|
+
export function verifyRazorpaySignature(params) {
|
|
72
|
+
const order_id = (params.order_id || '').trim();
|
|
73
|
+
const payment_id = (params.payment_id || '').trim();
|
|
74
|
+
const signature = (params.signature || '').trim();
|
|
75
|
+
// Validate missing fields: return 400
|
|
76
|
+
if (!order_id || !payment_id || !signature) {
|
|
77
|
+
const err = new Error('Missing required fields: order_id, payment_id, and signature are required.');
|
|
78
|
+
err.statusCode = 400;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
const secret = getRazorpayKeySecret();
|
|
82
|
+
if (!secret) {
|
|
83
|
+
const err = new Error('Razorpay Key Secret is not configured.');
|
|
84
|
+
err.statusCode = 500;
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
// Generate HMAC-SHA256 signature
|
|
88
|
+
const body = `${order_id}|${payment_id}`;
|
|
89
|
+
const expectedSignature = crypto
|
|
90
|
+
.createHmac('sha256', secret)
|
|
91
|
+
.update(body)
|
|
92
|
+
.digest('hex');
|
|
93
|
+
let isValid = false;
|
|
94
|
+
try {
|
|
95
|
+
isValid = crypto.timingSafeEqual(Buffer.from(expectedSignature, 'utf-8'), Buffer.from(signature, 'utf-8'));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
isValid = false;
|
|
99
|
+
}
|
|
100
|
+
if (!isValid) {
|
|
101
|
+
const err = new Error('Payment signature verification failed. Signature mismatch.');
|
|
102
|
+
err.statusCode = 400;
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
// Provision tier upgrade and persist to auth configuration
|
|
106
|
+
const tier = params.tier || 'pro';
|
|
107
|
+
const totalCredits = tier === 'enterprise' ? 10000 : (tier === 'pro' ? 1000 : 100);
|
|
108
|
+
const renewAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
109
|
+
let updatedAuth;
|
|
110
|
+
try {
|
|
111
|
+
const existing = loadAuthConfig();
|
|
112
|
+
const updated = {
|
|
113
|
+
...existing,
|
|
114
|
+
currentTier: tier,
|
|
115
|
+
creditsTotal: totalCredits,
|
|
116
|
+
creditsUsed: 0,
|
|
117
|
+
renewAt,
|
|
118
|
+
lastPaymentId: payment_id,
|
|
119
|
+
lastOrderId: order_id,
|
|
120
|
+
paymentProvider: 'razorpay',
|
|
121
|
+
};
|
|
122
|
+
saveAuthConfig(updated);
|
|
123
|
+
updatedAuth = updated;
|
|
124
|
+
}
|
|
125
|
+
catch { }
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
order_id,
|
|
129
|
+
payment_id,
|
|
130
|
+
tier,
|
|
131
|
+
totalCredits,
|
|
132
|
+
renewAt,
|
|
133
|
+
auth: updatedAuth,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=razorpay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"razorpay.js","sourceRoot":"","sources":["razorpay.ts"],"names":[],"mappings":"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"}
|
package/firebase.json
CHANGED
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
],
|
|
10
10
|
"cleanUrls": true,
|
|
11
11
|
"trailingSlash": false,
|
|
12
|
+
"headers": [
|
|
13
|
+
{
|
|
14
|
+
"source": "/**",
|
|
15
|
+
"headers": [
|
|
16
|
+
{
|
|
17
|
+
"key": "Content-Security-Policy",
|
|
18
|
+
"value": "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com https://www.paypal.com https://www.sandbox.paypal.com https://*.paypalobjects.com https://apis.google.com https://static.cloudflareinsights.com https://checkout.razorpay.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com https://cdnjs.cloudflare.com data:; img-src 'self' data: blob: https: https://*.paypalobjects.com https://cdn.razorpay.com; connect-src 'self' http://localhost:* http://127.0.0.1:* https://localhost:* https://identitytoolkit.googleapis.com https://securetoken.googleapis.com https://*.paypal.com https://*.paypalobjects.com https://api.razorpay.com https://lumberjack.razorpay.com https://lumberjack-rzp.razorpay.com https://api.openai.com https://api.groq.com https://integrate.api.nvidia.com https://generativelanguage.googleapis.com https://*.firebaseio.com https://*.googleapis.com https://cloudflareinsights.com https://static.cloudflareinsights.com; frame-src 'self' https://www.paypal.com https://www.sandbox.paypal.com https://*.firebaseapp.com https://api.razorpay.com; object-src 'none'; base-uri 'self';"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"key": "Cache-Control",
|
|
22
|
+
"value": "no-cache, no-store, must-revalidate"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
12
27
|
"rewrites": [
|
|
13
28
|
{
|
|
14
29
|
"source": "/auth",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ft-scout",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "FrontTerrain — The Onboarding Co-Pilot for New Codebases",
|
|
6
6
|
"main": "index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"dotenv": "^17.4.2",
|
|
41
41
|
"nodemailer": "^9.0.4",
|
|
42
42
|
"openai": "^7.1.0",
|
|
43
|
+
"razorpay": "^2.9.8",
|
|
43
44
|
"simple-git": "^3.36.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
@@ -47,4 +48,4 @@
|
|
|
47
48
|
"tsx": "^4.23.1",
|
|
48
49
|
"typescript": "^7.0.2"
|
|
49
50
|
}
|
|
50
|
-
}
|
|
51
|
+
}
|