微信支付CreateUnifiedOrder

微信API,入统一为struct,出统一用mxj再转struct
This commit is contained in:
gazebo
2019-11-19 18:28:04 +08:00
parent 57d00d1bc7
commit ed010619f1
3 changed files with 234 additions and 95 deletions

View File

@@ -9,9 +9,9 @@ import (
"git.rosy.net.cn/baseapi/utils"
)
type CDData string
type CData string
func (c CDData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
func (c CData) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(struct {
string `xml:",cdata"`
}{string(c)}, start)
@@ -21,7 +21,7 @@ type CallbackResponse struct {
XMLName xml.Name `json:"-" xml:"xml"`
ReturnCode string `json:"return_code" xml:"return_code"`
ReturnMsg CDData `json:"return_msg" xml:"return_msg"`
ReturnMsg CData `json:"return_msg" xml:"return_msg"`
}
var (
@@ -41,7 +41,7 @@ func Err2CallbackResponse(err error, data string) *CallbackResponse {
}
return &CallbackResponse{
ReturnCode: ResponseCodeFail,
ReturnMsg: CDData(returnMsg),
ReturnMsg: CData(returnMsg),
}
}

View File

@@ -28,33 +28,81 @@ const (
sigType = "MD5"
)
const (
FeeTypeCNY = "CNY"
)
const (
TradeTypeJSAPI = "JSAPI" // JSAPI支付或小程序支付
TradeTypeNative = "NATIVE"
TradeTypeAPP = "APP"
TradeTypeMicroPay = "MICROPAY"
)
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"`
MchID string `json:"mch_id" xml:"mch_id"`
NonceStr string `json:"nonce_str" xml:"nonce_str"`
Sign string `json:"sign" xml:"sign"`
SignType string `json:"sign_type,omitempty" xml:"sign_type,omitempty"`
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"`
SignType string `json:"sign_type,omitempty" xml:"sign_type,omitempty"`
}
type IRequestBase interface {
SetAppID(appID string)
SetMchID(mchID string)
SetNonceStr(nonceStr string)
SetSign(sign string)
SetSignType(signType string)
}
func (r *RequestBase) SetAppID(appID string) {
r.AppID = appID
}
func (r *RequestBase) SetMchID(mchID string) {
r.MchID = 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 ResponseResult struct {
XMLName xml.Name `json:"-" xml:"xml"`
// XMLName xml.Name `json:"-" xml:"xml"`
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"`
SignType string `json:"sign_type,omitempty" xml:"sign_type,omitempty"`
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"`
DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
OpenID string `json:"openid,omitempty" xml:"openid,omitempty"`
SignType string `json:"sign_type,omitempty" xml:"sign_type,omitempty"`
OpenID string `json:"openid,omitempty" xml:"openid,omitempty"`
}
type OrderQueryParam struct {
@@ -63,12 +111,80 @@ type OrderQueryParam struct {
OutTradeNo string `json:"out_trade_no" xml:"out_trade_no"`
}
type API struct {
appID string
appKey string
mchID string
client *http.Client
config *platformapi.APIConfig
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"`
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"`
}
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"`
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"`
}
func New(appID, appKey, mchID string, config ...*platformapi.APIConfig) *API {
@@ -109,10 +225,10 @@ func (a *API) signParam(params map[string]interface{}) (sig string) {
return sig
}
func unmarshalXML(data []byte, result interface{}) error {
d := xml.NewDecoder(bytes.NewReader(data))
return d.Decode(result)
}
// func unmarshalXML(data []byte, result interface{}) error {
// d := xml.NewDecoder(bytes.NewReader(data))
// return d.Decode(result)
// }
func mustMarshalXML(obj interface{}) []byte {
byteArr, err := xml.Marshal(obj)
@@ -122,69 +238,18 @@ func mustMarshalXML(obj interface{}) []byte {
return byteArr
}
func (a *API) AccessAPI(action string, params interface{}, baseParam *RequestBase) (retVal *ResponseResult, err error) {
baseParam.AppID = a.appID
baseParam.MchID = a.mchID
baseParam.NonceStr = utils.GetUUID()
baseParam.SignType = sigType
baseParam.Sign = a.signParam(utils.Struct2FlatMap(params))
func (a *API) AccessAPI(action string, requestParam IRequestBase) (retVal map[string]interface{}, err error) {
requestParam.SetAppID(a.appID)
requestParam.SetMchID(a.mchID)
requestParam.SetNonceStr(utils.GetUUID())
requestParam.SetSignType(sigType)
requestParam.SetSign(a.signParam(utils.Struct2FlatMap(requestParam)))
fullURL := utils.GenerateGetURL(prodURL, action, nil)
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
request, _ := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(mustMarshalXML(params)))
return request
},
a.config,
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
if jsonResult1 == nil {
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
}
retVal, errLevel, err = a.checkResult(jsonResult1[platformapi.KeyData].(string))
return errLevel, err
})
return retVal, err
}
func (a *API) checkResult(xmlStr string) (result *ResponseResult, errLevel string, err error) {
err = unmarshalXML([]byte(xmlStr), &result)
if err != nil {
errLevel = platformapi.ErrLevelGeneralFail
} else {
if result.ReturnCode != ResponseCodeSuccess {
errLevel = platformapi.ErrLevelGeneralFail
err = utils.NewErrorCode(result.ReturnMsg, result.ReturnCode)
result = nil
} else {
// if result.ResultCode != ResponseCodeSuccess {
// errLevel = platformapi.ErrLevelGeneralFail
// err = utils.NewErrorCode(result.ErrCodeDes, result.ErrCode)
// result = nil
// } else {
// }
}
}
return result, errLevel, err
}
func (a *API) AccessAPIByMap(action string, params map[string]interface{}) (retVal map[string]interface{}, err error) {
params2 := utils.MergeMaps(params, map[string]interface{}{
"appid": a.appID,
"mch_id": a.mchID,
"nonce_str": utils.GetUUID(),
"sign_type": sigType,
})
params2[sigKey] = a.signParam(params2)
fullURL := utils.GenerateGetURL(prodURL, action, nil)
xmlBytes, err := mxj.Map(params2).Xml("xml")
if err != nil {
return nil, err
}
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
request, _ := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(xmlBytes))
request, _ := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(mustMarshalXML(requestParam)))
return request
},
a.config,
@@ -198,6 +263,57 @@ func (a *API) AccessAPIByMap(action string, params map[string]interface{}) (retV
return retVal, err
}
// func (a *API) checkResult(xmlStr string) (result *ResponseResult, errLevel string, err error) {
// err = unmarshalXML([]byte(xmlStr), &result)
// if err != nil {
// errLevel = platformapi.ErrLevelGeneralFail
// } else {
// if result.ReturnCode != ResponseCodeSuccess {
// errLevel = platformapi.ErrLevelGeneralFail
// err = utils.NewErrorCode(result.ReturnMsg, result.ReturnCode)
// result = nil
// } else {
// // if result.ResultCode != ResponseCodeSuccess {
// // errLevel = platformapi.ErrLevelGeneralFail
// // err = utils.NewErrorCode(result.ErrCodeDes, result.ErrCode)
// // result = nil
// // } else {
// // }
// }
// }
// return result, errLevel, err
// }
// func (a *API) AccessAPIByMap(action string, params map[string]interface{}) (retVal map[string]interface{}, err error) {
// params2 := utils.MergeMaps(params, map[string]interface{}{
// "appid": a.appID,
// "mch_id": a.mchID,
// "nonce_str": utils.GetUUID(),
// "sign_type": sigType,
// })
// params2[sigKey] = a.signParam(params2)
// fullURL := utils.GenerateGetURL(prodURL, action, nil)
// xmlBytes, err := mxj.Map(params2).Xml("xml")
// if err != nil {
// return nil, err
// }
// err = platformapi.AccessPlatformAPIWithRetry(a.client,
// func() *http.Request {
// request, _ := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(xmlBytes))
// return request
// },
// a.config,
// func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
// if jsonResult1 == nil {
// return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
// }
// retVal, errLevel, err = a.checkResultAsMap(jsonResult1[platformapi.KeyData].(string))
// return errLevel, err
// })
// return retVal, err
// }
func (a *API) checkResultAsMap(xmlStr string) (result map[string]interface{}, errLevel string, err error) {
mv, err := mxj.NewMapXml([]byte(xmlStr))
if err != nil {
@@ -221,18 +337,26 @@ func (a *API) checkResultAsMap(xmlStr string) (result map[string]interface{}, er
return result, errLevel, err
}
func (a *API) OrderQuery(transactionID, outTradeNo string) (retVal map[string]interface{}, err error) {
// param := &OrderQueryParam{
// TransactionID: transactionID,
// OutTradeNo: outTradeNo,
// }
// retVal, err = a.AccessAPI("orderquery", param, &param.RequestBase)
param := map[string]interface{}{}
if transactionID != "" {
param["transaction_id"] = transactionID
} else {
param["out_trade_no"] = outTradeNo
}
retVal, err = a.AccessAPIByMap("orderquery", param)
return retVal, err
func SceneInfoList2String(sceneList []*CreateOrderSceneInfo) (str string) {
return string(utils.MustMarshal(sceneList))
}
func (a *API) OrderQuery(transactionID, outTradeNo string) (orderInfo *OrderInfo, err error) {
param := &OrderQueryParam{
TransactionID: transactionID,
OutTradeNo: outTradeNo,
}
retVal, err := a.AccessAPI("orderquery", param)
if err == nil {
err = utils.Map2StructByJson(retVal, &orderInfo, false)
}
return orderInfo, err
}
func (a *API) CreateUnifiedOrder(param *CreateOrderParam) (createOrderResult *CreateOrderResult, err error) {
retVal, err := a.AccessAPI("unifiedorder", param)
if err == nil {
err = utils.Map2StructByJson(retVal, &createOrderResult, false)
}
return createOrderResult, err
}

View File

@@ -29,3 +29,18 @@ func TestOrderQuery(t *testing.T) {
}
t.Log(utils.Format4Output(result, false))
}
func TestCreateUnifiedOrder(t *testing.T) {
result, err := api.CreateUnifiedOrder(&CreateOrderParam{
Body: "这里一个测试商品",
NotifyURL: "http://callback.test.jxc4.com/wxpay/msg/",
OutTradeNo: utils.GetUUID(),
SpbillCreateIP: "114.114.114.114",
TradeType: TradeTypeNative,
TotalFee: 1,
})
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}