github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mch/pay/unifiedorder.go (about) 1 package pay 2 3 import ( 4 "fmt" 5 "strconv" 6 "time" 7 8 "github.com/chanxuehong/wechat/mch/core" 9 "github.com/chanxuehong/wechat/util" 10 ) 11 12 // UnifiedOrder 统一下单. 13 func UnifiedOrder(clt *core.Client, req map[string]string) (resp map[string]string, err error) { 14 return clt.PostXML(core.APIBaseURL()+"/pay/unifiedorder", req) 15 } 16 17 type UnifiedOrderRequest struct { 18 XMLName struct{} `xml:"xml" json:"-"` 19 20 // 必选参数 21 Body string `xml:"body"` // 商品或支付单简要描述 22 OutTradeNo string `xml:"out_trade_no"` // 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号 23 TotalFee int64 `xml:"total_fee"` // 订单总金额,单位为分,详见支付金额 24 SpbillCreateIP string `xml:"spbill_create_ip"` // APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 25 NotifyURL string `xml:"notify_url"` // 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 26 TradeType string `xml:"trade_type"` // 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定 27 28 // 可选参数 29 DeviceInfo string `xml:"device_info"` // 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" 30 NonceStr string `xml:"nonce_str"` // 随机字符串,不长于32位。NOTE: 如果为空则系统会自动生成一个随机字符串。 31 SignType string `xml:"sign_type"` // 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 32 Detail string `xml:"detail"` // 商品名称明细列表 33 Attach string `xml:"attach"` // 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 34 FeeType string `xml:"fee_type"` // 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 35 TimeStart time.Time `xml:"time_start"` // 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 36 TimeExpire time.Time `xml:"time_expire"` // 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则 37 GoodsTag string `xml:"goods_tag"` // 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠 38 ProductId string `xml:"product_id"` // trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。 39 LimitPay string `xml:"limit_pay"` // no_credit--指定不能使用信用卡支付 40 OpenId string `xml:"openid"` // rade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。 41 SubOpenId string `xml:"sub_openid"` // trade_type=JSAPI,此参数必传,用户在子商户appid下的唯一标识。openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。 42 SceneInfo string `xml:"scene_info"` // 该字段用于上报支付的场景信息,针对H5支付有以下三种场景,请根据对应场景上报,H5支付不建议在APP端使用,针对场景1,2请接入APP支付,不然可能会出现兼容性问题 43 } 44 45 type UnifiedOrderResponse struct { 46 XMLName struct{} `xml:"xml" json:"-"` 47 48 // 必选返回 49 PrepayId string `xml:"prepay_id"` // 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 50 TradeType string `xml:"trade_type"` // 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,详细说明见参数规定 51 52 // 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断 53 DeviceInfo string `xml:"device_info"` // 调用接口提交的终端设备号。 54 CodeURL string `xml:"code_url"` // trade_type 为 NATIVE 时有返回,可将该参数值生成二维码展示出来进行扫码支付 55 MWebURL string `xml:"mweb_url"` // trade_type 为 MWEB 时有返回 56 } 57 58 // UnifiedOrder2 统一下单. 59 func UnifiedOrder2(clt *core.Client, req *UnifiedOrderRequest) (resp *UnifiedOrderResponse, err error) { 60 m1 := make(map[string]string, 24) 61 m1["body"] = req.Body 62 m1["out_trade_no"] = req.OutTradeNo 63 m1["total_fee"] = strconv.FormatInt(req.TotalFee, 10) 64 m1["spbill_create_ip"] = req.SpbillCreateIP 65 m1["notify_url"] = req.NotifyURL 66 m1["trade_type"] = req.TradeType 67 if req.DeviceInfo != "" { 68 m1["device_info"] = req.DeviceInfo 69 } 70 if req.NonceStr != "" { 71 m1["nonce_str"] = req.NonceStr 72 } else { 73 m1["nonce_str"] = util.NonceStr() 74 } 75 if req.SignType != "" { 76 m1["sign_type"] = req.SignType 77 } 78 if req.Detail != "" { 79 m1["detail"] = req.Detail 80 } 81 if req.Attach != "" { 82 m1["attach"] = req.Attach 83 } 84 if req.FeeType != "" { 85 m1["fee_type"] = req.FeeType 86 } 87 if !req.TimeStart.IsZero() { 88 m1["time_start"] = core.FormatTime(req.TimeStart) 89 } 90 if !req.TimeExpire.IsZero() { 91 m1["time_expire"] = core.FormatTime(req.TimeExpire) 92 } 93 if req.GoodsTag != "" { 94 m1["goods_tag"] = req.GoodsTag 95 } 96 if req.ProductId != "" { 97 m1["product_id"] = req.ProductId 98 } 99 if req.LimitPay != "" { 100 m1["limit_pay"] = req.LimitPay 101 } 102 if req.OpenId != "" { 103 m1["openid"] = req.OpenId 104 } 105 if req.SubOpenId != "" { 106 m1["sub_openid"] = req.SubOpenId 107 } 108 if req.SceneInfo != "" { 109 m1["scene_info"] = req.SceneInfo 110 } 111 112 m2, err := UnifiedOrder(clt, m1) 113 if err != nil { 114 return nil, err 115 } 116 117 // 校验 trade_type 118 respTradeType := m2["trade_type"] 119 if respTradeType != req.TradeType { 120 err = fmt.Errorf("trade_type mismatch, have: %s, want: %s", respTradeType, req.TradeType) 121 return nil, err 122 } 123 124 resp = &UnifiedOrderResponse{ 125 PrepayId: m2["prepay_id"], 126 TradeType: respTradeType, 127 DeviceInfo: m2["device_info"], 128 CodeURL: m2["code_url"], 129 MWebURL: m2["mweb_url"], 130 } 131 return resp, nil 132 }