github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mch/pay/micropay.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 // MicroPay 提交刷卡支付. 13 func MicroPay(clt *core.Client, req map[string]string) (resp map[string]string, err error) { 14 return clt.PostXML(core.APIBaseURL()+"/pay/micropay", req) 15 } 16 17 type MicroPayRequest 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 AuthCode string `xml:"auth_code"` // 扫码支付授权码,设备读取用户微信中的条码或者二维码信息 26 27 // 可选参数 28 DeviceInfo string `xml:"device_info"` // 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" 29 NonceStr string `xml:"nonce_str"` // 随机字符串,不长于32位。NOTE: 如果为空则系统会自动生成一个随机字符串。 30 SignType string `xml:"sign_type"` // 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 31 Detail string `xml:"detail"` // 商品名称明细列表 32 Attach string `xml:"attach"` // 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 33 FeeType string `xml:"fee_type"` // 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 34 GoodsTag string `xml:"goods_tag"` // 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠 35 LimitPay string `xml:"limit_pay"` // no_credit--指定不能使用信用卡支付 36 SceneInfo string `xml:"scene_info"` // 场景信息 37 } 38 39 type MicroPayResponse struct { 40 XMLName struct{} `xml:"xml" json:"-"` 41 42 // 必选返回 43 OpenId string `xml:"openid"` // 用户在商户appid下的唯一标识 44 IsSubscribe bool `xml:"is_subscribe"` // 用户是否关注公众账号 45 TradeType string `xml:"trade_type"` // 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,MICROPAY,详细说明见参数规定 46 BankType string `xml:"bank_type"` // 银行类型,采用字符串类型的银行标识 47 TotalFee int64 `xml:"total_fee"` // 订单总金额,单位为分 48 CashFee int64 `xml:"cash_fee"` // 现金支付金额订单现金支付金额,详见支付金额 49 TransactionId string `xml:"transaction_id"` // 微信支付订单号 50 OutTradeNo string `xml:"out_trade_no"` // 商户系统的订单号,与请求一致。 51 TimeEnd time.Time `xml:"time_end"` // 订单支付时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 52 53 // 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断 54 DeviceInfo string `xml:"device_info"` // 微信支付分配的终端设备号 55 SubOpenId string `xml:"sub_openid"` // 子商户appid下用户唯一标识,如需返回则请求时需要传sub_appid 56 SubIsSubscribe *bool `xml:"sub_is_subscribe"` // 用户是否关注子公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注 57 FeeType string `xml:"fee_type"` // 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 58 SettlementTotalFee *int64 `xml:"settlement_total_fee"` // 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 59 CouponFee *int64 `xml:"coupon_fee"` // 代金券金额 60 CashFeeType string `xml:"cash_fee_type"` // 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 61 Attach string `xml:"attach"` // 附加数据,原样返回 62 PromotionDetail string `xml:"promotion_detail"` // 营销详情 63 } 64 65 // MicroPay2 提交刷卡支付. 66 func MicroPay2(clt *core.Client, req *MicroPayRequest) (resp *MicroPayResponse, err error) { 67 m1 := make(map[string]string, 24) 68 m1["body"] = req.Body 69 m1["out_trade_no"] = req.OutTradeNo 70 m1["total_fee"] = strconv.FormatInt(req.TotalFee, 10) 71 m1["spbill_create_ip"] = req.SpbillCreateIP 72 m1["auth_code"] = req.AuthCode 73 if req.DeviceInfo != "" { 74 m1["device_info"] = req.DeviceInfo 75 } 76 if req.NonceStr != "" { 77 m1["nonce_str"] = req.NonceStr 78 } else { 79 m1["nonce_str"] = util.NonceStr() 80 } 81 if req.SignType != "" { 82 m1["sign_type"] = req.SignType 83 } 84 if req.Detail != "" { 85 m1["detail"] = req.Detail 86 } 87 if req.Attach != "" { 88 m1["attach"] = req.Attach 89 } 90 if req.FeeType != "" { 91 m1["fee_type"] = req.FeeType 92 } 93 if req.GoodsTag != "" { 94 m1["goods_tag"] = req.GoodsTag 95 } 96 if req.LimitPay != "" { 97 m1["limit_pay"] = req.LimitPay 98 } 99 if req.SceneInfo != "" { 100 m1["scene_info"] = req.SceneInfo 101 } 102 103 m2, err := MicroPay(clt, m1) 104 if err != nil { 105 return nil, err 106 } 107 108 resp = &MicroPayResponse{ 109 OpenId: m2["openid"], 110 TradeType: m2["trade_type"], 111 BankType: m2["bank_type"], 112 TransactionId: m2["transaction_id"], 113 OutTradeNo: m2["out_trade_no"], 114 DeviceInfo: m2["device_info"], 115 SubOpenId: m2["sub_openid"], 116 FeeType: m2["fee_type"], 117 CashFeeType: m2["cash_fee_type"], 118 Attach: m2["attach"], 119 PromotionDetail: m2["promotion_detail"], 120 } 121 122 if str := m2["is_subscribe"]; str != "" { 123 if str == "Y" || str == "y" { 124 resp.IsSubscribe = true 125 } 126 } 127 if str := m2["total_fee"]; str != "" { 128 if n, err := strconv.ParseInt(str, 10, 64); err != nil { 129 err = fmt.Errorf("parse total_fee:%q to int64 failed: %s", str, err.Error()) 130 return nil, err 131 } else { 132 resp.TotalFee = n 133 } 134 } 135 if str := m2["cash_fee"]; str != "" { 136 if n, err := strconv.ParseInt(str, 10, 64); err != nil { 137 err = fmt.Errorf("parse cash_fee:%q to int64 failed: %s", str, err.Error()) 138 return nil, err 139 } else { 140 resp.CashFee = n 141 } 142 } 143 if str := m2["time_end"]; str != "" { 144 if t, err := core.ParseTime(str); err != nil { 145 err = fmt.Errorf("parse time_end:%q to time.Time failed: %s", str, err.Error()) 146 return nil, err 147 } else { 148 resp.TimeEnd = t 149 } 150 } 151 152 if str := m2["sub_is_subscribe"]; str != "" { 153 if str == "Y" || str == "y" { 154 resp.SubIsSubscribe = util.Bool(true) 155 } else { 156 resp.SubIsSubscribe = util.Bool(false) 157 } 158 } 159 if str := m2["settlement_total_fee"]; str != "" { 160 if n, err := strconv.ParseInt(str, 10, 64); err != nil { 161 err = fmt.Errorf("parse settlement_total_fee:%q to int64 failed: %s", str, err.Error()) 162 return nil, err 163 } else { 164 resp.SettlementTotalFee = util.Int64(n) 165 } 166 } 167 if str := m2["coupon_fee"]; str != "" { 168 if n, err := strconv.ParseInt(str, 10, 64); err != nil { 169 err = fmt.Errorf("parse coupon_fee:%q to int64 failed: %s", str, err.Error()) 170 return nil, err 171 } else { 172 resp.CouponFee = util.Int64(n) 173 } 174 } 175 176 // 校验返回参数 177 if req.OutTradeNo != resp.OutTradeNo { 178 err = fmt.Errorf("out_trade_no mismatch, have: %s, want: %s", resp.OutTradeNo, req.OutTradeNo) 179 return nil, err 180 } 181 if req.TotalFee != resp.TotalFee { 182 err = fmt.Errorf("total_fee mismatch, have: %d, want: %d", resp.TotalFee, req.TotalFee) 183 return nil, err 184 } 185 return resp, nil 186 }