微信扫码支付
2019-06-11 12:14
1146 查看
业务需求
商户自定义金额生成二维码,扫描二维码进行自定义金额支付
官方文档链接
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4
接口代码
@RequestMapping(value = "/companyReceipt") @ResponseBody public String companyReceipt(TTemplatePay tTemplatePay) throws Exception { JSONObject jsonObject = new JSONObject(); PayUtil payUtil = new PayUtil(); String codeUrl = ""; // 公众账号ID payUtil.setAppid(PAY_APPID); // 商户号 payUtil.setMch_id(PAY_MCH_ID); payUtil.setOut_trade_no(WeixinpayUtil.getUUID()); payUtil.setNonce_str(WeixinpayUtil.getUUID()); // 通知地址(支付成功,回调接口地址) payUtil.setNotify_url(NOTIFY_URL); // 自定义支付金额(单位:分) payUtil.setTotal_fee(tTemplatePay.getTotalFee()*100); LOGGER.info("------企业支付生成支付二维码,支付金额money{}", payUtil.getTotal_fee()); // 企业指定金额生成支付二维码 Map<String, Object> map = WxCompanyReceiptUtil.wxCompanyReceipt(payUtil); if(StringUtils.equals("SUCCESS", map.get("result_code").toString()) && StringUtils.equals("SUCCESS", map.get("return_code").toString())){ tTemplatePay.setOutTradeNo(payUtil.getOut_trade_no()); tTemplatePay.setNonceStr(payUtil.getNonce_str()); // 支付订单信息添加到数据库 int i = payService.insertTemplateOrder(tTemplatePay); if(i > 0){ // 二维码链接 codeUrl = map.get("code_url").toString(); jsonObject.accumulate("codeUrl", codeUrl); LOGGER.info("------企业支付生成支付二维码,支付二维码链接{}", codeUrl); LOGGER.info("------企业生成支付二维码,扫码支付订单入库{}", "SUCCESS"); }else{ LOGGER.info("------企业生成支付二维码,扫码支付订单入库{}", "ERROR"); } }else{ LOGGER.info("------企业生成支付二维码{}", "ERROR"); } return jsonObject.toString(); }
组装参数请求获取二维码
package com.litte.util; import com.litte.entity.PayUtil; import com.litte.util.wxpay.WXPayUtil; import java.util.Map; public class WxCompanyReceiptUtil { /** * @Description: 企业指定金额生成支付二维码 * @Author: Mr.Jkx * @Date: 2019/4/10 10:40 */ public static Map<String, Object> wxCompanyReceipt(PayUtil payUtil) throws Exception { String stringA = "appid=" + payUtil.getAppid() + "&body=商品描述" + "&mch_id=" + payUtil.getMch_id() + "&nonce_str=" + payUtil.getNonce_str() + "¬ify_url=" + "https://www.xinxingshangstar.com/wxPay/companyReceiptBack" + "&out_trade_no=" + payUtil.getOut_trade_no() + "&spbill_create_ip=Ip" // 终端IP + "&total_fee="+ payUtil.getTotal_fee() + "&trade_type=NATIVE" + "&key=xinxingshang2018xinxingshang2018"; String sign = Md5Util.md5(stringA).toUpperCase(); String xml = "<xml>" + " <appid>" + payUtil.getAppid() + "</appid>" + " <body>商品描述</body>" + " <mch_id>" + payUtil.getMch_id() + "</mch_id>" + " <nonce_str>" + payUtil.getNonce_str() + "</nonce_str>" + " <notify_url>NOTIFY_URL</notify_url>" + " <out_trade_no>" + payUtil.getOut_trade_no() + "</out_trade_no>" + " <spbill_create_ip>Ip</spbill_create_ip>" + " <total_fee>" + payUtil.getTotal_fee() + "</total_fee>" + " <trade_type>NATIVE</trade_type>" + " <sign>" + sign + "</sign>" + "</xml> "; System 1a4bb .out.println("调试模式_统一下单接口 请求XML数据:" + xml); //调用统一下单接口,并接受返回的结果 String result = BasePay.httpRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xml); // 将解析结果存储在HashMap中 Map map = WXPayUtil.xmlToMap(result); return map; } }
请求方法类
package com.litte.util; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class BasePay { /** * * * * * @param requestUrl请求地址 * * @param requestMethod请求方法 * * @param outputStr参数 * */ public static String httpRequest(String requestUrl, String requestMethod, String outputStr) { // 创建SSLContext StringBuffer buffer = null; try { URL url = new URL(requestUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod(requestMethod); conn.setDoOutput(true); conn.setDoInput(true); conn.connect(); //往服务器端写内容 if (null != outputStr) { OutputStream os = conn.getOutputStream(); os.write(outputStr.getBytes("utf-8")); os.close(); } // 读取服务器端返回的内容 InputStream is = conn.getInputStream(); InputStreamReader isr = new InputStreamReader(is, "utf-8"); BufferedReader br = new BufferedReader(isr); buffer = new StringBuffer(); String line = null; while ((line = br.readLine()) != null) { buffer.append(line); } br.close(); } catch (Exception e) { e.printStackTrace(); } return buffer.toString(); } }
支付后回调请求接口
/** * @Description: 企业微信返回信息,更改订单状态 * @Author: Mr.Jkx * @Date: 2019/4/27 18:28 */ @RequestMapping(value = "/companyReceiptBack") @ResponseBody public void companyReceiptBack(HttpServletRequest request) throws Exception { String xmlStr = NotifyServlet.getWxXml(request); LOGGER.info("------扫描企业支付二维码回调请求数据{}", xmlStr); // 解析回调请求参数 Map map2 = WXPayUtil.xmlToMap(xmlStr); // 根据回调参数操作订单信息 // TODO }
微信相关工具类
package com.litte.util.wxpay; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringWriter; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.*; public class WXPayUtil { private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final Random RANDOM = new SecureRandom(); /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public static Map<String, String> xmlToMap(String strXML) throws Exception { try { Map<String, String> data = new HashMap<String, String>(); DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { // do nothing } return data; } catch (Exception ex) { WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML); throw ex; } } /** * 将Map转换为XML格式的字符串 * * @param data Map类型数据 * @return XML格式的字符串 * @throws Exception */ public static String mapToXml(Map<String, String> data) throws Exception { org.w3c.dom.Document document = WXPayXmlUtil.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key: data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { } return output; } /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @return 含有sign字段的XML */ public static String generateSignedXml(final Map<String, String> data, String key) throws Exception { return generateSignedXml(data, key, WXPayConstants.SignType.MD5); } /** * 生成带有 sign 的 XML 格式字符串 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名类型 * @return 含有sign字段的XML */ public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception { String sign = generateSignature(data, key, signType); data.put(WXPayConstants.FIELD_SIGN, sign); return mapToXml(data); } /** * 判断签名是否正确 * * @param xmlStr XML格式数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(String xmlStr, String key) throws Exception { Map<String, String> data = xmlToMap(xmlStr); if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key).equals(sign); } /** * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。 * * @param data Map类型数据 * @param key API密钥 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception { return isSignatureValid(data, key, WXPayConstants.SignType.MD5); } /** * 判断签名是否正确,必须包含sign字段,否则返回false。 * * @param data Map类型数据 * @param key API密钥 * @param signType 签名方式 * @return 签名是否正确 * @throws Exception */ public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception { if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key, signType).equals(sign); } /** * 生成签名 * * @param data 待签名数据 * @param key API密钥 * @return 签名 */ public static String generateSignature(final Map<String, String> data, String key) throws Exception { return generateSignature(data, key, WXPayConstants.SignType.MD5); } /** * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。 * * @param data 待签名数据 * @param key API密钥 * @param signType 签名方式 * @return 签名 */ public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception { Set<String> keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals(WXPayConstants.FIELD_SIGN)) { continue; } if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名 sb.append(k).append("=").append(data.get(k).trim()).append("&"); } sb.append("key=").append(key); if (WXPayConstants.SignType.MD5.equals(signType)) { return MD5(sb.toString()).toUpperCase(); } else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) { return HMACSHA256(sb.toString(), key); } else { throw new Exception(String.format("Invalid sign_type: %s", signType)); } } /** * 获取随机字符串 Nonce Str * * @return String 随机字符串 */ public static String generateNonceStr() { char[] nonceChars = new char[32]; for (int index = 0; index < nonceChars.length; ++index) { nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); } return new String(nonceChars); } /** * 生成 MD5 * * @param data 待处理数据 * @return MD5结果 */ public static String MD5(String data) throws Exception { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] array = md.digest(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 生成 HMACSHA256 * @param data 待处理数据 * @param key 密钥 * @return 加密结果 * @throws Exception */ public static String HMACSHA256(String data, String key) throws Exception { Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); sha256_HMAC.init(secret_key); byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte item : array) { sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb.toString().toUpperCase(); } /** * 日志 * @return */ public static Logger getLogger() { Logger logger = LoggerFactory.getLogger("wxpay java sdk"); return logger; } /** * 获取当前时间戳,单位秒 * @return */ public static long getCurrentTimestamp() { return System.currentTimeMillis()/1000; } /** * 获取当前时间戳,单位毫秒 * @return */ public static long getCurrentTimestampMs() { return System.currentTimeMillis(); } }
相关文章推荐
- 微信扫码支付功能详细教程————Java
- 微信扫码支付模式二的坑
- c#版在pc端发起微信扫码支付的实例
- .NET MVC结构框架下的微信扫码支付模式二 API接口开发测试
- 微信扫码支付--xml解析
- c#版在pc端发起微信扫码支付
- 微信扫码支付(模式一)遇到的那些坑
- JAVA微信扫码支付模式二功能实现以及回调
- php CI 微信支付扩展 微信扫码支付 jssdk 支付 退款
- 请问下大家,微信扫码支付时,出现:原生支付URL参数错误!请问这具体是什么错误呢? 是按照微信二维码格式生成的内容
- 微信扫码支付开发-公众号支付开发-视频教程7
- JAVA微信扫码支付模式二功能实现以及回调
- JAVA微信扫码支付模式二线上支付功能实现以及回调
- 微信扫码支付返回的code_url怎么生成二维码图片
- 微信公众平台开发[5] —— 微信扫码支付介绍
- 微信:微信扫码支付、调用统一下单接口、网站支付 + springmvc
- php实现微信原生支付(扫码支付)功能
- 微信扫码支付第一次出现404错误的解决办法
- 【转载】ASP.NET Core Web 支付功能接入 微信-扫码支付篇
- 【微信开发】PC端 微信扫码支付成功之后自动跳转