【微信支付】微信JSAPI支付流程

引言

手机微信APP支付,微信登入,手机微信JSAPI支付,微信退款,支付宝APP支付,支付宝手机上网站支付,支付宝退钱。小编都放进微信公众号: J ** A大贼船。微信搜一搜,便捷之后开发设计用哦!

官方网文档

JS-SDK表明文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1JSAPI支付文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1准备工作微信公众号配置关联域名(可在该域名下启用微信开放的JS接口,这儿配置前面域名) 先登陆微信公众号进到“公众号设置”的“作用设定”里填好“JS接口安全性域名”。填好接口配置信息内容(用以相互配合微信服务器验证)开发设计-基本上配置,配置URL,Token和转化成EncodingAESKey,配置完后此刻点一下递交是不行的,必须后面写好接口与相互配合公众号认证,如下所示编码。 @ApiOperation("公众号认证详细地址") @GetMapping("/baseCallback") public String baseCallback(String signature,String timestamp,String nonce,String echostr) throws Exception { return echostr; }配置ip授权管理获得一般accessToken时,请登陆“微信公众号-开发设计-基本上配置”提早将网络服务器IP地址加上到IP授权管理中,不然将不能启用取得成功商户平台配置设定支付文件目录(配置了才可以拉起手机微信收款台,配置前面域名,不然会报现阶段网页的Url没有注册的不正确)登陆微信支付商户平台(http://pay.weixin.qq.com)-->产品展示-->开发设计配置,设定后一般5min内起效。支付受权文件目录校检表明如下所示:1、假如支付受权文件目录设定为顶尖域名(例如:https:// ** .weixin.com/ ),那麼只校检顶尖域名,不校检后缀名;2、假如支付受权文件目录设定为多级别文件目录,便会实现全配对,例如设定支付受权文件目录为https:// ** .weixin.com/abc/123/,则具体要求网页页面文件目录不可以为https:// ** .weixin.com/abc/,也无法为https:// ** .weixin.com/abc/123/pay/,务必为https:// ** .weixin.com/abc/123/步骤流程JSSDK应用流程这一文档关键看 1简述和14手机微信支付,如下图;关键流程前面获得引入主要参数前面支付前要求后台管理获得图中流程三所需主要参数,并引入取得成功,前面最好是配置全局性,由于业务流程很有可能会拓展,别的接口会必须,如共享接口前面获得勾起手机微信收款台所需主要参数引入取得成功后->前面点击微信支付->后面向微信下单->提交订单完成后获得prepay_id->回到所需主要参数给前面->勾起收款台后面编码完成引进依靠 <!-- 手机微信支付 --> <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency>配置主要参数

application.yml

# 手机微信有关配置wx: #商家 ID(手机微信支付服务平台-账户中心-私人信息) MCH_ID: # APP_ID(微信开放平台或微信公众号搜索) H_APP_ID: # 密匙(微信开放平台或微信公众号搜索) H_APP_SECRET: # 信息加解密常用到的破译串(微信公众平台-基本上配置搜索) H_ENCODINGAESKEY: # token(微信公众平台-基本上配置搜索) H_TOKEN: # 支付密匙KEY(手机微信支付服务平台-账户中心-api安全性-api密匙) H_KEY: # 支付商家资格证书所述文件目录(手机微信支付服务平台-账户中心-api安全性-API资格证书) H_CERT_PATH: #支付取得成功调整详细地址 WX_CALLBACK_URL:

YmlParament

@Component@Datapublic class YmlParament { /*手机微信有关字段名*/ @Value("${wx.H_APP_ID}") private String h_app_id; @Value("${wx.H_APP_SECRET}") private String h_app_secret; @Value("${wx.H_ENCODINGAESKEY}") private String h_encodingaeskey; @Value("${wx.H_TOKEN}") private String h_token; @Value("${wx.MCH_ID}") private String mch_id; @Value("${wx.H_KEY}") private String h_key; @Value("${wx.H_CERT_PATH}") private String h_cert_path; @Value("${wx.WX_CALLBACK_URL}") private String wx_callback_url;获得前面config接口配置主要参数(查询jssdk文档附则1)转化成签字以前一定先了解一下jsapi_ticket,jsapi_ticket是微信公众号用以调用微信JS接口的临时性单据。通常情况下,jsapi_ticket的期限为7200秒,根据access_token来获得。因为获得jsapi_ticket的api调用频次十分比较有限,经常刷新jsapi_ticket会造成api调用受到限制,危害本身业务流程,开发人员需要在自身的服务项目全局性缓存文件jsapi_ticket 。Application 打开任务计划,用以按时更新@EnableSche ** ng//打开任务计划 关系式参照http://cron.qqe2.com/配置全局性缓存文件

WxParament

/** * 获得ACCESS_TOKEN * 更新标准: * 1、启动设置该值 * 2、一小时更新一次 */ public static String ACCESS_TOKEN; /** * 获得JSAPI_TICKET * 更新标准: * 1、启动设置该值 * 2、一小时更新一次 */ public static String JSAPI_TICKET;按时更新

InitServiceImpl

@Autowired private YmlParament ymlParament; @PostConstruct @Override public void init() { initAccessToken(); initJsapiTicket(); } /** * 复位AccessToken 一小时实行一次 *cron关系式可在线制作:https://cron.qqe2.com/ */ @Scheduled(cron = "0 0 0/1 * * ?") @Override public void initAccessToken() { try { WxParament.ACCESS_TOKEN = GetToken.getAccessToken(ymlParament.getH_app_id(), ymlParament.getH_app_secret()); } catch (Exception e) { log.error("<====initAccessToken初始化失败!==>" e); e.printStackTrace(); } log.info("<====复位initAccessToken取得成功,数值==>" WxParament.ACCESS_TOKEN); } /** * 复位JSAPI_TICKET 一小时实行一次 */ @Scheduled(cron = "0 0 0/1 * * ?") @Override public void initJsapiTicket() { try { log.info("<====已经更新 JSAPI_TICKET ==>"); WxParament.JSAPI_TICKET = GetToken.getTicket(ymlParament.getH_app_id(), ymlParament.getH_app_secret(), WxParament.ACCESS_TOKEN); } catch (Exception e) { log.error("<====initJsapiTicket初始化失败!==>" e); e.printStackTrace(); } log.info("<====刷新 JSAPI_TICKET 取得成功,数值 ==>" WxParament.JSAPI_TICKET); }

GetToken

/** * ~~~~~~~~~ 第一步 ~~~~~~~~~ * 有效期限两个小时,留意缓存文件 获得access_token,还记得配置ip授权管理 * 参照下列文档获得access_token(有效期限7200秒,开发人员需要在自身的服务项目全局性缓存文件access_token): * https://developers.weixin.qq.com/doc/offiaccount/Basic_Infor ** tion/Get_access_token.html * @param appid * @param secret * @throws Exception */ public static String getAccessToken(String appid, String secret) throws Exception { String res=HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", "grant_type=client_credential&appid=" appid "&secret=" secret); String access_token=JSON.parseObject(res).getString("access_token"); if(IsNull.isNull(access_token)) { throw new Exception(res); } return access_token; } /** * ~~~~~~~~~ 第二步 ~~~~~~~~~ * 有效期限两个小时,留意缓存文件 用第一步得到的access_token 选用http * GET方法要求得到jsapi_ticket(有效期限7200秒,开发人员需要在自身的服务项目全局性缓存文件jsapi_ticket): * https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi * @param appid * @param secret * @param accessToken 如果 accessToken 传null 则重新获取 access_token,如果accessToken有值,则直接获取Ticket * @throws Exception */ public static String getTicket(String appid, String secret,String accessToken) throws Exception { String token=IsNull.isNull(accessToken)?getAccessToken(appid, secret):accessToken; String res=HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", "access_token=" + token + "&type=jsapi"); String ticket=JSON.parseObject(res).getString("ticket"); if(IsNull.isNull(ticket)) { throw new Exception(res); } return ticket; } /** * * jsapi_ticket是公众号用于调用微信JS接口的临时票据 * 前台统一js签名,参考说明如下 * 1、业务说明:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#3 * 2、签名算法:1-JS-SDK使用权限签名算法:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 * @param jsapi_ticket,必须传入,每次有效时间两小时,如果频繁获取会报错 * @param url url(当前支付页面的URL,不包含#及其后面部分) * @return */ public static Map<String, String> getJsSignature(String jsapi_ticket, String url) { return sign(jsapi_ticket, url); } /*签名算法*/ private static Map<String, String> sign(String jsapi_ticket, String url) { Map<String, String> ret = new HashMap<String, String>(); String nonce_str = UUID.randomUUID().toString();; String timestamp = Long.toString(System.currentTimeMillis() / 1000); String signature = null; // 注意这里参数名必须全部小写,且必须有序 String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("url", url); ret.put("jsapi_ticket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; } private static String byteToHex(final byte[] hash) { For ** tter for ** tter = new For ** tter(); for (byte b : hash) { for ** tter.for ** t("%02x", b); } String result = for ** tter.toString(); for ** tter.close(); return result; }

HttpUtil

public static String get(String urlStr, Map<String, String> parameters) throws IOException { URL url = new URL(urlStr); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setDoInput(true); httpURLConnection.setDoOutput(true); // 设置该连接是可以输出的 httpURLConnection.setRequestMethod("GET"); // 设置请求方式 httpURLConnection.setRequestProperty("charset", "utf-8"); PrintWriter pw = new PrintWriter(new BufferedOutputStream(httpURLConnection.getOutputStream())); StringBuffer parameter = new StringBuffer(); parameter.append("1=1"); for (Entry<String, String> entry : parameters.entrySet()) { parameter.append("&" + entry.getKey() + "=" + entry.getValue()); } pw.write(parameter.toString());// 向连接中写数据(相当于发送数据给服务器) pw.flush(); pw.close(); BufferedReader br = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "utf-8")); String line = null; StringBuilder ** = new StringBuilder(); while ((line = br.readLine()) != null) { // 读取数据 ** .append(line + "n"); } br.close(); return ** .toString(); }

JacksonUtil

public class JacksonUtil { public static String parseString(String body, String field) { ObjectMapper ** pper = new ObjectMapper(); JsonNode node; try { node = ** pper.readTree(body); JsonNode leaf = node.get(field); if (leaf != null) { return leaf.asText(); } } catch (IOException e) { e.printStackTrace(); } return null; }}

微信支付初始化参数 @ApiOperation("微信支付初始化参数") @PostMapping("getJsSignature") public R getJsSignature(@RequestBody String body){ //(当前支付页面的URL,不包含#及其后面部分) String url = JacksonUtil.parseString(body, "url"); if(IsNull.isNull(url)) { return R.error("参数不能为空!"); } Map<String, Object> res = new HashMap<>(); Map<String, String> jsSignature = GetToken.getJsSignature(WxParament.JSAPI_TICKET, url); res.put("appId", ymlParament.getH_app_id()); res.put("timestamp",jsSignature.get("timestamp")); res.put("nonceStr",jsSignature.get("nonceStr")); res.put("signature",jsSignature.get("signature")); return R.ok(res); }获取openid可参考:http://mp.weixin.qq.com/s?__biz=MzIxOTc2MjUyNw==&mid=100000513&idx=1&sn=699495292cd824e1eb6c483492045510&chk ** =17d7193120a090278a97b04d8fe7f9ccfda55fdcebb8667897e26308c06ee5c0795e9854ffc8#rd微信统一下单微信统一下单接口文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1初始化微信支付配置@Componentpublic class WxConfig { @Autowired private YmlParament ymlParament; /** * 初始化微信支付配置 * @throws Exception */ @Bean(autowire = Autowire.BY_NAME,value = WxParament.H5_WX_PAY) public WXPay setH5WXPay() throws Exception { return new WXPay(new WxPayConfig( ymlParament.getH_cert_path(), ymlParament.getH_app_id(), ymlParament.getMch_id(), ymlParament.getH_key())); } }

WxPayConfig

public class WxPayConfig implements WXPayConfig { private byte[] certData; private String appID; private String mchID; private String key; public WxPayConfig(String certPath, String appID,String mchID,String key) throws Exception { File file = new File(certPath); InputStream certStream = new FileInputStream(file); this.certData = new byte[(int) file.length()]; certStream.read(this.certData); certStream.close(); this.appID = appID; this.mchID = mchID; this.key = key; }}微信下单接口,关键代码(服务层) @Resource(name = WxParament.H5_WX_PAY) private WXPay wxH5Pay;/* 微信统一下单 */ private Map<String, String> wxUnifiedOrder(String orderNo, String orderFee, String requestIp, String openid) throws RuntimeException { Map<String, String> data = new HashMap<String, String>(); data.put("nonce_str", WXPayUtil.generateNonceStr()); data.put("body", "我来下单啦"); data.put("out_trade_no", orderNo); data.put("sign_type", "MD5"); data.put("total_fee", orderFee); data.put("spbill_create_ip", requestIp); data.put("openid", openid); data.put("notify_url",ymlParament.getWx_callback_url()); data.put("trade_type", "JSAPI"); // 此处指定为JSAPI支付 Map<String, String> wxOrderResult = WxPay.unifiedorder(data,wxH5Pay); if("FAIL".equals(wxOrderResult.get("return_code"))){ throw new RuntimeException(wxOrderResult.get("return_msg")); } /* IsNull自定义,主要判断非空 */ if (IsNull.isNull(wxOrderResult.get("prepay_id"))) { throw new RuntimeException("微信支付下单成功后,返回的prepay_id为空"); } return wxOrderResult; } @Override public Map<String, String> insertWxH5Pay(String orderNo,String orderFee, String requestIp, String openid) { try { /* 微信下单 */ Map<String, String> wxOrderResult = wxUnifiedOrder(orderNo, orderFee, requestIp, openid); Map<String, String> chooseWXPay = new HashMap<>(); /* 这里我遇到一个大坑,这里的key值是要驼峰式写法,而APP支付却不用!!! */ chooseWXPay.put("appId", ymlParament.getH_app_id()); chooseWXPay.put("timeStamp", WxUtils.create_timestamp()); chooseWXPay.put("nonceStr", wxOrderResult.get("nonce_str")); chooseWXPay.put("package", "prepay_id=" + wxOrderResult.get("prepay_id")); chooseWXPay.put("signType", "MD5"); String signature = WXPayUtil.generateSignature(chooseWXPay, ymlParament.getH_key()); chooseWXPay.put("paySign", signature); return chooseWXPay; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } }controller层(略)微信支付成功回调关键代码 @Resource(name = WxParament.H5_WX_PAY) private WXPay wxH5Pay; @ApiOperation("微信支付回调") @PostMapping("callback") public String callback(HttpServletRequest request) throws Exception { try { // 1、获取参数 Map<String, String> params = getMapByRequest(request); log.info("微信回调回来啦!!!!" + params); // 2、校验 checkCallbackWxPay(params); //业务逻辑 return wxPaySuccess(); } catch (Exception e) { e.printStackTrace(); return wxPayFail(e.getMessage()); } } /** * 获取微信过来的请求参数 * @param request * @return * @throws Exception */ private static Map<String, String> getMapByRequest(HttpServletRequest request) throws Exception{ InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } Map<String, String> ret= WXPayUtil.xmlToMap(new String(outSteam.toByteArray(), "utf-8")); outSteam.close(); inStream.close(); return ret; } /*校验 */ private void checkCallbackWxPayAircraft(Map<String, String> params) throws Exception { if ("FAIL".equals(params.get("result_code"))) { throw new Exception("微信支付失败"); } if (!checkWxCallbackSing(params, wxH5Pay)) { throw new Exception("微信支付回调签名认证失败"); } //校验金额 //判断订单是否重复 //....业务逻辑 } /** * 检查微信回调签名 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 * * @param wp 传入:@Autowired WXPay */ private static boolean checkWxCallbackSing(Map<String, String> notifyMap, WXPay wp) throws Exception { return wp.isPayResultNotifySignatureValid(notifyMap); } /** * 微信支付返回参数结果封装 */ private static String wxPaySuccess() throws Exception { Map<String, String> succResult = new HashMap<String, String>(); succResult.put("return_code", "SUCCESS"); succResult.put("return_msg", "OK"); return WXPayUtil. ** pToXml(succResult); } /** * @param mess 错误消息提示 * @return 微信返回错我的提示 * @throws Exception */ private static String wxPayFail(String mess) throws Exception { Map<String, String> succResult = new HashMap<String, String>(); succResult.put("return_code", "FAIL"); succResult.put("return_msg", IsNull.isNull(mess)?"自定义异常错误!":mess); return WXPayUtil. ** pToXml(succResult); }补充如果不想验证签名,还有一种方式判断是否支付成功,就是调用微信查询订单接口查看是否支付成功关键代码/*调用微信查询订单接口*/ Map<String, String> orderqueryRes = orderquery(wXH5Pay,params.get("out_trade_no")); /*交易成功*/ if (!"SUCCESS".equals(orderqueryRes.get("trade_state"))){ throw new Exception("<===微信支付失败====>订单号为【"+ params.get("out_trade_no")+ "】的订单"); } /** * 查询支付结果 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2 */ private static Map<String, String> orderquery(WXPay wxpay, String outTradeNo) throws Exception { Map<String, String> parament = new HashMap<String, String>(); parament.put("out_trade_no", outTradeNo); return wxpay.orderQuery(parament); }

扫码免费用

源码支持二开

申请免费使用

在线咨询