This commit is contained in:
Flatlogic Bot 2025-05-24 05:55:59 +00:00
parent ba4e173c9f
commit e93f945df3
8 changed files with 181 additions and 1 deletions

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,25 @@ const config = {
clientId: process.env.MS_CLIENT_ID || '',
clientSecret: process.env.MS_CLIENT_SECRET || '',
},
amadeus: {
clientId: process.env.AMADEUS_CLIENT_ID || 'YnlNGEWG0bMODXXFh59aWczdKy2Abr3l',
clientSecret: process.env.AMADEUS_CLIENT_SECRET || 'YaSiX7mGOu3dhDz6',
baseURL: 'https://test.api.amadeus.com',
},
tap: {
secretKey: process.env.TAP_SECRET_KEY || 'sk_test_XKokBfNWv6FIYuTMg5LPjhJ',
publishableKey: process.env.TAP_PUBLISHABLE_KEY || 'pk_test_EtHFV4BuPQokJT6jiROls87Y',
merchantId: process.env.TAP_MERCHANT_ID || '26374580',
baseURL: 'https://api.tap.company',
successUrl: process.env.TAP_SUCCESS_URL || 'https://flyaru.com/payment-success',
failureUrl: process.env.TAP_FAILURE_URL || 'https://flyaru.com/payment-failed',
webhookUrl: process.env.TAP_WEBHOOK_URL || 'https://flyaru.com/api/webhook',
force3DSecure: true,
capture: 'capture',
refundWindowDays: 30,
},
uploadDir: os.tmpdir(),
email: {
from: 'FlyAru-Powered by Arwa Travel Group <app@flatlogic.app>',

View File

@ -36,6 +36,8 @@ const permissionsRoutes = require('./routes/permissions');
const agenciesRoutes = require('./routes/agencies');
const cartRoutes = require('./routes/cart');
const amadeusRoutes = require('./routes/amadeus');
const getBaseUrl = (url) => {
if (!url) return '';
@ -150,6 +152,15 @@ app.use(
cartRoutes,
);
// Amadeus flight search routes
app.use(
'/api/amadeus',
passport.authenticate('jwt', { session: false }),
amadeusRoutes
);
app.use(
'/api/openai',
passport.authenticate('jwt', { session: false }),

View File

@ -0,0 +1,30 @@
const express = require('express');
const wrapAsync = require('../helpers').wrapAsync;
const passport = require('passport');
const AmadeusService = require('../services/amadeusService');
const router = express.Router();
/**
* @route GET /api/amadeus/flights
* @desc Search flights via Amadeus
* Query params: originLocationCode, destinationLocationCode, departureDate, adults, currency, etc.
*/
router.get(
'/flights',
passport.authenticate('jwt', { session: false }),
wrapAsync(async (req, res) => {
const params = {
originLocationCode: req.query.originLocationCode,
destinationLocationCode: req.query.destinationLocationCode,
departureDate: req.query.departureDate,
adults: req.query.adults || 1,
max: req.query.max || 10,
currency: req.query.currency || 'SAR'
};
const result = await AmadeusService.searchFlights(params);
res.json(result);
})
);
module.exports = router;

View File

@ -4,6 +4,12 @@ const CartService = require('../services/cart');
const CartDBApi = require('../db/api/cart');
const wrapAsync = require('../helpers').wrapAsync;
const tapService = require('../services/tapService');
const { v4: uuidv4 } = require('uuid');
const db = require('../db/models');
const passport = require('passport');
const config = require('../config');
const router = express.Router();
@ -434,6 +440,45 @@ router.get(
wrapAsync(async (req, res) => {
const payload = await CartDBApi.findBy({ id: req.params.id });
// Checkout endpoint
router.post(
'/checkout',
passport.authenticate('jwt', { session: false }),
wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const carts = await CartDBApi.findAll({ user: currentUser.id }, null, { currentUser });
if (!carts.rows.length) {
return res.status(400).json({ message: 'Cart is empty.' });
}
const cart = carts.rows[0];
const items = await db.cartitem.findAll({ where: { cartId: cart.id } });
const amount = items.reduce((sum, item) => sum + item.quantity * item.unitprice, 0);
const chargeData = {
amount,
currency: config.tap.currency || 'SAR',
threeDSecure: config.tap.force3DSecure,
capture: config.tap.capture,
description: `Charge for cart ${cart.id}`,
reference: {
track: uuidv4(),
payment: uuidv4(),
order: cart.id,
},
post: { url: config.tap.webhookUrl },
redirect: { url: config.tap.successUrl },
customer: {
id: currentUser.id,
email: currentUser.email,
first_name: currentUser.firstName,
last_name: currentUser.lastName,
},
};
const charge = await tapService.createCharge(chargeData);
res.json({ charge });
})
);
res.status(200).send(payload);
}),
);

View File

@ -0,0 +1,51 @@
const axios = require('axios');
const qs = require('qs');
const config = require('../config');
let accessToken = null;
let tokenExpiresAt = 0;
/**
* Obtain or refresh Amadeus OAuth token
*/
async function getAccessToken() {
const now = Date.now();
if (accessToken && now < tokenExpiresAt) {
return accessToken;
}
const tokenUrl = `${config.amadeus.baseURL}/v1/security/oauth2/token`;
const data = qs.stringify({
grant_type: 'client_credentials',
client_id: config.amadeus.clientId,
client_secret: config.amadeus.clientSecret,
});
const resp = await axios.post(tokenUrl, data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
accessToken = resp.data.access_token;
// expires_in is seconds
tokenExpiresAt = now + resp.data.expires_in * 1000 - 60000; // refresh 1 min early
return accessToken;
}
/**
* Search flights via Amadeus
* @param {Object} params originLocationCode, destinationLocationCode, departureDate, adults, currency
*/
async function searchFlights(params) {
const token = await getAccessToken();
const url = `${config.amadeus.baseURL}/v2/shopping/flight-offers`;
const resp = await axios.get(url, {
params: params,
headers: {
Authorization: `Bearer ${token}`
}
});
return resp.data;
}
module.exports = {
searchFlights,
};

View File

@ -0,0 +1,22 @@
const axios = require('axios');
const config = require('../config');
// Initialize Tap API client
const tapClient = axios.create({
baseURL: config.tap.baseURL,
headers: {
Authorization: `Bearer ${config.tap.secretKey}`,
'Content-Type': 'application/json',
},
});
/**
* Create a charge via Tap Payments
* @param {Object} data - payload for /v2/charges
*/
async function createCharge(data) {
const resp = await tapClient.post('/v2/charges', data);
return resp.data;
}
module.exports = { createCharge };

View File

@ -0,0 +1 @@
{}