微信公众号支付

历经三天的爬坑之路,一套完整的 JAVA 版 JSAPI 微信支付和大家分享。

首先看官方文档,其中虽有一点点小差错,但还是很重要,毕竟它是根。
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=1_1

准备工作

公众号配置和申请商户申请省略,直接上功能

一、首先下载官方提供的工具类demo

在这里插入图片描述

二、把这些工具类Copy到自己的项目中

在这里插入图片描述

在这里插入图片描述

流程

统一下单 —》 H5调起微信支付 —》 回调参数
场景案例:充值话费流量

在这里插入图片描述
场景介绍:
1)点击套餐时,统一下单;
2)下单完成后返回参数给H5页面做为调用微信支付页面的数据;
3)然后跳转到你的微信支付界面(输入密码界面);
4)输入密码支付成功;
5)回调信息用做通知。

一、前台

// 跳转微信支付(单击套餐时)
onBridgeReady() {
    // 【1】首先调用后台,统一下单 【对应后台下单方法,在后文有定义】
    this.util.postRequest('/wx/unifiedOrder',{}).then(response => {
        // 返回信息
        let data = response.data.result;
        // 【2】如果下单成功
        if(data.return_code === 'SUCCESS') {
            // 调起支付(此处是自定义工具中封装)
            this.util.onBridgeReady(data);
        }
    });
}

/**
  * 调用微信支付(工具js中)
  */
 static onBridgeReady(params) {
   // 判断对象是否存在
   if (typeof WeixinJSBridge == "undefined"){
     if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
     }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
     }
   }else{
     // 【主要功能】在微信手机端浏览器 
     // 下单时返回的数据,传入后台用做支付使用
     var param = { "appId":params.appid, "package":"prepay_id="+params.prepay_id };
     // 【1】ajax调用后台,使参数添加完整 【对应后台方法后文有定义】
     this.postRequest("/wx/goH5Pay", param).then(response => {
       // 整理完的参数
       let data = response.data.result;
       // 【2】参数传入,微信中H5页面调起支付
       WeixinJSBridge.invoke(
         'getBrandWCPayRequest', {
             "appId": data.appId, // 公众号名称,由商户传入     
             "timeStamp": data.timeStamp, // 时间戳,自1970年以来的秒数 (10位) 
             "nonceStr": data.nonceStr, // 随机串     
             "package": data.package, // 统一下单接口返回的prepay_id参数值,提交格式如prepay_id=***  
             "signType": data.signType, // 微信签名方式类型
             "paySign": data.paySign, //微信签名 
         },
         res => {
           if(res.err_msg == "get_brand_wcpay_request:ok" ){
          	 //使用以上方式判断前端返回,微信团队郑重提示:
             //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
             // 如果成功跳转微信支付页面
         } 
       });
     });
   }
 }

二、后台

1.统一下单

(1)配置 MyConfig 继承工具抽象类 WXPayConfig
package com.mesunday.util.pay;
import java.io.*;

/**
 * 自定义配置
 */
public class MyConfig extends WXPayConfig{
    private byte[] certData;

    /**
     * 构造方法读取证书, 通过getCertStream 可以使sdk获取到证书
     * @throws Exception
     */
    public MyConfig() throws Exception {
   		 // 【必填】此处为你申请的证书存放的路径
        String certPath = "D:\\cert\\apiclient_cert.p12";  
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }
	// 【必填】个人公众号的appid (此处为假数据)
    public String getAppID() { return "wx81107deaedd12"; }
    
    // 【必填】个人申请的商户号 (此处为假数据)
    public String getMchID() { return "1536399165"; }
    
	// 【必填】个人商户的API密钥 (此处为假数据)
    public String getKey() { return "c439h20196b286876b769fd196b2190k"; }

    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }
    
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    /**
     * 【必須實現此方法,否則無法使用】
     * @return
     */
    public IWXPayDomain getWXPayDomain() {
        IWXPayDomain iwxPayDomain = new IWXPayDomain() {
            @Override
            public void report(String domain, long elapsedTimeMillis, Exception ex) {}

            @Override
            public DomainInfo getDomain(WXPayConfig config) {
                return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        };
        return iwxPayDomain;
    }
}
(2)统一下单
/**
  * 【统一下单】(正是前台调用的统一下单接口)
  * controller省略,此处是service
  * @return
  */
 public Map unifiedOrder() throws Exception {
     // 返回信息给前台
     Map<String, String> resp = new DataRow<>();

     // 自定义的配置用于实现
     MyConfig myConfig = new MyConfig();
     WXPay wxpay = new WXPay(myConfig);
     // 商戶訂單號
     String outTradeNo = WXPayUtil.generateNonceStr();

     // 添加 map data 数据,对应官方请求参数列表
     // 官方:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     DataRow<String, String> mapData = new DataRow();
     mapData.put("appid", projectprops.getAppid()); // 公众APPid
     mapData.put("mch_id", "1536399165"); // 商户号
     mapData.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串32位(工具方法)
     mapData.put("body","话费流量支付"); // 商品描述
     mapData.put("out_trade_no", outTradeNo); // 商户订单号
     mapData.put("total_fee","1"); // 标价金额(單位分)
     mapData.put("spbill_create_ip","120.56.220.18"); // 终端IP,公众平台白名单
     mapData.put("notify_url", "http://" + projectprops.getDomainurl() + "/api/wx/dealOrder/"+outTradeNo); // 通知地址,支付成功时回调的参数(外网可以访问),这里把订单号传过去
     mapData.put("trade_type","JSAPI"); // 交易类型
     mapData.put("openid", "ouAmpjl9ZZdSiMNYazgz7Ah8zqw4"); // openid【必填,官方未说明必填】
     // 签名需要其它数据信息,最后设置
     String sign = WXPayUtil.generateSignature(mapData, "c439h20196b286876b769fd196b2190k");
     mapData.put("sign", sign); // 签名
     // 调用统一下单
     try {
         resp = wxpay.unifiedOrder(mapData);
         System.out.println(resp);
     } catch (Exception e) {
         e.printStackTrace();
     }
     return resp;
 }

下单成功后返回的信息,前台取到需要的参数下单成功返回数据

2.H5调用微信端支付

其中参数犹为重要,所以这里在后台生成,同时参考官方参数列表
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

/**
 * H5支付时需要的参数
 * @return
 * @throws Exception
 */
public DataRow goH5Pay(DataRow data) throws Exception {
    DataRow<String, String> param = new DataRow();
    param.put("appId", data.getString("appId")); // 公众号名称,这里是由前台传过来
    param.put("nonceStr", WXPayUtil.generateNonceStr()); // 随机串(工具生成)
    param.put("package", data.getString("package")); // 统一下单返回的prepay_id,注意官方格式,这里也是由前台传过来
    param.put("signType", "MD5"); // 微信签名方式
    param.put("timeStamp", String.valueOf(WXPayUtil.getCurrentTimestamp())); // 时间戳,单位秒(10位)
    // 签名需要其它数据信息,最后设置
    String sign = WXPayUtil.generateSignature(param, "c439h20196b286876b769fd196b2190k");
    param.put("paySign", sign); // 签名
    // 输出参数示例
    for(Map.Entry<String,String> p : param.entrySet()) {
        System.out.println(p.getKey() + " : " + p.getValue());
    }

    return param;
}

在这里插入图片描述
在添加参数后调用微信支付,如果正常跳到微信支付,则跳过此处。
如果有异常,不能跳转微信支付页面,即官方给的返回为,前台可以用 alert() 输出JSON格式查看错误信息
get_brand_wcpay_request:fail —— 支付失败

异常两种情况:如下

在这里插入图片描述
出现图 a:
(1)首先对照自己参数是否真的有问题,主要是签名,使用微信平台测试工具
https://pay.weixin.qq.com/wiki/tools/signverify/
(2)自己生成的签名和官方的明明一样,还是报签名错误,此时做以下修改工具代码
在这里插入图片描述
出现图b:
是商户平台上,你当前的H5页面的URL支付目录未授权,所以配置,如下图
在这里插入图片描述

如果微信跳转成功如下

在这里插入图片描述

3.支付完成会通知,下单时设置的通知路由

这里通知路由查询订单,如下

/**
  * 支付成功回調,查询订单信息
  * @param outTradeNo 商戶訂單號【下单时传入的订单号】
  * @return
  */
 @RequestMapping(value = "/dealOrder/{outTradeNo:.+}")
 @ResponseBody
 public String goH5Pay(@PathVariable String outTradeNo) {
     logger.debug("支付处理");
     try {
         // 自定义配置
         MyConfig myConfig = new MyConfig();
         WXPay wxpay = new WXPay(myConfig);

         // 加入商戶訂單號
         Map<String, String> data = new HashMap();
         data.put("out_trade_no", outTradeNo);
         // 查詢訂單
         Map<String, String> resp = wxpay.orderQuery(data);

         // 控制台输出,查看数据,如下图【【结合官方文档查看参数含义】】
         for(Map.Entry<String,String> p : resp.entrySet()) {
             System.out.println(p.getKey() + " : " + p.getValue());
         }
     } catch (Exception e) {

     }
     return ResultUtil.resultJsonString("null");
 }

在这里插入图片描述

其它方法退款查询、下账单查询,与上图账单查询道理相同

作者微信公众号“怪東瓜”,有问题私聊,我们共同探讨实用技术,练出最美身材。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值