fitlien-services/functions/src/payments/cashfree/createLink.ts

134 lines
4.9 KiB
TypeScript

import { onRequest } from "firebase-functions/v2/https";
import { Request } from "firebase-functions/v2/https";
import { getCorsHandler } from "../../shared/middleware";
import { getAdmin, getLogger } from "../../shared/config";
import axios from "axios";
const { v4: uuidv4 } = require('uuid');
const corsHandler = getCorsHandler();
const admin = getAdmin();
const logger = getLogger();
interface CashfreeLinkRequest {
amount: number;
customerName?: string;
customerEmail: string;
customerPhone: string;
productInfo?: string;
userId?: string;
gymId?: string;
orderId: string;
}
interface CashfreeLinkResponse {
link_id: string;
link_url: string;
link_expiry_time: string;
link_status: string;
link_qrcode: string;
[key: string]: any;
}
export const createCashfreeLink = onRequest({
region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => {
return corsHandler(request, response, async () => {
try {
const authHeader = request.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
response.status(401).json({ error: 'Unauthorized' });
return;
}
const idToken = authHeader.split('Bearer ')[1];
const decodedToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedToken.uid;
const linkRequest = request.body as CashfreeLinkRequest;
if (!linkRequest.amount || !linkRequest.customerEmail || !linkRequest.customerPhone) {
response.status(400).json({ error: 'Missing required fields' });
return;
}
const clientId = process.env.CASHFREE_CLIENT_ID;
const clientSecret = process.env.CASHFREE_CLIENT_SECRET;
if (!clientId || !clientSecret) {
logger.error('Cashfree credentials not configured');
response.status(500).json({ error: 'Payment gateway configuration error' });
return;
}
const expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
const expirationString = expirationDate.toISOString();
const apiUrl = process.env.CASHFREE_LINK_URL;
const linkId = uuidv4();
const requestHeaders = {
'x-client-id': clientId,
'x-client-secret': clientSecret,
'x-api-version': '2025-01-01',
'Content-Type': 'application/json'
};
const requestBody = {
link_id: linkId,
link_amount: linkRequest.amount,
link_currency: "INR",
link_purpose: linkRequest.productInfo,
customer_details: {
customer_phone: linkRequest.customerPhone,
customer_email: linkRequest.customerEmail,
customer_name: linkRequest.customerName,
},
link_partial_payments: false,
link_notify: {
send_sms: true,
send_email: true
},
link_expiry_time: expirationString,
link_notes: {
order_id: linkRequest.orderId,
gym_id: linkRequest.gymId,
user_id: linkRequest.userId
}
};
const cashfreeResponse = await axios.post<CashfreeLinkResponse>(
apiUrl!,
requestBody,
{ headers: requestHeaders }
);
await admin.firestore().collection('payment_links').doc(linkRequest.orderId).set({
requestUserId: uid,
amount: linkRequest.amount,
customerEmail: linkRequest.customerEmail,
customerPhone: linkRequest.customerPhone,
userId: linkRequest.userId,
gymId: linkRequest.gymId,
orderId: linkRequest.orderId,
...cashfreeResponse.data,
createdAt: admin.firestore.FieldValue.serverTimestamp(),
});
response.json({
success: true,
linkId: linkId,
linkUrl: cashfreeResponse.data.link_url,
linkExpiryTime: cashfreeResponse.data.link_expiry_time,
linkStatus: cashfreeResponse.data.link_status,
linkQRCode: cashfreeResponse.data.link_qrcode
});
} catch (error: any) {
logger.error('Cashfree link creation error:', error);
const statusCode = error.response?.status || 500;
response.status(statusCode).json({
success: false,
error: error.response?.data?.message || 'Failed to create payment link',
details: error.response?.data || error.message
});
}
});
});