4.微信支付之刷卡支付

实现微信支付刷卡支付

刷卡支付是用户展示钱包内的“刷微信卡条码/二维码”给商户系统,商户扫描后直接完成扣款。

主要应用线下面对面收银的场景,需要有扫描设备的支撑,最常见的莫过于扫码枪,但注意不能是激光扫码枪,因为这种枪因为反光的原因无法扫码手机上的条形码

使用场景

  • 用户打开微信,进入”我”->“钱包”->“刷卡”,展示条形码的界面

  • 收银台在商户系统操作生成支付订单,用户确认支付金额

  • 收银员使用扫码枪扫码用户展示的条形码/二维码,获得条形码/二维码信息,商户系统使用刷卡支付接口提交支付

  • 如果需要验密,手机端会弹出密码输入框要求用户输入密码;否则直接完成扣款

验密规则
  • 支付金额>1000的交易需要输入密码

  • 用户账户每天最多允许5笔免密交易,超过需要输入密码

  • 微信支付后台判定用户行为有异常,符合免密规则的也要求输入密码

被扫支付接口

https://api.mch.weixin.qq.com/pay/micropay ,在package config里面为URL_MICROPAY变量
  • 数据提交方式为post方式

  • 数据格式为xml格式

开发流程

  • 使用扫码备读设取微信用户刷卡授权码
  • 将订单信息和授权码组成特定的xml数据格式,以post的方式提交的到接口地址(建议先传到商户自己的服务器,然后在商户的服务器端向接口以post的方式来提交支付,当然也可以在收银台发起,但不建议这样做,网络,安全在收银台都无法有效保障)

在上面向接口提交支付的时候,会返回对应的结果,分2种情况,一种是直接扣款,称为免密;一种是微信支付要求用户输入密码,称为验密

  • 免密:返回支付结果,告知商户支付结果,成功或者失败。成功的话同时用户也会收到支付的通知,通过微信,短信的方式
  • 验密:返回支付结果,返回的结果为USERPAYING状态,表示用户正在输入密码,此时商户可以每隔5秒,使用查询订单来查询该笔支付的结果

开发实现

刷卡支付实现中用到了package tools里面的微信支付签名,网络请求,生成随机字符串,请先去实现

创建package micropay

创建文件夹micropay

创建支付请求数据,并实现签名以及请求发送

创建文件 micropayrequest.go,实现:

  • Mircopayrequest结构体:存储被扫支付需要提交的参数
  • func (v *Mircopayrequest) Signmd5():对Mircopayrequest里面的非空字段进行签名,并存储签名
  • func (v *Mircopayrequest) Xml():根据Mircopayrequest里面的非空字段,生成对应的xml格式数据,并存储该数据
  • func (v Mircopayrequest) Dorequest():向被扫支付接口以post的请求方式发送生成的xml数据,并返回结果

代码实现

package micropay

import (
    "encoding/xml"
    "wechatpaygolang/config"
    "wechatpaygolang/tools"
)

//1.创建Mircopayrequest结构体,存放我们的请求字段,带有 omitempty 表示该字段选填,其他的字段属于必填
type Mircopayrequest struct {
    XMLName          xml.Name `xml:"xml"`                   //表示xml数据格式
    Appid            string   `xml:"appid"`                 //公众账号ID
    Attach           string   `xml:"attach,omitempty"`      //附加数据
    Auth_code        string   `xml:"auth_code"`             //授权码
    Body             string   `xml:"body"`                  //商品描述
    Device_info      string   `xml:"device_info,omitempty"` //设备号
    Detail           string   `xml:"detail,omitempty"`      //商品详情
    Fee_type         string   `xml:"fee_type,omitempty"`    //货币类型
    Goods_tag        string   `xml:"goods_tag,omitempty"`   //商品标记
    Mch_id           string   `xml:"mch_id"`                //商户号
    Nonce_str        string   `xml:"nonce_str"`             //随机字符串
    Out_trade_no     string   `xml:"out_trade_no"`          //商户订单号
    Sign             string   `xml:"sign"`                  //签名
    Spbill_create_ip string   `xml:"spbill_create_ip"`      //终端IP
    Time_expite      string   `xml:"time_expite,omitempty"` //交易失效时间
    Time_start       string   `xml:"time_start,omitempty"`  //交易起始时间
    Total_fee        string   `xml:"total_fee"`             //总金额

    RequestXML string `xml:"-"` //存放最终生成的xml请求数据,不参与签名以及xml的组成
}

//2.对Mircopayrequest里面的字段进行md5签名,存储到Mircopayrequest里面的Sign变量
func (v *Mircopayrequest) Signmd5() bool {
    sign := tools.Wechatpay_SignMD5(*v, config.API_KEY)
    v.Sign = sign
    return true
}

//3.生成Mircopayrequest对应的xml数据,存储到Mircopayrequest里面的RequestXML变量
func (v *Mircopayrequest) Xml() error {
    xmlresult, err := tools.XmlEndoestruct(v)
    v.RequestXML = xmlresult
    return err
}

//4.向刷卡接口发送post的请求,请求的数据为我们的生成存储在RequestXML变量的xml数据
//  并且将得到的数据解析到Mircopayresponse结构体
func (v Mircopayrequest) Dorequest() Mircopayresponse {
    //发送post请求,f返回数据为data
    data := tools.Post(config.URL_MICROPAY, v.RequestXML)

    //解析data到Mircopayresponse结构体
    mircopayresponse := Mircopayresponse{}
    tools.XmlDecodebytes(data, &mircopayresponse)
    mircopayresponse.ReponseXML = string(data)
    //返回结果
    return mircopayresponse

}
func value2CDATA(v string) CDATAText {
    return CDATAText{"<![CDATA[" + v + "]]>"}
}

type CDATAText struct {
    Text string `xml:",innerxml"`
}

创建支付结果数据

创建文件 micropayresponse.go,存储发起支付请求后的返回结果,实现代码如下

package micropay

import (
"encoding/xml"
)

//1.创建Mircopayresponse结构体,存放我们的请求字段,带有 omitempty 表示该字段选填,其他的字段属于必填,支付请求的结果会被解析到该结构体

type Mircopayresponse struct {
XMLName        xml.Name `xml:"xml"`
Return_code    string   `xml:"return_code"`           //返回状态码
Return_msg     string   `xml:"return_msg"`            //返回信息
Appid          string   `xml:"appid"`                 //公众账号ID
Mch_id         string   `xml:"mch_id"`                //商户号
Device_info    string   `xml:"device_info,omitempty"` //设备号
Nonce_str      string   `xml:"nonce_str"`             //随机字符串
Sign           string   `xml:"sign"`                  //签名
Result_code    string   `xml:"result_code"`           //业务结果
Err_code       string   `xml:"err_code"`              //错误代码
Err_code_des   string   `xml:"err_code_des"`          //错误代码描述
Is_subscribe   string   `xml:"is_subscribe"`          //是否关注公众账号
Trade_type     string   `xml:"trade_type"`            //交易类型
Bank_type      string   `xml:"bank_type"`             //付款银行
Fee_type       string   `xml:"fee_type"`              //货币类型
Total_fee      string   `xml:"total_fee"`             //总金额
Cash_fee_type  string   `xml:"cash_fee_type"`         //现金支付货币类型
Cash_fee       string   `xml:"cash_fee"`              //现金支付金额
Coupon_fee     string   `xml:"coupon_fee"`            //代金券或立减优惠金额
Transaction_id string   `xml:"transaction_id"`        //微信支付订单号
Out_trade_no   string   `xml:"out_trade_no"`          //商户订单号
Attach         string   `xml:"attach"`                //商家数据包
Time_end       string   `xml:"time_end"`              //支付完成时间

ReponseXML string `xml:"-"` //结果xml串

}

发起支付

创建文件miropay.go,执行被扫支付操作,实现代码如下

package micropay

import (
    "fmt"
    "net/http"
    "wechatpaygolang/config"
    "wechatpaygolang/tools"
)

type Show struct {
    Resault string
}

//1.执行刷卡支付功能入口
func Micropay(w http.ResponseWriter, r *http.Request) {

    r.ParseForm()

    //获得要支付的金额
    fee := r.FormValue("fee")

    //获取用户的刷卡二维码
    authcode := r.FormValue("authcode")

    //创建Mircopayrequest请求参数
    v := &Mircopayrequest{Appid: config.APP_ID, Mch_id: config.MCH_ID}
    v.Auth_code = authcode
    if fee != "" {
        v.Total_fee = fee
    } else {
        v.Total_fee = "1"
    }

    v.Body = "weixin"
    v.Nonce_str = tools.Getnoncestr(32)
    v.Out_trade_no = tools.Getnoncestr(32)
    v.Spbill_create_ip = "127.0.0.1"
    // v.Goods_tag = "das"

    //对请求参数进行MD5签名,得到签名字串
    v.Signmd5()
    // v.Sign = "607642655893AF4616B3DC406E659F32"

    //把请求参数转换为xml格式的数据
    v.Xml()
    fmt.Fprintf(w, "请求数据如下\n")

    fmt.Fprintf(w, v.RequestXML+"\n\n\n\n")
    //发起支付请求,并得到返回结果
    response := v.Dorequest()

    //输出支付结果信息
    fmt.Printf("返回状态:%s \n", response.Return_code)
    fmt.Printf("返回信息: %s \n", response.Return_msg)
    fmt.Printf("业务结果: %s \n", response.Result_code)
    fmt.Printf("错误代码: %s \n", response.Err_code)
    fmt.Printf("错误代码描述: %s\n", response.Err_code_des)
    fmt.Println("========================================")

    //打印支付结果到网页
    w.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域,解决跨域问题
    fmt.Fprintf(w, "支付返回数据如下\n")

    fmt.Fprintf(w, response.ReponseXML)

}

和/micropay地址关联

main.go文件里面

  • 加入头文件"wechatpaygolang/micropay"

  • rout函数里面加入路由

    func rout(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    
    path := r.URL.Path
    
    if path == "/helloworld" {
        fmt.Println("被扫支付")
        helloworld.HelloWorld(w, r)
    
    } else if path == "/micropay" {
        fmt.Println("被扫支付")
        micropay.Micropay(w, r)
    
    } 
    
    }
    

这里我们就完成对url地址:/micropaymicropay.Micropay()函数的关联

测试

打开浏览器,访问地址:localhost/micropay?fee=1&authcode=AUTHCODE,得到如下运行结果,这里我的付款码不对,所以不会正确扣款

支付结果判断

reponse.Return_code为SUCCESS,response.Result_code为SUCCESS,表示支付成功 

reponse.Return_code为SUCCESS,reponse.Result_code为FAIL,reponse.Err_code为USERPAYING 表示正在输入密码

更多reponse.Err_code以及对应的处理办法

名称描述原因解决方案
SYSTEMERROR接口返回错误系统超时请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作
ORDERPAID订单已支付复订单号重复请确认该订单号是否重复支付,如果是新单,请使用新订单号提交
NOAUTH商户无权限商户没有开通被扫支付权限请开通商户号权产品或商务申请
AUTHCODEEXPIRE二维码已过期,请用户在微信上刷新后再试用户的条码已经过期请收银员提示用户,请用户在微信上刷新条码,然后请收银员重新扫码。 直接将错误展示给收银员
NOTENOUGH余额不足用户的零钱余额不足请收银员提示用户更换当前支付的卡,然后请收银员重新扫码。建议:商户系统返回给收银台的提示为“用户余额不足.提示用户换卡支付”
NOTSUPORTCARD不支持卡类型用户使用卡种不支持当前支付形式,请用户重新选择卡种建议:商户系统返回给收银台的提示为“该卡不支持当前支付,提示用户换卡支付或绑新卡支付”
ORDERCLOSED订单已关闭该订单已关商户订单号异常,请重新下单支付
ORDERREVERSED订单已撤销当前订单已经被撤销当前订单状态为“订单已撤销”,请提示用户重新支付
BANKERROR银行系统异常银行端超时请立即调用被扫订单结果查询API,查询当前订单的不同状态,决定下一步的操作
USERPAYING用户支付中,需要输入密码该笔交易因为业务规则要求,需要用户输入支付密码等待5秒,然后调用被扫订单结果查询API,查询当前订单的不同状态,决定下一步的操作
AUTH_CODE_ERROR授权码参数错误请求参数未按指引进行填写每个二维码仅限使用一次,请刷新再试
AUTH_CODE_INVALID授权码检验错误收银员扫描的不是微信支付的条码请扫描微信支付被扫条码/二维码
XML_FORMAT_ERRORXML格式错误XML格式错误请检查XML参数格式是否正确
REQUIRE_POST_METHOD请使用post方法未使用post传递参数请检查请求参数是否通过post方法提交
SIGNERROR签名错误参数签名结果不正确请检查签名参数和方法是否都符合签名算法要求
LACK_PARAMS缺少参数缺少必要的请求参数请检查参数是否齐全
NOT_UTF8编码格式错误未使用指定编码格式请使用UTF8编码格式
BUYER_MISMATCH支付帐号错误暂不支持同一笔订单更换支付方请确认支付方是否相同
APPID_NOT_EXISTAPPID不存在参数中缺少APPID请检查APPID是否正确
MCHID_NOT_EXISTMCHID不存在参数中缺少MCHID请检查MCHID是否正确
OUT_TRADE_NO_USED商户订单号重复同一笔交易不能多次提交请核实商户订单号是否重复提交
APPID_MCHID_NOT_MATCHappid和mch_id不匹配appid和mch_id不匹配请确认appid和mch_id是否匹配

刷卡支付常见错误

签名错误

第一步:检查本地签名程序是否正确

使用签名检查工具(http://mch.weixin.qq.com/wiki/tools/signverify/),检查签名本地签名程序是否有错误

如何用签名检查工具生成的签名和本地程序生成的签名不一致,说明程序有错误,请先检查本地代码程序。

如果无误,表示程序没有问题,进行下一步API秘钥设置

第二步:API密钥设置

在使用签名检查工具检查没有错误的情况下, 可以确定是API密钥设置出错。

微信支付必须保证本地签名使用的key值和在商户平台设置的API密钥一致。

设置方法:

登陆商户平台(https://pay.weixin.qq.com/index.php/home/login) ,设置位置:账户设置-安全设置-API安全

如有遇到其他错误请留言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值