666 lines
23 KiB
Go
666 lines
23 KiB
Go
package wxpayapi
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/hmac"
|
||
"crypto/md5"
|
||
"crypto/sha256"
|
||
"crypto/tls"
|
||
"encoding/xml"
|
||
"fmt"
|
||
"net/http"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.rosy.net.cn/baseapi"
|
||
"git.rosy.net.cn/baseapi/platformapi"
|
||
"git.rosy.net.cn/baseapi/utils"
|
||
"github.com/clbanning/mxj"
|
||
)
|
||
|
||
const (
|
||
prodURL = "https://api.mch.weixin.qq.com"
|
||
sandboxURL = "https://api.mch.weixin.qq.com/sandboxnew"
|
||
)
|
||
|
||
const (
|
||
ResponseCodeSuccess = "SUCCESS"
|
||
ResponseCodeFail = "FAIL"
|
||
|
||
sigKey = "sign"
|
||
sigTypeKey = "sign_type"
|
||
sigTypeMd5 = "MD5"
|
||
sigTypeSha256 = "HMAC-SHA256"
|
||
|
||
specialAction = "mmpaymkttransfers/promotion/transfers"
|
||
)
|
||
|
||
const (
|
||
FeeTypeCNY = "CNY"
|
||
)
|
||
|
||
const (
|
||
TradeTypeJSAPI = "JSAPI" // JSAPI支付(或小程序支付)
|
||
TradeTypeNative = "NATIVE"
|
||
TradeTypeAPP = "APP"
|
||
TradeTypeMicroPay = "MICROPAY"
|
||
|
||
OptYes = "Y"
|
||
OptNo = "N"
|
||
|
||
Relation = "PARTNER"
|
||
AccountTypeWx = "PERSONAL_WECHATID"
|
||
AccountTypeOpen = "PERSONAL_OPENID"
|
||
CheckName = "NO_CHECK"
|
||
)
|
||
|
||
var (
|
||
specialSignActions = map[string]string{
|
||
"secapi/pay/multiprofitsharing": "secapi/pay/multiprofitsharing",
|
||
"pay/profitsharingaddreceiver": "pay/profitsharingaddreceiver",
|
||
"pay/profitsharingremovereceiver": "pay/profitsharingremovereceiver",
|
||
"secapi/pay/profitsharingreturn": "secapi/pay/profitsharingreturn",
|
||
"secapi/pay/profitsharingfinish": "secapi/pay/profitsharingfinish",
|
||
}
|
||
)
|
||
|
||
type API struct {
|
||
appID string
|
||
appKey string
|
||
mchID string
|
||
client *http.Client
|
||
config *platformapi.APIConfig
|
||
}
|
||
|
||
type RequestBase struct {
|
||
XMLName xml.Name `json:"-" xml:"xml"`
|
||
|
||
AppID string `json:"appid" xml:"appid,omitempty"`
|
||
MchID string `json:"mch_id" xml:"mch_id,omitempty"`
|
||
// 企业付款基础参数不一样
|
||
AppID2 string `json:"mch_appid,omitempty" xml:"mch_appid,omitempty"`
|
||
MchID2 string `json:"mchid,omitempty" xml:"mchid,omitempty"`
|
||
|
||
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
SignType string `json:"sign_type,omitempty" xml:"sign_type,omitempty"`
|
||
}
|
||
|
||
type IRequestBase interface {
|
||
SetAppID(action, appID string)
|
||
SetMchID(action, mchID string)
|
||
SetNonceStr(nonceStr string)
|
||
SetSign(sign string)
|
||
SetSignType(signType string)
|
||
}
|
||
|
||
func (r *RequestBase) SetAppID(action, appID string) {
|
||
if action != specialAction {
|
||
r.AppID = appID
|
||
} else {
|
||
r.AppID2 = appID
|
||
}
|
||
}
|
||
|
||
func (r *RequestBase) SetMchID(action, mchID string) {
|
||
if action != specialAction {
|
||
r.MchID = mchID
|
||
} else {
|
||
r.MchID2 = mchID
|
||
}
|
||
}
|
||
|
||
func (r *RequestBase) SetNonceStr(nonceStr string) {
|
||
r.NonceStr = nonceStr
|
||
}
|
||
|
||
func (r *RequestBase) SetSign(sign string) {
|
||
r.Sign = sign
|
||
}
|
||
|
||
func (r *RequestBase) SetSignType(signType string) {
|
||
r.SignType = signType
|
||
}
|
||
|
||
type OrderQueryParam struct {
|
||
RequestBase
|
||
TransactionID string `json:"transaction_id" xml:"transaction_id"`
|
||
OutTradeNo string `json:"out_trade_no" xml:"out_trade_no"`
|
||
}
|
||
|
||
type OrderInfo struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
AppID string `json:"appid" xml:"appid"`
|
||
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
|
||
Attach string `json:"attach"`
|
||
BankType string `json:"bank_type"`
|
||
CashFee string `json:"cash_fee"`
|
||
CashFeeType string `json:"cash_fee_type"`
|
||
FeeType string `json:"fee_type"`
|
||
IsSubscribe string `json:"is_subscribe"`
|
||
OpenID string `json:"openid"`
|
||
OutTradeNo string `json:"out_trade_no"`
|
||
TimeEnd string `json:"time_end"`
|
||
TotalFee string `json:"total_fee"`
|
||
TradeState string `json:"trade_state"`
|
||
TradeStateDesc string `json:"trade_state_desc"`
|
||
TradeType string `json:"trade_type"`
|
||
TransactionID string `json:"transaction_id"`
|
||
}
|
||
|
||
type CreateOrderSceneInfo struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
AreaCode string `json:"area_code"`
|
||
Address string `json:"address"`
|
||
}
|
||
|
||
type CreateOrderParam struct {
|
||
RequestBase
|
||
Body string `json:"body" xml:"body"`
|
||
NotifyURL string `json:"notify_url" xml:"notify_url"`
|
||
OutTradeNo string `json:"out_trade_no" xml:"out_trade_no"`
|
||
SpbillCreateIP string `json:"spbill_create_ip" xml:"spbill_create_ip"`
|
||
TradeType string `json:"trade_type" xml:"trade_type"`
|
||
TotalFee int `json:"total_fee" xml:"total_fee"`
|
||
|
||
// SubAppID string `json:"sub_appid" xml:"sub_appid,omitempty"`
|
||
SubMchID string `json:"sub_mch_id" xml:"sub_mch_id,omitempty"`
|
||
Detail CData `json:"detail.omitempty" xml:"detail,omitempty"`
|
||
Attach string `json:"attach,omitempty" xml:"attach,omitempty"`
|
||
FeeType string `json:"fee_type,omitempty" xml:"fee_type,omitempty"`
|
||
TimeStart string `json:"time_start,omitempty" xml:"time_start,omitempty"`
|
||
TimeExpire string `json:"time_expire,omitempty" xml:"time_expire,omitempty"`
|
||
GoodsTag string `json:"goods_tag,omitempty" xml:"goods_tag,omitempty"`
|
||
ProductID string `json:"product_id,omitempty" xml:"product_id,omitempty"`
|
||
LimitPay string `json:"limit_pay,omitempty" xml:"limit_pay,omitempty"`
|
||
OpenID string `json:"openid,omitempty" xml:"openid,omitempty"`
|
||
Receipt string `json:"receipt,omitempty" xml:"receipt,omitempty"`
|
||
SceneInfo string `json:"scene_info,omitempty" xml:"scene_info,omitempty"`
|
||
ProfitSharing string `json:"profit_sharing,omitempty" xml:"profit_sharing,omitempty"`
|
||
}
|
||
|
||
type CloseOrderParam struct {
|
||
RequestBase
|
||
OutTradeNo string `json:"out_trade_no" xml:"out_trade_no"`
|
||
}
|
||
|
||
type CreateOrderResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
AppID string `json:"appid" xml:"appid"`
|
||
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ResultMsg string `json:"result_msg" xml:"result_msg"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
|
||
TradeType string `json:"trade_type"`
|
||
PrepayID string `json:"prepay_id"`
|
||
CodeURL string `json:"code_url"`
|
||
}
|
||
|
||
type PayRefundParam struct {
|
||
RequestBase
|
||
|
||
TransactionID string `json:"transaction_id,omitempty" xml:"transaction_id,omitempty"`
|
||
OutTradeNo string `json:"out_trade_no,omitempty" xml:"out_trade_no,omitempty"`
|
||
OutRefundNo string `json:"out_refund_no" xml:"out_refund_no"`
|
||
TotalFee int `json:"total_fee" xml:"total_fee"`
|
||
RefundFee int `json:"refund_fee" xml:"refund_fee"`
|
||
|
||
RefundFeeType string `json:"refund_fee_type,omitempty" xml:"refund_fee_type,omitempty"`
|
||
RefundDesc CData `json:"refund_desc,omitempty" xml:"refund_desc,omitempty"`
|
||
NotifyURL string `json:"notify_url,omitempty" xml:"notify_url,omitempty"`
|
||
}
|
||
|
||
type PayRefundResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
AppID string `json:"appid" xml:"appid"`
|
||
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ResultMsg string `json:"result_msg" xml:"result_msg"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
|
||
CashFee string `json:"cash_fee"`
|
||
CashRefundFee string `json:"cash_refund_fee"`
|
||
CouponRefundCount string `json:"coupon_refund_count"`
|
||
CouponRefundFee string `json:"coupon_refund_fee"`
|
||
OutRefundNo string `json:"out_refund_no"`
|
||
OutTradeNo string `json:"out_trade_no"`
|
||
RefundChannel string `json:"refund_channel"`
|
||
RefundFee string `json:"refund_fee"`
|
||
RefundID string `json:"refund_id"`
|
||
TotalFee string `json:"total_fee"`
|
||
TransactionID string `json:"transaction_id"`
|
||
}
|
||
|
||
type MultiProfitSharingParam struct {
|
||
RequestBase
|
||
TransactionID string `json:"transaction_id" xml:"transaction_id"`
|
||
OutOrderNo string `json:"out_order_no" xml:"out_order_no"`
|
||
Receivers CData `json:"receivers" xml:"receivers"`
|
||
}
|
||
|
||
type MultiProfitSharingResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
AppID string `json:"appid" xml:"appid"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
|
||
TransactionID string `json:"transaction_id" xml:"transaction_id"`
|
||
OutOrderNo string `json:"out_order_no" xml:"out_order_no"`
|
||
OrderID string `json:"order_id" xml:"order_id"`
|
||
}
|
||
|
||
type TransfersParam struct {
|
||
RequestBase
|
||
PartnerTradeNo string `json:"partner_trade_no" xml:"partner_trade_no"`
|
||
OpenID string `json:"openid" xml:"openid"`
|
||
CheckName string `json:"check_name" xml:"check_name"`
|
||
ReUserName string `json:"re_user_name,omitempty" xml:"re_user_name,omitempty"`
|
||
Amount int `json:"amount" xml:"amount"`
|
||
Desc string `json:"desc" xml:"desc"`
|
||
SpbillCreateIP string `json:"spbill_create_ip" xml:"spbill_create_ip"`
|
||
}
|
||
|
||
type TransfersResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
AppID string `json:"appid" xml:"appid"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
|
||
|
||
PartnerTradeNo string `json:"partner_trade_no" xml:"partner_trade_no"`
|
||
PaymentNo string `json:"payment_no" xml:"payment_no"`
|
||
PaymentTime string `json:"payment_time" xml:"payment_time"`
|
||
}
|
||
|
||
type ProfitSharingReceiverParam struct {
|
||
RequestBase
|
||
Receiver CData `json:"receiver" xml:"receiver"`
|
||
}
|
||
|
||
type ProfitSharingReceiverResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
AppID string `json:"appid" xml:"appid"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
Receiver string `json:"receiver" xml:"receiver"`
|
||
}
|
||
|
||
type ReturnProfitSharingParam struct {
|
||
RequestBase
|
||
// OrderID string `json:"order_id" xml:"order_id"`
|
||
OutOrderNo string `json:"out_order_no" xml:"out_order_no"`
|
||
OutReturnNo string `json:"out_return_no" xml:"out_return_no"`
|
||
ReturnAccountType string `json:"return_account_type" xml:"return_account_type"`
|
||
ReturnAccount string `json:"return_account" xml:"return_account"`
|
||
ReturnAmount int `json:"return_amount" xml:"return_amount"`
|
||
Description string `json:"description" xml:"description"`
|
||
}
|
||
|
||
type ReturnProfitSharingResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ErrorCode string `json:"error_code,omitempty" xml:"error_code,omitempty"`
|
||
ErrorMsg string `json:"error_msg,omitempty" xml:"error_msg,omitempty"`
|
||
|
||
AppID string `json:"appid" xml:"appid"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
OrderID string `json:"order_id" xml:"order_id"`
|
||
OutOrderNo string `json:"out_order_no" xml:"out_order_no"`
|
||
OutReturnNo string `json:"out_return_no" xml:"out_return_no"`
|
||
ReturnNo string `json:"return_no" xml:"return_no"`
|
||
ReturnAccountType string `json:"return_account_type" xml:"return_account_type"`
|
||
ReturnAccount string `json:"return_account" xml:"return_account"`
|
||
ReturnAmount int `json:"return_amount" xml:"return_amount"`
|
||
Description string `json:"description" xml:"description"`
|
||
Result string `json:"result" xml:"result"`
|
||
FailReason string `json:"fail_reason,omitempty" xml:"fail_reason,omitempty"`
|
||
FinishTime string `json:"finish_time" xml:"finish_time"`
|
||
}
|
||
|
||
type FinishProfitSharingParam struct {
|
||
RequestBase
|
||
TransactionID string `json:"transaction_id" xml:"transaction_id"`
|
||
OutOrderNo string `json:"out_order_no" xml:"out_order_no"`
|
||
Description string `json:"description" xml:"description"`
|
||
}
|
||
|
||
type FinishProfitSharingResult struct {
|
||
ReturnCode string `json:"return_code" xml:"return_code"`
|
||
ReturnMsg string `json:"return_msg" xml:"return_msg"`
|
||
|
||
ResultCode string `json:"result_code" xml:"result_code"`
|
||
ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"`
|
||
ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
|
||
AppID string `json:"appid" xml:"appid"`
|
||
MchID string `json:"mch_id" xml:"mch_id"`
|
||
NonceStr string `json:"nonce_str" xml:"nonce_str"`
|
||
Sign string `json:"sign" xml:"sign"`
|
||
|
||
TransactionID string `json:"transaction_id" xml:"transaction_id"`
|
||
OutOrderNo string `json:"out_order_no" xml:"out_order_no"`
|
||
OrderID string `json:"order_id" xml:"order_id"`
|
||
}
|
||
|
||
func New(appID, appKey, mchID string, config ...*platformapi.APIConfig) *API {
|
||
curConfig := platformapi.DefAPIConfig
|
||
if len(config) > 0 {
|
||
curConfig = *config[0]
|
||
}
|
||
return &API{
|
||
appID: appID,
|
||
appKey: appKey,
|
||
mchID: mchID,
|
||
client: &http.Client{Timeout: curConfig.ClientTimeout},
|
||
config: &curConfig,
|
||
}
|
||
}
|
||
|
||
func NewWithCertificate(appID, appKey, mchID string, certPEMBlock, keyPEMBlock interface{}, config ...*platformapi.APIConfig) (a *API) {
|
||
var certs tls.Certificate
|
||
var err error
|
||
if binCertPEMBlock, ok := certPEMBlock.([]byte); ok {
|
||
certs, err = tls.X509KeyPair(binCertPEMBlock, keyPEMBlock.([]byte))
|
||
} else {
|
||
certs, err = tls.LoadX509KeyPair(certPEMBlock.(string), keyPEMBlock.(string))
|
||
}
|
||
if err == nil {
|
||
// pool := x509.NewCertPool()
|
||
// pool.AppendCertsFromPEM(rootCa.([]byte))
|
||
a = New(appID, appKey, mchID, config...)
|
||
a.client.Transport = &http.Transport{
|
||
TLSClientConfig: &tls.Config{
|
||
// RootCAs: pool,
|
||
Certificates: []tls.Certificate{certs},
|
||
},
|
||
}
|
||
} else {
|
||
baseapi.SugarLogger.Warnf("NewWithCertificate failed with err:%v", err)
|
||
}
|
||
return a
|
||
}
|
||
|
||
func (a *API) GetAppID() string {
|
||
return a.appID
|
||
}
|
||
|
||
func (a *API) GetMchID() string {
|
||
return a.mchID
|
||
}
|
||
|
||
func (a *API) signParam(signType string, params map[string]interface{}) (sig string) {
|
||
var valueList []string
|
||
for k, v := range params {
|
||
if k != sigKey {
|
||
if str := fmt.Sprint(v); str != "" {
|
||
valueList = append(valueList, fmt.Sprintf("%s=%s", k, str))
|
||
}
|
||
}
|
||
}
|
||
sort.Sort(sort.StringSlice(valueList))
|
||
valueList = append(valueList, fmt.Sprintf("key=%s", a.appKey))
|
||
sig = strings.Join(valueList, "&")
|
||
var binSig []byte
|
||
if signType == sigTypeSha256 {
|
||
mac := hmac.New(sha256.New, []byte(a.appKey))
|
||
mac.Write([]byte(sig))
|
||
binSig = mac.Sum(nil)
|
||
} else {
|
||
binSig2 := md5.Sum([]byte(sig))
|
||
binSig = binSig2[:]
|
||
}
|
||
sig = fmt.Sprintf("%X", binSig)
|
||
// baseapi.SugarLogger.Debug(sig)
|
||
return sig
|
||
}
|
||
|
||
func mustMarshalXML(obj interface{}) []byte {
|
||
byteArr, err := xml.Marshal(obj)
|
||
if err != nil {
|
||
panic(fmt.Sprintf("err when Marshal obj:%v with error:%v", obj, err))
|
||
}
|
||
return byteArr
|
||
}
|
||
|
||
func (a *API) AccessAPI(action string, requestParam IRequestBase) (retVal map[string]interface{}, err error) {
|
||
requestParam.SetAppID(action, a.appID)
|
||
requestParam.SetMchID(action, a.mchID)
|
||
requestParam.SetNonceStr(utils.GetUUID())
|
||
var sigType string
|
||
if specialSignActions[action] != "" {
|
||
sigType = sigTypeSha256
|
||
requestParam.SetSignType(sigType)
|
||
} else if action == specialAction {
|
||
sigType = sigTypeMd5
|
||
} else {
|
||
sigType = sigTypeMd5
|
||
requestParam.SetSignType(sigType)
|
||
}
|
||
signStr := a.signParam(sigType, utils.Struct2FlatMap(requestParam))
|
||
requestParam.SetSign(signStr)
|
||
|
||
fullURL := utils.GenerateGetURL(prodURL, action, nil)
|
||
|
||
//var responseStr string
|
||
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
||
func() *http.Request {
|
||
request, _ := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(mustMarshalXML(requestParam)))
|
||
return request
|
||
},
|
||
a.config,
|
||
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
|
||
//responseStr = bodyStr
|
||
if jsonResult1 == nil {
|
||
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
|
||
}
|
||
retVal, errLevel, err = a.checkResultAsMap(jsonResult1[platformapi.KeyData].(string))
|
||
return errLevel, err
|
||
})
|
||
if err == nil {
|
||
if utils.Interface2String(retVal["result_code"]) != ResponseCodeSuccess {
|
||
err = utils.NewErrorCode(utils.Interface2String(retVal["err_code_des"]), utils.Interface2String(retVal["err_code"]))
|
||
if err != nil {
|
||
}
|
||
retVal = nil
|
||
}
|
||
}
|
||
return retVal, err
|
||
}
|
||
|
||
func (a *API) parseXmlStrAsMap(xmlStr string) (result map[string]interface{}, errLevel string, err error) {
|
||
mv, err := mxj.NewMapXml([]byte(xmlStr))
|
||
if err != nil {
|
||
errLevel = platformapi.ErrLevelGeneralFail
|
||
} else {
|
||
result = mv["xml"].(map[string]interface{})
|
||
}
|
||
return result, errLevel, err
|
||
}
|
||
|
||
func (a *API) checkResultAsMap(xmlStr string) (result map[string]interface{}, errLevel string, err error) {
|
||
result, errLevel, err = a.parseXmlStrAsMap(xmlStr)
|
||
if err == nil {
|
||
returnCode := utils.Interface2String(result["return_code"])
|
||
if returnCode != ResponseCodeSuccess {
|
||
errLevel = platformapi.ErrLevelGeneralFail
|
||
err = utils.NewErrorCode(utils.Interface2String(result["return_msg"]), returnCode)
|
||
result = nil
|
||
}
|
||
}
|
||
return result, errLevel, err
|
||
}
|
||
|
||
func SceneInfoList2String(sceneList []*CreateOrderSceneInfo) (str string) {
|
||
return string(utils.MustMarshal(sceneList))
|
||
}
|
||
|
||
func Time2PayTime(t time.Time) (str string) {
|
||
return t.Format("20060102150405")
|
||
}
|
||
|
||
func PayTime2Time(str string) (t time.Time) {
|
||
t, _ = time.ParseInLocation("20060102150405", str, time.Local)
|
||
return t
|
||
}
|
||
|
||
func (a *API) mapData2Err(mapData map[string]interface{}) (err error) {
|
||
if resultCode := utils.Interface2String(mapData["result_code"]); resultCode != ResponseCodeSuccess {
|
||
err = utils.NewErrorCode(utils.Interface2String(mapData["err_code_des"]), utils.Interface2String(mapData["err_code"]))
|
||
}
|
||
return err
|
||
}
|
||
|
||
func (a *API) translateResult(mapData map[string]interface{}, dataPtr interface{}) (err error) {
|
||
if err = a.mapData2Err(mapData); err == nil && dataPtr != nil {
|
||
err = utils.Map2StructByJson(mapData, dataPtr, false)
|
||
}
|
||
return err
|
||
}
|
||
|
||
func (a *API) OrderQuery(transactionID, outTradeNo string) (orderInfo *OrderInfo, err error) {
|
||
param := &OrderQueryParam{
|
||
TransactionID: transactionID,
|
||
OutTradeNo: outTradeNo,
|
||
}
|
||
retVal, err := a.AccessAPI("pay/orderquery", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &orderInfo)
|
||
}
|
||
return orderInfo, err
|
||
}
|
||
|
||
func (a *API) CreateUnifiedOrder(param *CreateOrderParam) (createOrderResult *CreateOrderResult, err error) {
|
||
retVal, err := a.AccessAPI("pay/unifiedorder", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &createOrderResult)
|
||
}
|
||
return createOrderResult, err
|
||
}
|
||
|
||
// 关闭订单
|
||
// https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
|
||
// 注意:订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟。
|
||
// 此函数好像可以重复操作,且关闭一个不存在的订单也不会报错的样
|
||
func (a *API) CloseOrder(outTradeNo string) (err error) {
|
||
retVal, err := a.AccessAPI("pay/closeorder", &CloseOrderParam{
|
||
OutTradeNo: outTradeNo,
|
||
})
|
||
if err == nil {
|
||
err = a.translateResult(retVal, nil)
|
||
}
|
||
return err
|
||
}
|
||
|
||
func (a *API) PayRefund(param *PayRefundParam) (refundResult *PayRefundResult, err error) {
|
||
if a.client.Transport == nil {
|
||
return nil, fmt.Errorf("没有配置证书,不能退款")
|
||
}
|
||
retVal, err := a.AccessAPI("secapi/pay/refund", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &refundResult)
|
||
}
|
||
return refundResult, err
|
||
}
|
||
|
||
//请求多次分账
|
||
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_6&index=2
|
||
func (a *API) MultiProfitSharing(param *MultiProfitSharingParam) (result *MultiProfitSharingResult, err error) {
|
||
retVal, err := a.AccessAPI("secapi/pay/multiprofitsharing", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &result)
|
||
}
|
||
return result, err
|
||
}
|
||
|
||
//企业付款
|
||
//https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
|
||
func (a *API) Transfers(param *TransfersParam) (result *TransfersResult, err error) {
|
||
retVal, err := a.AccessAPI(specialAction, param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &result)
|
||
}
|
||
return result, err
|
||
}
|
||
|
||
//添加分账接收方
|
||
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_3&index=4
|
||
func (a *API) AddProfitSharingReceiver(param *ProfitSharingReceiverParam) (result *ProfitSharingReceiverResult, err error) {
|
||
retVal, err := a.AccessAPI("pay/profitsharingaddreceiver", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &result)
|
||
}
|
||
return result, err
|
||
}
|
||
|
||
//删除分账接收方
|
||
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_4&index=5
|
||
func (a *API) DeleteProfitSharingReceiver(param *ProfitSharingReceiverParam) (result *ProfitSharingReceiverResult, err error) {
|
||
retVal, err := a.AccessAPI("pay/profitsharingremovereceiver", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &result)
|
||
}
|
||
return result, err
|
||
}
|
||
|
||
//分账回退
|
||
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_7&index=7
|
||
func (a *API) ReturnProfitSharing(param *ReturnProfitSharingParam) (result *ReturnProfitSharingResult, err error) {
|
||
retVal, err := a.AccessAPI("secapi/pay/profitsharingreturn", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &result)
|
||
}
|
||
return result, err
|
||
}
|
||
|
||
//完结分账
|
||
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_5&index=6
|
||
func (a *API) FinishProfitSharing(param *FinishProfitSharingParam) (result *FinishProfitSharingResult, err error) {
|
||
retVal, err := a.AccessAPI("secapi/pay/profitsharingfinish", param)
|
||
if err == nil {
|
||
err = a.translateResult(retVal, &result)
|
||
}
|
||
return result, err
|
||
}
|