**
初次接触微信支付,内心万马奔腾,产品,客户一直催,微信支付文档也不知道怎么看,记录一下微信支付开发步骤,,,,,先解释微信支付流程!其他文档会给加密,解密,给微信支付参数的代码
*** 1*.背景:初次接触微信支付,客户打算进行使用微信支付在注册之后后台审核之后让会员进行缴费使用的 刚刚接触微信支付的开发文档真是想骂娘,现在聊聊java后端开发遇到的坑—pc端 使用的NATIVE 返回的二维码进行支付**
什么是NATIVE:
1.不跟微信似的官方解释,就告诉你是干啥的,图片微信下单的时候可以看到
返回的有二维码连接的地址看出 就是给你个二维码,让你用微信扫描付钱(这是在统一下单的时候成功返回的参数)
开发步骤:
2.1:说明。首先要统一下单(微信开发完档API列表上看),这是我们后端的活,统一下单可不是付钱,就是告诉微信我要付钱了,你把收款账户给我,然后返回上图数据,你给前端就行了,前端怎么处理我们不管!
2.1.1:这里说下统一下单所需要的参数,
生个这个对象SortedMap<String, Object> params = new TreeMap<>();
把参数put进去就行
大家先注意必填哪一列,只传必填项就行,不是必填的传不传看你想法,不受啥影响,
变量名要求:例如appid:key值只能是appid ----严格区分大小写
必填数据怎么拿到:
公众账号ID-appid:给客户要,让他注册微信商户平台就能拿到
商户号-mch_id:同上
(客户不知道咋注册微信商户平台,微信文档操作连接甩他脸上,https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_1.shtml)
随机字符串-nonce_str:自己随便生成一个小于32位的就行,我自己生成了个十位的
商品描述-body:顾明思议,就是你卖的啥商品给他说一下
商户订单号-out_trade_no:自己生成的订单号,下单之前最好生成个自己系统的未支付订单号,回调成功的时候在
金额-total_fee:商品的金额 都是以分计算的 你付0.1元 你要给微信传个1
终端ip-spbill_create_ip,没特别要求直接用微信提供的实例值(123.12.12.123)
通知地址:-notify_url:这个对小白来说最恶心,文章最下面重点介绍,意思:相当于用户付完款,微信访问你这个接口,然后微信给你说一句他付款成功了,把东西给人家吧,然后你得在这个接口再给微信返回一个ok 告诉微信我知道了 不然微信会一直访问你的接口,(接口要确保外网能访问到)随后上代码,直接复制过去吧
交易类型-trade_type:这个就是给微信说你现在下单的是那种支付方式(JSAPI -JSAPI支付,NATIVE -Native支付,APP -APP支付)文章讲的NATIVE,就直接写进去就行
签名-sign:看这个生成小白看微信文档能恶心死,首先我们需要密钥,给客户要,他在商户平台能拿到要API的密钥 最近另一个项目做App微信支付好像还有个API3什么玩意,客户给了我没用到,然后我们要把其他必填的进行加密当做sign的value值,
@characterEncoding UTF-8
@ params sign之前的map数据
@API_KEY 上面说的密钥
//生成签名 public static String createSign(String characterEncoding, Map<String, ?> params) { List<String> kList = new ArrayList<>(params.keySet()); kList.sort(String::compareTo); StringBuffer sb = new StringBuffer(); for (String key : kList) { String val = StringUtils.toString(params.get(key)); if (null != val && !"".equals(val) && !"sign".equals(key) && !"key".equals(key)) { sb.append(key + "=" + val + "&"); } } sb.append("key=" + API_KEY); log.info(sb.toString()); String sign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; }
返回的sign 在放入之前的map
组装参数
微信接收参数据接收xml类型的,所以把之前的map数据进行组装
//请求xml组装 public static String getRequestXml(SortedMap<String, Object> params) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = params.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String key = (String) entry.getKey(); String value = StringUtils.toString(entry.getValue()); if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) { sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">"); } else { sb.append("<" + key + ">" + value + "</" + key + ">"); } } sb.append("</xml>"); return sb.toString(); }
发送给微信
//请求方法 /** @requestUrl 微信下单地址 @requestMethod post 请求 @组装xml返回的xml字符串 */ public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { try { URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); conn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { log.error("连接超时:", ce); } catch (Exception e) { log.error("https请求异常:", e); } return null; }
请求之后微信会返回数据
String result = httpsRequest($1,$2,$3);
把返回结果,String转成map,map.get(“code_url”),拿到二维码地址给前端就好了,
notify_url 回调地址详解:
直接上代码
public void payCallback() throws Exception { nputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } inStream.close(); String resultXml = new String(outSteam.toByteArray(), StandardCharsets.UTF_8); outSteam.close(); // 通过map验签名,看看签名是否正确 String sign = WechatPayUtil.createSign(WechatConstants.PayConstants.PAY_SIGN_CHARSET, resMap); if (!sign.equals(resMap.get("sign"))) { throw new BusinessException("失败的参数"); } log.info("微信回调结果:{}", resultXml); //转map Map<String, String> resMap = WechatPayUtil.doXMLParse(resultXml); // 看自己业务 //通信判断失败的时候 resMap.get("return_code") 为FAIL //成功的时候resMap.get("return_code") 为success //支付成功或者失败判断 success FAIL resMap.get("result_code") String resXml = ""; // 成功给微信的通知 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; //失败给微信的报文 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; // BufferedOutputStream out = new BufferedOutputStream( response.getOutputStream()); response.setContentType("text/xml"); out.write(resXml.getBytes()); out.flush(); out.close(); log.info("通知微信调用成功:{}",resXml); }
开发是根据微信支付之前的开发文档做的–https://pay.weixin.qq.com/wiki/doc/api/index.html
现在微信开发有新的文档但是逻辑功能是差不多的
APP,JSAPI与NATIVE开发大同小异
如有错误,欢迎留言指出