import crypto from "crypto"; import axios from "axios"; import dotenv from 'dotenv'; import path from "path"; import { fileURLToPath } from "url"; import { getData } from "../libs/cache.js"; import Logs from "../libs/logs.js"; dotenv.config(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PARTNER_DATA_FILE = path.join(__dirname, '../data/partner.json'); /** * Partner API environment variables */ const PARTNER_API_BASE = 'https://dev.api.ppai.win/api/p/gate'; const { PARTNER_APP_ID, PARTNER_APP_KEY } = process.env; const PARTNER_DATA = {}; /** * 加载本地数据 */ const loadLocalData = async () => { const data = await getData(PARTNER_DATA_FILE); Object.assign(PARTNER_DATA, data); } loadLocalData().catch(error => { Logs.out('not load partner data', error.message); }); /** * 生成SHA256 */ const generateSHA256 = (message) => { return crypto.createHash('sha256').update(message).digest('hex'); } /** * 生成随机 nonce */ const generateNonce = () => { return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); } /** * 生成签名 */ const generateSignature = (appKey, action, timestamp, nonce) => { return generateSHA256(`${appKey}${action}${timestamp}${nonce}`); } /** * 获取时间戳 秒级 */ const getTimestamp = () => { return Math.floor(Date.now() / 1000); } /** * 时间窗口校验 */ const isTimestampValid = (timestamp, window=300) => { const now = getTimestamp(); return Math.abs(now - timestamp) <= window; } /** * 请求Partner数据接口 */ export const requestData = async (action, data) => { const timestamp = getTimestamp(); const nonce = generateNonce(); const sign = generateSignature(PARTNER_APP_KEY, action, timestamp, nonce); const appid = PARTNER_APP_ID; const requestData = { action, appid, nonce, sign, timestamp }; if (Object.keys(data).length > 0) { requestData.params = data; } return axios.post(PARTNER_API_BASE, requestData).then(res => res.data); } export const updateSolutions = async (solutions) => { if (process.env.NODE_ENV == 'development') { return Promise.resolve({ message: 'development mode' }); } return requestData('opps.soccer', solutions); } /** * 接收Partner数据 */ export const receivePartnerData = async (data) => { const { action, appid, nonce, sign, timestamp, params } = data; if (!isTimestampValid(timestamp)) { return Promise.reject(new Error('timestamp invalid')); } const partnerData = PARTNER_DATA[appid]; if (!partnerData) { return Promise.reject(new Error('appid invalid')); } const newSign = generateSignature(partnerData.appkey, action, timestamp, nonce); if (newSign !== sign) { return Promise.reject(new Error('sign invalid')); } return Promise.resolve({ action, params }); } export default { updateSolutions, receivePartnerData };