微信商户支付

  先吐槽下微信,完全就是个坑,文档确实有,但是个别细节没有提,有些得自己试。

  下面总结下微信商户支付的开发,用处一般是用户从商户提现。

  首先,要获取提现用户的openId,openId是用户对于公众号的唯一标识,openId是用户在公众 号中的唯一标识,一个openId只属于一个公众号。openId在用户关注公众号之类的活动中可以得到。

  接下来按照微信的文档,准备需要提交的参数:

  

        Map<String, Object> paras = new HashMap<>();
        paras.put("mch_appid" , "*******");
    ......    

  然后是根据参数生成签名:

  生成前要对参数根据参数名的ascii码顺序排序:

  

        List<Map.Entry<String, Object>> list = new ArrayList<>(paras.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Object>>() {
            public int compare(Map.Entry<String, Object> kv1, Map.Entry<String, Object> kv2) {
                return kv1.getKey().compareTo(kv2.getKey());
            }
        });    

  生成签名:

    private String getSign(List<Map.Entry<String, Object>> list) {
        StringBuffer para = new StringBuffer();

        if (!list.isEmpty()) {
            for (Map.Entry<String, Object> one : list) {
                String value = String.valueOf(one.getValue());
                if (isNotEmpty(value)){
                    para.append("&")
                            .append(one.getKey())
                            .append("=")
                            .append(one.getValue());
                }
            }
            para.deleteCharAt(0);
            para.append("&key=" + WXConstants.SECRET_KEY);
        }

        return Md5Util.encodeUpper(para.toString());
    }

  注意:所有参数后需要加入秘钥,并且key一定要放在最后,秘钥在商户后台的API安全中设置

  然后要以同样的顺序生成xml格式的参数,这个参数里要加入签名sign,key是不需要加进来的。

  

    private String getWXPara(List<Map.Entry<String, Object>> list){
        StringBuffer para = new StringBuffer();
        para.append("<xml>");

        for (Map.Entry<String, Object> one : list){
            para.append(String.format("<%s>", one.getKey()))
                    .append(String.format("<![CDATA[%s]]>", one.getValue()))
                    .append(String.format("</%s>", one.getKey()));
        }

        para.append("</xml>");
        return para.toString();
    }

  有了参数就可以通过微信提供的接口提交数据了,提交时是需要证书的,证书在商户平台API安全中下载。

  带着证书发送:

        public String post(String para, String pathCA, String key) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
        log.info("wx.post, {} : request {} & para : {}\n", this.name, this.url, para);
        StringEntity entity = new StringEntity(para, EscapeUtil.CHARSET_UTF8);
        HttpPost post = new HttpPost(url);
        post.setEntity(entity);

        CloseableHttpClient client = getSSLClient(pathCA, key);
        CloseableHttpResponse response = client.execute(post);

        int httpCode = response.getStatusLine().getStatusCode();
        byte[] bs = EntityUtils.toByteArray(response.getEntity());
        String result = new String(bs, EscapeUtil.CHARSET_UTF8);
        if (httpCode != 200){
            log.warn("wx.post.httpCodeErr, {} : return {} & httpCode : {} & content : {} & para : {}\n", this.name, httpCode, result, this.url, para);
        }
        log.info("wx.post, {} : return {} & para : {}\n", this.name, result, para);
        return content;
    }
    private CloseableHttpClient getSSLClient(final String pathCA, final String key) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
        KeyStore keyStore  = KeyStore.getInstance(WXConstants.CERT_TYPE);
        FileInputStream instream = new FileInputStream(new File(pathCA));try {
            keyStore.load(is, key.toCharArray());
        } finally {
            is.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, key.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { WXConstants.SSL_PROTOCOL },
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        return httpclient;
    }

  如果人品还过得去,一般就可以了,不过还有一个小坑,微信文档上说返回成功的时候返回值中会有sign,不过实际上其实是没有的。。。

 

posted @ 2015-05-19 16:38  draculav  阅读(821)  评论(0编辑  收藏  举报