All checks were successful
Deploy FitLien services to Dev / Deploy to Dev (push) Successful in 4m15s
Co-authored-by: AllenTJ7 <163137620+AllenTJ7@users.noreply.github.com> Reviewed-on: #23 Co-authored-by: Allen T J <allentj@cosq.net> Co-committed-by: Allen T J <allentj@cosq.net>
134 lines
4.7 KiB
TypeScript
134 lines
4.7 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 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
|
|
});
|
|
}
|
|
});
|
|
});
|