微信支付CreateUnifiedOrder
微信API,入统一为struct,出统一用mxj再转struct
This commit is contained in:
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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, ¶m.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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user