1、接入前的准备
官方文档地址
jsapi下单官方文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
jsapi调起支付官方文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
jsapi支付通知回调文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
接入前的准备:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_1.shtml
公众号绑定商户号(略)
登录商户号配置v3秘钥
登录商户号配置商户证书
流程比较多,可以看官方提供的步骤操作,操作完后本地会生成三个证书文件
登录商户号设置支付授权目录
登录公众号后台设置授权域名
2、引入jar
我这里用的是gradle
implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.4.2'
implementation group: 'com.github.binarywang', name: 'weixin-java-common', version: '3.3.0'
3、创建三个工具方法
构造jsApi下单请求参数
/*** 构造微信JsApi付款的json* @param appid* @param mchid* @param description* @param out_trade_no* @param notify_url* @param amount* @return*/public static JSONObject buildWxJsApiPayJson(String appid , String mchid , String description , String out_trade_no , String notify_url , String amount, String openId){//订单金额jsonJSONObject amountJson = new JSONObject();amountJson.put("total",Integer.valueOf(amount));amountJson.put("currency","CNY");//支付者jsonJSONObject payerJson = new JSONObject();payerJson.put("openid",openId);//基础信息jsonJSONObject json = new JSONObject();json.put("appid",appid);json.put("mchid",mchid);json.put("description",description);json.put("out_trade_no",out_trade_no);json.put("notify_url",notify_url);json.put("amount",amountJson);json.put("payer",payerJson);return json;}
创建下单httpClient方法:
/**
*wxMchid商户号
*wxCertno证书编号
*wxCertPath证书地址
*wxPaternerKey v3秘钥
*url jsapi下单地址
*body 构造好的消息体
*/
public static JSONObject doPostWexinV3(String wxMchid,String wxCertno,String wxCertPath,String wxPaternerKey,String url, String body) {// 自动更新证书功能AutoUpdateCertificatesVerifier verifier = null;try {// 名词解释:CERTNO:证书序列号;CERTPATH:证书在你服务器的地址(apiclient_key.pem)verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(wxMchid, new PrivateKeySigner(wxCertno,PemUtil.loadPrivateKey(new FileInputStream(wxCertPath)))),wxPaternerKey.getBytes("utf-8"));} catch (FileNotFoundException e) {System.err.println("证书未找到!=====================");e.printStackTrace();} catch (UnsupportedEncodingException e) {System.err.println("文件流错误!=====================");e.printStackTrace();}WechatPayHttpClientBuilder builder = null;try {builder = WechatPayHttpClientBuilder.create().withMerchant(wxMchid,wxCertno, PemUtil.loadPrivateKey(new FileInputStream(wxCertPath))).withValidator(new WechatPay2Validator(verifier));} catch (FileNotFoundException e) {System.err.println("证书未找到!=====================");e.printStackTrace();}HttpClient httpClient = builder.build();HttpPost httpPost = new HttpPost(url);httpPost.addHeader("Content-Type","application/json;chartset=utf-8");httpPost.addHeader("Accept", "application/json");try{if(body==null){throw new IllegalArgumentException("data参数不能为空");}StringEntity stringEntity = new StringEntity(body,"utf-8");httpPost.setEntity(stringEntity);// 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新HttpResponse httpResponse = httpClient.execute(httpPost);HttpEntity httpEntity = httpResponse.getEntity();if(httpResponse.getStatusLine().getStatusCode() == 200){String jsonResult = EntityUtils.toString(httpEntity);return JSONObject.parseObject(jsonResult);}else{System.err.println("微信支付错误信息"+EntityUtils.toString(httpEntity));}}catch (Exception e){e.printStackTrace();}return null;}
获取签名方法
/**
*wxCertPath证书地址
*prepay_id jsapi下单接口返回的参数
*/
public static JSONObject getTokenWeixin (String appId,String wxCertPath, String prepay_id) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException, java.security.InvalidKeyException {// 获取随机字符串String nonceStr = getNonceStr();// 获取微信小程序支付packageString packagestr = "prepay_id=" + prepay_id;long timestamp = System.currentTimeMillis() / 1000;//签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值String message = buildMessageTwo(appId,timestamp,nonceStr,packagestr);//获取对应的签名String signature = sign(wxCertPath,message.getBytes("utf-8"));// 组装返回JSONObject json = new JSONObject();json.put("timeStamp", String.valueOf(timestamp));json.put("nonceStr", nonceStr);json.put("package", packagestr);json.put("signType", "RSA");json.put("paySign", signature);json.put("appId", appId);return json;}public static String getNonceStr(){return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);}private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {return appId + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ packag + "\n";}private static String sign(String wxCertPath,byte[] message) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException, java.security.InvalidKeyException {Signature sign = Signature.getInstance("SHA256withRSA"); //SHA256withRSAsign.initSign(PemUtil.loadPrivateKey(new FileInputStream(wxCertPath))); // 微信证书私钥sign.update(message);return Base64.getEncoder().encodeToString(sign.sign());}
4、获取微信加密签名控制层接口
给当前url鉴权,否则前端无法唤起支付窗口
/*** 获取微信加密签名* @return*/@PostMapping("getSignPackage")public ResponseResult getSignPackage(String url) {ResponseResult result = new ResponseResult<>();WxJsapiSignature jsapiSignature;if (StringUtils.isEmpty(url)) {return result.error(500,"缺少参数!");}try{jsapiSignature = createJsapiSignature(url);}catch (Exception e){logger.error("getSignPackage_error:"+e.getMessage());return result.error(500,"请求异常!");}return result.success(jsapiSignature);}/*** 微信加密签名参数拼装* @param url* @return* @throws Exception*/public WxJsapiSignature createJsapiSignature(String url) throws Exception {long timestamp = System.currentTimeMillis() / 1000L;String randomStr = RandomUtils.getRandomStr();String jsapiTicket =getAccessToken();String signature = SHA1.genWithAmple(new String[]{"jsapi_ticket=" + jsapiTicket, "noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url});WxJsapiSignature jsapiSignature = new WxJsapiSignature();jsapiSignature.setAppId(wxAppid);jsapiSignature.setTimestamp(timestamp);jsapiSignature.setNonceStr(randomStr);jsapiSignature.setUrl(url);jsapiSignature.setSignature(signature);return jsapiSignature;}
5、创建下单控制层接口
//拼接微信支付参数
JSONObject requestJson = buildWxJsApiPayJson(wxAppid,wxMchid,rechargeType.getMealName(),orderNo,notifyUrl,amount,openid);
//发送post请求"统一下单接口"返回预支付id:prepay_id
//下单接口:https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
JSONObject stringObjectJson = doPostWexinV3(wxMchid,wxCertno,wxCertPath,wxPaternerKey,wxJsApiurl, requestJson.toJSONString());
JSONObject resJson = getTokenWeixin(wxAppid,wxCertPath, String.valueOf(stringObjectJson.get("prepay_id")));return result.success(resJson);
6、创建支付回调控制层接口
/*** 微信回调接口* @param body* @param request* @return*/@RequestMapping("wxJsApiCallback")public Map wxJsApiCallback(@RequestBody Map body, HttpServletRequest request) {Map<String, Object> result = new HashMap();//1:获取微信支付回调的获取签名信息String timestamp = request.getHeader("Wechatpay-Timestamp");String nonce = request.getHeader("Wechatpay-Nonce");ObjectMapper objectMapper = new ObjectMapper();try {// 2: 开始解析报文体String data = objectMapper.writeValueAsString(body);String message = timestamp + "\n" + nonce + "\n" + data + "\n";//3:获取应答签名String sign = request.getHeader("Wechatpay-Signature");//4:获取平台对应的证书String serialNo = request.getHeader("Wechatpay-Serial");Map<String, String> resource = (Map) body.get("resource");// 5:回调报文解密AesUtil aesUtil = new AesUtil(wxPaternerKey.getBytes());//解密后json字符串String decryptToString = aesUtil.decryptToString(resource.get("associated_data").getBytes(),resource.get("nonce").getBytes(),resource.get("ciphertext"));//6:获取微信支付返回的信息com.alibaba.fastjson.JSONObject jsonData = com.alibaba.fastjson.JSONObject.parseObject(decryptToString);logger.info("wxJsApiCallback responseJson:" + jsonData.toJSONString());//7: 支付状态的判断 如果是success就代表支付成功if ("SUCCESS".equals(jsonData.get("trade_state"))) {// 8:获取支付的交易单号,流水号,和附属参数String out_trade_no = jsonData.get("out_trade_no").toString();String transaction_id = jsonData.get("transaction_id").toString();com.alibaba.fastjson.JSONObject amount = jsonData.getJSONObject("amount");// 订单金额信息int payMoney = amount.getIntValue("payer_total"); //实际支付金额// 成功处理}else{// 失败处理}result.put("code", "SUCCESS");result.put("message", "成功");} catch (Exception e) {result.put("code", "fail");result.put("message", "系统错误");e.printStackTrace();}return result;}