forked from chanxuehong/wechat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
refund.go
180 lines (163 loc) · 7.16 KB
/
refund.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package pay
import (
"fmt"
"strconv"
"github.com/fxrobot/wechat/mch/core"
wechatutil "github.com/fxrobot/wechat/util"
)
// Refund 申请退款.
// NOTE: 请求需要双向证书.
func Refund(clt *core.Client, req map[string]string) (resp map[string]string, err error) {
return clt.PostXML(core.APIBaseURL()+"/secapi/pay/refund", req)
}
type RefundRequest struct {
XMLName struct{} `xml:"xml" json:"-"`
// 必选参数, TransactionId 和 OutTradeNo 二选一即可.
TransactionId string `xml:"transaction_id"` // 微信生成的订单号,在支付通知中有返回
OutTradeNo string `xml:"out_trade_no"` // 商户侧传给微信的订单号
OutRefundNo string `xml:"out_refund_no"` // 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
TotalFee int64 `xml:"total_fee"` // 订单总金额,单位为分,只能为整数,详见支付金额
RefundFee int64 `xml:"refund_fee"` // 退款总金额,订单总金额,单位为分,只能为整数,详见支付金额
// 可选参数
NonceStr string `xml:"nonce_str"` // 随机字符串,不长于32位。NOTE: 如果为空则系统会自动生成一个随机字符串。
SignType string `xml:"sign_type"` // 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
RefundFeeType string `xml:"refund_fee_type"` // 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
RefundDesc string `xml:"refund_desc"` // 若商户传入,会在下发给用户的退款消息中体现退款原因
RefundAccount string `xml:"refund_account"` // 退款资金来源
NotifyUrl string `xml:"notify_url"` // 通知地址url
}
type RefundResponse struct {
XMLName struct{} `xml:"xml" json:"-"`
// 必选返回
TransactionId string `xml:"transaction_id"` // 微信订单号
OutTradeNo string `xml:"out_trade_no"` // 商户系统内部的订单号
OutRefundNo string `xml:"out_refund_no"` // 商户退款单号
RefundId string `xml:"refund_id"` // 微信退款单号
RefundFee int64 `xml:"refund_fee"` // 退款总金额,单位为分,可以做部分退款
TotalFee int64 `xml:"total_fee"` // 订单总金额,单位为分,只能为整数,详见支付金额
CashFee int64 `xml:"cash_fee"` // 现金支付金额,单位为分,只能为整数,详见支付金额
// 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断
SettlementRefundFee *int64 `xml:"settlement_refund_fee"` // 退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
SettlementTotalFee *int64 `xml:"settlement_total_fee"` // 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
FeeType string `xml:"fee_type"` // 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
CashFeeType string `xml:"cash_fee_type"` // 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
CashRefundFee *int64 `xml:"cash_refund_fee"` // 现金退款金额,单位为分,只能为整数,详见支付金额
}
// Refund2 申请退款.
// NOTE:
// 1. 请求需要双向证书.
// 2. 该函数不支持 代金券 功能, 如果有 代金券 功能请使用 Refund 函数.
func Refund2(clt *core.Client, req *RefundRequest) (resp *RefundResponse, err error) {
m1 := make(map[string]string, 16)
if req.TransactionId != "" {
m1["transaction_id"] = req.TransactionId
}
if req.OutTradeNo != "" {
m1["out_trade_no"] = req.OutTradeNo
}
m1["out_refund_no"] = req.OutRefundNo
m1["total_fee"] = strconv.FormatInt(req.TotalFee, 10)
m1["refund_fee"] = strconv.FormatInt(req.RefundFee, 10)
if req.NonceStr != "" {
m1["nonce_str"] = req.NonceStr
} else {
m1["nonce_str"] = wechatutil.NonceStr()
}
if req.SignType != "" {
m1["sign_type"] = req.SignType
}
if req.RefundFeeType != "" {
m1["refund_fee_type"] = req.RefundFeeType
}
if req.RefundDesc != "" {
m1["refund_desc"] = req.RefundDesc
}
if req.RefundAccount != "" {
m1["refund_account"] = req.RefundAccount
}
if req.NotifyUrl != "" {
m1["notify_url"] = req.NotifyUrl
}
m2, err := Refund(clt, m1)
if err != nil {
return nil, err
}
resp = &RefundResponse{
TransactionId: m2["transaction_id"],
OutTradeNo: m2["out_trade_no"],
OutRefundNo: m2["out_refund_no"],
RefundId: m2["refund_id"],
FeeType: m2["fee_type"],
CashFeeType: m2["cash_fee_type"],
}
if str := m2["refund_fee"]; str != "" {
if n, err := strconv.ParseInt(str, 10, 64); err != nil {
err = fmt.Errorf("parse refund_fee:%q to int64 failed: %s", str, err.Error())
return nil, err
} else {
resp.RefundFee = n
}
}
if str := m2["total_fee"]; str != "" {
if n, err := strconv.ParseInt(str, 10, 64); err != nil {
err = fmt.Errorf("parse total_fee:%q to int64 failed: %s", str, err.Error())
return nil, err
} else {
resp.TotalFee = n
}
}
if str := m2["cash_fee"]; str != "" {
if n, err := strconv.ParseInt(str, 10, 64); err != nil {
err = fmt.Errorf("parse cash_fee:%q to int64 failed: %s", str, err.Error())
return nil, err
} else {
resp.CashFee = n
}
}
if str := m2["settlement_refund_fee"]; str != "" {
if n, err := strconv.ParseInt(str, 10, 64); err != nil {
err = fmt.Errorf("parse settlement_refund_fee:%q to int64 failed: %s", str, err.Error())
return nil, err
} else {
resp.SettlementRefundFee = wechatutil.Int64(n)
}
}
if str := m2["settlement_total_fee"]; str != "" {
if n, err := strconv.ParseInt(str, 10, 64); err != nil {
err = fmt.Errorf("parse settlement_total_fee:%q to int64 failed: %s", str, err.Error())
return nil, err
} else {
resp.SettlementTotalFee = wechatutil.Int64(n)
}
}
if str := m2["cash_refund_fee"]; str != "" {
if n, err := strconv.ParseInt(str, 10, 64); err != nil {
err = fmt.Errorf("parse cash_refund_fee:%q to int64 failed: %s", str, err.Error())
return nil, err
} else {
resp.CashRefundFee = wechatutil.Int64(n)
}
}
// 校验返回参数
if req.TransactionId != "" && resp.TransactionId != "" && req.TransactionId != resp.TransactionId {
err = fmt.Errorf("transaction_id mismatch, have: %s, want: %s", resp.TransactionId, req.TransactionId)
return nil, err
}
if req.OutTradeNo != "" && resp.OutTradeNo != "" && req.OutTradeNo != resp.OutTradeNo {
err = fmt.Errorf("out_trade_no mismatch, have: %s, want: %s", resp.OutTradeNo, req.OutTradeNo)
return nil, err
}
if req.OutRefundNo != "" && resp.OutRefundNo != "" && req.OutRefundNo != resp.OutRefundNo {
err = fmt.Errorf("out_refund_no mismatch, have: %s, want: %s", resp.OutRefundNo, req.OutRefundNo)
return nil, err
}
if req.TotalFee != resp.TotalFee {
err = fmt.Errorf("total_fee mismatch, have: %d, want: %d", resp.TotalFee, req.TotalFee)
return nil, err
}
if req.RefundFee != resp.RefundFee {
err = fmt.Errorf("refund_fee mismatch, have: %d, want: %d", resp.RefundFee, req.RefundFee)
return nil, err
}
return resp, nil
}