Skip to content

Commit

Permalink
fix refund notify decrypt struct problem
Browse files Browse the repository at this point in the history
  • Loading branch information
iGoogle-ink committed Mar 16, 2021
1 parent a21507e commit 419a268
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 51 deletions.
58 changes: 32 additions & 26 deletions wechat/v3/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,13 @@ type Amount struct {
PayerTotal int `json:"payer_total,omitempty"` // 用户支付金额,单位为分
Currency string `json:"currency,omitempty"` // CNY:人民币,境内商户号仅支持人民币
PayerCurrency string `json:"payer_currency,omitempty"` // 用户支付币种
Refund int `json:"refund,omitempty"` // 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额。服务商模式下无此字段
}

type RefundAmount struct {
Total int `json:"total,omitempty"` // 订单总金额,单位为分,只能为整数
Refund int `json:"refund,omitempty"` // 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额,如果有使用券,后台会按比例退
PayerTotal int `json:"payer_total,omitempty"` // 用户实际支付金额,单位为分,只能为整数
PayerRefund int `json:"payer_refund"` // 退款给用户的金额,不包含所有优惠券金额
}

type CombineAmount struct {
Expand Down Expand Up @@ -256,7 +262,7 @@ type CombineQueryOrder struct {
CombineOutTradeNo string `json:"combine_out_trade_no"` // 合单支付总订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一
SceneInfo *SceneInfo `json:"scene_info,omitempty"` // 支付场景描述
SubOrders []*SubOrders `json:"sub_orders"` // 最多支持子单条数:50
Payer *Payer `json:"combine_payer_info"` // 支付者信息
CombinePayerInfo *Payer `json:"combine_payer_info"` // 支付者信息
}

type TradeBill struct {
Expand All @@ -280,33 +286,33 @@ type DownloadBill struct {
}

type RefundOrderResponse struct {
RefundID string `json:"refund_id" valid:"Required"` // 微信支付退款号
OutRefundNo string `json:"out_refund_no" valid:"Required"` // 商户退款单号
TransactionID string `json:"transaction_id" valid:"Required"` // 微信支付系统生成的订单号
OutTradeNo string `json:"out_trade_no" valid:"Required"` // 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
Channel string `json:"channel" valid:"Required"` // 退款渠道
UserReceivedAccount string `json:"user_received_account" valid:"Required"` // 退款入账账户
SuccessTime string `json:"success_time"` // 退款成功时间
CreateTime string `json:"create_time" valid:"Required"` // 退款创建时间
Status string `json:"status" valid:"Required"` // 退款状态
FundsAccount string `json:"funds_account"` // 资金账户
Amount *Amount `json:"amount" valid:"Required"` // 金额信息
PromotionDetail []*PromotionDetail `json:"promotion_detail"` // 优惠退款信息
RefundID string `json:"refund_id"` // 微信支付退款号
OutRefundNo string `json:"out_refund_no"` // 商户退款单号
TransactionID string `json:"transaction_id"` // 微信支付系统生成的订单号
OutTradeNo string `json:"out_trade_no"` // 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
Channel string `json:"channel"` // 退款渠道
UserReceivedAccount string `json:"user_received_account"` // 退款入账账户
SuccessTime string `json:"success_time"` // 退款成功时间
CreateTime string `json:"create_time"` // 退款创建时间
Status string `json:"status"` // 退款状态
FundsAccount string `json:"funds_account"` // 资金账户
Amount *RefundQueryAmount `json:"amount"` // 金额信息
PromotionDetail []*RefundQueryPromotionDetail `json:"promotion_detail"` // 优惠退款信息
}

type RefundQueryResponse struct {
RefundID string `json:"refund_id" valid:"Required"` // 微信支付退款号
OutRefundNo string `json:"out_refund_no" valid:"Required"` // 商户退款单号
TransactionID string `json:"transaction_id" valid:"Required"` // 微信支付系统生成的订单号
OutTradeNo string `json:"out_trade_no" valid:"Required"` // 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
Channel string `json:"channel" valid:"Required"` // 退款渠道
UserReceivedAccount string `json:"user_received_account" valid:"Required"` // 退款入账账户
SuccessTime string `json:"success_time"` // 退款成功时间
CreateTime string `json:"create_time" valid:"Required"` // 退款创建时间
Status string `json:"status" valid:"Required"` // 退款状态
FundsAccount string `json:"funds_account"` // 资金账户
Amount *RefundQueryAmount `json:"amount"` // 金额信息
PromotionDetail []*RefundQueryPromotionDetail `json:"promotion_detail"` // 优惠退款信息
RefundID string `json:"refund_id"` // 微信支付退款号
OutRefundNo string `json:"out_refund_no"` // 商户退款单号
TransactionID string `json:"transaction_id"` // 微信支付系统生成的订单号
OutTradeNo string `json:"out_trade_no"` // 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
Channel string `json:"channel"` // 退款渠道
UserReceivedAccount string `json:"user_received_account"` // 退款入账账户
SuccessTime string `json:"success_time"` // 退款成功时间
CreateTime string `json:"create_time"` // 退款创建时间
Status string `json:"status"` // 退款状态
FundsAccount string `json:"funds_account"` // 资金账户
Amount *RefundQueryAmount `json:"amount"` // 金额信息
PromotionDetail []*RefundQueryPromotionDetail `json:"promotion_detail"` // 优惠退款信息
}

type RefundQueryAmount struct {
Expand Down
125 changes: 100 additions & 25 deletions wechat/v3/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,41 @@ type Resource struct {
}

type V3DecryptResult struct {
Amount *Amount `json:"amount"`
Appid string `json:"appid"`
Attach string `json:"attach"`
BankType string `json:"bank_type"`
Mchid string `json:"mchid"`
OutTradeNo string `json:"out_trade_no"`
Payer *Payer `json:"payer"`
SuccessTime string `json:"success_time"`
TransactionId string `json:"transaction_id"`
TradeType string `json:"trade_type"`
TradeState string `json:"trade_state"`
TradeStateDesc string `json:"trade_state_desc"`
TradeType string `json:"trade_type"`
TransactionId string `json:"transaction_id"`
PromotionDetail []*PromotionDetail `json:"promotion_detail"`
BankType string `json:"bank_type"`
Attach string `json:"attach"`
SuccessTime string `json:"success_time"`
Payer *Payer `json:"payer"`
Amount *Amount `json:"amount"`
SceneInfo *SceneInfo `json:"scene_info"`
PromotionDetail []*PromotionDetail `json:"promotion_detail"`
}

type V3DecryptRefundResult struct {
Mchid string `json:"mchid"`
OutTradeNo string `json:"out_trade_no"`
TransactionId string `json:"transaction_id"`
OutRefundNo string `json:"out_refund_no"`
RefundId string `json:"refund_id"`
RefundStatus string `json:"refund_status"`
SuccessTime string `json:"success_time"`
UserReceivedAccount string `json:"user_received_account"`
Amount *RefundAmount `json:"amount"`
}

type V3DecryptCombineResult struct {
CombineAppid string `json:"combine_appid"`
CombineMchid string `json:"combine_mchid"`
CombineOutTradeNo string `json:"combine_out_trade_no"`
SceneInfo *SceneInfo `json:"scene_info"`
SubOrders []*SubOrders `json:"sub_orders"` // 最多支持子单条数:50
CombinePayerInfo *Payer `json:"combine_payer_info"` // 支付者信息
}

type V3NotifyReq struct {
Expand All @@ -54,7 +75,7 @@ type V3NotifyRsp struct {
Message string `json:"message"`
}

// V3ParseNotify 解析微信回调请求的参数到 V3NotifyReq 结构体
// 解析微信回调请求的参数到 V3NotifyReq 结构体
func V3ParseNotify(req *http.Request) (notifyReq *V3NotifyReq, err error) {
bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
defer req.Body.Close()
Expand All @@ -75,15 +96,15 @@ func V3ParseNotify(req *http.Request) (notifyReq *V3NotifyReq, err error) {
return notifyReq, nil
}

// VerifySign 异步通知验签
// 异步通知验签
func (v *V3NotifyReq) VerifySign(wxPkContent string) (err error) {
if v.SignInfo != nil {
return V3VerifySign(v.SignInfo.HeaderTimestamp, v.SignInfo.HeaderNonce, v.SignInfo.SignBody, v.SignInfo.HeaderSignature, wxPkContent)
}
return errors.New("verify notify sign, bug SignInfo is nil")
}

// DecryptCipherText 解密回调中的加密订单信息
// 解密普通支付回调中的加密订单信息
func (v *V3NotifyReq) DecryptCipherText(apiV3Key string) (result *V3DecryptResult, err error) {
if v.Resource != nil {
result, err = V3DecryptNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
Expand All @@ -96,24 +117,33 @@ func (v *V3NotifyReq) DecryptCipherText(apiV3Key string) (result *V3DecryptResul
return nil, errors.New("notify data Resource is nil")
}

// Deprecated
// V3ParseNotifyToBodyMap 解析微信回调请求的参数到 gopay.BodyMap
// 暂时不推荐此方法,请使用 wechat.V3ParseNotify()
func V3ParseNotifyToBodyMap(req *http.Request) (bm gopay.BodyMap, err error) {
bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
defer req.Body.Close()
if err != nil {
xlog.Error("err:", err)
return
// 解密普通退款回调中的加密订单信息
func (v *V3NotifyReq) DecryptRefundCipherText(apiV3Key string) (result *V3DecryptRefundResult, err error) {
if v.Resource != nil {
result, err = V3DecryptRefundNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%+v)", string(bytes), err)
}
return result, nil
}
bm = make(gopay.BodyMap)
if err = json.Unmarshal(bs, &bm); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s):%+v", string(bs), err)
return nil, errors.New("notify data Resource is nil")
}

// 解密合单支付回调中的加密订单信息
func (v *V3NotifyReq) DecryptCombineCipherText(apiV3Key string) (result *V3DecryptCombineResult, err error) {
if v.Resource != nil {
result, err = V3DecryptCombineNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%+v)", string(bytes), err)
}
return result, nil
}
return bm, nil
return nil, errors.New("notify data Resource is nil")
}

// V3DecryptNotifyCipherText 解密回调中的加密订单信息
// 解密普通支付回调中的加密订单信息
func V3DecryptNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
Expand All @@ -126,3 +156,48 @@ func V3DecryptNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (
}
return result, nil
}

// 解密普通退款回调中的加密订单信息
func V3DecryptRefundNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptRefundResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%+v", err)
}
result = &V3DecryptRefundResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%+v", string(decrypt), err)
}
return result, nil
}

// 解密合单支付回调中的加密订单信息
func V3DecryptCombineNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptCombineResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%+v", err)
}
result = &V3DecryptCombineResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%+v", string(decrypt), err)
}
return result, nil
}

// Deprecated
// 解析微信回调请求的参数到 gopay.BodyMap
// 暂时不推荐此方法,请使用 wechat.V3ParseNotify()
func V3ParseNotifyToBodyMap(req *http.Request) (bm gopay.BodyMap, err error) {
bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
defer req.Body.Close()
if err != nil {
xlog.Error("err:", err)
return
}
bm = make(gopay.BodyMap)
if err = json.Unmarshal(bs, &bm); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s):%+v", string(bs), err)
}
return bm, nil
}

0 comments on commit 419a268

Please sign in to comment.