你的位置:首页 > Java教程

[Java教程]Java实现JsApi方式的微信支付


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

functiononBridgeReady(){  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(")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;  privateUnifiedOrderReqData(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;  }  publicintgetTotal_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     */    publicUnifiedOrderReqDataBuilder(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);    }    publicUnifiedOrderReqDataBuilder(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.import com.thoughtworks.xstream.io.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;  publicstatic 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);    }  }  publicstatic 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();    }  }  publicstatic String doPost(String url, Object object2 {    String result = null;    HttpPost httpPost = new HttpPost(url);    //解决XStream对出现双下划线的bug    XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new ("-_", "_")));    //将要提交给API的数据对象转换成    String postDatato(object2info("API,POST过去的数据是:");    logger.info(postData//得指明使用UTF-8编码,否则到API服务器    StringEntity postEntity = new StringEntity(postData"UTF-8");    httpPost.addHeader("Content-Type", "text/);    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.import com.unstoppedable.protocol.UnifiedOrderReqData;import org.import javax.import java.io.IOException;import java.util.Map;/** * Created by hupeng on 2015/7/28. */public class WxPayApi {  publicstatic Map<String,Object> UnifiedOrder(UnifiedOrderReqData reqData) throws IOException, SAXException, ParserConfigurationException {    String res = HttpService.doPost(Configure.UNIFIED_ORDER_API, reqData);    return getMapFrom(res);  }  publicstaticvoidmain(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));  }}

返回的

<>  <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></>

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

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

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