feat(luban): 优化 channelTwo 处理逻辑并添加多重 URL 参数解码
- 新增 decodeURLParam 方法,实现对 PayURL 查询参数的多重 decodeURIComponent 解码 - channelTwo 中使用 decodeURLParam 解析 PayURL 查询参数,模拟浏览器端逻辑 - 根据解析结果动态构造请求 URL,并设置更完整的浏览器请求头 - 补充日志上报请求逻辑,增加对 urlPostStart 和 isLog 参数的处理 - 调整 HTTP 客户端超时设置及代理配置 - 优化日志输出,增加请求 URL 信息,方便跟踪卡密提交过程
This commit is contained in:
@@ -29,6 +29,49 @@ type SendCardTaskTypeLuban struct {
|
||||
sendCardTaskTypeSendCardTaskBase
|
||||
}
|
||||
|
||||
// decodeURLParam 对 PayURL 进行处理,模拟浏览器端 JavaScript 的 getUrlParams 逻辑
|
||||
// 将 PayURL 作为 window.location 处理,对其 query 部分执行多重 decodeURIComponent
|
||||
// JavaScript 逻辑: s.replace(/\+/g, " ") 然后执行四次 decodeURIComponent
|
||||
func (s *SendCardTaskTypeLuban) decodeURLParam(payURL string) map[string]string {
|
||||
parsedURL, err := url.Parse(payURL)
|
||||
if err != nil {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
// 获取 search 部分
|
||||
search := parsedURL.RawQuery
|
||||
if search == "" {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
urlParams := make(map[string]string)
|
||||
|
||||
// 解析查询参数
|
||||
queryValues, err := url.ParseQuery(search)
|
||||
if err != nil {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
// 对每个参数进行解码
|
||||
for key, values := range queryValues {
|
||||
if len(values) > 0 {
|
||||
// 先将 + 替换为空格,模拟 JavaScript 中的 s.replace(pl, " ")
|
||||
decoded := strings.ReplaceAll(values[0], "+", " ")
|
||||
// 执行四次 URL 解码,匹配 JavaScript 中的多重 decodeURIComponent 逻辑
|
||||
for i := 0; i < 4; i++ {
|
||||
unescaped, err := url.QueryUnescape(decoded)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
decoded = unescaped
|
||||
}
|
||||
urlParams[key] = decoded
|
||||
}
|
||||
}
|
||||
|
||||
return urlParams
|
||||
}
|
||||
|
||||
func (s *SendCardTaskTypeLuban) CreateOrder(ctx context.Context, roadUid string, faceValue float64) (OrderPoolItem, error) {
|
||||
orderPoolItem := OrderPoolItem{}
|
||||
roadInfo := road.GetRoadInfoByRoadUid(ctx, roadUid)
|
||||
@@ -94,7 +137,6 @@ func (s *SendCardTaskTypeLuban) CreateOrder(ctx context.Context, roadUid string,
|
||||
}
|
||||
|
||||
func (s *SendCardTaskTypeLuban) channelOne(ctx context.Context, orderItem OrderPoolItem, task SendCardTask) error {
|
||||
otelTrace.Logger.WithContext(ctx).Info("处理任务", zap.Any("orderItem", orderItem), zap.Any("task", task))
|
||||
query, err := url.Parse(orderItem.PayURL)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("解析URL失败", zap.Error(err))
|
||||
@@ -139,37 +181,29 @@ func (s *SendCardTaskTypeLuban) channelOne(ctx context.Context, orderItem OrderP
|
||||
}
|
||||
|
||||
func (s *SendCardTaskTypeLuban) channelTwo(ctx context.Context, orderItem OrderPoolItem, task SendCardTask) error {
|
||||
otelTrace.Logger.WithContext(ctx).Info("处理任务", zap.Any("orderItem", orderItem), zap.Any("task", task))
|
||||
query, err := url.Parse(orderItem.PayURL)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("解析URL失败", zap.Error(err))
|
||||
return errors.New("解析URL失败")
|
||||
}
|
||||
orderUrl := fmt.Sprintf("https://abc.pvcrr.com/baolingqiu/tdeal_order/postCardAndSecret")
|
||||
time.Sleep(time.Second * 30)
|
||||
|
||||
// 使用有序结构体保证 JSON 序列化的字段顺序
|
||||
params := struct {
|
||||
TradeNo string `json:"tradeNo"`
|
||||
Sign string `json:"sign"`
|
||||
CardNo string `json:"card_no"`
|
||||
CardPwd string `json:"card_pwd"`
|
||||
}{
|
||||
TradeNo: query.Query().Get("tradeNo"),
|
||||
Sign: query.Query().Get("sign"),
|
||||
CardNo: task.CardInfo.CardNo,
|
||||
CardPwd: task.CardInfo.Data,
|
||||
// 对 PayURL 的 query 部分执行多重解码,模拟 JavaScript 的 getUrlParams 函数
|
||||
decodedParams := s.decodeURLParam(orderItem.PayURL)
|
||||
|
||||
// 获取 urlPostStart 参数,用于构建请求 URL
|
||||
urlPostStart := decodedParams["urlPostStart"]
|
||||
if urlPostStart == "" {
|
||||
urlPostStart = "baolingqiu"
|
||||
}
|
||||
jsonBody, _ := json.Marshal(params)
|
||||
|
||||
client := resty.New().SetTimeout(time.Second * 5).SetRetryCount(0).SetHeaders(map[string]string{
|
||||
// 创建 HTTP 客户端,设置完整的浏览器请求头
|
||||
client := resty.New().SetTimeout(time.Second * 10).SetRetryCount(0).SetHeaders(map[string]string{
|
||||
"Accept": "application/json, text/javascript, */*; q=0.01",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Cache-Control": "no-cache",
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"DNT": "1",
|
||||
"Origin": "https://abc.pvcrr.com",
|
||||
"Origin": fmt.Sprintf("%s://%s", query.Scheme, query.Host),
|
||||
"Pragma": "no-cache",
|
||||
"Referer": orderItem.PayURL,
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
@@ -177,22 +211,51 @@ func (s *SendCardTaskTypeLuban) channelTwo(ctx context.Context, orderItem OrderP
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"sec-ch-ua": "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"",
|
||||
"sec-ch-ua": `"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"`,
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": "\"macOS\"",
|
||||
"sec-ch-ua-platform": `"macOS"`,
|
||||
"Connection": "keep-alive",
|
||||
}).OnBeforeRequest(func(client *resty.Client, request *resty.Request) error {
|
||||
proxy, _ := utils.GetProxy(ctx, utils.GenerateId(), string(SendCardTaskTypeEnumLuban+"channelTwo"))
|
||||
proxy, _ := utils.GetProxy(ctx, task.LocalOrderID, string(SendCardTaskTypeEnumLuban+"channelTwo"))
|
||||
if proxy != "" {
|
||||
client.SetProxy(proxy)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
otelresty.TraceClient(client)
|
||||
|
||||
// Step 1: 模拟访问页面
|
||||
_, _ = client.R().SetContext(ctx).Get(orderItem.PayURL)
|
||||
|
||||
time.Sleep(time.Second * 30)
|
||||
// Step 2: 发送日志上报请求 (如果有 urlPostStart 和 isLog 参数)
|
||||
if decodedParams["urlPostStart"] != "" && decodedParams["isLog"] != "" {
|
||||
logUrl := fmt.Sprintf("%s://%s/%s/tdeal_order/postRecodeLog/", query.Scheme, query.Host, urlPostStart)
|
||||
_, err := client.R().SetContext(ctx).SetQueryParam("tradeNo", decodedParams["tradeNo"]).Get(logUrl)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Warn("日志上报请求失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: 构造提交数据,使用有序结构体保证 JSON 序列化的字段顺序
|
||||
// 按照 demo.html 中 imgJSON 的顺序: tradeNo, sign, saltSign, stealSign, card_no, card_pwd
|
||||
params := struct {
|
||||
TradeNo string `json:"tradeNo"`
|
||||
Sign string `json:"sign"`
|
||||
SaltSign string `json:"saltSign"`
|
||||
StealSign string `json:"stealSign"`
|
||||
CardNo string `json:"card_no"`
|
||||
CardPwd string `json:"card_pwd"`
|
||||
}{
|
||||
TradeNo: decodedParams["tradeNo"],
|
||||
Sign: decodedParams["sign"],
|
||||
SaltSign: decodedParams["saltSign"],
|
||||
StealSign: decodedParams["stealSign"],
|
||||
CardNo: task.CardInfo.CardNo,
|
||||
CardPwd: task.CardInfo.Data,
|
||||
}
|
||||
jsonBody, _ := json.Marshal(params)
|
||||
|
||||
orderUrl := fmt.Sprintf("%s://%s/%s/tdeal_order/postCardAndSecret", query.Scheme, query.Host, urlPostStart)
|
||||
response, err := client.R().SetContext(ctx).SetQueryParams(map[string]string{
|
||||
"json": string(jsonBody),
|
||||
}).Get(orderUrl)
|
||||
@@ -201,7 +264,7 @@ func (s *SendCardTaskTypeLuban) channelTwo(ctx context.Context, orderItem OrderP
|
||||
return errors.New("请求失败")
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("回调", zap.String("response", response.String()), zap.Any("params", params))
|
||||
otelTrace.Logger.WithContext(ctx).Info("卡密提交回调", zap.String("response", response.String()), zap.Any("params", params), zap.String("orderUrl", orderUrl))
|
||||
|
||||
submitData := struct {
|
||||
Code int64 `json:"code"`
|
||||
|
||||
@@ -50,7 +50,6 @@ func TestSendCardTaskTypeLuban_CreateOrder(t *testing.T) {
|
||||
paramsStr[k] = convertor.ToString(v)
|
||||
}
|
||||
response, _ := webClient.R().SetFormData(paramsStr).Post("http://jiuzpay.xyz:56700/api/pay/create_order")
|
||||
otelTrace.Logger.WithContext(t.Context()).Info("请求结果", zap.String("response", response.String()), zap.Any("params", params))
|
||||
responseStruct := struct {
|
||||
RetCode string `json:"retCode"`
|
||||
PayUrl string `json:"payUrl"`
|
||||
|
||||
Reference in New Issue
Block a user