PC网页微信扫码支付(模式二)

微信支付:模式二(JAVA)

支付过程

开始

前言:由于微信官方文档非常扯淡,也不够阿里文档详细,集中和简单,并且官方提供demo多样性和复杂性,此次我采用的是模式二,http工具类请求。

  1. 首先,我们需要在开发平台获取我们需要的参数。

    • appid:产品配置中获得app_id
    • device_info:WEB(自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB")
    • mch_id:商家号
    • notify_url:回调路径
    • spbill_create_ip:项目ip(可以127.0.0.1,但是不可以localhost)
    • trade_type:NATIVE
    • key:密钥
    • url:https://api.mch.weixin.qq.com/pay/unifiedorder
  2. 工具类

    1. WxConfig.class(微信配置)

       public class WxConfig {
           public static final String appid = ""; //app_id
           public static final String body = "订单支付"; // 订单内容
           public static final String device_info = "WEB"; // 类型:网站
           public static final String mch_id = ""; // 商家号
           public static final String notify_url = "/wx/paycallback.json"; // 回调路径
           public static final String spbill_create_ip = "127.0.0.1"; // 项目ip
           public static final String trade_type = "NATIVE";
           public static final String key = ""; //key
           public static final String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //请求路径
       }
      
    2. SignGenerate.class(生成请求微信参数格式)

       public final class SignGenerate {
           // 生成md5加密后的sign
           public static String wxSignGenerate(Map<String, String> paramsMap, String key) {
               StringBuilder builder = new StringBuilder();
               for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
                   builder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
               }
               builder.append("key=").append(key);
               return DigestUtils.md5Hex(builder.toString()).toUpperCase();
           }
      
           // 将配置生成微信需要的xml格式
           public static String buildWxXmlJson(String productNumber,String amount){
               StringBuilder data = new StringBuilder("<xml>");
               Map<String, String> paramsMap = new LinkedHashMap<>();
               paramsMap.put("appid", WxConfig.appid);
               paramsMap.put("body", WxConfig.body);
               paramsMap.put("device_info", WxConfig.device_info);
               paramsMap.put("mch_id", WxConfig.mch_id);
               paramsMap.put("nonce_str", SerialGenerator.genSerialCode());
               paramsMap.put("notify_url", WxConfig.notify_url);
               paramsMap.put("out_trade_no", productNumber);
               paramsMap.put("spbill_create_ip", WxConfig.spbill_create_ip);
               paramsMap.put("total_fee", amount);
               paramsMap.put("trade_type", WxConfig.trade_type);
               String sign = wxSignGenerate(paramsMap, WxConfig.key);
               paramsMap.put("sign", sign);
               for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
                   data.append("<").append(entry.getKey()).append(">").append(entry.getValue()).append("</").append(entry.getKey()).append(">");
               }
               data.append("</xml>");
               return data.toString();
           }
       }
      
    3. WeixinWebUtil(微信请求工具类)

       public class WeixinWebUtil {
           private static Logger log = LoggerFactory.getLogger(WeixinWebUtil.class);
      
           public static String doGet(String url, String charset, int connectTimeout, int readTimeout) {
               return httpRequest(url, "GET", null, charset, connectTimeout, readTimeout, null);
           }
       
           public static String doGet(String url, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) {
               return httpRequest(url, "GET", null, charset, connectTimeout, readTimeout, headerMap);
           }
       
       
           //url为不带 ? 号的url
           public static String doGet(String url, Map<String, String> paramMap, String charset, int connectTimeout, int readTimeout) throws IOException {
               String query = buildQuery(paramMap, charset);
               return httpRequest(url + "?" + query, "GET", null, charset, connectTimeout, readTimeout, null);
           }
       
           public static String doGet(String url, Map<String, String> paramMap, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
               String query = buildQuery(paramMap, charset);
               return httpRequest(url + "?" + query, "GET", null, charset, connectTimeout, readTimeout, headerMap);
           }
       
           public static String doPost(String url, Map<String, String> paramMap, String jsonParam, String charset, int connectTimeout, int readTimeout) throws IOException {
               String query = buildQuery(paramMap, charset);
               return httpRequest(url + "?" + query, "POST", jsonParam, charset, connectTimeout, readTimeout, null);
           }
       
           public static String doPost(String url, Map<String, String> paramMap, String jsonParam, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) throws IOException {
               String query = buildQuery(paramMap, charset);
               return httpRequest(url + "?" + query, "POST", jsonParam, charset, connectTimeout, readTimeout, headerMap);
           }
       
           public static String doPost(String url, String jsonParam, String charset, int connectTimeout, int readTimeout) {
               return httpRequest(url, "POST", jsonParam, charset, connectTimeout, readTimeout, null);
           }
       
           public static String doPost(String url, String jsonParam, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) {
               return httpRequest(url, "POST", jsonParam, charset, connectTimeout, readTimeout, headerMap);
           }
       
       
           /**
            * 发起https请求并获取结果
            *
            * @param requestUrl 请求地址
            * @param requestMethod 请求方式(GET、POST)
            * @param outputStr 提交的数据
            * @return String
            */
           private static String httpRequest(String requestUrl, String requestMethod, String outputStr, String charset, int connectTimeout, int readTimeout, Map<String, String> headerMap) {
               StringBuffer buffer = new StringBuffer();
               try {
                   // 创建SSLContext对象,并使用我们指定的信任管理器初始化
                   TrustManager[] tm = { new MyX509TrustManager() };
                   SSLContext sslContext = SSLContext.getInstance("TLSV1"); //使用TLS协议加密
       //          SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
                   sslContext.init(null, tm, new java.security.SecureRandom());
                   // 从上述SSLContext对象中得到SSLSocketFactory对象
                   SSLSocketFactory ssf = sslContext.getSocketFactory();
       
                   URL url = new URL(requestUrl);
                   HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
                   httpUrlConn.setSSLSocketFactory(ssf);
       
                   httpUrlConn.setDoOutput(true);
                   httpUrlConn.setDoInput(true);
                   httpUrlConn.setUseCaches(false);
                   httpUrlConn.setConnectTimeout(connectTimeout);
                   httpUrlConn.setReadTimeout(readTimeout);
                   // 设置请求方式(GET/POST)
                   httpUrlConn.setRequestMethod(requestMethod);
       
                   if(headerMap!=null){
                       for(Map.Entry<String, String> entry : headerMap.entrySet()){
                           httpUrlConn.setRequestProperty(entry.getKey(), entry.getValue());
                       }
                   }
       
                   if ("GET".equalsIgnoreCase(requestMethod)){
                       httpUrlConn.connect();
                   }
       
                   // 当有数据需要提交时
                   if (StringUtils.isNotBlank(outputStr)) {
                       OutputStream outputStream = httpUrlConn.getOutputStream();
                       // 注意编码格式,防止中文乱码
                       outputStream.write(outputStr.getBytes(charset));
                       outputStream.close();
                   }
       
                   // 将返回的输入流转换成字符串
                   InputStream inputStream = httpUrlConn.getInputStream();
                   InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset);
                   BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
       
                   String str = null;
                   while ((str = bufferedReader.readLine()) != null) {
                       buffer.append(str);
                   }
                   bufferedReader.close();
                   inputStreamReader.close();
                   // 释放资源
                   inputStream.close();
                   inputStream = null;
                   httpUrlConn.disconnect();
               } catch (ConnectException ce) {
                   log.error("Weixin server connection timed out.");
               } catch (Exception e) {
                   log.error("https request error:", e);
               }
               return buffer.toString();
           }
       
       
           public static String buildQuery(Map<String, String> params, String charset) throws IOException {
               if (params == null || params.isEmpty()) {
                   return null;
               }
       
               StringBuilder query = new StringBuilder();
               Set<Map.Entry<String, String>> entries = params.entrySet();
               boolean hasParam = false;
       
               for (Map.Entry<String, String> entry : entries) {
                   String name = entry.getKey();
                   String value = entry.getValue();
                   // 忽略参数名或参数值为空的参数
                   if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(value)) {
                       if (hasParam) {
                           query.append("&");
                       } else {
                           hasParam = true;
                       }
       
                       query.append(name).append("=").append(URLEncoder.encode(value, charset));
                   }
               }
       
               return query.toString();
           }
       }
       }
      

    4.XMLUtil.class(解析WxXML响应,将XML字符串转为Map)

     public final class XMLUtil {
     /**
      * 将微信xml格式解析成map
      * @param xmlString
      * @return
      * @throws DocumentException
      */
     public static Map<String, String> parseXmlStr(String xmlString) throws DocumentException {
         //去掉前后的xml标签
         xmlString = xmlString.replaceAll("</?xml>", "");
         System.out.println(xmlString);
         //匹配一段一段这样的数据   <attach><![CDATA[支付测试]]></attach>
         Pattern pattern = Pattern.compile("<.*?/.*?>");
         Matcher matcher = pattern.matcher(xmlString);
         //配置是否包含<![CDATA[CNY]]> CDATA 包裹的数据
         Pattern pattern2 = Pattern.compile("!.*]");
         Map<String, String> map = new HashMap<>();
         while (matcher.find()) {
             //获取键
             String key = matcher.group().replaceAll(".*/", "");
             key = key.substring(0, key.length() - 1);
             Matcher matcher2 = pattern2.matcher(matcher.group());
             String value = matcher.group().replaceAll("</?.*?>", "");
             //获取值
             if (matcher2.find() && !value.equals("DATA")) {
                 value = matcher2.group().replaceAll("!.*\\[", "");
                 value = value.substring(0, value.length() - 2);
             }
             map.put(key, value);
         }
         return map;
     }
    

    }

  3. 业务

     String xmlJson = SignGenerate.buildWxXmlJson("商品号", "金额");
     String result = WeixinWebUtil.doPost(WxConfig.url, xmlJson, "UTF-8", 3000, 3000); // 发送请求
     Map<String, String> resultMap = XMLUtil.parseXmlStr(result); // 解析Wx的相应参数
     String code_url resultMap.get("code_url"); // code_url是Wx响应的扫码网页路径
     // 生成二维码
     ByteArrayOutputStream outputStream = QRCode.from(code_url).to(ImageType.PNG).stream();
     httpResponse.setContentType("image/png");
     httpResponse.setContentLength(outputStream.size());
     OutputStream outStream = httpResponse.getOutputStream();
     outStream.write(outputStream.toByteArray());
     outStream.flush();
     outStream.close();
    
  4. Maven依赖

     <dependency>
         <groupId>com.thoughtworks.xstream</groupId>
         <artifactId>xstream</artifactId>
         <version>${xstream.version}</version>
     </dependency>
     <dependency>
         <groupId>org.codehaus.jettison</groupId>
         <artifactId>jettison</artifactId>
         <version>${jettison.version}</version>
     </dependency>
     <dependency>
         <groupId>com.google.zxing</groupId>
         <artifactId>core</artifactId>
         <version>${zxing.version}</version>
     </dependency>
     <dependency>
         <groupId>net.glxn</groupId>
         <artifactId>qrgen</artifactId>
         <version>${qrgen.version}</version>
     </dependency>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容