公众号支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。应用场景有:

  1. ◆ 用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付
  2. ◆ 用户的好友在朋友圈、聊天窗口等分享商家页面连接,用户点击链接打开商家页面,完成支付
  3. ◆ 将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付
  4. 项目描述:本系统采用SpringMVC 、AngularJs框架进行开发,开发的系统依托于微信,即在微信公众账号内进入商家公众号,并打开相应的支付页面,完成支付。因此需要采用公众号支付。
项目开发流程:
一、微信网页授权,即获取code
    首先前端通过url转到login方法,并携带相应的参数如PayVO类,如totalFee等。而后调用getOauthConnectUrl方法获得拼接的地址,并把拼接后的地址传给前端,在前端转向改链接。
    1.拼接的地址传给前端触发,原因不清楚,后端触发不了,报“跨域”的错误
    2.只能在微信中打开才能获得code,并且打开连接的微信账号必须关注相应的公众号才可以获得
    3. 获得code的url如下图,可以看到有两种方式。应用授权作用域scope取snsapi_base 时,不弹出授权页面,直接跳转,只能获取用户openid;当scope取snsapi_userinfo 时弹出授权页面,可通过openid拿到昵称、性别、所在地,即使在未关注的情况下,只要用户授权,也能获取其信息。
    /*** 获得用户的code,并转向支付页面(pay/pay)* @return*/@RequestMapping("/login")@ResponseBodypublic JsonApi login(PayVO payVO) {LogUtils.trace("--------------------/pay/login-------------------------------");String oauthUrl;                 //重定向链接oauthUrl = PayUtil.getOauthConnectUrl(WechatConts.OauthScope.BASE, payVO.getTotalFee());LogUtils.trace("oauthUrl:" + oauthUrl);Map<String, String> map = new HashMap<String, String>() ;map.put("url", oauthUrl);return new JsonApi(map);}
public class PayVO {private String totalFee;public String getTotalFee() {return totalFee;}public void setTotalFee(String totalFee) {this.totalFee = totalFee;}
}

/*** 获得授权URL* @param scope* @param state* @return*/public static String getOauthConnectUrl(WechatConts.OauthScope scope, String state) {/*** 获得访问授权所需要的信息* 如:appId, Redirect_uri, scope*/String appId = WxConfigure.AppId;String redirectUrl = "http://" + WxConfigure.Redirect_uri;String connectUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";String oauthScope = scope.getScope();if (state.length() > 100) {LogUtils.info("stateUrl too Long:" + state);state = state.substring(0, 100);}String stateUrl = UrlUtil.urlEncode(state);if (stateUrl.length() >= 128) {//微信允许的最大state长度是128位LogUtils.info("stateUrl too Long" + stateUrl);stateUrl = UrlUtil.urlEncode(state.substring(0, 80));}
<pre name="code" class="java">public class WxOauthAccessToken {int errCode;String errMsg;String accessToken; //公众号的唯一标识int expiresIn;String refreshToken;String openId;String scope;public WxOauthAccessToken(ErrorMng.ErrorCode errorCode) {this.errCode = errorCode.getErrorID();this.errMsg = errorCode.getErrorMsg();}public WxOauthAccessToken(JSONObject jsonObject) {if (jsonObject.getInteger("errcode") == null) {this.accessToken = (String) jsonObject.get("access_token");this.expiresIn = (int) jsonObject.get("expires_in");this.refreshToken = (String) jsonObject.get("refresh_token");this.openId = (String) jsonObject.get("openid");this.scope = (String) jsonObject.get("scope");} else {this.errCode = jsonObject.getInteger("errcode");this.errMsg = jsonObject.getString("errmsg");}}

return connectUrl.replace("APPID", appId) .replace("REDIRECT_URI", UrlUtil.urlEncode(redirectUrl)) .replace("SCOPE", oauthScope) .replace("STATE", stateUrl); }

二、通过code换取网页授权access_token,继而获得openid
     第一步中的redirect_url指向下面的地址,当成功时会携带code和相应的参数跳转到该方法下,在该方法里调用getOauthAccessToken方法获得相应的内容,如WxOauthAccessToken类,get和set方法隐藏掉了。
public class WxOauthAccessToken {int errCode;String errMsg;String accessToken; //公众号的唯一标识int expiresIn;String refreshToken;String openId;String scope;public WxOauthAccessToken(ErrorMng.ErrorCode errorCode) {this.errCode = errorCode.getErrorID();this.errMsg = errorCode.getErrorMsg();}public WxOauthAccessToken(JSONObject jsonObject) {if (jsonObject.getInteger("errcode") == null) {this.accessToken = (String) jsonObject.get("access_token");this.expiresIn = (int) jsonObject.get("expires_in");this.refreshToken = (String) jsonObject.get("refresh_token");this.openId = (String) jsonObject.get("openid");this.scope = (String) jsonObject.get("scope");} else {this.errCode = jsonObject.getInteger("errcode");this.errMsg = jsonObject.getString("errmsg");}}

    /*** 付款页面* @param request* @param response* @return* @throws Exception*/@RequestMapping("/callback")public String pay(HttpServletRequest request, HttpServletResponse response) throws Exception {LogUtils.trace("-----------------------/pay/callback/--------------------------------");/*** 获得code和state字段*/String code = request.getParameter("code");String state = request.getParameter("state");int total = (int)(Float.parseFloat(state)*100);/*** 获取openId*/WxOauthAccessToken oauthAccessToken = PayUtil.getOauthAccessToken(code);String openId = oauthAccessToken.getOpenId();
    /*** 需要第一步引导获得用户的code,才能拿到该用户的accessToken* @param code* @return*/public static WxOauthAccessToken getOauthAccessToken(String code) {String oauthUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";try {JSONObject oauthResult = JsonApi.getJson(oauthUrl.replace("APPID", AccessInfo.AppId.VALUE.getAppId()).replace("SECRET", AccessInfo.AppSecret.VALUE.getAppSecret()).replace("CODE", code));return new WxOauthAccessToken(oauthResult);} catch (IOException e) {LogUtils.error("Wechat Get Oauth AccessToken Fail" + e.getStackTrace());return new WxOauthAccessToken(ErrorMng.ErrorCode.WECHAT_API_INVOKE_FAIL);}}

三、执行统一下单接口
     首先初始化统一接口需要的各个必须参数(公众号支付统一下单接口),而后调用JsApiReqUtils类的httpsRequest方法请求微信的统一下单接口。其次将返回的值根据前端JsAPi需要的值(发起一个微信支付请求)进行封装并返回给前端,此时是采取的是url带值的方式。
  /*** 付款页面* @param request* @param response* @return* @throws Exception*/@RequestMapping("/callback")public String pay(HttpServletRequest request, HttpServletResponse response) throws Exception {LogUtils.trace("-----------------------/pay/callback/--------------------------------");/*** 获得code和state字段*/String code = request.getParameter("code");String state = request.getParameter("state");int total = (int)(Float.parseFloat(state)*100);/*** 获取openId*/WxOauthAccessToken oauthAccessToken = PayUtil.getOauthAccessToken(code);String openId = oauthAccessToken.getOpenId();/*** 执行统一下单接口,初始化各参数*/Date now = new Date();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");String outTradeNo = "NO" + dateFormat.format( now );String body = "测试";String nonceStr = StringUtil.generateRandomString(16);
//        String spBillCreateIP = ReqUtils.getRealIp(request);String spBillCreateIP = "127.0.0.1";//初始化传入参数JsApiReqData jsApiReqData =new JsApiReqData(body,nonceStr,outTradeNo,total,spBillCreateIP, openId);String info = MobiMessage.JsApiReqData2xml(jsApiReqData).replaceAll("__", "_");LogUtils.trace(info);String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";StringBuffer sb = JsApiReqUtils.httpsRequest(url, "POST", info);if (!"".equals(sb.toString())) {/*** 得到预支付ID,即prepay_id*/Map<String, String> getMap = MobiMessage.parseXml(new String(sb.toString().getBytes(), "utf-8"));LogUtils.trace(getMap);String prepay_id = getMap.get("prepay_id");LogUtils.trace(prepay_id);// 生成支付签名,这个签名给微信支付的调用使用Integer timeStamp = DateUtils.getCurrentTimestamp();JsApiToJsData jsApiToJsData = new JsApiToJsData(timeStamp, nonceStr,prepay_id);String redirectUrl = "/#pay?appId=APPID&timeStamp=TIMESTAMP&nonceStr=NONCESTR&prepay_id=PREPAYID&signType=MD5&paySign=SIGN";redirectUrl = redirectUrl.replace("APPID", jsApiToJsData.getAppid()).replace("TIMESTAMP", jsApiToJsData.getTimeStamp() + "").replace("NONCESTR", jsApiToJsData.getNonce_str()).replace("PREPAYID", jsApiToJsData.getPrepay_id()).replace("SIGN", jsApiToJsData.getSign());LogUtils.trace(redirectUrl);return "redirect:" + redirectUrl ;} else {LogUtils.trace("统一下单失败!");return "";}}
/*** 请求公众号支付API需要提交的数据* @author Created by fenghui.* @date Created on 2016/09/06/9:28**/
public class JsApiReqData {//每个字段具体的意思请查看API文档private String appid = "";private String mch_id = "";private String body = "";private String nonce_str = "";private String sign = "";private String notify_url = "";private String out_trade_no = "";private String spbill_create_ip = "";private Integer total_fee = 0;private String trade_type = "";private String openid = "";/*** @param body   要支付的商品的描述信息,用户会在支付成功页面里看到这个信息* @param nonceStr  随机字符串,不长于32 位* @param outTradeNo  商户系统内部的订单号,32个字符内可包含字母, 确保在商户系统唯一* @param totalFee   订单总金额,单位为“分”,只能整数* @param spBillCreateIP  订单生成的机器IP* @param openid  用户标识,trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。*/public JsApiReqData(String body,String nonceStr,String outTradeNo,Integer totalFee,String spBillCreateIP,String openid){//微信分配的公众号ID(开通公众号之后可以获取到)setAppid(WxConfigure.AppId);//微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)setMch_id(WxConfigure.Mch_id);//接收微信支付异步通知后的回调地址setNotify_url(WxConfigure.Notify_url);//交易类型,取值如下:JSAPI,NATIVE,APP,setTrade_type(WxConfigure.Trade_type);//要支付的商品的描述信息,用户会在支付成功页面里看到这个信息setBody(body);//商户系统内部的订单号,32个字符内可包含字母, 确保在商户系统唯一setOut_trade_no(outTradeNo);//订单总金额,单位为“分”,只能整数setTotal_fee(totalFee);//订单生成的机器IPsetSpbill_create_ip(spBillCreateIP);//随机字符串,不长于32 位setNonce_str(nonceStr);//用户标识,trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。setOpenid(openid);//根据API给的签名规则进行签名SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();parameters.put("appid", appid);parameters.put("mch_id", mch_id);parameters.put("nonce_str", nonce_str);parameters.put("body", body);parameters.put("out_trade_no", out_trade_no);parameters.put("notify_url", notify_url);parameters.put("total_fee", total_fee);parameters.put("spbill_create_ip", spbill_create_ip);parameters.put("trade_type", trade_type);parameters.put("openid", openid);String sign = DictionarySort.createSign(parameters);//根据给的签名规则进行签名setSign(sign);//把签名数据设置到Sign这个属性中}
public class JsApiToJsData {//每个字段具体的意思请查看API文档private String appid = "";private Integer timeStamp = 0;private String nonce_str = "";private String sign = "";private String prepay_id = "";private String signType = "";/*** @param nonceStr  随机字符串,不长于32 位*/public JsApiToJsData(Integer timeStamp,String nonceStr,String prepay_id){//微信分配的公众号ID(开通公众号之后可以获取到)setAppid(WxConfigure.AppId);setTimeStamp(timeStamp);setNonce_str(nonceStr);setPrepay_id(prepay_id);setSignType("MD5");//根据API给的签名规则进行签名SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();parameters.put("appId", appid);parameters.put("timeStamp", timeStamp);parameters.put("nonceStr", nonceStr);parameters.put("package", "prepay_id=" + prepay_id);parameters.put("signType",signType);//根据给的签名规则进行签名String sign = DictionarySort.createSign(parameters);setSign(sign);//把签名数据设置到Sign这个属性中}

类StringUtil主要用于产生随机字符串。

public class StringUtil {public static String generateRandomString() {return generateRandomString(16);}public static String generateRandomString(int length) {String seekChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";int seekLength = seekChars.length();String str = "";for (int i = 0; i < length; i++) {str += seekChars.charAt((int) (Math.random() * seekLength));}return str;}public static String generateRandomNumber() {return generateRandomNumber(6);}public static String generateRandomNumber(int length) {String seekChars = "0123456789";int seekLength = seekChars.length();String str = "";for (int i = 0; i < length; i++) {str += seekChars.charAt((int) (Math.random() * seekLength));}return str;}
}

类JsApiReqUtils用于获得 网页支付提交用户端ip和触发http请求。

public class JsApiReqUtils {//获取真实IPpublic static String getRealIp(HttpServletRequest request) {if (request == null) {LogUtils.error("getRealIp request null");return "0.0.0.0";}String realIp = request.getHeader("X-Real-IP");LogUtils.trace("realIp:" + realIp);if (realIp != null && realIp.length() > 0) {return realIp;}realIp = request.getHeader("X-Forwarded-For");LogUtils.trace("realIp2:" + realIp);if (realIp != null && realIp.length() > 0) {return realIp;}return request.getRemoteAddr();}public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output)throws Exception {HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();//加入数据conn.setRequestMethod(requestMethod);conn.setDoOutput(true);BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());buffOutStr.write(output.getBytes("utf-8"));buffOutStr.flush();buffOutStr.close();//获取输入流BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line = null;StringBuffer sb = new StringBuffer();while((line = reader.readLine())!= null){sb.append(line);}return sb;}}
    MobiMessage类主要用于把json格式的数据转换为微信需要的xml格式的数据,并把返回值从xml格式解析为map格式。
public class MobiMessage {public static Map<String,String> xml2map(HttpServletRequest request) throws IOException, DocumentException {Map<String,String> map = new HashMap<String, String>();SAXReader reader = new SAXReader();InputStream inputStream = request.getInputStream();Document document = reader.read(inputStream);Element root = document.getRootElement();List<Element> list = root.elements();for(Element e:list){map.put(e.getName(), e.getText());}inputStream.close();return map;}//订单转换成xmlpublic static String JsApiReqData2xml(JsApiReqData jsApiReqData){/*XStream xStream = new XStream();xStream.alias("xml",productInfo.getClass());return xStream.toXML(productInfo);*/MobiMessage.xstream.alias("xml",jsApiReqData.getClass());return MobiMessage.xstream.toXML(jsApiReqData);}public static String RefundReqData2xml(RefundReqData refundReqData){/*XStream xStream = new XStream();xStream.alias("xml",productInfo.getClass());return xStream.toXML(productInfo);*/MobiMessage.xstream.alias("xml",refundReqData.getClass());return MobiMessage.xstream.toXML(refundReqData);}public static String class2xml(Object object){return "";}public static Map<String, String> parseXml(String xml) throws Exception {Map<String, String> map = new HashMap<String, String>();Document document = DocumentHelper.parseText(xml);Element root = document.getRootElement();List<Element> elementList = root.elements();for (Element e : elementList)map.put(e.getName(), e.getText());return map;}//扩展xstream,使其支持CDATA块private static XStream xstream = new XStream(new XppDriver() {public HierarchicalStreamWriter createWriter(Writer out) {return new PrettyPrintWriter(out) {// 对所有xml节点的转换都增加CDATA标记boolean cdata = true;//@SuppressWarnings("unchecked")public void startNode(String name, Class clazz) {super.startNode(name, clazz);}protected void writeText(QuickWriter writer, String text) {if (cdata) {writer.write("<![CDATA[");writer.write(text);writer.write("]]>");} else {writer.write(text);}}};}});}
DateUtils类主要用于获得当前的时间戳。
public class DateUtils {public static int minTimestamp = 0; //最小的时间戳public static int maxTimestamp = 1999999999;//最大的时间戳public static int offset = 0;/*** 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串** @param date* @return yyyy-MM-dd HH:mm:ss*/public static String date2Str(Date date) {return date2Str(date, "yyyy-MM-dd HH:mm:ss");}/*** 按照参数format的格式,日期转字符串** @param date* @param format* @return*/public static String date2Str(Date date, String format) {if (date != null) {SimpleDateFormat sdf = new SimpleDateFormat(format);return sdf.format(date);} else {return "";}}public static int getCurrentTimestamp() {return (int) (System.currentTimeMillis() / 1000 + offset);}public static boolean setCurrentTimestamp(int timestamp) {int systemTimestamp = (int) (System.currentTimeMillis() / 1000);offset = timestamp - systemTimestamp;return true;}
}

类DictionarySort主要根据微信通过的签名规则用于签名,其中key设置路径:微信商户平台-->账户中心-->API安全-->API密钥-->设置API密钥。
public class DictionarySort {/*** 签名算法* @param parameters* @return*/public static String createSign(SortedMap<Object, Object> parameters) {StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)Iterator it = es.iterator();String key = "1082ae0cd40043df9c4531b597970646";while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();Object v = entry.getValue();if (null != v && !"".equals(v)&& !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + key);LogUtils.trace(sb.toString());String sign = CryptoUtils.MD5(sb.toString()).toUpperCase();return sign;}}

四、汇款后的通知页面

统一下单的参数notify_url决定了付款后的通知地址,应该把起设为改地址,如
//付款成功后的通知地址public static final String Notify_url = "***/pay/notification";
注意:此时微信会重复发送通知给应用,因此此时接受通知业务的逻辑必须能够识别是否为第一次通知,不然会重复处理接到的通知。官方文档上说可以给微信返回处理成功的通知,但经过多次试验都没能够解决该问题。因此最后采取在业务逻辑上处理通知,即第一次通知进行处理,其余的通知一律忽视。
 /*** 付款后通知跳轉頁面* @param request* @param response* @return* @throws IOException* @throws DocumentException*/@RequestMapping("/notification")@ResponseBodypublic JsonApi notification(HttpServletRequest request,HttpServletResponse response) throws IOException, DocumentException {Map<String, String> map = MobiMessage.xml2map(request);LogUtils.trace(map);//根据测试成功后的返回数据来进行业务逻辑操作,比如存数据库等return new JsonApi();}

五、设置测试目录

    必须设置公众号支付的测试授权目录和支付授权目录,该目录必须和发起支付请求的页面保持一致,比如发起支付请求的url是http://***/#/pay,只需要把该目录设为http://***/即可。

微信公众号支付开发 --Java相关推荐

  1. java微信公众号支付开发平台_微信公众号支付demo,微信公众号支付Java DEMO

    1.5.4微信验证的控制方法: /** * 微信验证 * 请填写接口配置信息,此信息需要你有自己的服务器资源,填写的URL需要正确响应微信发送的Token验证 * 验证服务器地址的有效性 * 开发者提 ...

  2. 微信公众号支付开发步骤Java(超详细)

    做为一个刚刚做完微信公众号的小白,我不得不吐槽一下微信给的官方文档,里面那坑一个接一个,我这是跳进去再爬出来,一下给做了四天,本来技术就不够好,还被文档带的跑偏跑偏...我在这给大家整理一份超级详细的 ...

  3. 微信公众号支付开发手记(node)

    微信支付 前言 总结一下最近业务开发中对微信公众号支付的开发过程,微信支付的开发前提是已经具备可上线微信公众号开发的基础上进行的,如果你的开发阶段目前停留在起步,建议参考这篇文章开始. 好了,来聊一聊 ...

  4. 微信公众号支付开发全过程(java版)

    文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源. 一.微信官方文档微信支付开发流程(公众号支付) 首先我们到微信支付的官方文档的开发步骤部分查 ...

  5. java微信公众号支付开发平台_Java微信公众平台开发之公众号支付(微信内H5调起支付)...

    官方文档 准备工作:已通过微信认证的公众号,必须通过ICP备案域名(否则会报支付失败) 借鉴了很多大神的文章,在此先谢过了 整个支付流程,看懂就很好写了 一.设置支付目录 在微信公众平台设置您的公众号 ...

  6. 公众号支付demo java_微信公众号支付开发全过程(java版)

    文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源. 一.微信官方文档微信支付开发流程(公众号支付) 首先我们到微信支付的官方文档的开发步骤部分查 ...

  7. 微信公众号支付开发(一):前期准备

    微信公众号认证(服务号认证) 说明:如果没有发布文章的需建议认证服务号,如果每个自然月每天都要发文章认证订阅号,这里是认证服务号. 准备工作: 注册一个新邮箱. 1. 注册微信公众号账号:注册一个微信 ...

  8. 关于微信学习之微信公众号支付开发

    关于微信支付初学者一定有很多的疑问,首先我们需要结合一下自己的程序操作微信支付所需要的业务流程. 1.如我这边需要开发一个公众号支付: 首先用户扫码访问后台(或者通过公众号直接进入,点击一个支付请求按 ...

  9. 微信公众号支付开发配置

    一.微信公众平台配置 登陆微信公众平台 微信支付->开发配置 1.测试白名单:把自己的微信号加上 2.测试授权目录:修改为项目所属域名,不加端口号.例如:http://d****n.g**.ne ...

最新文章

  1. AI 一分钟 | 南京大学成立人工智能研究院;三星关联实体已收购 AI 搜索引擎创业公司Kngine的全部股份
  2. 基于纯Java代码的Spring容器和Web容器零配置的思考和实现(3) - 使用配置
  3. idea terminal中文乱码_Terminal优雅的办公带来超高的效率
  4. 【机器视觉】Qt集成Halcon开发环境详解(二)
  5. Linux中的cron计划任务配置详解
  6. 软件测试实验1:为三角形问题编写一个Java程序,并用Junit测试程序
  7. python如何打印字符串_如何在Python中打印“漂亮”字符串输出
  8. ubuntu下修改mysql编码,使能支持中文
  9. 手机耗电统计app_华为手机有哪些功能关掉比较好?
  10. 如何在Mac电脑上更改地区或国家位置设定?
  11. sd卡卡槽_SD卡无法读取最完整解决办法汇总
  12. 模型边缘自发光材质——Shader
  13. 大数据可视化课程笔记 6
  14. python绘制网络拓扑图_python绘制网络拓扑_网络拓扑的python表示
  15. matlab算sma,如何计算简单移动平均线(SMA)
  16. 用户 不在 sudoers 文件中。此事将被报告
  17. 电视服务器绑上电池信号强吗,路由器上面绑电池,可以增加WiFi信号,真的有作用吗?...
  18. java计算机毕业设计研究生推免系统源码+数据库+系统+lw文档+部署
  19. 2020/8/3/ATC工具以及AIPP
  20. Android应用发送短信的实现

热门文章

  1. 【zabbix监控三】zabbix之部署代理服务器
  2. YOLOv2—更改CelebA数据集的bbox [by zhangzexuan][17.9.24updated]
  3. 假设检验之z-检验,t-检验,卡方检验
  4. tf.nn.moments( )函数的使用
  5. vscode中使用ssh连接linux(树莓派)opencv不能使用imshow()函数输出图片和视频的解决方法
  6. 第四讲:1.定时、延时任务控制小台灯打开/关闭
  7. 破甲两千六 Spring Cloud 教程(三):添加Spring Cloud 的 Netflix Eureka 插件,实现服务端、客户端的发现与注册
  8. JAVA学习记录(取反运算~)
  9. postgresql扩展Geometry类型
  10. String以及StringBuffer的基本操作