微信支付JSAPI--JAVA开发

微信支付–JAVA开发
1.使用JSAPI支付方式.
JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。开发文档
应用场景有:
线下场所:调用接口生成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付
公众号场景:用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付
PC网站场景:在网站中展示二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付
也即是说,jsapi支付均需在微信浏览器内部进行.
2.准备工作:
(1)微信支付支持在公众平台注册并完成微信认证的服务号,政府或媒体订阅号接入支付功能。
(2)申请商户平台并设置对应信息
(3)主要配置信息包含:公众号appid,appsecret;商户平台mchid,apikey
3.业务流程时序图:
微信支付JSAPI--JAVA开发_第1张图片
商户系统和微信支付系统主要交互:
1、商户server调用统一下单接口请求订单,api参见公共api【统一下单API】
2、商户server接收支付通知,api参见公共api【支付结果通知API】
3、商户server查询支付结果,api参见公共api【查询订单API】
4.前期配置:
(1)设置支付目录:
支付目录填写发起jsapi支付请求的页面地址.
在微信商户平台(pay.weixin.qq.com)设置您的JSAPI支付支付目录,设置路径:商户平台–>产品中心–>开发配置,如图7.7所示。JSAPI支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。
(2)设置授权域名
需要在公众号里配置授权域名.
开发JSAPI支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败

5.开发步骤:
在微信浏览器中打开H5页面执行JS调起支付请求,需要获取请求必须的参数.所以,大体的开发流程如下:
A.通过网页授权,获取到用户的code;
B.通过code获取用户基本信息(主要获取openid: jsapi必传参数);
C.调用”统一下单”API获取prepay_id(预支付交易会话标识)并后台封装数据返回前端页面以备支付使用;
D.浏览器页面通过js调起支付请求,前端可根据支付结果返回展示页面(不一定可靠);
E.后台接收支付结果通知回调,确认支付结果(必须通过此回调确认是否成功支付);
F.必要时,可以再次调用”订单查询API”再次核对.

6.具体步骤: (推荐下载”微信web开发者工具”进行浏览器页面调试)
①打开支付场景页面时,请求网页授权域名:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
其中:
APPID为公众号appid,;
REDIRECT_URI为该域名请求成功后携带code返回的跳转页面(注意,此url不可携带参数);
SCOPE分两类:
snsapi_base为静默授权,不会弹出授权弹出,默认用户同意授权,官方文档讲此方法只能获取到用户openid,无法获取微信名,性别,头像等基本信息;
snsapi_userinfo方式,访问授权域名时,会弹出授权页面,用户点击”同意”才会跳转,可以获取用户基本信息
②请求成功后,微信会跳转至”REDIRECT_URI”地址,并携带一个code和state数据:
redirect_uri/?code=CODE&state=STATE state没啥用,主要获取code
code说明: code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
③拿到code后,传入后台,通过code调用”网页授权code获取基本信息URL”地址,去获取到用户的openid等信息(https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code)
APPID,SECRET是公众号的配置信息,CODE就是我们前端页面传入的code
④拿到用户openid,根据官方文档参数说明,需要封装参数并进行签名,再调用”统一下单API”去请求获取到支付所需的prepay_id.
统一下单参数主要有11个字段必传:

appid 公众账号ID String(32)
mch_id 商户号 String(32)
nonce_str 随机字符串 String(32) 后台生成,sdk有示例
sign 签名 String(32) 后台生成
body 商品描述 String(128)
out_trade_no 商户订单号 String(32)
total_fee 标价金额 Int 单位为”分”,不可带小数点
spbill_create_ip 终端IP String(64) 用户的客户端ip,后台request获取
notify_url 通知地址 String(256) 异步接收支付成功回调通知
trade_type 交易类型 String(16) JSAPI, NATIVE, APP
openid 用户标识 String(128) JSAPI方式必传

后台主要负责3个数据:nonce_str, ip, sign
nonce_str可通过sdk里的方法生成;
sign:签名,是将除sign之外的10个参数通过MD5签名算法生成,此处需要用到商户平台的apiKey;
spbill_create_ip:客户端ip,也即是用户点击支付的微信浏览器的ip:
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader(“x-forwarded-for”);
if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {
ip = request.getHeader(“Proxy-Client-IP”);
}
if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {
ip = request.getHeader(“WL-Proxy-Client-IP”);
}
if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip.equals(“0:0:0:0:0:0:0:1”) ? “127.0.0.1” : ip;
}
⑤11个参数处理完毕后,需要将map转换为xml格式的字符串,调用post请求访问”统一下单API”: https://api.mch.weixin.qq.com/pay/unifiedorder
此处有个坑注意:sdk里有一个设置header的参数:
httpPost.addHeader(“User-Agent”, WXPayConstants.USER_AGENT);
这个不设置会出错~~~~
请求成功后,会返回xml格式的结果,主要有trade_type,prepay_id,我们就需要拿到prepay_id即可.
⑥最后一步,封装js页面调起jsapi支付所需的参数: 建议Map集合封装
appid 公众号appid
timeStamp 时间戳,秒级
nonceStr 随机字符串,sdk生成吧
package 注意:package为java关键字!!!所以推荐map集合来封装吧~~~~
signType 签名方式: MD5
paySign 上面5个参数再次签名
所有数据处理完毕,后台工作完成,将参数返回前端页面;
⑦拿到后台封装好的数据,在前端页面js调起jsapi支付请求,这里就可以使用"微信web开发者工具"来调试了哟.很简单了,代码官网有示例: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
⑧最后的最后!!!如果支付成功,自行页面逻辑处理或跳转展示页面.但是,需记住,还有一个支付成功通知!!!也就是第4步”统一下单”时填写的notify_url回调.需在此回调中,接收数据,验签->数据匹配(如金额,订单号等匹配是否一致)->业务数据处理,修改数据库数据支付状态等等操作.
注意:如果校验成功,记得即时响应微信服务器: response.getWriter().write(“ ”);
建议,前端页面支付成功跳转展示页面时,通过查询接口获取后台异步通知校验成功后的数据.

贴上部分代码:

/**
 * 根据code获取openId
 *
 * @param code
 * @return openId
 */
private String getOpenId(String code) {
    String getUrl = Constants.CODE_TOKEN_URL.replace("APPID", appId)
        .replace("SECRET", appSecret).replace("CODE", code);
    System.out.println("url: " + getUrl);
    JSONObject jsonObject = getUrlJson(getUrl);
    return jsonObject.get("openid").toString();
}

/**
 * httpget请求
 *
 * @param url
 * @return JSONObject
 */
private JSONObject getUrlJson(String url) {
    JSONObject jsonObject = null;
    HttpClient client = HttpClientBuilder.create().build();
    HttpGet httpGet = new HttpGet(url);
    HttpResponse response;
    try {
        response = client.execute(httpGet);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "UTF-8");
        logger.info("getUrlJson: {}", result);
        jsonObject = JSONObject.parseObject(result);
        httpGet.releaseConnection();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return jsonObject;
}

/**
 * 网页请求生成支付订单
 *
 * @param request
 * @param map
 * @return
 */
@Override
public Map createOrderOpenId(HttpServletRequest request, Map map) {
   logger.info("createOrder------: {}", map);
    Map orderMap = new HashMap();
    String openId = getOpenId(map.get("code").toString());
    if (StringUtils.isEmpty(openId)) {
        return orderMap;
    }
    try {
        String clientIp = getClientIp(request);
        logger.info("createOrder获取clientIp------: {}", clientIp);
        orderMap.put("appid", appId);  //  微信支付分配的公众账号ID(企业号corpid即为此appId)
        orderMap.put("mch_id", mchId); //  商户号
        orderMap.put("nonce_str", WXPayUtil.generateNonceStr());    //  随机字符串
        orderMap.put("body", map.get("body"));   //  商品描述
        orderMap.put("out_trade_no", map.get("outTradeNo"));   //  商户订单号
        orderMap.put("total_fee", map.get("totalFee"));   //  订单总金额,单位为分
        orderMap.put("spbill_create_ip", clientIp);   //  终端IP,用户的客户端IP
        orderMap.put("notify_url", backUrl + "/payNotify"); //  通知地址-异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
        orderMap.put("trade_type", map.get("tradeType"));    //  交易类型	 JSAPI -JSAPI支付
        orderMap.put("openid", openId); //  用户标识
        //  获取sign
        orderMap.put("sign", WXPayUtil.generateSignature(orderMap, apiKey));    //  key: apiKey
        //  订单数据拼接完成,转换map为xml的String字符串
        String xmlData = WXPayUtil.mapToXml(orderMap);
        //  统一下单->获取订单的prepay_id
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost("https://" + WXPayConstants.DOMAIN_API + WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX);
        StringEntity postEntity = new StringEntity(xmlData, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT);
        httpPost.setEntity(postEntity);
        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        //  获取统一下单接口返回数据
        String resXml = EntityUtils.toString(httpEntity, "UTF-8");
        logger.info("获取统一下单接口返回数据--resXml----: {}", resXml);
        Map orderResMap = WXPayUtil.xmlToMap(resXml);
        String prepayId = orderResMap.get("prepay_id").toString();
        logger.info("createOrder获取prepayId------: {}", prepayId);
        //  存储订单信息
        map.put("clientIp", clientIp);
        map.put("openId", openId);
        map.put("prepayId", prepayId);
        insertOrder(map);
        //  封装前端调起JSAPI支付所需参数
        Map payMap = new HashMap();
        payMap.put("appId", appId);  //  公众号id
        payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp()); //  时间戳,转换成秒数
        payMap.put("nonceStr", WXPayUtil.generateNonceStr());    //  随机字符串
        payMap.put("package", "prepay_id=" + prepayId);   //  订单详情扩展字符串
        payMap.put("signType", "MD5");   //  签名方式
        //  再次签名获取paySign
        payMap.put("paySign", WXPayUtil.generateSignature(payMap, apiKey));   //  签名 key:apiKey
        return payMap;
    } catch (Exception e) {
        e.printStackTrace();
        return new HashMap(0);
    }
}

/**
 * 支付成功回调通知
 *
 * @param request
 * @param response
 * @return
 */
@Override
public void payNotify(HttpServletRequest request, HttpServletResponse response) {
    logger.info("微信支付成功回调通知---------------------");
    String returnStr = "";
    InputStream is = null;
    try {
        is = request.getInputStream();//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
        String xmlStr = convertStreamToString(is);
        Map notifyMap = WXPayUtil.xmlToMap(xmlStr);//将微信发的xml转map
        Map signMap = new HashMap(notifyMap);
        signMap.remove("sign");
        //  验签
        String localSign = WXPayUtil.generateSignature(signMap, apiKey);
        if (!notifyMap.get("sign").equals(localSign)) {
            logger.info("验签不通过");
            return;
        }
        //同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知。如果已处理过,直接给微信支付返回成功。
        //  获取本地订单信息比对
        String resultCode = notifyMap.get("result_code");
        PayOrder localOrder = payMapper.singleOrder(notifyMap.get("out_trade_no"));
        if (localOrder.getCallbackState() == 1 && resultCode.equals("SUCCESS")) {
            //重复通知,直接响应微信服务器
            response.getWriter().write(returnStr);
            is.close();
            return;
        }
        //判断金额是否一致
        if (localOrder.getTotalFee() != Integer.valueOf(notifyMap.get("total_fee"))) {
            return;
        }
        //修改订单的支付状态
        if (notifyMap.get("return_code").equals("SUCCESS")) {
            localOrder.setCallbackState(1);
            localOrder.setTradeState(resultCode);
            localOrder.setBankType(notifyMap.get("bank_type"));
            localOrder.setTransactionId(notifyMap.get("transaction_id"));
            payMapper.updateByPrimaryKeySelective(localOrder);
        }
        //响应微信服务器
        response.getWriter().write(returnStr);
        is.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return;
}

你可能感兴趣的:(第三方支付)