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 admin = getAdmin(); const logger = getLogger(); const corsHandler = getCorsHandler(); export const createPhonePeOrder = 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 { const decodedToken = await admin.auth().verifyIdToken(idToken); const uid = decodedToken.uid; const { merchantOrderId, amount, expireAfter, metaInfo, paymentFlow } = request.body; if (!merchantOrderId || !amount || !paymentFlow || !expireAfter) { response.status(400).json({ error: 'Missing required fields' }); return; } if (!paymentFlow.type || !paymentFlow.merchantUrls || !paymentFlow.merchantUrls.redirectUrl) { response.status(400).json({ error: 'Invalid payment flow configuration' }); return; } 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; } try { 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 paymentResponse = await axios.post( `${apiUrl}/checkout/v2/pay`, request.body, { headers: { 'Content-Type': 'application/json', 'Authorization': `O-Bearer ${accessToken}`, }, } ); try { await admin.firestore().collection('payment_orders').doc(merchantOrderId).set({ userId: uid, amount: amount / 100, orderStatus: paymentResponse.data.state || 'PENDING', paymentGateway: 'PhonePe', createdAt: new Date(), merchantOrderId: merchantOrderId, paymentUrl: paymentResponse.data.redirectUrl, orderId: paymentResponse.data.orderId, expireAt: new Date(paymentResponse.data.expireAt), // rawResponse: paymentResponse.data, metaInfo: metaInfo || {} }); } catch (firestoreError) { logger.error('Error storing order in Firestore:', firestoreError); } response.json({ ...paymentResponse.data, merchantOrderId: merchantOrderId }); logger.info(`PhonePe order created: ${merchantOrderId}`); } catch (apiError: any) { logger.error('PhonePe API error:', apiError); response.status(apiError.response?.status || 500).json({ success: false, error: 'Payment gateway error', details: apiError.response?.data || apiError.message, code: apiError.code }); } } catch (authError) { logger.error('Authentication error:', authError); response.status(401).json({ success: false, error: 'Invalid authentication token' }); } } catch (error: any) { logger.error('PhonePe order creation error:', error); response.status(500).json({ success: false, error: 'Failed to create payment order', details: error.message }); } }); });