支付篇—微信扫码支付

应用场景:
商户根据商品信息,生成商品二维码,用户通过微信扫一扫功能扫描该二维码,完成支付。
支付模式:
现在微信扫码支付支持两种模式。
模式一需要商户必须先在公众平台后台设置支付回调URL。URL实现的功能:接收用户扫码后微信支付系统回调的productid和openid。
模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。
我使用的模式二,也就是微信的统一下单接口。该模式的业务流程时序图如下:
这里写图片描述
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。

整个支付流程精简下来主要就是2点:
①调用接口获取支付二维码并显示出来
②微信回调接口,通知支付结果
针对这2点我写了两个方法,一个是获取二维码,一个是提供给微信的支付结果回调接口。
开发过程中需要用到微信提供的jar包,maven地址如下:

        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
/**
 * 该类为配置类,主要是关于公众号和商户的一些配置信息抽离出来
 * @author 
 *
 * 2017年11月17日
 */
public class MyConfig implements WXPayConfig{

    //公众账号ID
    public String getAppID() {
        return "wx11bd61834b0d57ef";
    }

    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    //商户秘钥
    public String getKey() {
        return "1234567890";
    }

    //商户号
    public String getMchID() {
        return "1234567890";
    }

}
    /**
     * 获得微信支付二维码
     * @param req
     * @param resp
     * @throws IOException
     */
    @RequestMapping(value = "/getWxPayCode")
    public void getWxPayCode(HttpServletRequest req, HttpServletResponse resp)
            throws IOException{
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        String out_trade_no = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");

        Map<String, String> data = new HashMap<String, String>();
        data.put("body", "填写商品名称"); //商品描述
        data.put("out_trade_no", out_trade_no); //商户订单号,不可重复
        data.put("device_info", "");    //设备号
        data.put("fee_type", "CNY");    //标价币种(默认人民币)
        data.put("total_fee", "1");     //标价金额,单位:分
        data.put("spbill_create_ip", "127.0.0.1");  //终端IP
        data.put("notify_url", "https://www.baidu.com/getWxPayNotify.action");  //通知地址,必须是外网能访问的地址
        data.put("trade_type", "NATIVE");  // 此处指定为扫码支付
        data.put("product_id", "12");   //商品ID

        Map<String, String> respnoe = null;
        try {
            respnoe = wxpay.unifiedOrder(data);
            String codeUrl = respnoe.get("code_url");
            System.out.println("返回的二维码url:" + codeUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }

        resp.setContentType("text/html;charset=UTF-8");
        OutputStream os = resp.getOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);
        oos.writeObject(respnoe.toString());
        oos.flush();
        oos.close();
        os.close();
    }

这样就完成了微信支付的二维码请求。在进行请求的时候,微信是以xml格式的数据进行的请求,不过微信已经在提供的jar包里进行了封装,所以我们就不必再进行数据封装了。
商户后台将该二维码地址返回给手机端,手机端通过zxing工具包,将该地址转换成二维码供用户扫码支付。当用户扫码支付完成后,微信后台会回调我们提供给微信后台的接口,就是刚才我们设置的notify_url参数,这里一定要记得是外网能够访问到的地址!
下面是处理微信回调的方法:

/**
     * 获得微信支付通知回调结果
     * @param req
     * @param resp
     * @throws Exception 
     * @throws IOException
     */
    @RequestMapping(value = "/getWxPayNotify")
    public void getWxPayNotify(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String notifyData = "";
        try {
            InputStream is = req.getInputStream();
            StringBuffer sb = new StringBuffer();  
            String s;
            BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            while ((s = in.readLine()) != null){  
                sb.append(s);  
            }  
            in.close();  
            is.close();

            notifyData = sb.toString();
            MyConfig config = new MyConfig();
            WXPay wxpay = new WXPay(config);
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);  // 转换成map
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                logger.info("支付成功");
                // 签名正确
                // 进行处理。
                // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
            }
            else {
                // 签名错误,如果数据里没有sign字段,也认为是签名错误
                logger.error("支付失败");
            }

            logger.info("微信支付返回的通知为:" + notifyMap);

            resp.setContentType("text/html;charset=UTF-8");
            OutputStream os = resp.getOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeObject(notifyData);
            oos.flush();
            oos.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

具体的处理方法这里我就不写了,微信回调的时候需要验证签名是否正确,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。另外当微信回调我们的接口的时候,我们需要给微信后台返回应答,已经接收到回调,不然微信将会重新发起通知(通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)

另外当客户端一定时间内没有接收到微信后台的回调时,也可以主动查询订单状态,跟统一下单差不多,只是把下单改为查询

Map<String, String> resp = wxpay.orderQuery(data);

微信扫码支付暂时写到这里,后续如果有什么变化,再添加。附上微信支付的开发文档,里面会有各个参数的说明, 上面的代码没说的太详细。
统一下单:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
支付结果通知:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7

如需转载,请注明出处!

展开阅读全文

没有更多推荐了,返回首页