Java通过JsApi方式实现微信支付

jerry Java 2016年03月10日 收藏

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

  1. function onBridgeReady(){
  2. WeixinJSBridge.invoke(
  3. 'getBrandWCPayRequest', {
  4. "appId" : "wx2421b1c4370ec43b", //公众号名称,由商户传入
  5. "timeStamp":" 1395712654", //时间戳,自1970年以来的秒数
  6. "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串
  7. "package" : "u802345jgfjsdfgsdg888",
  8. "signType" : "MD5", //微信签名方式:
  9. "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
  10. },
  11. function(res){
  12. if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
  13. }
  14. );
  15. }
  16. if (typeof WeixinJSBridge == "undefined"){
  17. if( document.addEventListener ){
  18. document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
  19. }else if (document.attachEvent){
  20. document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
  21. document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
  22. }
  23. }else{
  24. onBridgeReady();
  25. }
  26.  
  27.  

以上传入的参数package,即为prepay_id

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

  1. package com.unstoppedable.protocol;
  2.  
  3.  
  4.  
  5. import com.unstoppedable.common.Configure;
  6. import com.unstoppedable.common.HttpService;
  7. import com.unstoppedable.common.RandomStringGenerator;
  8. import com.unstoppedable.common.Signature;
  9.  
  10. import java.lang.reflect.Field;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13.  
  14.  
  15. public class UnifiedOrderReqData {
  16.  
  17. private String appid;
  18. private String mch_id;
  19. private String device_info;
  20. private String nonce_str;
  21. private String sign;
  22. private String body;
  23. private String detail;
  24. private String attach;
  25. private String out_trade_no;
  26. private String fee_type;
  27. private int total_fee;
  28. private String spbill_create_ip;
  29. private String time_start;
  30. private String time_expire;
  31. private String goods_tag;
  32. private String notify_url;
  33. private String trade_type;
  34. private String product_id;
  35. private String limit_pay;
  36. private String openid;
  37.  
  38. private UnifiedOrderReqData(UnifiedOrderReqDataBuilder builder) {
  39. this.appid = builder.appid;
  40. this.mch_id = builder.mch_id;
  41. this.device_info = builder.device_info;
  42. this.nonce_str = RandomStringGenerator.getRandomStringByLength(32);
  43. this.body = builder.body;
  44. this.detail = builder.detail;
  45. this.attach = builder.attach;
  46. this.out_trade_no = builder.out_trade_no;
  47. this.fee_type = builder.fee_type;
  48. this.total_fee = builder.total_fee;
  49. this.spbill_create_ip = builder.spbill_create_ip;
  50. this.time_start = builder.time_start;
  51. this.time_expire = builder.time_expire;
  52. this.goods_tag = builder.goods_tag;
  53. this.notify_url = builder.notify_url;
  54. this.trade_type = builder.trade_type;
  55. this.product_id = builder.product_id;
  56. this.limit_pay = builder.limit_pay;
  57. this.openid = builder.openid;
  58. this.sign = Signature.getSign(toMap());
  59. }
  60.  
  61.  
  62. public void setAppid(String appid) {
  63. this.appid = appid;
  64. }
  65.  
  66. public void setMch_id(String mch_id) {
  67. this.mch_id = mch_id;
  68. }
  69.  
  70. public void setDevice_info(String device_info) {
  71. this.device_info = device_info;
  72. }
  73.  
  74. public void setNonce_str(String nonce_str) {
  75. this.nonce_str = nonce_str;
  76. }
  77.  
  78. public void setSign(String sign) {
  79. this.sign = sign;
  80. }
  81.  
  82. public void setBody(String body) {
  83. this.body = body;
  84. }
  85.  
  86. public void setDetail(String detail) {
  87. this.detail = detail;
  88. }
  89.  
  90. public void setAttach(String attach) {
  91. this.attach = attach;
  92. }
  93.  
  94. public void setOut_trade_no(String out_trade_no) {
  95. this.out_trade_no = out_trade_no;
  96. }
  97.  
  98. public void setFee_type(String fee_type) {
  99. this.fee_type = fee_type;
  100. }
  101.  
  102. public void setTotal_fee(int total_fee) {
  103. this.total_fee = total_fee;
  104. }
  105.  
  106. public void setSpbill_create_ip(String spbill_create_ip) {
  107. this.spbill_create_ip = spbill_create_ip;
  108. }
  109.  
  110. public void setTime_start(String time_start) {
  111. this.time_start = time_start;
  112. }
  113.  
  114. public void setTime_expire(String time_expire) {
  115. this.time_expire = time_expire;
  116. }
  117.  
  118. public void setGoods_tag(String goods_tag) {
  119. this.goods_tag = goods_tag;
  120. }
  121.  
  122. public void setNotify_url(String notify_url) {
  123. this.notify_url = notify_url;
  124. }
  125.  
  126. public void setTrade_type(String trade_type) {
  127. this.trade_type = trade_type;
  128. }
  129.  
  130. public void setProduct_id(String product_id) {
  131. this.product_id = product_id;
  132. }
  133.  
  134. public void setLimit_pay(String limit_pay) {
  135. this.limit_pay = limit_pay;
  136. }
  137.  
  138. public void setOpenid(String openid) {
  139. this.openid = openid;
  140. }
  141.  
  142. public Map<String, Object> toMap() {
  143. Map<String, Object> map = new HashMap<String, Object>();
  144. Field[] fields = this.getClass().getDeclaredFields();
  145. for (Field field : fields) {
  146. Object obj;
  147. try {
  148. obj = field.get(this);
  149. if (obj != null) {
  150. map.put(field.getName(), obj);
  151. }
  152. } catch (IllegalArgumentException e) {
  153. e.printStackTrace();
  154. } catch (IllegalAccessException e) {
  155. e.printStackTrace();
  156. }
  157. }
  158. return map;
  159. }
  160.  
  161.  
  162. public static class UnifiedOrderReqDataBuilder {
  163. private String appid;
  164. private String mch_id;
  165. private String device_info;
  166. private String body;
  167. private String detail;
  168. private String attach;
  169. private String out_trade_no;
  170. private String fee_type;
  171. private int total_fee;
  172. private String spbill_create_ip;
  173. private String time_start;
  174. private String time_expire;
  175. private String goods_tag;
  176. private String notify_url;
  177. private String trade_type;
  178. private String product_id;
  179. private String limit_pay;
  180. private String openid;
  181.  
  182. public UnifiedOrderReqDataBuilder(String appid, String mch_id, String body, String out_trade_no, Integer total_fee,
  183. String spbill_create_ip, String notify_url, String trade_type) {
  184. if (appid == null) {
  185. throw new IllegalArgumentException("传入参数appid不能为null");
  186. }
  187. if (mch_id == null) {
  188. throw new IllegalArgumentException("传入参数mch_id不能为null");
  189. }
  190. if (body == null) {
  191. throw new IllegalArgumentException("传入参数body不能为null");
  192. }
  193. if (out_trade_no == null) {
  194. throw new IllegalArgumentException("传入参数out_trade_no不能为null");
  195. }
  196. if (total_fee == null) {
  197. throw new IllegalArgumentException("传入参数total_fee不能为null");
  198. }
  199. if (spbill_create_ip == null) {
  200. throw new IllegalArgumentException("传入参数spbill_create_ip不能为null");
  201. }
  202. if (notify_url == null) {
  203. throw new IllegalArgumentException("传入参数notify_url不能为null");
  204. }
  205. if (trade_type == null) {
  206. throw new IllegalArgumentException("传入参数trade_type不能为null");
  207. }
  208. this.appid = appid;
  209. this.mch_id = mch_id;
  210. this.body = body;
  211. this.out_trade_no = out_trade_no;
  212. this.total_fee = total_fee;
  213. this.spbill_create_ip = spbill_create_ip;
  214. this.notify_url = notify_url;
  215. this.trade_type = trade_type;
  216. }
  217.  
  218. public UnifiedOrderReqDataBuilder setDevice_info(String device_info) {
  219. this.device_info = device_info;
  220. return this;
  221. }
  222.  
  223. public UnifiedOrderReqDataBuilder setDetail(String detail) {
  224. this.detail = detail;
  225. return this;
  226. }
  227.  
  228. public UnifiedOrderReqDataBuilder setAttach(String attach) {
  229. this.attach = attach;
  230. return this;
  231. }
  232.  
  233. public UnifiedOrderReqDataBuilder setFee_type(String fee_type) {
  234. this.fee_type = fee_type;
  235. return this;
  236. }
  237.  
  238. public UnifiedOrderReqDataBuilder setTime_start(String time_start) {
  239. this.time_start = time_start;
  240. return this;
  241. }
  242.  
  243. public UnifiedOrderReqDataBuilder setTime_expire(String time_expire) {
  244. this.time_expire = time_expire;
  245. return this;
  246. }
  247.  
  248. public UnifiedOrderReqDataBuilder setGoods_tag(String goods_tag) {
  249. this.goods_tag = goods_tag;
  250. return this;
  251. }
  252.  
  253. public UnifiedOrderReqDataBuilder setProduct_id(String product_id) {
  254. this.product_id = product_id;
  255. return this;
  256. }
  257.  
  258. public UnifiedOrderReqDataBuilder setLimit_pay(String limit_pay) {
  259. this.limit_pay = limit_pay;
  260. return this;
  261. }
  262.  
  263. public UnifiedOrderReqDataBuilder setOpenid(String openid) {
  264. this.openid = openid;
  265. return this;
  266. }
  267.  
  268.  
  269. public UnifiedOrderReqData build() {
  270.  
  271. if("JSAPI".equals(this.trade_type) && this.openid == null) {
  272. throw new IllegalArgumentException("当传入trade_type为JSAPI时,openid为必填参数");
  273. }
  274. if("NATIVE".equals(this.trade_type) && this.product_id == null) {
  275. throw new IllegalArgumentException("当传入trade_type为NATIVE时,product_id为必填参数");
  276. }
  277. return new UnifiedOrderReqData(this);
  278. }
  279. }
  280.  
  281.  
  282.  
  283. }
  284.  

因为有些参数为必填,有些参数为选填。而且sign要等所有参数传入之后才能计算的出,所以这里用了builder模式。关于builder模式。

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

  1. package com.unstoppedable.common;
  2.  
  3. import com.thoughtworks.xstream.XStream;
  4. import com.thoughtworks.xstream.io.xml.DomDriver;
  5. import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
  6. import org.apache.commons.logging.Log;
  7. import org.apache.commons.logging.LogFactory;
  8. import org.apache.http.HttpEntity;
  9. import org.apache.http.HttpResponse;
  10. import org.apache.http.client.ClientProtocolException;
  11. import org.apache.http.client.ResponseHandler;
  12. import org.apache.http.client.config.RequestConfig;
  13. import org.apache.http.client.methods.HttpGet;
  14. import org.apache.http.client.methods.HttpPost;
  15. import org.apache.http.conn.ConnectTimeoutException;
  16. import org.apache.http.conn.ConnectionPoolTimeoutException;
  17. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  18. import org.apache.http.conn.ssl.SSLContexts;
  19. import org.apache.http.entity.StringEntity;
  20. import org.apache.http.impl.client.CloseableHttpClient;
  21. import org.apache.http.impl.client.HttpClients;
  22. import org.apache.http.util.EntityUtils;
  23.  
  24. import javax.net.ssl.SSLContext;
  25. import java.io.File;
  26. import java.io.FileInputStream;
  27. import java.io.IOException;
  28. import java.net.SocketTimeoutException;
  29. import java.security.KeyStore;
  30.  
  31. /**
  32. * Created by hupeng on 2015/7/28.
  33. */
  34. public class HttpService {
  35. private static Log logger = LogFactory.getLog(HttpService.class);
  36.  
  37. private static CloseableHttpClient httpClient = buildHttpClient();
  38.  
  39. //连接超时时间,默认10秒
  40. private static int socketTimeout = 5000;
  41.  
  42. //传输超时时间,默认30秒
  43. private static int connectTimeout = 5000;
  44.  
  45. private static int requestTimeout = 5000;
  46.  
  47. public static CloseableHttpClient buildHttpClient() {
  48.  
  49. try {
  50. KeyStore keyStore = KeyStore.getInstance("PKCS12");
  51. FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));//加载本地的证书进行https加密传输
  52. try {
  53. keyStore.load(instream, Configure.getCertPassword().toCharArray());//设置证书密码
  54. } finally {
  55. instream.close();
  56. }
  57.  
  58.  
  59. // Trust own CA and all self-signed certs
  60. SSLContext sslcontext = SSLContexts.custom()
  61. .loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray())
  62. .build();
  63. // Allow TLSv1 protocol only
  64. SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
  65. sslcontext,
  66. new String[]{"TLSv1"},
  67. null,
  68. SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
  69.  
  70. RequestConfig requestConfig = RequestConfig.custom()
  71. .setConnectTimeout(connectTimeout)
  72. .setConnectionRequestTimeout(requestTimeout)
  73. .setSocketTimeout(socketTimeout).build();
  74.  
  75. httpClient = HttpClients.custom()
  76. .setDefaultRequestConfig(requestConfig)
  77. .setSSLSocketFactory(sslsf)
  78. .build();
  79.  
  80. return httpClient;
  81. } catch (Exception e) {
  82. throw new RuntimeException("error create httpclient......", e);
  83. }
  84. }
  85.  
  86.  
  87.  
  88. public static String doGet(String requestUrl) throws Exception {
  89. HttpGet httpget = new HttpGet(requestUrl);
  90. try {
  91.  
  92.  
  93. logger.debug("Executing request " + httpget.getRequestLine());
  94. // Create a custom response handler
  95. ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
  96.  
  97. @Override
  98. public String handleResponse(
  99. final HttpResponse response) throws ClientProtocolException, IOException {
  100. int status = response.getStatusLine().getStatusCode();
  101. if (status >= 200 && status < 300) {
  102. HttpEntity entity = response.getEntity();
  103. return entity != null ? EntityUtils.toString(entity) : null;
  104. } else {
  105. throw new ClientProtocolException("Unexpected response status: " + status);
  106. }
  107. }
  108.  
  109. };
  110.  
  111. return httpClient.execute(httpget, responseHandler);
  112. } finally {
  113. httpget.releaseConnection();
  114. }
  115. }
  116.  
  117. public static String doPost(String url, Object object2Xml) {
  118.  
  119. String result = null;
  120.  
  121. HttpPost httpPost = new HttpPost(url);
  122.  
  123. //解决XStream对出现双下划线的bug
  124. XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
  125.  
  126. //将要提交给API的数据对象转换成XML格式数据Post给API
  127. String postDataXML = xStreamForRequestPostData.toXML(object2Xml);
  128.  
  129. logger.info("API,POST过去的数据是:");
  130. logger.info(postDataXML);
  131.  
  132. //得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
  133. StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
  134. httpPost.addHeader("Content-Type", "text/xml");
  135. httpPost.setEntity(postEntity);
  136.  
  137. //设置请求器的配置
  138.  
  139. logger.info("executing request" + httpPost.getRequestLine());
  140.  
  141. try {
  142. HttpResponse response = httpClient.execute(httpPost);
  143.  
  144. HttpEntity entity = response.getEntity();
  145.  
  146. result = EntityUtils.toString(entity, "UTF-8");
  147.  
  148. } catch (ConnectionPoolTimeoutException e) {
  149. logger.error("http get throw ConnectionPoolTimeoutException(wait time out)", e);
  150.  
  151. } catch (ConnectTimeoutException e) {
  152. logger.error("http get throw ConnectTimeoutException", e);
  153.  
  154. } catch (SocketTimeoutException e) {
  155. logger.error("http get throw SocketTimeoutException", e);
  156.  
  157. } catch (Exception e) {
  158. logger.error("http get throw Exception", e);
  159.  
  160. } finally {
  161. httpPost.abort();
  162. }
  163.  
  164. return result;
  165. }
  166. }
  167.  

然后是我们的总入口:

  1. package com.unstoppedable.service;
  2.  
  3. import com.unstoppedable.common.Configure;
  4. import com.unstoppedable.common.HttpService;
  5. import com.unstoppedable.common.XMLParser;
  6. import com.unstoppedable.protocol.UnifiedOrderReqData;
  7. import org.xml.sax.SAXException;
  8.  
  9. import javax.xml.parsers.ParserConfigurationException;
  10. import java.io.IOException;
  11. import java.util.Map;
  12.  
  13. /**
  14. * Created by hupeng on 2015/7/28.
  15. */
  16. public class WxPayApi {
  17.  
  18. public static Map<String,Object> UnifiedOrder(UnifiedOrderReqData reqData) throws IOException, SAXException, ParserConfigurationException {
  19. String res = HttpService.doPost(Configure.UNIFIED_ORDER_API, reqData);
  20. return XMLParser.getMapFromXML(res);
  21. }
  22.  
  23. public static void main(String[] args) throws Exception {
  24. UnifiedOrderReqData reqData = new UnifiedOrderReqData.UnifiedOrderReqDataBuilder("appid", "mch_id", "body", "out_trade_no", 1, "spbill_create_ip", "notify_url", "JSAPI").setOpenid("openid").build();
  25. System.out.println(UnifiedOrder(reqData));
  26.  
  27.  
  28. }
  29. }
  30.  

返回的xml为:

  1. <xml>
  2. <return_code><![CDATA[SUCCESS]]></return_code>
  3. <return_msg><![CDATA[OK]]></return_msg>
  4. <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  5. <mch_id><![CDATA[10000100]]></mch_id>
  6. <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
  7. <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
  8. <result_code><![CDATA[SUCCESS]]></result_code>
  9. <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
  10. <trade_type><![CDATA[JSAPI]]></trade_type>
  11. </xml>

return_code 和result_code都为SUCCESS的时候会返回我们需要的prepay_id。。。,然后在jsapi中使用他就可以了。。

以上就是本文的全部内容,希望对大家的学习有所帮助。