flowerpig 7 месяцев назад
Родитель
Сommit
66eea4fd11

+ 3 - 0
webServer/src/channels/factory/ChannelFactory.ts

@@ -8,6 +8,7 @@ import { ThreeSixZeroChannelHandler } from "../handlers/ThreeSixZeroChannelHandl
 import { MuZiChannelHandler } from "../handlers/MuZiChannelHandler";
 import { QiAiH5ChannelHandler } from "../handlers/QiAiH5ChannelHandler";
 import { MuZiIosChannelHandler } from "../handlers/MuZiIosChannelHandler";
+import { HupuChannelHandler } from "../handlers/HupuChannelHandler";
 
 
 const logger = require("../../utils/log");
@@ -39,6 +40,8 @@ class ChannelFactory {
     this.registerHandler(8, new QiAiH5ChannelHandler()); // qi ai
     this.registerHandler(9, new MuZiIosChannelHandler()); // muzi ios
     this.registerHandler(10, new SanLiAndroidChannelHandler()); // muzi ios
+    this.registerHandler(11, new SanLiAndroidChannelHandler()); // 小游戏
+    this.registerHandler(12, new HupuChannelHandler()); // Hupu QuickSDK
   }
 
   /**

+ 161 - 0
webServer/src/channels/handlers/HupuChannelHandler.ts

@@ -0,0 +1,161 @@
+import {Context} from "koa";
+import axios from "axios";
+import {parseString} from "xml2js";
+
+import {ChannelHandler, LoginResult, PaymentResult} from "../interfaces/ChannelHandler";
+import {ChannelConfig} from "../../config/channelConfig";
+import {SignatureVerifier} from "../../utils/SignatureVerifier";
+import QuickAsy from "../../utils/quickAsy";
+import {PaymentHelper} from "../../utils/PaymentHelper";
+
+const logger = require("../../utils/log");
+
+/**
+ * Hupu(QuickSDK)渠道处理器
+ * 负责登录验证与支付回调处理
+ */
+export class HupuChannelHandler implements ChannelHandler {
+
+    /**
+     * QuickSDK 登录验证
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async handleLogin(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        const data = ctx.request.body as any;
+        const {token, uid, channel_code, product_code} = data || {};
+        const finalProductCode = product_code || config.loginConfig?.productCode;
+
+        if (!token || !uid) {
+            logger.warn("Hupu登录验证失败: 缺少必要参数", {token, uid});
+            return {code: 0, msg: "缺少必要参数 token 或 uid"};
+        }
+
+        if (!finalProductCode) {
+            logger.error("Hupu登录验证失败: 未配置productCode");
+            return {code: 0, msg: "服务器未配置productCode"};
+        }
+
+        const requestUrl = "http://checkuser.quickapi.net/v2/checkUserInfo";
+        const params: Record<string, string> = {
+            token,
+            uid,
+            product_code: finalProductCode
+        };
+        if (channel_code) {
+            params.channel_code = channel_code;
+        }
+
+        logger.info("Hupu登录验证请求", {url: requestUrl, params});
+
+        try {
+            const response = await axios.get(requestUrl, {params, timeout: 8000});
+            logger.info("Hupu登录验证响应", {data: response.data});
+
+            if (response.data === "1") {
+                return {code: 1, msg: "success"};
+            }
+
+            logger.warn("Hupu登录验证失败: 接口返回非1", {data: response.data});
+            return {code: 0, msg: "登录验证失败"};
+        } catch (error) {
+            logger.error("Hupu登录验证异常", error);
+            return {code: 0, msg: "登录验证异常"};
+        }
+    }
+
+    /**
+     * QuickSDK 支付回调
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async handlePayment(ctx: Context, config: ChannelConfig): Promise<PaymentResult> {
+        const data = ctx.request.body as any;
+        logger.info("Hupu支付回调参数", {url: ctx.href, params: data});
+
+        const {nt_data, sign, md5Sign} = data || {};
+        if (!nt_data || !sign || !md5Sign) {
+            logger.warn("Hupu支付回调失败: 缺少必要参数", {nt_data, sign, md5Sign});
+            return {code: 0, msg: "缺少必要参数"};
+        }
+
+        const md5Key = config.paymentConfig?.signKey;
+        const callbackKey = config.paymentConfig?.callbackKey;
+
+        if (!md5Key || !callbackKey) {
+            logger.error("Hupu支付回调失败: 未配置QuickSDK密钥");
+            return {code: 0, msg: "服务器未配置渠道密钥"};
+        }
+
+        if (!SignatureVerifier.verifyQuickSign(data, md5Key)) {
+            logger.warn("Hupu支付回调失败: 签名验证失败");
+            return {code: 0, msg: "签名验证失败"};
+        }
+
+        try {
+            const xmlData = QuickAsy.decode(nt_data, callbackKey);
+            const parsed = await this.parseQuickXml(xmlData);
+
+            const message = parsed.quicksdk_message.message[0];
+            const orderId = message.game_order?.[0];
+            const outTradeNo = message.order_no?.[0];
+            const status = message.status?.[0];
+            const amountStr = message.amount?.[0];
+
+            if (!orderId || !outTradeNo || typeof status === "undefined" || !amountStr) {
+                logger.error("Hupu支付回调失败: XML缺少必要字段", {message});
+                return {code: 0, msg: "回调数据不完整"};
+            }
+
+            if (status !== "0") {
+                logger.warn("Hupu支付状态非成功", {status});
+                return {code: 0, msg: "支付状态失败"};
+            }
+
+            const amount = parseFloat(amountStr);
+
+            const validation = await PaymentHelper.validateOrder(orderId);
+            if (!validation.valid) {
+                return {
+                    code: validation.message?.includes("重复发货") ? 1 : 0,
+                    msg: validation.message || "订单验证失败"
+                };
+            }
+
+            const orderInfo = validation.orderInfo;
+            if (Number(orderInfo.amount) !== amount) {
+                logger.warn("Hupu支付金额不匹配", {
+                    orderId,
+                    requestAmount: amount,
+                    orderAmount: orderInfo.amount
+                });
+                return {code: 0, msg: "订单金额不一致"};
+            }
+
+            logger.info(`Hupu支付订单${orderId}开始发货`);
+            const result = await PaymentHelper.deliverOrder(
+                orderInfo,
+                ctx.request.ip,
+                validation.url,
+                outTradeNo
+            );
+            logger.info(`Hupu支付订单${orderId}发货完成`, {result});
+            return result;
+        } catch (error) {
+            logger.error("Hupu支付回调解析异常", error);
+            return {code: 0, msg: "回调解析异常"};
+        }
+    }
+
+    private parseQuickXml(xml: string): Promise<any> {
+        return new Promise((resolve, reject) => {
+            parseString(xml, (err, result) => {
+                if (err) {
+                    return reject(err);
+                }
+                resolve(result);
+            });
+        });
+    }
+}
+

+ 16 - 0
webServer/src/config/channelConfig.ts

@@ -22,6 +22,9 @@ import {
   QIAI_QUICK_H5_CALLBACK_KEY,
   QIAI_QUICK_H5_PRODUCT_CODE,
   QIAI_QUICK_H5_MD5_KEY,
+  HUPU_QUICK_MD5_KEY,
+  HUPU_QUICK_CALLBACK_KEY,
+  HUPU_QUICK_PRODUCT_CODE,
 } from "./thirdParams";
 
 // 渠道配置接口定义
@@ -198,4 +201,17 @@ export const channelConfigs: Record<number, ChannelConfig> = {
       productCode: QUICK_PRODUCT_CODE,
     },
   },
+  12: {
+    // Hupu QuickSDK
+    channelId: 14,
+    name: "虎扑",
+    platform: "hupu",
+    paymentConfig: {
+      signKey: HUPU_QUICK_MD5_KEY,
+      callbackKey: HUPU_QUICK_CALLBACK_KEY,
+    },
+    loginConfig: {
+      productCode: HUPU_QUICK_PRODUCT_CODE,
+    },
+  },
 };

+ 7 - 1
webServer/src/config/thirdParams.ts

@@ -89,4 +89,10 @@ export const SECRET_KEY_QIHU = "IrIRKXs674NiNZiNSaaPQsgX904LwFgu";
 
 export const QIAI_QUICK_H5_MD5_KEY = "qnevushjm7deklbc1mbr55iqejzxnyco";
 export const QIAI_QUICK_H5_CALLBACK_KEY = "37750567448382738619439231053656";
-export const QIAI_QUICK_H5_PRODUCT_CODE = "80939198711722706880933346971719";
+export const QIAI_QUICK_H5_PRODUCT_CODE = "80939198711722706880933346971719";
+
+// Hupu QuickSDK
+export const HUPU_QUICK_MD5_KEY = "jjbzkexkkar01pcpkrwgb7i0plwnpmu8";
+export const HUPU_QUICK_CALLBACK_KEY = "50008469510564927526915032459625";
+export const HUPU_QUICK_PRODUCT_CODE = "06273907688357351140953453508499";
+export const HUPU_QUICK_PRODUCT_KEY = "12641617";