fix: peacematch 兑换用 multipart/form-data(与抓包一致)

根据实际兑换抓包修正:
- Content-Type 从 urlencoded 改回 multipart/form-data(浏览器实际用 multipart)
- postMultipart 支持组合 token csrf\x00ctn,自动拆分并合并 cvl_csrf_token+acf_ccn 到 Cookie
- 移除不再需要的 postForm 方法和 net/url 导入
- 验证:表单字段(manualID/commodityID/num/rid/ctn)与抓包完全一致,
  storedetail 确认无 preExchange 字段(时间校准修复正确)

抓包来源:/Users/yml/Desktop/抓包/douyu/7.29/电竞手册-兑换并查询信息与积分.har
This commit is contained in:
yml2213
2026-07-29 11:08:49 +08:00
parent 599f9d71f0
commit ed3fd5f09e
+15 -42
View File
@@ -7,7 +7,6 @@ import (
"io"
"mime/multipart"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
@@ -430,7 +429,7 @@ func (c *DouyuClient) Exchange(cookie, commodityID string, csrf *string) (string
} `json:"data"`
}
if c.Activity.UseCTN {
// 电竞手册:用 ctn(acf_ccn) 鉴权,urlencoded 表单
// 电竞手册:用 ctn(acf_ccn) 鉴权,multipart 表单(与浏览器抓包一致)
ctn := ""
if csrf != nil {
parts := strings.SplitN(*csrf, "\x00", 2)
@@ -446,7 +445,7 @@ func (c *DouyuClient) Exchange(cookie, commodityID string, csrf *string) (string
}
ctn = ctnVal
}
if err := c.postForm(c.Activity.ExchangeURL, cookie, csrf, "正式兑换", map[string]string{
if err := c.postMultipart(c.Activity.ExchangeURL, cookie, csrf, "正式兑换", map[string]string{
"manualID": c.Activity.ManualID,
"commodityID": commodityID,
"num": "1",
@@ -558,11 +557,22 @@ func (c *DouyuClient) postMultipart(url, cookie string, csrf *string, label stri
if err != nil {
return err
}
// 处理组合 token:csrf\x00ctn(电竞手册用 ctn 鉴权时)
// 拆分后 cvl_csrf_token 和 acf_ccn 都合并到 Cookie 头
currentCSRF := ""
ctnVal := ""
if csrf != nil {
currentCSRF = *csrf
parts := strings.SplitN(*csrf, "\x00", 2)
currentCSRF = parts[0]
if len(parts) > 1 {
ctnVal = parts[1]
}
}
c.setBrowserHeaders(req, utils.MergeCookie(cookie, "cvl_csrf_token", currentCSRF))
mergedCookie := utils.MergeCookie(cookie, "cvl_csrf_token", currentCSRF)
if ctnVal != "" {
mergedCookie = utils.MergeCookie(mergedCookie, "acf_ccn", ctnVal)
}
c.setBrowserHeaders(req, mergedCookie)
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := c.doJSON(req, out, label)
if resp != nil {
@@ -570,43 +580,6 @@ func (c *DouyuClient) postMultipart(url, cookie string, csrf *string, label stri
if nextCSRF != *csrf {
logger.LogEvent("网络", "info", "响应刷新CSRF已同步: %s %s %s", label, req.Method, requestPath(req))
}
*csrf = nextCSRF
}
}
return err
}
// postForm 发送 application/x-www-form-urlencoded POST 请求。
// 用于电竞手册等使用 ctn 鉴权的活动。
// csrf 参数为组合值 "cvl_csrf_token\x00acf_ccn",两者都会合并到 Cookie 头。
func (c *DouyuClient) postForm(targetURL, cookie string, csrf *string, label string, fields map[string]string, out any) error {
vals := url.Values{}
for k, v := range fields {
vals.Set(k, v)
}
req, err := http.NewRequest("POST", targetURL, strings.NewReader(vals.Encode()))
if err != nil {
return err
}
// 拆分组合 token,合并到 Cookie
csrfVal := ""
ctnVal := ""
if csrf != nil {
parts := strings.SplitN(*csrf, "\x00", 2)
csrfVal = parts[0]
if len(parts) > 1 {
ctnVal = parts[1]
}
}
mergedCookie := utils.MergeCookie(cookie, "cvl_csrf_token", csrfVal)
if ctnVal != "" {
mergedCookie = utils.MergeCookie(mergedCookie, "acf_ccn", ctnVal)
}
c.setBrowserHeaders(req, mergedCookie)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
resp, err := c.doJSON(req, out, label)
if resp != nil && csrf != nil {
if nextCSRF := csrfFromResponse(resp); nextCSRF != "" {
if ctnVal != "" {
*csrf = nextCSRF + "\x00" + ctnVal
} else {