fitlien-services/functions/src/payments/phonepe/checkStatus.ts
2025-05-02 19:04:21 +05:30

140 lines
4.9 KiB
TypeScript

import axios from "axios";
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";
const admin = getAdmin();
const logger = getLogger();
const corsHandler = getCorsHandler();
export const checkPhonePePaymentStatus = 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];
try {
await admin.auth().verifyIdToken(idToken);
const merchantOrderId = request.query.merchantOrderId as string;
if (!merchantOrderId) {
response.status(400).json({ error: 'Missing merchant order ID' });
return;
}
const details = request.query.details === 'true';
const errorContext = request.query.errorContext === 'true';
const clientId = process.env.PHONEPE_CLIENT_ID;
const clientSecret = process.env.PHONEPE_CLIENT_SECRET;
const apiUrl = process.env.PHONEPE_API_URL;
const clientVersion = process.env.PHONEPE_CLIENT_VERSION || '1';
if (!clientId || !clientSecret || !apiUrl) {
logger.error('PhonePe credentials not configured');
response.status(500).json({ error: 'Payment gateway configuration error' });
return;
}
const tokenResponse = await axios.post(
`${apiUrl}/v1/oauth/token`,
{
client_id: clientId,
client_version: clientVersion,
client_secret: clientSecret,
grant_type: 'client_credentials',
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
const accessToken = tokenResponse.data.access_token;
const queryParams = new URLSearchParams();
if (details) queryParams.append('details', 'true');
if (errorContext) queryParams.append('errorContext', 'true');
const queryString = queryParams.toString() ? `?${queryParams.toString()}` : '';
const statusResponse = await axios.get(
`${apiUrl}/checkout/v2/order/${merchantOrderId}/status${queryString}`,
{
headers: {
'Content-Type': 'application/json',
'Authorization': `O-Bearer ${accessToken}`,
},
}
);
const orderQuery = await admin.firestore()
.collection('payment_orders')
.where('merchantOrderId', '==', merchantOrderId)
.limit(1)
.get();
if (orderQuery.empty) {
logger.error(`No payment order found with PhonePe orderId: ${merchantOrderId}`);
response.status(404).json({
success: false,
error: 'Payment order not found',
message: `No record found for PhonePe order ID: ${merchantOrderId}`
});
return;
}
const orderDoc = orderQuery.docs[0];
await orderDoc.ref.update({
orderStatus: statusResponse.data.state || 'UNKNOWN',
lastChecked: new Date(),
statusResponse: statusResponse.data
});
logger.info('PhonePe status response data:', JSON.stringify(statusResponse.data));
response.json({
success: true,
state: statusResponse.data.state,
data: statusResponse.data
});
} catch (authError: any) {
logger.error('Authentication error:', authError);
if (authError.response) {
logger.error('API error details:', {
status: authError.response.status,
data: authError.response.data
});
response.status(authError.response.status).json({
success: false,
error: 'API error',
details: authError.response.data
});
} else {
response.status(401).json({
success: false,
error: 'Invalid authentication token or API error',
message: authError.message
});
}
}
} catch (error: any) {
logger.error('PhonePe payment status check error:', error);
response.status(500).json({
success: false,
error: 'Failed to check payment status',
details: error.message
});
}
});
});