小程序支付的交互图如下:
小程序支付时序图
商户系统和微信支付系统主要交互:
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】
2、商户server调用支付统一下单,api参见公共api【统一下单API】
3、商户server调用再次签名,api参见公共api【再次签名】
4、商户server接收支付通知,api参见公共api【支付结果通知API】
5、商户server查询支付结果,api参见公共api【查询订单API】
以下是统一下单API
//响应信息
type WXPayResp struct {
Return_code string `xml:"return_code"`
Return_msg string `xml:"return_msg"`
Nonce_str string `xml:"nonce_str"`
Prepay_id string `xml:"prepay_id"`
}
//微信支付
func (index *IndexController) WxPay(){
info := make(map[string]interface{}, 0)
fmt.Println("访问ip",index.Request.RemoteAddr)
ip := utils.Substr(index.Request.RemoteAddr, 0, strings.Index(index.Request.RemoteAddr, ":"))
total_fee,_ := strconv.ParseFloat(index.GetString("total_fee"),64) //单位 分
openId := index.GetString("openId"); //"oKYr_0GkE-Izt9N9Wn43sapI9Pqw"
body := "费用说明";
//订单号
orderNo := index.GetString("orderNo"); //"wx"+utils.ToStr(time.Now().Unix()) + string(utils.Krand(4, 0))
//随机数
nonceStr := time.Now().Format("20060102150405") + string(utils.Krand(4, 0))
var reqMap = make(map[string]interface{}, 0)
reqMap["appid"] = utils.Wx_Appid//微信小程序appid
reqMap["body"] = body //商品描述
reqMap["mch_id"] = utils.Wx_Mchid //商户号
reqMap["nonce_str"] = nonceStr //随机数
reqMap["notify_url"] = "http://test.com.cn/weixinNotice.jspx" //通知地址
reqMap["openid"] = openId //商户唯一标识 openid
reqMap["out_trade_no"] = orderNo //订单号
reqMap["spbill_create_ip"] = ip //用户端ip //订单生成的机器 IP
reqMap["total_fee"] = total_fee * 100 //订单总金额,单位为分
reqMap["trade_type"] = "JSAPI" //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
reqMap["sign"] = WxPayCalcSign(reqMap,utils.WX_KEY)
reqStr := Map2Xml(reqMap)
fmt.Println("请求xml",reqStr)
client := &http.Client{}
// 调用支付统一下单API
req, err := http.NewRequest("POST", "https://api.mch.weixin.qq.com/pay/unifiedorder", strings.NewReader(reqStr))
if err != nil {
// handle error
}
req.Header.Set("Content-Type", "text/xml;charset=utf-8")
resp, err := client.Do(req)
defer resp.Body.Close()
body2, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
fmt.Println("解析响应内容失败",err)
return
}
fmt.Println("响应数据",string(body2))
var resp1 WXPayResp
err = xml.Unmarshal(body2, &resp1)
if err != nil {
panic(err)
return
}
// 返回预付单信息
if strings.ToUpper(resp1.Return_code) == "SUCCESS"{
fmt.Println("预支付申请成功")
// 再次签名
var resMap = make(map[string]interface{}, 0)
resMap["appId"] = utils.Wx_Appid
resMap["nonceStr"] = resp1.Nonce_str //商品描述
resMap["package"] = "prepay_id=" + resp1.Prepay_id //商户号
resMap["signType"] = "MD5" //签名类型
resMap["timeStamp"] = utils.ToStr(time.Now().Unix()) //当前时间戳
resMap["paySign"] = WxPayCalcSign(resMap,utils.WX_KEY)
// 返回5个支付参数及sign 用户进行确认支付
fmt.Println("支付参数",resMap)
index.Console(resMap)
}else{
info["msg"] = "微信请求支付失败"
index.Console(info)
}
}
//微信支付计算签名的函数
func WxPayCalcSign(mReq map[string]interface{}, key string) (sign string) {
//STEP 1, 对key进行升序排序.
sorted_keys := make([]string, 0)
for k, _ := range mReq {
sorted_keys = append(sorted_keys, k)
}
sort.Strings(sorted_keys)
//STEP2, 对key=value的键值对用&连接起来,略过空值
var signStrings string
for _, k := range sorted_keys {
logger.Printf("k=%v, v=%v\n", k, mReq[k])
value := fmt.Sprintf("%v", mReq[k])
if value != "" {
signStrings = signStrings + k + "=" + value + "&"
}
}
//STEP3, 在键值对的最后加上key=API_KEY
if key != "" {
signStrings = signStrings + "key=" + key
}
fmt.Println("加密前-----",signStrings)
//STEP4, 进行MD5签名并且将所有字符转为大写.
md5Ctx := md5.New()
md5Ctx.Write([]byte(signStrings)) //
cipherStr := md5Ctx.Sum(nil)
upperSign := strings.ToUpper(hex.EncodeToString(cipherStr))
fmt.Println("加密后-----",upperSign)
return upperSign
}
//微信支付计算签名的函数
func Map2Xml(mReq map[string]interface{}) (xml string) {
sb := bytes.Buffer{}
sb.WriteString("<xml>")
for k,v := range mReq{
sb.WriteString("<"+k+">"+utils.ToStr(v)+"</"+k+">")
}
sb.WriteString("</xml>")
return sb.String()
}