|
@@ -1,330 +1,72 @@
|
|
|
-// 导入必要的模块
|
|
|
|
|
-import Msg from "../utils/msg";
|
|
|
|
|
|
|
+import { Context } from "koa";
|
|
|
import { getServerList, formatDate } from "../utils/common";
|
|
import { getServerList, formatDate } from "../utils/common";
|
|
|
import { Account } from "../config/thirdParams";
|
|
import { Account } from "../config/thirdParams";
|
|
|
|
|
+import Msg from "../utils/msg";
|
|
|
|
|
+import axios from "axios";
|
|
|
|
|
|
|
|
const Order = require("../model/OrderModel");
|
|
const Order = require("../model/OrderModel");
|
|
|
const CryptoJS = require("crypto-js");
|
|
const CryptoJS = require("crypto-js");
|
|
|
-const axios = require("axios");
|
|
|
|
|
const logger = require("../utils/log");
|
|
const logger = require("../utils/log");
|
|
|
|
|
|
|
|
|
|
+// 导入小程序配置
|
|
|
|
|
+import {
|
|
|
|
|
+ MINI_APP_DOMAIN,
|
|
|
|
|
+ MINI_APP_PRIVATE_KEY,
|
|
|
|
|
+ MINI_APP_GAME_ID,
|
|
|
|
|
+ MINI_APP_API_KEY,
|
|
|
|
|
+ MINI_APP_STAT_API_KEY
|
|
|
|
|
+} from "../config/thirdParams";
|
|
|
|
|
+
|
|
|
|
|
+// 导入渠道处理器和配置
|
|
|
|
|
+import { channelFactory } from "../channels/factory/ChannelFactory";
|
|
|
|
|
+import { channelConfigs } from "../config/channelConfig";
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 小程序控制器
|
|
* 小程序控制器
|
|
|
- * 处理小程序相关的登录、支付、内容安全等功能
|
|
|
|
|
|
|
+ * 处理内容安全审核和角色名称修改上报功能
|
|
|
*/
|
|
*/
|
|
|
-class MiniAppController {
|
|
|
|
|
- // 小程序SDK接入配置
|
|
|
|
|
- // 游光文档:http://platform-doc.ttwmz.com/docs/channel/channel/awy/xyx-fear-end.html#%E9%89%B4%E6%9D%83
|
|
|
|
|
- private miniAppDomain: string = "wefunol.com"; // 小程序域名
|
|
|
|
|
- private miniAppPrivateKey: string = "yduLY3ovg4NwTHMMemGg1vO6VHuYBcYD"; // 发货私钥
|
|
|
|
|
- private miniAppGameId: string = "1540"; // 游戏ID
|
|
|
|
|
- private miniAppApiKey: string = "your_api_key_here"; // 内容安全API密钥
|
|
|
|
|
- private miniAppStatApiKey: string = "your_stat_api_key_here"; // 统计API密钥
|
|
|
|
|
-
|
|
|
|
|
|
|
+class MiniappController {
|
|
|
constructor() {
|
|
constructor() {
|
|
|
// 初始化配置
|
|
// 初始化配置
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 获取HTTPS代理配置
|
|
|
|
|
- * @returns HTTPS代理配置
|
|
|
|
|
|
|
+ * 获取小程序渠道处理器
|
|
|
|
|
+ * @returns 渠道处理器实例
|
|
|
*/
|
|
*/
|
|
|
- private getHttpsAgent() {
|
|
|
|
|
- const https = require('https');
|
|
|
|
|
- return new https.Agent({
|
|
|
|
|
- rejectUnauthorized: false,
|
|
|
|
|
- secureProtocol: 'TLSv1_2_method',
|
|
|
|
|
- timeout: 10000
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ private static getChannelHandler() {
|
|
|
|
|
+ return channelFactory.getHandler(11); // 小程序渠道ID
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 获取通用请求头
|
|
|
|
|
- * @returns 请求头配置
|
|
|
|
|
|
|
+ * 获取小程序渠道配置
|
|
|
|
|
+ * @returns 渠道配置
|
|
|
*/
|
|
*/
|
|
|
- private getCommonHeaders() {
|
|
|
|
|
- return {
|
|
|
|
|
- 'User-Agent': 'Mozilla/5.0 (compatible; WebServer/1.0)',
|
|
|
|
|
- 'Accept': 'application/json',
|
|
|
|
|
- 'Content-Type': 'application/json'
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ private static getChannelConfig() {
|
|
|
|
|
+ return channelConfigs[11]; // 小程序渠道ID
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 小程序登录鉴权
|
|
|
|
|
|
|
+ * 内容安全审核接口
|
|
|
* @param ctx Koa上下文
|
|
* @param ctx Koa上下文
|
|
|
*/
|
|
*/
|
|
|
- async wxLogin(ctx: any) {
|
|
|
|
|
|
|
+ async contentSecurityCheck(ctx: Context) {
|
|
|
try {
|
|
try {
|
|
|
- const data = ctx.request.body;
|
|
|
|
|
- const token = data.token;
|
|
|
|
|
|
|
+ const handler = MiniappController.getChannelHandler();
|
|
|
|
|
+ const config = MiniappController.getChannelConfig();
|
|
|
|
|
|
|
|
- if (!token) {
|
|
|
|
|
|
|
+ if (!handler) {
|
|
|
ctx.body = {
|
|
ctx.body = {
|
|
|
code: -1,
|
|
code: -1,
|
|
|
- msg: "缺少token参数",
|
|
|
|
|
|
|
+ msg: "渠道处理器未找到",
|
|
|
data: null
|
|
data: null
|
|
|
};
|
|
};
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const loginUrl = `https://wxlogin.${this.miniAppDomain}.com/wxlogin?cmd=checkUserToken&token=${token}`;
|
|
|
|
|
- logger.info("小程序登录请求", { token, loginUrl });
|
|
|
|
|
-
|
|
|
|
|
- const wxLogin = await axios.get(loginUrl, {
|
|
|
|
|
- httpsAgent: this.getHttpsAgent(),
|
|
|
|
|
- headers: this.getCommonHeaders()
|
|
|
|
|
- });
|
|
|
|
|
- logger.info("小程序登录响应", { data: wxLogin.data });
|
|
|
|
|
-
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- code: 0,
|
|
|
|
|
- msg: "success",
|
|
|
|
|
- data: wxLogin.data,
|
|
|
|
|
- };
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error('wxLogin error:', error);
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- code: -1,
|
|
|
|
|
- msg: error.message || "登录验证失败",
|
|
|
|
|
- data: null,
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- /**
|
|
|
|
|
- * 小程序发货接口
|
|
|
|
|
- * @param ctx Koa上下文
|
|
|
|
|
- */
|
|
|
|
|
- async wxPay(ctx: any) {
|
|
|
|
|
- try {
|
|
|
|
|
- // 获取请求参数
|
|
|
|
|
- const params = ctx.request.query;
|
|
|
|
|
- logger.info("小程序支付回调参数:", { url: ctx.href, params: params });
|
|
|
|
|
-
|
|
|
|
|
- // 验证必要参数
|
|
|
|
|
- const requiredParams = ['uid', 'rmb', 'reqid', 'trans_id', 'product_id', 'notify_id', 'sign'];
|
|
|
|
|
- for (const param of requiredParams) {
|
|
|
|
|
- if (!params[param]) {
|
|
|
|
|
- logger.error(`缺少必要参数: ${param}`);
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 验证签名
|
|
|
|
|
- const parameters = Object.keys(params)
|
|
|
|
|
- .filter(key => key !== 'sign')
|
|
|
|
|
- .map(key => `${key}=${params[key]}`);
|
|
|
|
|
-
|
|
|
|
|
- const expectedSignature = this.generateSignature(parameters, this.miniAppPrivateKey);
|
|
|
|
|
- if (params.sign !== expectedSignature) {
|
|
|
|
|
- logger.error("小程序支付签名验证失败", {
|
|
|
|
|
- received: params.sign,
|
|
|
|
|
- expected: expectedSignature
|
|
|
|
|
- });
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 获取订单信息
|
|
|
|
|
- const orderId = params.reqid;
|
|
|
|
|
- const orderInfo = (await Order.getOrder(orderId))[0];
|
|
|
|
|
-
|
|
|
|
|
- if (!orderInfo) {
|
|
|
|
|
- logger.error(`订单${orderId}不存在`);
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (orderInfo.status == 2) {
|
|
|
|
|
- logger.info(`订单${orderId}已经重复发货`);
|
|
|
|
|
- ctx.body = "SUCCESS";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 验证金额
|
|
|
|
|
- const amount = parseFloat(params.rmb);
|
|
|
|
|
- if (orderInfo.amount != amount) {
|
|
|
|
|
- logger.error("订单金额不一致", {
|
|
|
|
|
- "orderInfo.amount": orderInfo.amount,
|
|
|
|
|
- "params.amount": amount,
|
|
|
|
|
- });
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 订单校验 - 验证支付是否成功
|
|
|
|
|
- const orderCheckResult = await this.wxOrderCheck(params.notify_id);
|
|
|
|
|
- if (!orderCheckResult.success) {
|
|
|
|
|
- logger.error(`订单${orderId}校验失败: ${orderCheckResult.message}`);
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 获取服务器URL
|
|
|
|
|
- const url = await getServerList(orderInfo.server_id, orderInfo.channel_id);
|
|
|
|
|
- if (!url) {
|
|
|
|
|
- logger.error(`区服id错误: serverId ${orderInfo.server_id}`);
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 发货处理
|
|
|
|
|
- logger.info(`小程序支付订单${orderId}通知游戏发货开始`);
|
|
|
|
|
- const result = await this.deliverOrder(
|
|
|
|
|
- orderInfo,
|
|
|
|
|
- ctx.request.ip,
|
|
|
|
|
- url,
|
|
|
|
|
- params.trans_id
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- if ((result as any).code === 1) {
|
|
|
|
|
- logger.info(`小程序支付订单${orderId}发货成功`);
|
|
|
|
|
- ctx.body = "SUCCESS";
|
|
|
|
|
- } else {
|
|
|
|
|
- logger.error(`小程序支付订单${orderId}发货失败: ${(result as any).msg}`);
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("小程序支付处理出错:", error);
|
|
|
|
|
- ctx.body = "FAIL";
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- /**
|
|
|
|
|
- * 订单校验 - 验证支付是否成功
|
|
|
|
|
- * @param notifyId 平台通知ID
|
|
|
|
|
- * @returns 校验结果
|
|
|
|
|
- */
|
|
|
|
|
- async wxOrderCheck(notifyId: string): Promise<{ success: boolean; message: string }> {
|
|
|
|
|
- try {
|
|
|
|
|
- // 构建验证参数
|
|
|
|
|
- const parameters = [
|
|
|
|
|
- `gameid=${this.miniAppGameId}`,
|
|
|
|
|
- `notify_id=${notifyId}`
|
|
|
|
|
- ];
|
|
|
|
|
-
|
|
|
|
|
- // 生成签名
|
|
|
|
|
- const signature = this.generateSignature(parameters, this.miniAppPrivateKey);
|
|
|
|
|
-
|
|
|
|
|
- // 构建验证URL
|
|
|
|
|
- const verifyUrl = `https://login.${this.miniAppDomain}/pay/paygate/verify.php?gameid=${this.miniAppGameId}¬ify_id=${notifyId}&sign=${signature}`;
|
|
|
|
|
-
|
|
|
|
|
- logger.info("订单校验请求", { notifyId, verifyUrl });
|
|
|
|
|
-
|
|
|
|
|
- // 调用平台验证接口
|
|
|
|
|
- const response = await axios.get(verifyUrl, {
|
|
|
|
|
- httpsAgent: this.getHttpsAgent(),
|
|
|
|
|
- headers: {
|
|
|
|
|
- ...this.getCommonHeaders(),
|
|
|
|
|
- 'Accept': 'text/plain'
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- logger.info("订单校验响应", { notifyId, response: response.data });
|
|
|
|
|
-
|
|
|
|
|
- // 检查返回结果
|
|
|
|
|
- if (response.data === "SUCCESS") {
|
|
|
|
|
- return {
|
|
|
|
|
- success: true,
|
|
|
|
|
- message: "订单支付成功"
|
|
|
|
|
- };
|
|
|
|
|
- } else {
|
|
|
|
|
- return {
|
|
|
|
|
- success: false,
|
|
|
|
|
- message: "订单支付失败或无效"
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("订单校验出错:", { notifyId, error: error.message });
|
|
|
|
|
- return {
|
|
|
|
|
- success: false,
|
|
|
|
|
- message: "订单校验失败"
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 内容安全审核接口
|
|
|
|
|
- * @param ctx Koa上下文
|
|
|
|
|
- */
|
|
|
|
|
- async contentSecurityCheck(ctx: any) {
|
|
|
|
|
- try {
|
|
|
|
|
- const data = ctx.request.body;
|
|
|
|
|
- logger.info("内容安全审核请求参数:", data);
|
|
|
|
|
-
|
|
|
|
|
- // 验证必要参数
|
|
|
|
|
- const requiredParams = ['uid', 'gameid', 'signature', 'timestamp', 'nonce', 'scene', 'content'];
|
|
|
|
|
- for (const param of requiredParams) {
|
|
|
|
|
- if (!data[param]) {
|
|
|
|
|
- logger.error(`缺少必要参数: ${param}`);
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- code: -1,
|
|
|
|
|
- msg: `缺少必要参数: ${param}`,
|
|
|
|
|
- data: null
|
|
|
|
|
- };
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 验证签名算法1
|
|
|
|
|
- const isValidSignature = this.verifySignatureAlgorithm1(data);
|
|
|
|
|
- if (!isValidSignature) {
|
|
|
|
|
- logger.error("内容安全审核签名验证失败");
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- code: -1,
|
|
|
|
|
- msg: "签名验证失败",
|
|
|
|
|
- data: null
|
|
|
|
|
- };
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 构建请求参数
|
|
|
|
|
- const requestData = {
|
|
|
|
|
- uid: data.uid,
|
|
|
|
|
- gameid: data.gameid,
|
|
|
|
|
- signature: data.signature,
|
|
|
|
|
- timestamp: data.timestamp,
|
|
|
|
|
- nonce: data.nonce,
|
|
|
|
|
- scene: data.scene,
|
|
|
|
|
- content: data.content,
|
|
|
|
|
- nickname: data.nickname || '',
|
|
|
|
|
- title: data.title || '',
|
|
|
|
|
- usersign: data.usersign || ''
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 调用内容安全API
|
|
|
|
|
- const apiUrl = `https://api.${this.miniAppDomain}/mpcommon/?cmd=wxaMsgSecCheck`;
|
|
|
|
|
- logger.info("调用内容安全API:", { url: apiUrl, data: requestData });
|
|
|
|
|
-
|
|
|
|
|
- const response = await axios.post(apiUrl, requestData, {
|
|
|
|
|
- httpsAgent: this.getHttpsAgent(),
|
|
|
|
|
- headers: this.getCommonHeaders()
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- logger.info("内容安全API响应:", response.data);
|
|
|
|
|
-
|
|
|
|
|
- // 处理响应结果
|
|
|
|
|
- if (response.data && response.data.error === 0) {
|
|
|
|
|
- const result = response.data.result;
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- code: 0,
|
|
|
|
|
- msg: "success",
|
|
|
|
|
- data: {
|
|
|
|
|
- suggest: result.suggest, // pass/review/reject
|
|
|
|
|
- label: result.label,
|
|
|
|
|
- trace_id: response.data.trace_id,
|
|
|
|
|
- detail: response.data.detail
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- } else {
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- code: -1,
|
|
|
|
|
- msg: response.data?.errmsg || "内容安全检测失败",
|
|
|
|
|
- data: null
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 调用渠道处理器的内容安全审核方法
|
|
|
|
|
+ const result = await (handler as any).contentSecurityCheck(ctx, config);
|
|
|
|
|
+ ctx.body = result;
|
|
|
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
logger.error("内容安全审核出错:", error);
|
|
logger.error("内容安全审核出错:", error);
|
|
@@ -340,75 +82,22 @@ class MiniAppController {
|
|
|
* 角色名称修改上报
|
|
* 角色名称修改上报
|
|
|
* @param ctx Koa上下文
|
|
* @param ctx Koa上下文
|
|
|
*/
|
|
*/
|
|
|
- async editUserRoleInfo(ctx: any) {
|
|
|
|
|
|
|
+ async editUserRoleInfo(ctx: Context) {
|
|
|
try {
|
|
try {
|
|
|
- const data = ctx.request.body;
|
|
|
|
|
- logger.info("角色名称修改上报请求参数:", data);
|
|
|
|
|
-
|
|
|
|
|
- // 验证必要参数
|
|
|
|
|
- const requiredParams = ['openId', 'time', 'gameid', 'serverid', 'playerName', 'sign'];
|
|
|
|
|
- for (const param of requiredParams) {
|
|
|
|
|
- if (!data[param]) {
|
|
|
|
|
- logger.error(`缺少必要参数: ${param}`);
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- error: -1,
|
|
|
|
|
- errmsg: `缺少必要参数: ${param}`
|
|
|
|
|
- };
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 验证签名算法2
|
|
|
|
|
- const isValidSignature = this.verifySignatureAlgorithm2(data);
|
|
|
|
|
- if (!isValidSignature) {
|
|
|
|
|
- logger.error("角色名称修改上报签名验证失败");
|
|
|
|
|
|
|
+ const handler = MiniappController.getChannelHandler();
|
|
|
|
|
+ const config = MiniappController.getChannelConfig();
|
|
|
|
|
+
|
|
|
|
|
+ if (!handler) {
|
|
|
ctx.body = {
|
|
ctx.body = {
|
|
|
error: -1,
|
|
error: -1,
|
|
|
- errmsg: "签名验证失败"
|
|
|
|
|
|
|
+ errmsg: "渠道处理器未找到"
|
|
|
};
|
|
};
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 构建请求参数
|
|
|
|
|
- const requestData = {
|
|
|
|
|
- openId: data.openId,
|
|
|
|
|
- time: data.time,
|
|
|
|
|
- gameid: data.gameid,
|
|
|
|
|
- serverid: data.serverid,
|
|
|
|
|
- playerName: encodeURIComponent(data.playerName), // 中文URL编码
|
|
|
|
|
- sign: data.sign
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 调用角色信息修改API
|
|
|
|
|
- const apiUrl = `https://platform.${this.miniAppDomain}.com/stat/api/?cmd=editUserRoleInfo`;
|
|
|
|
|
- logger.info("调用角色信息修改API:", { url: apiUrl, data: requestData });
|
|
|
|
|
-
|
|
|
|
|
- const response = await axios.get(apiUrl, {
|
|
|
|
|
- params: requestData,
|
|
|
|
|
- timeout: 10000,
|
|
|
|
|
- headers: {
|
|
|
|
|
- 'User-Agent': 'Mozilla/5.0 (compatible; WebServer/1.0)',
|
|
|
|
|
- 'Accept': 'application/json'
|
|
|
|
|
- },
|
|
|
|
|
- httpsAgent: new (require('https').Agent)({
|
|
|
|
|
- rejectUnauthorized: false,
|
|
|
|
|
- secureProtocol: 'TLSv1_2_method'
|
|
|
|
|
- })
|
|
|
|
|
- });
|
|
|
|
|
- logger.info("角色信息修改API响应:", response.data);
|
|
|
|
|
-
|
|
|
|
|
- // 处理响应结果
|
|
|
|
|
- if (response.data && response.data.error === 0) {
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- error: 0,
|
|
|
|
|
- errmsg: ""
|
|
|
|
|
- };
|
|
|
|
|
- } else {
|
|
|
|
|
- ctx.body = {
|
|
|
|
|
- error: response.data?.error || -1,
|
|
|
|
|
- errmsg: response.data?.errmsg || "角色信息修改失败"
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 调用渠道处理器的角色名称修改上报方法
|
|
|
|
|
+ const result = await (handler as any).editUserRoleInfo(ctx, config);
|
|
|
|
|
+ ctx.body = result;
|
|
|
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
logger.error("角色名称修改上报出错:", error);
|
|
logger.error("角色名称修改上报出错:", error);
|
|
@@ -418,278 +107,6 @@ class MiniAppController {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 签名算法2验证
|
|
|
|
|
- * @param data 请求数据
|
|
|
|
|
- * @returns 验证结果
|
|
|
|
|
- */
|
|
|
|
|
- private verifySignatureAlgorithm2(data: any): boolean {
|
|
|
|
|
- try {
|
|
|
|
|
- const { openId, time, gameid, serverid, playerName, sign } = data;
|
|
|
|
|
-
|
|
|
|
|
- // 构建待签名字符串 - 按字母顺序排序
|
|
|
|
|
- const params: any = {
|
|
|
|
|
- openId: openId.toString(),
|
|
|
|
|
- time: time.toString(),
|
|
|
|
|
- gameid: gameid.toString(),
|
|
|
|
|
- serverid: serverid.toString(),
|
|
|
|
|
- playerName: playerName.toString()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 按字母顺序排序并拼接
|
|
|
|
|
- const sortedKeys = Object.keys(params).sort();
|
|
|
|
|
- const queryString = sortedKeys.map(key => `${key}=${params[key]}`).join('&');
|
|
|
|
|
-
|
|
|
|
|
- // 添加统计API密钥
|
|
|
|
|
- const stringToSign = `${queryString}&key=${this.miniAppStatApiKey}`;
|
|
|
|
|
-
|
|
|
|
|
- // 计算MD5签名
|
|
|
|
|
- const expectedSignature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
|
|
|
|
|
-
|
|
|
|
|
- logger.info("签名算法2验证详情:", {
|
|
|
|
|
- queryString,
|
|
|
|
|
- stringToSign,
|
|
|
|
|
- expectedSignature,
|
|
|
|
|
- receivedSignature: sign
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return sign === expectedSignature;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("签名算法2验证出错:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 生成角色信息修改签名(供客户端使用)
|
|
|
|
|
- * @param params 签名参数
|
|
|
|
|
- * @returns 签名结果
|
|
|
|
|
- */
|
|
|
|
|
- generateRoleInfoSignature(params: any): string | null {
|
|
|
|
|
- try {
|
|
|
|
|
- const { openId, time, gameid, serverid, playerName } = params;
|
|
|
|
|
-
|
|
|
|
|
- // 构建待签名字符串 - 按字母顺序排序
|
|
|
|
|
- const signParams: any = {
|
|
|
|
|
- openId: openId.toString(),
|
|
|
|
|
- time: time.toString(),
|
|
|
|
|
- gameid: gameid.toString(),
|
|
|
|
|
- serverid: serverid.toString(),
|
|
|
|
|
- playerName: playerName.toString()
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 按字母顺序排序并拼接
|
|
|
|
|
- const sortedKeys = Object.keys(signParams).sort();
|
|
|
|
|
- const queryString = sortedKeys.map(key => `${key}=${signParams[key]}`).join('&');
|
|
|
|
|
-
|
|
|
|
|
- // 添加统计API密钥
|
|
|
|
|
- const stringToSign = `${queryString}&key=${this.miniAppStatApiKey}`;
|
|
|
|
|
-
|
|
|
|
|
- // 计算MD5签名
|
|
|
|
|
- const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
|
|
|
|
|
-
|
|
|
|
|
- return signature;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("生成角色信息修改签名出错:", error);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 签名算法1验证
|
|
|
|
|
- * @param data 请求数据
|
|
|
|
|
- * @returns 验证结果
|
|
|
|
|
- */
|
|
|
|
|
- private verifySignatureAlgorithm1(data: any): boolean {
|
|
|
|
|
- try {
|
|
|
|
|
- const { uid, gameid, timestamp, nonce, scene, content, nickname, title, usersign, signature } = data;
|
|
|
|
|
-
|
|
|
|
|
- // 构建待签名字符串 - 按字母顺序排序
|
|
|
|
|
- const params: any = {
|
|
|
|
|
- uid: uid.toString(),
|
|
|
|
|
- gameid: gameid.toString(),
|
|
|
|
|
- timestamp: timestamp.toString(),
|
|
|
|
|
- nonce: nonce.toString(),
|
|
|
|
|
- scene: scene.toString(),
|
|
|
|
|
- content: content
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 添加可选参数(如果存在)
|
|
|
|
|
- if (nickname) params.nickname = nickname;
|
|
|
|
|
- if (title) params.title = title;
|
|
|
|
|
- if (usersign) params.usersign = usersign;
|
|
|
|
|
-
|
|
|
|
|
- // 按字母顺序排序并拼接
|
|
|
|
|
- const sortedKeys = Object.keys(params).sort();
|
|
|
|
|
- const queryString = sortedKeys.map(key => `${key}=${params[key]}`).join('&');
|
|
|
|
|
-
|
|
|
|
|
- // 添加API密钥
|
|
|
|
|
- const stringToSign = `${queryString}&key=${this.miniAppApiKey}`;
|
|
|
|
|
-
|
|
|
|
|
- // 计算MD5签名
|
|
|
|
|
- const expectedSignature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
|
|
|
|
|
-
|
|
|
|
|
- logger.info("签名验证详情:", {
|
|
|
|
|
- queryString,
|
|
|
|
|
- stringToSign,
|
|
|
|
|
- expectedSignature,
|
|
|
|
|
- receivedSignature: signature
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return signature === expectedSignature;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("签名算法1验证出错:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 生成内容安全签名(供客户端使用)
|
|
|
|
|
- * @param params 签名参数
|
|
|
|
|
- * @returns 签名结果
|
|
|
|
|
- */
|
|
|
|
|
- generateContentSecuritySignature(params: any): string | null {
|
|
|
|
|
- try {
|
|
|
|
|
- const { uid, gameid, timestamp, nonce, scene, content, nickname, title, usersign } = params;
|
|
|
|
|
-
|
|
|
|
|
- // 构建待签名字符串 - 按字母顺序排序
|
|
|
|
|
- const signParams: any = {
|
|
|
|
|
- uid: uid.toString(),
|
|
|
|
|
- gameid: gameid.toString(),
|
|
|
|
|
- timestamp: timestamp.toString(),
|
|
|
|
|
- nonce: nonce.toString(),
|
|
|
|
|
- scene: scene.toString(),
|
|
|
|
|
- content: content
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 添加可选参数(如果存在)
|
|
|
|
|
- if (nickname) signParams.nickname = nickname;
|
|
|
|
|
- if (title) signParams.title = title;
|
|
|
|
|
- if (usersign) signParams.usersign = usersign;
|
|
|
|
|
-
|
|
|
|
|
- // 按字母顺序排序并拼接
|
|
|
|
|
- const sortedKeys = Object.keys(signParams).sort();
|
|
|
|
|
- const queryString = sortedKeys.map(key => `${key}=${signParams[key]}`).join('&');
|
|
|
|
|
-
|
|
|
|
|
- // 添加API密钥
|
|
|
|
|
- const stringToSign = `${queryString}&key=${this.miniAppApiKey}`;
|
|
|
|
|
-
|
|
|
|
|
- // 计算MD5签名
|
|
|
|
|
- const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
|
|
|
|
|
-
|
|
|
|
|
- return signature;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("生成内容安全签名出错:", error);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 发货处理方法
|
|
|
|
|
- * @param orderInfo 订单信息
|
|
|
|
|
- * @param ip 客户端IP
|
|
|
|
|
- * @param url 游戏服务器URL
|
|
|
|
|
- * @param out_trade_no 外部交易号
|
|
|
|
|
- * @returns 发货结果
|
|
|
|
|
- */
|
|
|
|
|
- private async deliverOrder(orderInfo: any, ip: string, url: string, out_trade_no: string): Promise<{ code: number; msg: string }> {
|
|
|
|
|
- return new Promise((resolve) => {
|
|
|
|
|
- const sendMsg = new Msg();
|
|
|
|
|
- logger.info("通知游戏服务器url", { url: url });
|
|
|
|
|
-
|
|
|
|
|
- sendMsg.connect(url, Account);
|
|
|
|
|
- setTimeout(async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- // 构建消息参数
|
|
|
|
|
- const params = JSON.stringify({
|
|
|
|
|
- account: orderInfo.uid,
|
|
|
|
|
- channel_id: orderInfo.channel_id,
|
|
|
|
|
- order: orderInfo.order_id,
|
|
|
|
|
- id: orderInfo.product_id,
|
|
|
|
|
- cnt: 100,
|
|
|
|
|
- money: orderInfo.amount,
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- logger.info("通知游戏服务器参数", { data: params });
|
|
|
|
|
- logger.info("通知游戏服务器orderInfo", { orderInfo: orderInfo });
|
|
|
|
|
- let send_res = sendMsg.CG_ASK_LOGIN(
|
|
|
|
|
- Account,
|
|
|
|
|
- 0,
|
|
|
|
|
- "",
|
|
|
|
|
- "cn",
|
|
|
|
|
- "CN",
|
|
|
|
|
- ip,
|
|
|
|
|
- params,
|
|
|
|
|
- orderInfo.server_id
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- if (!send_res) {
|
|
|
|
|
- resolve({ code: 0, msg: "通知服务器失败" });
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 更新订单状态
|
|
|
|
|
- const update_time = formatDate(new Date());
|
|
|
|
|
- const res = await Order.updateOrderStats(
|
|
|
|
|
- orderInfo.order_id,
|
|
|
|
|
- 2,
|
|
|
|
|
- out_trade_no,
|
|
|
|
|
- update_time,
|
|
|
|
|
- orderInfo.uid
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- if (res.affectedRows <= 0) {
|
|
|
|
|
- logger.info(`订单${orderInfo.order_id} 发货失败`);
|
|
|
|
|
- resolve({ code: 0, msg: "发货失败" });
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- resolve({ code: 1, msg: "发货成功" });
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- logger.error("发货过程出错:", error);
|
|
|
|
|
- resolve({ code: 0, msg: "发货失败" });
|
|
|
|
|
- }
|
|
|
|
|
- }, 1500);
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 小程序签名函数(签名算法3)
|
|
|
|
|
- * @param parameters 参数数组
|
|
|
|
|
- * @param privateKey 私钥
|
|
|
|
|
- * @returns 签名结果
|
|
|
|
|
- */
|
|
|
|
|
- private generateSignature(parameters: string[], privateKey: string): string {
|
|
|
|
|
- // 1. 过滤掉空值和sign参数
|
|
|
|
|
- const filteredParams = parameters.filter(param => {
|
|
|
|
|
- const [key, value] = param.split('=');
|
|
|
|
|
- return key !== 'sign' && value && value.trim() !== '';
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // 2. 对参数进行URL编码
|
|
|
|
|
- const encodedParams = filteredParams.map(param => {
|
|
|
|
|
- const [key, value] = param.split('=');
|
|
|
|
|
- const encodedValue = encodeURIComponent(value);
|
|
|
|
|
- return `${key}=${encodedValue}`;
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // 3. 按字母顺序排序
|
|
|
|
|
- encodedParams.sort((a, b) => {
|
|
|
|
|
- return a.localeCompare(b);
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // 4. 用&连接所有参数
|
|
|
|
|
- const queryString = encodedParams.join('&');
|
|
|
|
|
-
|
|
|
|
|
- // 5. 拼接私钥
|
|
|
|
|
- const stringToSign = `${queryString}&key=${privateKey}`;
|
|
|
|
|
-
|
|
|
|
|
- // 6. 计算MD5并转大写
|
|
|
|
|
- const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
|
|
|
|
|
-
|
|
|
|
|
- return signature;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-module.exports = new MiniAppController();
|
|
|
|
|
|
|
+module.exports = new MiniappController();
|