From 80bdf8b00a002dce923de2bfd48dd940cb48fbcd Mon Sep 17 00:00:00 2001 From: wolter <11@gmail> Date: Wed, 29 Jul 2026 16:47:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=82=AE=E5=82=A8=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=AE=8C=E5=96=84=E8=A1=A5=E5=85=85=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/third/paymentService/psbc/bill.go | 12 ++++- app/third/paymentService/psbc/client.go | 68 +++++++++++++++--------- app/third/paymentService/psbc/notify.go | 6 +-- app/third/paymentService/psbc/order.go | 12 ++++- app/third/paymentService/psbc/pay_url.go | 35 ++++++++---- app/third/paymentService/psbc/refund.go | 12 ++++- app/third/paymentService/psbc/types.go | 31 +++++++++-- app/third/paymentService/psbc_service.go | 40 +++++++------- 8 files changed, 148 insertions(+), 68 deletions(-) diff --git a/app/third/paymentService/psbc/bill.go b/app/third/paymentService/psbc/bill.go index db95ae0..ea39dd0 100644 --- a/app/third/paymentService/psbc/bill.go +++ b/app/third/paymentService/psbc/bill.go @@ -54,14 +54,18 @@ func (c *Client) BillQuery(date time.Time) (*BillQueryResponse, error) { return nil, fmt.Errorf("读取响应失败: %v", err) } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP请求失败,状态码: %d,响应: %s", resp.StatusCode, string(body)) + } + responseData, err := c.DecryptResponse(string(body), false) if err != nil { - return nil, fmt.Errorf("解密响应失败: %v", err) + return nil, fmt.Errorf("解密响应失败: %v,原始响应: %s", err, string(body)) } var result BillQueryResponse if err := json.Unmarshal([]byte(responseData), &result); err != nil { - return nil, fmt.Errorf("解析响应失败: %v", err) + return nil, fmt.Errorf("解析响应失败: %v,响应内容: %s", err, responseData) } return &result, nil @@ -109,5 +113,9 @@ func (c *Client) BillDownload(fileId string, date time.Time) ([]byte, error) { return nil, fmt.Errorf("读取响应失败: %v", err) } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP请求失败,状态码: %d,响应: %s", resp.StatusCode, string(body)) + } + return body, nil } diff --git a/app/third/paymentService/psbc/client.go b/app/third/paymentService/psbc/client.go index c0cc074..cea030e 100644 --- a/app/third/paymentService/psbc/client.go +++ b/app/third/paymentService/psbc/client.go @@ -118,32 +118,47 @@ func (c *Client) EncryptMobile(inputJson string, signStr string) (string, string func (c *Client) DecryptResponse(respJson string, isRequest bool) (string, error) { var reqData map[string]string if err := json.Unmarshal([]byte(respJson), &reqData); err != nil { - return "", err + return "", fmt.Errorf("解析响应JSON失败: %v,响应内容: %s", err, respJson) + } + + dataKey := "response" + if isRequest { + dataKey = "request" + } + + _, hasData := reqData[dataKey] + _, hasSignature := reqData["signature"] + _, hasEncryptKey := reqData["encryptKey"] + + if !hasData || !hasSignature || !hasEncryptKey { + if code, ok := reqData["code"]; ok { + msg := reqData["msg"] + if msg == "" { + msg = reqData["message"] + } + return "", fmt.Errorf("银行返回错误,错误码: %s,错误信息: %s", code, msg) + } + if respCode, ok := reqData["respCode"]; ok { + respMsg := reqData["respMsg"] + return "", fmt.Errorf("银行返回错误,错误码: %s,错误信息: %s", respCode, respMsg) + } + missingFields := []string{} + if !hasData { + missingFields = append(missingFields, dataKey) + } + if !hasSignature { + missingFields = append(missingFields, "signature") + } + if !hasEncryptKey { + missingFields = append(missingFields, "encryptKey") + } + return "", fmt.Errorf("响应格式不正确,缺少字段: %v,响应内容: %s", missingFields, respJson) } reqData["accessToken"] = "" - var keys []string - if isRequest { - keys = []string{"request", "signature", "encryptKey", "accessToken"} - } else { - keys = []string{"response", "signature", "encryptKey", "accessToken"} - } - - var inEncryptKey, inData, inSignature string - for _, key := range keys { - data, ok := reqData[key] - if !ok { - return "", fmt.Errorf("请求数据中不存在%s", key) - } - switch key { - case "request", "response": - inData = data - case "signature": - inSignature = data - case "encryptKey": - inEncryptKey = data - } - } + inData := reqData[dataKey] + inSignature := reqData["signature"] + inEncryptKey := reqData["encryptKey"] // 验签 checked := c.verify(fmt.Sprintf("%s%s%s", inData, inEncryptKey, ""), inSignature) @@ -213,7 +228,12 @@ func getSM4IV() []byte { } func generateSM4Key() []byte { - return make([]byte, 16) + key := make([]byte, 16) + _, err := rand.Read(key) + if err != nil { + panic(fmt.Sprintf("生成SM4密钥失败: %v", err)) + } + return key } func pkcs5Padding(ciphertext []byte, blockSize int) []byte { diff --git a/app/third/paymentService/psbc/notify.go b/app/third/paymentService/psbc/notify.go index 4cfb08a..9fbecad 100644 --- a/app/third/paymentService/psbc/notify.go +++ b/app/third/paymentService/psbc/notify.go @@ -25,7 +25,7 @@ func (c *Client) ParseNotify(rawJson string) (*NotifyRequest, error) { } // VerifyAndParseNotify 验签并解析回调通知 -func (c *Client) VerifyAndParseNotify(rawJson string) (map[string]interface{}, error) { +func (c *Client) VerifyAndParseNotify(rawJson string) (*YouChuOrderNotifyRequest, error) { var reqData map[string]string if err := json.Unmarshal([]byte(rawJson), &reqData); err != nil { return nil, fmt.Errorf("解析通知失败: %v", err) @@ -44,10 +44,10 @@ func (c *Client) VerifyAndParseNotify(rawJson string) (map[string]interface{}, e return nil, fmt.Errorf("解密通知失败: %v", err) } - var result map[string]interface{} + var result YouChuOrderNotifyRequest if err := json.Unmarshal([]byte(decrypted), &result); err != nil { return nil, fmt.Errorf("解析解密后数据失败: %v", err) } - return result, nil + return &result, nil } diff --git a/app/third/paymentService/psbc/order.go b/app/third/paymentService/psbc/order.go index 1575173..c5d897c 100644 --- a/app/third/paymentService/psbc/order.go +++ b/app/third/paymentService/psbc/order.go @@ -58,14 +58,22 @@ func (c *Client) OrderQuery(orderNo string) (*OrderQueryResponse, error) { return nil, fmt.Errorf("读取响应失败: %v", err) } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP请求失败,状态码: %d,响应: %s", resp.StatusCode, string(body)) + } + responseData, err := c.DecryptResponse(string(body), false) if err != nil { - return nil, fmt.Errorf("解密响应失败: %v", err) + return nil, fmt.Errorf("解密响应失败: %v,原始响应: %s", err, string(body)) } var result OrderQueryResponse if err := json.Unmarshal([]byte(responseData), &result); err != nil { - return nil, fmt.Errorf("解析响应失败: %v", err) + return nil, fmt.Errorf("解析响应失败: %v,响应内容: %s", err, responseData) + } + + if result.RespCode != "" && result.RespCode != "0000" && result.RespCode != "00" { + return nil, fmt.Errorf("订单查询失败,错误码: %s,错误信息: %s", result.RespCode, result.RespMsg) } return &result, nil diff --git a/app/third/paymentService/psbc/pay_url.go b/app/third/paymentService/psbc/pay_url.go index 015e910..de0e20c 100644 --- a/app/third/paymentService/psbc/pay_url.go +++ b/app/third/paymentService/psbc/pay_url.go @@ -4,20 +4,26 @@ import ( "encoding/hex" "fmt" "net/url" + "strings" ) // CreatePaymentLink 创建支付链接 func (c *Client) CreatePaymentLink(req PaymentLinkRequest) (*PaymentLinkResponse, error) { - payUrlTemplate := "%s&fromId=include_sb&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + var payUrlTemplate string + if len(req.BackUrl) > 0 && strings.Contains(req.BackUrl, "?") { + payUrlTemplate = "%s&fromId=include_sb&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + } else { + payUrlTemplate = "%s?fromId=include_sb&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + } orderData := map[string]string{ "merchantNo": c.cfg.MchtNo, "orderInfos": req.ProductName, "orderNo": req.OrderNo, "amount": req.Price, - "successUrl": hex.EncodeToString([]byte(c.cfg.SuccessUrl + req.OrderNo)), + "successUrl": hex.EncodeToString([]byte(c.cfg.SuccessUrl)), "acctType": "2", - "notifyUrl": hex.EncodeToString([]byte(c.cfg.SuccessUrl + req.OrderNo)), + "notifyUrl": hex.EncodeToString([]byte(c.cfg.NotifyUrl)), } signData := map[string]string{ @@ -55,10 +61,21 @@ func (c *Client) CreatePaymentLink(req PaymentLinkRequest) (*PaymentLinkResponse // CreatePaymentLinkWithBackUrl 创建支付链接(带 fromId 判断) func (c *Client) CreatePaymentLinkWithBackUrl(req PaymentLinkRequest) (*PaymentLinkResponse, error) { var payUrlTemplate string - if len(req.BackUrl) > 0 && (contains(req.BackUrl, "fromId")) { - payUrlTemplate = "%s&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + hasQuery := len(req.BackUrl) > 0 && strings.Contains(req.BackUrl, "?") + hasFromId := len(req.BackUrl) > 0 && contains(req.BackUrl, "fromId") + + if hasFromId { + if hasQuery { + payUrlTemplate = "%s&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + } else { + payUrlTemplate = "%s?tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + } } else { - payUrlTemplate = "%s&fromId=include_sb&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + if hasQuery { + payUrlTemplate = "%s&fromId=include_sb&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + } else { + payUrlTemplate = "%s?fromId=include_sb&tysdPayParams=%s&tysdShopid=%s&tysdEncryptKey=%s&returnUrl=%s&showTitleBar=%s" + } } orderData := map[string]string{ @@ -66,9 +83,9 @@ func (c *Client) CreatePaymentLinkWithBackUrl(req PaymentLinkRequest) (*PaymentL "orderInfos": req.ProductName, "orderNo": req.OrderNo, "amount": req.Price, - "successUrl": hex.EncodeToString([]byte(c.cfg.SuccessUrl + req.OrderNo)), + "successUrl": hex.EncodeToString([]byte(c.cfg.SuccessUrl)), "acctType": "2", - "notifyUrl": hex.EncodeToString([]byte(c.cfg.SuccessUrl + req.OrderNo)), + "notifyUrl": hex.EncodeToString([]byte(c.cfg.NotifyUrl)), } signData := map[string]string{ @@ -104,5 +121,5 @@ func (c *Client) CreatePaymentLinkWithBackUrl(req PaymentLinkRequest) (*PaymentL } func contains(s, substr string) bool { - return len(s) > 0 && len(substr) > 0 && (s == substr || len(s) > len(substr) && s[:len(substr)] == substr) + return len(s) > 0 && len(substr) > 0 && strings.Contains(s, substr) } diff --git a/app/third/paymentService/psbc/refund.go b/app/third/paymentService/psbc/refund.go index 6c527c5..8c7a699 100644 --- a/app/third/paymentService/psbc/refund.go +++ b/app/third/paymentService/psbc/refund.go @@ -61,14 +61,22 @@ func (c *Client) Refund(req RefundRequest) (*RefundResponse, error) { return nil, fmt.Errorf("读取响应失败: %v", err) } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP请求失败,状态码: %d,响应: %s", resp.StatusCode, string(body)) + } + responseData, err := c.DecryptResponse(string(body), false) if err != nil { - return nil, fmt.Errorf("解密响应失败: %v", err) + return nil, fmt.Errorf("解密响应失败: %v,原始响应: %s", err, string(body)) } var result RefundResponse if err := json.Unmarshal([]byte(responseData), &result); err != nil { - return nil, fmt.Errorf("解析响应失败: %v", err) + return nil, fmt.Errorf("解析响应失败: %v,响应内容: %s", err, responseData) + } + + if result.RespCode != "" && result.RespCode != "0000" && result.RespCode != "00" { + return nil, fmt.Errorf("退款申请失败,错误码: %s,错误信息: %s", result.RespCode, result.RespMsg) } return &result, nil diff --git a/app/third/paymentService/psbc/types.go b/app/third/paymentService/psbc/types.go index 34e29b2..62aa057 100644 --- a/app/third/paymentService/psbc/types.go +++ b/app/third/paymentService/psbc/types.go @@ -48,10 +48,14 @@ type OrderQueryRequest struct { // OrderQueryResponse 订单查询响应 type OrderQueryResponse struct { - OrderNo string `json:"orderNo"` - OrderSta string `json:"orderSta"` - RespCode string `json:"code"` - RespMsg string `json:"respMsg"` + RespCode string `json:"code"` + RespMsg string `json:"respMsg"` + RespCd string `json:"respCd"` + ReqTraceId string `json:"reqTraceId"` //请求方流水号或者订单号, + OrderNo string `json:"orderNo"` //统一收单订单号 + OrderSta string `json:"orderSta"` //03-支付成功 04-支付失败 05-检查失败 + TxnFg string `json:"txnFg"` //0-正常 2-已部分退货 3- 已全部退货 + TxnAmt string `json:"txnAmt"` // 交易金额 单位为元,精确到小数点后2位 } // RefundRequest 退款请求 @@ -66,8 +70,13 @@ type RefundRequest struct { type RefundResponse struct { RespCode string `json:"code"` RespMsg string `json:"respMsg"` + RespCd string `json:"respCd"` + TxnCode string `json:"txnCode"` + SourceId string `json:"sourceId"` + ReqTraceId string `json:"ReqTraceId"` RefundOrderNo string `json:"refundOrderNo"` RefundOrderSta string `json:"refundOrderSta"` + MchtNo string `json:"mchtNo"` TxnAmt string `json:"txnAmt"` } @@ -103,6 +112,20 @@ type NotifyResponse struct { LinkUrl string `json:"linkUrl"` } +type YouChuOrderNotifyRequest struct { + TxnCode string `json:"txnCode"` + SourceId string `json:"sourceId"` + ReqDate string `json:"reqDate"` + ReqTraceId string `json:"reqTraceId"` // 商户提交的订单号 + OrderNo string `json:"orderNo"` // 银行的订单号 + TxnAmt string `json:"txnAmt"` + OrderSta string `json:"orderSta"` //03-支付成功 04-支付失败05-检查失败 + MchtNo string `json:"mchtNo"` + TermId string `json:"termId"` + TxnFlag string `json:"txnFlag"` //01-消费 03-退货 + TxnKind string `json:"txnKind"` //01-银行卡 02-微信 03-支付宝 +} + // OrderStatus 订单状态 type OrderStatus string diff --git a/app/third/paymentService/psbc_service.go b/app/third/paymentService/psbc_service.go index a8e0af9..94c486c 100644 --- a/app/third/paymentService/psbc_service.go +++ b/app/third/paymentService/psbc_service.go @@ -5,7 +5,6 @@ import ( psbc "PaymentCenter/app/third/paymentService/psbc" "PaymentCenter/config" "context" - "errors" "fmt" "github.com/qit-team/snow-core/log/logger" "strconv" @@ -101,17 +100,26 @@ func PsbcOrderQuery(ctx context.Context, psbcConfig PsbcPay, orderNo string) (Pa tradeStateDesc = "未支付" } - amountTotal := int64(0) - payerTotal := int64(0) + var amountTotal int64 + var payerTotal int64 + if resp.TxnAmt != "" { + amountFloat, err := strconv.ParseFloat(resp.TxnAmt, 64) + if err == nil { + amountTotal = int64(amountFloat * 100) + payerTotal = int64(amountFloat * 100) + } + } + + successTime := "" outTradeNo, _ := strconv.ParseInt(orderNo, 10, 64) return PayOrderQueryInfo{ AppId: psbcConfig.AppID, OutTradeNo: outTradeNo, - TransactionId: "", + TransactionId: resp.OrderNo, TradeState: tradeState, TradeStateDesc: tradeStateDesc, - SuccessTime: "", + SuccessTime: successTime, AmountTotal: amountTotal, PayerTotal: payerTotal, }, nil @@ -177,26 +185,14 @@ func PsbcVerifyAndParseNotify(rawBody string, psbcConfig PsbcPay) (orderId int64 return 0, 0, 0, "", err } - orderNoStr := "" - if orderNo, ok := result["orderNo"].(string); ok { - orderNoStr = orderNo - } - if orderNoStr == "" { - return 0, 0, 0, "", errors.New("回调数据中缺少订单号") - } - - orderId, err = strconv.ParseInt(orderNoStr, 10, 64) + orderId, err = strconv.ParseInt(result.ReqTraceId, 10, 64) if err != nil { logger.Error(context.Background(), "PsbcVerifyAndParseNotify 发生错误", fmt.Sprintf("订单号转换失败,错误信息:%s", err.Error())) return 0, 0, 0, "", err } - amountStr := "" - if amount, ok := result["amount"].(string); ok { - amountStr = amount - } - if amountStr != "" { - amountFloat, err := strconv.ParseFloat(amountStr, 64) + if result.TxnAmt != "" { + amountFloat, err := strconv.ParseFloat(result.TxnAmt, 64) if err == nil { amountTotal = int64(amountFloat * 100) payerTotal = amountTotal @@ -204,8 +200,8 @@ func PsbcVerifyAndParseNotify(rawBody string, psbcConfig PsbcPay) (orderId int64 } orderSta := "" - if sta, ok := result["orderSta"].(string); ok { - orderSta = sta + if result.OrderSta != "" { + orderSta = result.OrderSta } switch orderSta {