zoukankan      html  css  js  c++  java
  • Java实现JsApi方式的微信支付

    要使用JsApi进行微信支付,首先要从微信获得一个prepay_id,然后通过调用微信的jsapi完成支付,JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
    示例代码如下:

    function onBridgeReady(){
       WeixinJSBridge.invoke(
           'getBrandWCPayRequest', {
               "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
               "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
               "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
               "package" : "u802345jgfjsdfgsdg888",     
               "signType" : "MD5",         //微信签名方式:     
               "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
           },
           function(res){     
               if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
           }
       ); 
    }
    if (typeof WeixinJSBridge == "undefined"){
       if( document.addEventListener ){
           document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
       }else if (document.attachEvent){
           document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
           document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
       }
    }else{
       onBridgeReady();
    }
    

    以上传入的参数package,即为prepay_id
    详细文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7

    下面讲的是获得参数来调用jsapi
    我们调用JSAPI时,必须获得用户的openid,(trade_type=JSAPI,openid为必填参数。)
    首先定义一个请求的对象:

    package com.unstoppedable.protocol;
    
    
    import com.thoughtworks.xstream.annotations.XStreamAlias;
    import com.unstoppedable.common.Configure;
    import com.unstoppedable.common.RandomStringGenerator;
    import com.unstoppedable.common.Signature;
    
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    @XStreamAlias("xml")
    public class UnifiedOrderReqData {
    
        private String appid;
        private String mch_id;
        private String device_info;
        private String nonce_str;
        private String sign;
        private String body;
        private String detail;
        private String attach;
        private String out_trade_no;
        private String fee_type;
        private int total_fee;
        private String spbill_create_ip;
        private String time_start;
        private String time_expire;
        private String goods_tag;
        private String notify_url;
        private String trade_type;
        private String product_id;
        private String limit_pay;
        private String openid;
    
        private UnifiedOrderReqData(UnifiedOrderReqDataBuilder builder) {
            this.appid = builder.appid;
            this.mch_id = builder.mch_id;
            this.device_info = builder.device_info;
            this.nonce_str = RandomStringGenerator.getRandomStringByLength(32);
            this.body = builder.body;
            this.detail = builder.detail;
            this.attach = builder.attach;
            this.out_trade_no = builder.out_trade_no;
            this.fee_type = builder.fee_type;
            this.total_fee = builder.total_fee;
            this.spbill_create_ip = builder.spbill_create_ip;
            this.time_start = builder.time_start;
            this.time_expire = builder.time_expire;
            this.goods_tag = builder.goods_tag;
            this.notify_url = builder.notify_url;
            this.trade_type = builder.trade_type;
            this.product_id = builder.product_id;
            this.limit_pay = builder.limit_pay;
            this.openid = builder.openid;
            this.sign = Signature.getSign(toMap());
        }
    
        public String getAppid() {
            return appid;
        }
    
        public String getMch_id() {
            return mch_id;
        }
    
        public String getDevice_info() {
            return device_info;
        }
    
        public String getNonce_str() {
            return nonce_str;
        }
    
        public String getSign() {
            return sign;
        }
    
        public String getBody() {
            return body;
        }
    
        public String getDetail() {
            return detail;
        }
    
        public String getAttach() {
            return attach;
        }
    
        public String getOut_trade_no() {
            return out_trade_no;
        }
    
        public String getFee_type() {
            return fee_type;
        }
    
        public int getTotal_fee() {
            return total_fee;
        }
    
        public String getSpbill_create_ip() {
            return spbill_create_ip;
        }
    
        public String getTime_start() {
            return time_start;
        }
    
        public String getTime_expire() {
            return time_expire;
        }
    
        public String getGoods_tag() {
            return goods_tag;
        }
    
        public String getNotify_url() {
            return notify_url;
        }
    
        public String getTrade_type() {
            return trade_type;
        }
    
        public String getProduct_id() {
            return product_id;
        }
    
        public String getLimit_pay() {
            return limit_pay;
        }
    
        public String getOpenid() {
            return openid;
        }
    
        public Map<String, Object> toMap() {
            Map<String, Object> map = new HashMap<String, Object>();
            Field[] fields = this.getClass().getDeclaredFields();
            for (Field field : fields) {
                Object obj;
                try {
                    obj = field.get(this);
                    if (obj != null) {
                        map.put(field.getName(), obj);
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return map;
        }
    
    
        public static class UnifiedOrderReqDataBuilder {
            private String appid;
            private String mch_id;
            private String device_info;
            private String body;
            private String detail;
            private String attach;
            private String out_trade_no;
            private String fee_type;
            private int total_fee;
            private String spbill_create_ip;
            private String time_start;
            private String time_expire;
            private String goods_tag;
            private String notify_url;
            private String trade_type;
            private String product_id;
            private String limit_pay;
            private String openid;
    
            /**
             * 使用配置中的 appid 和  mch_id
             *
             * @param body
             * @param out_trade_no
             * @param total_fee
             * @param spbill_create_ip
             * @param notify_url
             * @param trade_type
             */
            public UnifiedOrderReqDataBuilder(String body, String out_trade_no, Integer total_fee,
                                              String spbill_create_ip, String notify_url, String trade_type) {
    
                this(Configure.getAppid(), Configure.getMchid(), body, out_trade_no, total_fee,
                        spbill_create_ip, notify_url, trade_type);
    
            }
    
            public UnifiedOrderReqDataBuilder(String appid, String mch_id, String body, String out_trade_no, Integer total_fee,
                                              String spbill_create_ip, String notify_url, String trade_type) {
                if (appid == null) {
                    throw new IllegalArgumentException("传入参数appid不能为null");
                }
                if (mch_id == null) {
                    throw new IllegalArgumentException("传入参数mch_id不能为null");
                }
                if (body == null) {
                    throw new IllegalArgumentException("传入参数body不能为null");
                }
                if (out_trade_no == null) {
                    throw new IllegalArgumentException("传入参数out_trade_no不能为null");
                }
                if (total_fee == null) {
                    throw new IllegalArgumentException("传入参数total_fee不能为null");
                }
                if (spbill_create_ip == null) {
                    throw new IllegalArgumentException("传入参数spbill_create_ip不能为null");
                }
                if (notify_url == null) {
                    throw new IllegalArgumentException("传入参数notify_url不能为null");
                }
                if (trade_type == null) {
                    throw new IllegalArgumentException("传入参数trade_type不能为null");
                }
                this.appid = appid;
                this.mch_id = mch_id;
                this.body = body;
                this.out_trade_no = out_trade_no;
                this.total_fee = total_fee;
                this.spbill_create_ip = spbill_create_ip;
                this.notify_url = notify_url;
                this.trade_type = trade_type;
            }
    
            public UnifiedOrderReqDataBuilder setDevice_info(String device_info) {
                this.device_info = device_info;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setDetail(String detail) {
                this.detail = detail;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setAttach(String attach) {
                this.attach = attach;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setFee_type(String fee_type) {
                this.fee_type = fee_type;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setTime_start(String time_start) {
                this.time_start = time_start;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setTime_expire(String time_expire) {
                this.time_expire = time_expire;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setGoods_tag(String goods_tag) {
                this.goods_tag = goods_tag;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setProduct_id(String product_id) {
                this.product_id = product_id;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setLimit_pay(String limit_pay) {
                this.limit_pay = limit_pay;
                return this;
            }
    
            public UnifiedOrderReqDataBuilder setOpenid(String openid) {
                this.openid = openid;
                return this;
            }
    
    
            public UnifiedOrderReqData build() {
    
                if ("JSAPI".equals(this.trade_type) && this.openid == null) {
                    throw new IllegalArgumentException("当传入trade_type为JSAPI时,openid为必填参数");
                }
                if ("NATIVE".equals(this.trade_type) && this.product_id == null) {
                    throw new IllegalArgumentException("当传入trade_type为NATIVE时,product_id为必填参数");
                }
                return new UnifiedOrderReqData(this);
            }
        }
    
    
    }
    
    
    

    因为有些参数为必填,有些参数为选填。而且sign要等所有参数传入之后才能计算的出,所以这里用了builder模式。关于builder模式。关于每个参数的定义,参考说明文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

    我们选用httpclient进行网络传输。

    package com.unstoppedable.common;
    
    import com.thoughtworks.xstream.XStream;
    import com.thoughtworks.xstream.io.xml.DomDriver;
    import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.ResponseHandler;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.conn.ConnectTimeoutException;
    import org.apache.http.conn.ConnectionPoolTimeoutException;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLContexts;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import javax.net.ssl.SSLContext;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.SocketTimeoutException;
    import java.security.KeyStore;
    
    /**
     * Created by hupeng on 2015/7/28.
     */
    public class HttpService {
        private static Log logger = LogFactory.getLog(HttpService.class);
    
        private static CloseableHttpClient httpClient = buildHttpClient();
    
        //连接超时时间,默认10秒
        private static int socketTimeout = 5000;
    
        //传输超时时间,默认30秒
        private static int connectTimeout = 5000;
    
        private static int requestTimeout = 5000;
    
        public static CloseableHttpClient buildHttpClient() {
    
            try {
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));//加载本地的证书进行https加密传输
                try {
                    keyStore.load(instream, Configure.getCertPassword().toCharArray());//设置证书密码
                } finally {
                    instream.close();
                }
    
    
                // Trust own CA and all self-signed certs
                SSLContext sslcontext = SSLContexts.custom()
                        .loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray())
                        .build();
                // Allow TLSv1 protocol only
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslcontext,
                        new String[]{"TLSv1"},
                        null,
                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
    
                RequestConfig requestConfig = RequestConfig.custom()
                        .setConnectTimeout(connectTimeout)
                        .setConnectionRequestTimeout(requestTimeout)
                        .setSocketTimeout(socketTimeout).build();
    
                httpClient = HttpClients.custom()
                        .setDefaultRequestConfig(requestConfig)
                        .setSSLSocketFactory(sslsf)
                        .build();
    
                return httpClient;
            } catch (Exception e) {
                throw new RuntimeException("error create httpclient......", e);
            }
        }
    
    
    
        public static String doGet(String requestUrl) throws Exception {
            HttpGet httpget = new HttpGet(requestUrl);
            try {
    
    
                logger.debug("Executing request " + httpget.getRequestLine());
                // Create a custom response handler
                ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
    
                    @Override
                    public String handleResponse(
                            final HttpResponse response) throws ClientProtocolException, IOException {
                        int status = response.getStatusLine().getStatusCode();
                        if (status >= 200 && status < 300) {
                            HttpEntity entity = response.getEntity();
                            return entity != null ? EntityUtils.toString(entity) : null;
                        } else {
                            throw new ClientProtocolException("Unexpected response status: " + status);
                        }
                    }
    
                };
    
                return httpClient.execute(httpget, responseHandler);
            } finally {
                httpget.releaseConnection();
            }
        }
    
        public static String doPost(String url, Object object2Xml) {
    
            String result = null;
    
            HttpPost httpPost = new HttpPost(url);
    
            //解决XStream对出现双下划线的bug
            XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
    
            //将要提交给API的数据对象转换成XML格式数据Post给API
            String postDataXML = xStreamForRequestPostData.toXML(object2Xml);
    
            logger.info("API,POST过去的数据是:");
            logger.info(postDataXML);
    
            //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
            StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
            httpPost.addHeader("Content-Type", "text/xml");
            httpPost.setEntity(postEntity);
    
            //设置请求器的配置
    
            logger.info("executing request" + httpPost.getRequestLine());
    
            try {
                HttpResponse response = httpClient.execute(httpPost);
    
                HttpEntity entity = response.getEntity();
    
                result = EntityUtils.toString(entity, "UTF-8");
    
            } catch (ConnectionPoolTimeoutException e) {
                logger.error("http get throw ConnectionPoolTimeoutException(wait time out)", e);
    
            } catch (ConnectTimeoutException e) {
                logger.error("http get throw ConnectTimeoutException", e);
    
            } catch (SocketTimeoutException e) {
                logger.error("http get throw SocketTimeoutException", e);
    
            } catch (Exception e) {
                logger.error("http get throw Exception", e);
    
            } finally {
                httpPost.abort();
            }
    
            return result;
        }
    }
    
    

    然后是我们的总入口:

    package com.unstoppedable.service;
    
    import com.unstoppedable.common.Configure;
    import com.unstoppedable.common.HttpService;
    import com.unstoppedable.common.XMLParser;
    import com.unstoppedable.protocol.UnifiedOrderReqData;
    import org.xml.sax.SAXException;
    
    import javax.xml.parsers.ParserConfigurationException;
    import java.io.IOException;
    import java.util.Map;
    
    /**
     * Created by hupeng on 2015/7/28.
     */
    public class WxPayApi {
    
        public static Map<String,Object> UnifiedOrder(UnifiedOrderReqData reqData) throws IOException, SAXException, ParserConfigurationException {
            String res  = HttpService.doPost(Configure.UNIFIED_ORDER_API, reqData);
            return XMLParser.getMapFromXML(res);
        }
    
        public static void main(String[] args) throws Exception {
            UnifiedOrderReqData reqData = new UnifiedOrderReqData.UnifiedOrderReqDataBuilder("appid", "mch_id", "body", "out_trade_no", 1, "spbill_create_ip", "notify_url", "JSAPI").setOpenid("openid").build();
            System.out.println(UnifiedOrder(reqData));
    
    
        }
    }
    

    返回的xml为:

    <xml>
       <return_code><![CDATA[SUCCESS]]></return_code>
       <return_msg><![CDATA[OK]]></return_msg>
       <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
       <mch_id><![CDATA[10000100]]></mch_id>
       <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
       <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
       <result_code><![CDATA[SUCCESS]]></result_code>
       <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
       <trade_type><![CDATA[JSAPI]]></trade_type>
    </xml>
    

    return_code 和result_code都为SUCCESS的时候会返回我们需要的prepay_id。。。

    然后在jsapi中使用他就可以了。。

    代码托管在https://github.com/hupengcool/wxpay_jsapi ,欢迎指正

  • 相关阅读:
    第二阶段小组站立会议-4-26
    小组站立会议-2014-04-25张永组
    第二阶段小组站立会议-2014-04-24
    小组站立会议-张永组-4-23
    第二阶段会议-阶段目标-张永组
    小组项目第二阶段会议--2014420
    团队下阶段任务分配会议记录-张永组
    Floaty Fish(内测版)发布前一天-------张永组
    电梯调度设计之初感想——蔡迎盈&&曹玉松
    单元测试——我的认识
  • 原文地址:https://www.cnblogs.com/hupengcool/p/4684551.html
Copyright © 2011-2022 走看看