微信公众号授权,支付,退款总结
经过两周的研究,终于又把微信支付也搞定了。作为一个技术人员就应该有总结思考的习惯。这里将自己做微信踩过的一些坑,以及自己的思考记录下来,希望能帮助各位和我一样做微信支付的小伙伴。
1.支付前准备
1.1首先两个平台接入账户。
- 商户平台:https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
- 公众平台:https://mp.weixin.qq.com/
1.2两个平台的作用
3 商户平台:支付收款方,通俗点将就是网站用户支付的钱给谁。里面有商户的一些信息以及秘钥支付时要用。4.公众号平台:在这里需要它提供网页授权的一些信息。
2.微信公众号支付流程
关于支付流程,官方给了一张很详细的流程图如下:
当然这是所有支付的流程图,在这里博主结合自己的实现方式也画了一张只有授权和公众号支付流程的图。大家可以参考有画的不对的地方还请路过的大牛多多指教。
3. 微信公众号支付的那些坑
1:获取授权的时候,访问授权接口不要用ajax提交。如果用ajax提交方式提交请求,微信服务端会报跨域请求不允许的错误。2:获取授权openid的时候appid,secret一定要正确,这里的secret获取方式为下图,如果忘记,可以重置:
3:如果用的springmvc的框架,在统一支付接口记得用注解@ResponseBody,因为统一支付接口返回的数据一般是一个map,这个map中的数据前台页面要解析,所以需要这个注解。4:调用统一支付接口时,请求报文是xml格式的,所以要把需要的参数转变为xml形式。5:异步回调方法是支付成功后微信通知商户后台时调用,所以测试时需要在外网测试7:授权时,只授一次就行,所以在授权之前判断是否已经授权。8:微信支付以分为单位,支付金额需要注意转换单位。9:如果签名失败,一定要仔细检查参数是否都拼接完毕,拼接正确,一般签名失败最可能的原因就是secret错误,应该去微信公众平台重新查找并且核对。10:退款时,注意双向证书的路径。一般退款回调失败最可能的原因就是证书出错。
4. 微信公众号开发需要的一些官网网站
商户平台开发者文档网址
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
用户授权需要查看的网址,重点查看用户管理相关说明
http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
商户平台微信支付网址
https://pay.weixin.qq.com/wiki/doc/api/index.html
商户平台登录网址
https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2Findex.php%2Fcore%2Faccount
微信公众平台登录入口
https://mp.weixin.qq.com/
5.公众号支付代码
前端网页js代码
var pay_orderno; //订单编号//判断是否是授权回来的outhvar outh = $.getUrlParam("outh");if (!empty(outh)){//说明outh不为空,即为授权回来的,则直接微信支付wxzhifu();}function getRequest() {var url = location.search; //获取url中"?"符后的字串var theRequest = new Object();if (url.indexOf("?") != -1) {var str = url.substr(1);strs = str.split("&");for (var i = 0; i < strs.length; i++) {theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);}}return theRequest;}//保存订单信息function saveOrder(){var mode = "";if($("#AliPay")[0].checked) {mode = "支付宝";}else if($("#bankpay")[0].checked) {mode = "银联支付";}else if($("#wxpay")[0].checked) {mode = "微信支付";}var dishjson = data.dishJson; //订单表dishJson内容var dishJson = (JSON.parse(dishjson))[0];var oldcanpin = dishJson.canpin;var newcanpin = oldcanpin + ",支付方式:"+ mode; //拼接支付方式以后的菜品信息//alert(newcanpin);dishJson["canpin"] = newcanpin;data["dishJson"] = JSON.stringify(dishJson);var url="/cyDishorder/saveCyDcOrders";var successfull= function (datas) {var bizData=datas.bizData;pay_orderno = bizData.orderNum;//alert(pay_orderno);//var paymoney = $("#ydmoney").text().substring(1);if(datas.rtnCode == "0000000"){pay(); //跳转支付方法//alert("==============")}}ajaxPostFun(url, data, successfull,null, "保存餐厅预定订单");}//跳转到支付页面function pay() {if($("#AliPay")[0].checked) {location.href = server+"/AliPayDingCan/" + pay_orderno;}else if($("#bankpay")[0].checked) {location.href = server+"/CYChinapay/" + pay_orderno;}else if($("#wxpay")[0].checked){//查询该用户是否微信授权var url = server+"/Member/FindById/"+userid;var successFun = function (data) {if(empty(data.bizData.openid)){//alert("微信未授权");//如果该用户没有授权,则进行授权操作wxshouquan();}else{//alert("微信已授权");//用户已经授权,直接微信支付wxzhifu();}}ajaxPostFun(url, {}, successFun, null, "查询用户是否授权");}else {layer.msg("请选择支付方式");return false;}}//微信授权function wxshouquan() {location.href = server + "/CyWechatPay/outh?userid=" + userid;}//微信支付function wxzhifu(){var url = server+"/CyWechatPay/unifiedorder";ajaxPostFun(url, {userid:userid,orderno:pay_orderno}, function(res) {var _data = res.bizData;function onBridgeReady(){WeixinJSBridge.invoke('getBrandWCPayRequest', {"appId":_data.appId, //公众号名称,由商户传入"timeStamp":_data.timeStamp, //时间戳,自1970年以来的秒数"nonceStr":_data.nonceStr, //随机串"package" :_data.package,"signType":_data.signType, //微信签名方式:"paySign": _data.paySign //微信签名},function(ress){alert( JSON.stringify(ress));if(ress.err_msg == "get_brand_wcpay_request:ok" ) {layer.msg("支付成功!",{time:2000});//window.location.href = "buyGoodsChenggong.html?orderid="+orderid;}else if(ress.err_msg == "get_brand_wcpay_request:cancel"){layer.msg("支付取消!",{time:2000});}else {layer.msg("未知错误!",{time:2000});} // 使用以上方式判断前端返回,微信团队郑重提示: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();}}, null, "微信支付获取package包");}
支付,退款,controller代码
@Controller
@RequestMapping(value = "/CyWechatPay")
public class CyWechatController {@Autowiredprivate IMemberService memberService;@Autowiredprivate IMobileCyDishorderService mobileCyDishorderService;@Autowiredprivate IMobileMemberService mobileMemberService;@Autowiredprivate IMobileServiceStyleService mobileServiceStyleService;private static Logger logger = Logger.getLogger(CyWechatController.class);private static String url_snsapi_base = "https://open.weixin.qq.com/connect/oauth2/authorize";private static String url_access_token = "https://api.weixin.qq.com/sns/oauth2/access_token";private static String url_unifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";/*** 统一下单* @return prepare_id 预付款id* @throws IOException* @throws JDOMException*/@ResponseBody@RequestMapping("/unifiedorder")public Map<String, Object> unifiedorder(@RequestParam(value = "orderno", required = true)String orderno, String userid,HttpServletRequest request) throws Exception {//根据订单号查询订单时间CyDishorderEntity orderEntity = (CyDishorderEntity) mobileCyDishorderService.findOne("orderNum", orderno);//获得随机数String nonce_str = WxTool.getRandomCode(32);//根据用户id查询用户信息Map<String,Object> memberEntity =memberService.findByid("id",userid);String openid = memberEntity.get("openid").toString();//生成xml,参数说明(说明,标识码,订单号,IP,价格,openid用户在微信端的唯一标示)String xml = this.getXmlData(orderEntity.getRemarks(), nonce_str,orderno, WxTool.getRemoteHost(request), new BigDecimal(orderEntity.getPayMoney()),openid);logger.info("生成的订单信息======>" + xml);//订单提交的微信地址(预支付地址)String result = HttpRequest.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xml);//告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。@SuppressWarnings("unchecked")Map<String, Object> map2 = XMLUtil.doXMLParse(result);logger.info("统一下单接口返回的结果集======>" + map2);//return_code为微信返回的状态码,SUCCESS表示支付成功//return_msg 如非空,为错误原因 签名失败 参数格式校验错误if (map2.get("return_code").toString().equalsIgnoreCase("SUCCESS")&& map2.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {//预支付成功logger.info("微信预支付成功!");//map2.put("timestamp",Long.toString(System.currentTimeMillis() / 1000));//传递map2给前台页面处理Map<String, Object> map = new HashMap<String, Object>();map.put("appId", map2.get("appid"));map.put("timeStamp", Long.toString(System.currentTimeMillis() / 1000));map.put("nonceStr", map2.get("nonce_str"));map.put("package", "prepay_id=" + map2.get("prepay_id"));map.put("signType", "MD5");map.put("paySign", WxTool.getSignUtil(map, ConfigUtil.APP_SECRECT));logger.info("统一下------->" + map);return map;} else {//支付失败,进行相应的失败业务处理logger.info("微信预支付失败!");//传递null给页面处理return null;}}/*** 申请退款*/@RequestMapping("refund/{orderno}")@ResponseBodypublic String refund(@PathVariable String orderno) throws Exception {//根据订单号查询订单CyDishorderEntity orderEntity = (CyDishorderEntity) mobileCyDishorderService.findOne("orderNum", orderno);//获得随机数String nonce_str = WxTool.getRandomCode(32);StringBuilder sb2 = new StringBuilder();//微信签名需要的参数Map<String, Object> signMap = new HashMap<String, Object>();signMap.put("appid",ConfigUtil.APPID);//应用APPIDsignMap.put("mch_id",ConfigUtil.MCH_ID);//微信支付商户号signMap.put("nonce_str",nonce_str);//随机数signMap.put("op_user_id",ConfigUtil.MCH_ID);signMap.put("out_trade_no",orderEntity.getOrderNum());//订单号out_refund_nosignMap.put("out_refund_no",orderEntity.getRefund_queryid());//退款流水号signMap.put("refund_fee",new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue());//退款金额signMap.put("total_fee",new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue());//总金额signMap.put("transaction_id","");//微信生成的订单号,在支付通知中有返回//生成xml,微信要求的xml形式StringBuffer xml =new StringBuffer();xml.append("<xml>");xml.append("<appid>"+ConfigUtil.APPID+"</appid>");//应用IDxml.append("<mch_id>"+ConfigUtil.MCH_ID+"</mch_id>");//微信支付分配的商户号xml.append("<nonce_str>"+nonce_str+"</nonce_str>");//随机字符串,不长于32位。xml.append("<op_user_id>"+ConfigUtil.MCH_ID+"</op_user_id>");//操作员,默认为商户号xml.append("<out_refund_no>"+orderEntity.getRefund_queryid()+"</out_refund_no>");//商户退款单号,商户系统内部的退款单号,商户系统内部唯一xml.append("<out_trade_no>"+orderEntity.getOrderNum()+"</out_trade_no>");//商户订单号xml.append("<refund_fee>"+new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue()+"</refund_fee>");//退款金额xml.append("<total_fee>"+new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue()+"</total_fee>");//订单金额xml.append("<transaction_id>"+"</transaction_id>");//微信订单号,微信生成的订单号,在支付通知中有返回xml.append("<sign>"+WxTool.getSignUtil(signMap, ConfigUtil.APP_SECRECT)+"</sign>");//签名xml.append("</xml>");logger.info("生成的申请退款信息=============================>" + xml.toString());/*** JAVA使用证书文件*/logger.info("加载证书开始=========================================》》》》》");//指定读取证书格式为PKCS12KeyStore keyStore = KeyStore.getInstance("PKCS12");//读取本机存放的PKCS12证书文件FileInputStream instream = new FileInputStream(new File("/home/smit/down/apiclient_cert.p12"));try {//指定PKCS12的密码(商户ID)keyStore.load(instream, ConfigUtil.MCH_ID.toCharArray());} finally {instream.close();}//ssl双向验证发送http请求报文SSLContext sslcontext = null;sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, ConfigUtil.MCH_ID.toCharArray()).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");StringEntity se = new StringEntity(xml.toString(), "UTF-8");httppost.setEntity(se);//定义响应实例对象CloseableHttpResponse responseEntry = null;String xmlStr2 = null;//读入响应流中字符串的引用responseEntry = httpclient.execute(httppost);//发送请求HttpEntity entity = responseEntry.getEntity();//获得响应实例对象if (entity != null) {//读取响应流的内容BufferedReader bufferedReader = null;bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));while ((xmlStr2 = bufferedReader.readLine()) != null) {sb2.append(xmlStr2);}}Map<String, Object> map = XMLUtil.doXMLParse(sb2.toString());logger.info("申请退款接口返回的结果集======>" + map);//return_code为微信返回的状态码,SUCCESS表示申请退款成功,return_msg 如非空,为错误原因 签名失败 参数格式校验错误if (map.get("return_code").toString().equalsIgnoreCase("SUCCESS")&& map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {logger.info("****************退款申请成功!**********************");//修改订单状态为申请退款orderEntity.setOrderStatus(CyOrderStatusEnum.REFUND_SUCCESS.getCode());mobileCyDishorderService.update(orderEntity);return "SUCCESS";} else {logger.info("*****************退款申请失败!*********************");return "FAIL";}}//生成xml方法private static String getXmlData(String body, String nonce_str, String tradeNo,String ip, BigDecimal totla, String openid) throws Exception {Map<String, Object> signMap = new HashMap<String, Object>();signMap.put("appid", ConfigUtil.APPID);//应用APPIDsignMap.put("nonce_str", nonce_str);//随机数signMap.put("body", body);//商品描述signMap.put("mch_id", ConfigUtil.MCH_ID);//微信支付商户号signMap.put("notify_url", "http://域名/CyWechatPay/paysuccess.do");//异步通知回调地址signMap.put("out_trade_no", tradeNo);//订单号signMap.put("total_fee", totla.multiply(new BigDecimal(100)).intValue());//总金额signMap.put("trade_type", "JSAPI");//支付类型signMap.put("spbill_create_ip", ip);//客户端IP地址signMap.put("openid", openid);//支付用户的唯一标识StringBuffer xml = new StringBuffer();xml.append("<xml>");xml.append("<appid>" + signMap.get("appid") + "</appid>");//应用IDxml.append("<body>" + signMap.get("body") + "</body>");xml.append("<mch_id>" + signMap.get("mch_id") + "</mch_id>");//微信支付分配的商户号xml.append("<nonce_str>" + signMap.get("nonce_str") + "</nonce_str>");//随机字符串,不长于32位。xml.append("<notify_url>" + signMap.get("notify_url") + "</notify_url>");//接收微信支付异步通知回调地址xml.append("<openid>" + signMap.get("openid") + "</openid>");//用户的openid 唯一标示,用户授权时或的xml.append("<out_trade_no>" + signMap.get("out_trade_no") + "</out_trade_no>");//订单号xml.append("<spbill_create_ip>" + signMap.get("spbill_create_ip") + "</spbill_create_ip>");//用户端实际ipxml.append("<total_fee>" + signMap.get("total_fee") + "</total_fee>");//订单总金额,单位为分xml.append("<trade_type>" + signMap.get("trade_type") + "</trade_type>");//支付类型xml.append("<sign>" + WxTool.getSignUtil(signMap, ConfigUtil.APP_SECRECT) + "</sign>");//签名xml.append("</xml>");return xml.toString();}//微信支付异步回调@RequestMapping(value = "/paysuccess")public void PaySuccess(HttpServletRequest request, HttpServletResponse response){try {logger.info("微信支付回调开始========================================================");PrintWriter print = null;InputStream inStream = request.getInputStream();ByteArrayOutputStream outSteam = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = inStream.read(buffer)) != -1) {outSteam.write(buffer, 0, len);}outSteam.close();inStream.close();String result = new String(outSteam.toByteArray(), "utf-8");//告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。@SuppressWarnings("unchecked")Map<Object, Object> map = XMLUtil.doXMLParse(result);//return_code为微信返回的状态码,SUCCESS表示支付成功//return_msg 如非空,为错误原因 签名失败 参数格式校验错误logger.info("微信返回的信息:"+map.toString());if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")&& map.get("return_code").toString().equalsIgnoreCase("SUCCESS")) {String tradeNo = map.get("out_trade_no").toString();//这里做出判断,防止微信服务的多次调用这里,造成一次支付,生成多个订单CyDishorderEntity order =(CyDishorderEntity)mobileCyDishorderService.findOne("orderNum",tradeNo);if(order!=null && order.getOrderStatus().equals(CyOrderStatusEnum.PREPAID.getCode())){//该订单已经支付成功,直接returnresponse.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");}updateOrder(map);//更新订单状态}response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");}catch (Exception e) {e.printStackTrace();}}/*** 用户授权** @param response* @throws IOException*/@RequestMapping("/outh")public void outh(@RequestParam(value = "userid", required = true) int userid,HttpServletResponse response, HttpServletRequest request) {StringBuffer url = request.getRequestURL();//String tempContextUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append("/").toString();try {// 回调地址String redirecturl = URLEncoder.encode(new String(("http://域名/CyWechatPay/getopenid.do?userid=" + userid).getBytes("utf-8"), "utf-8"),"utf-8");logger.info("[微信获取OPENID回调地址]"+redirecturl);// 用户授权 snsapi_userinforesponse.sendRedirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid="+ ConfigUtil.APPID+ "&redirect_uri="+ redirecturl+ "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/*** 商户获取用户资料** @param request* @return* @throws IOException*/@RequestMapping("/getopenid")public void getopenid(@RequestParam(value = "userid", required = true) int userid,HttpServletRequest request, HttpServletResponse response) {String code = request.getParameter("code");Map<String, Object> map = null; // 存放授权access_token和openidMap<String, Object> map2 = null;// 存放用户基本资料open// 获取授权的access_token和openidlogger.info("[获取授权的access_token和openid]");map = WXUtils.requestUrl("https://api.weixin.qq.com/sns/oauth2/access_token?appid="+ ConfigUtil.APPID+ "&secret="+ConfigUtil.APP_SECRECT+ "&code="+ code+ "&grant_type=authorization_code");String access_token = map.get("access_token").toString();String openid = map.get("openid").toString();// 获取用户资料map2 = WXUtils.requestUrl("https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid + "&lang=zh_CN");//设置用户的openidmemberService.updateuser(Integer.valueOf(userid),openid);try {response.sendRedirect("http://域名/Mobile/cyLijiZhifuX.html?=outh=outh");} catch (IOException e) {e.printStackTrace();}}//更新订单信息private void updateOrder(Map<Object, Object> map) {//TODO:支付成功后更新订单的逻辑。}}
以上为个人在做完微信支付后的一些总结,如有疑问请加qq群:451232132咨询。
微信公众号授权,支付,退款总结相关推荐
- vue开发项目微信公众号授权支付开发
一.注册微信公众号服务号并填写企业信息(个人订阅号没有开发微信支付的权限) 链接: https://mp.weixin.qq.com/ 二.在微信公众号内进行微信认证(3-5个工作日) 三.在微信公众 ...
- 微信登录 sdk 服务器,微信登录(微信公众号授权)的开发(详解)——两三行代码的事,何必呢...
新版重构的SDK已经开始在写了,具体的使用方法参考 新版重构的SDK已经开始在写了,具体的使用方法参考 新版重构的SDK已经开始在写了,具体的使用方法参考 新版重构的SDK已经开始在写了,具体的使用方 ...
- 微信公众号授权步骤详细步骤介绍和整合springboot开发(java版)
文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源. 一.微信公众号授权步骤 首先到微信公众平台注册账号,可以看到有四种类型(服务号,订阅号,小程 ...
- 微信登录(微信公众号授权)的开发(详解)
1.UnionId和OpenId 微信登录最重要的两个返回信息,一个是UnionId,一个是OpenId.两者之间有着必然的联系. 首先,先来理一下微信开放平台的架构.开发微信登录,必须有一个开放平台 ...
- java微信公众号JSAPI支付以及所遇到的坑
java微信公众号JSAPI支付以及所遇到的坑 上周做了个支付宝微信扫码支付,今天总结一下.微信相比支付宝要麻烦许多 由于涉及到代理商,没办法,让我写个详细的申请流程,懵逼啊. 笔记地址 http:/ ...
- vue微信公众号授权开发流程
vue微信公众号授权开发流程 项目采用的是vue2.0开发的,还未更新到vue3.0.项目描述:只有通过微信授权登录,不需要绑定手机号. 1.注册公众号,这个不多说了 2.配置公众号 在[公众号设置] ...
- 微信公众号授权登录(asp.net + angular)
微信是时下最火的,上面有数以亿计的用户,如果能接入微信将大大减低注册门槛,当然,接入微信登录是有门槛的.微信登录一般有两个,一个是微信开放平台授权登录,一个是微信公众号授权登录,两者都需要认证才可以继 ...
- vue移动端项目微信公众号授权登录
前言 在我们做移动端项目时, 很多功能是以登录后才能进行后续的操作, 并且许多pc端的网页都有微信扫码登录功能, 为了做到pc与移动端统一, 往往移动端项目需要添加微信登录功能, 那么为什么手机端不能 ...
- Java在Web端微信公众号授权登录
Java在Web端微信公众号授权登录 1.需要在微信开发平台配置 url:是自己服务中的微信需要推给你的地址(需要使用二级域名,可以去添加链接描述)购买9块钱1个月使用权或者白嫖都可 token 这个 ...
- uniapp中h5网页微信公众号授权
uniapp微信网页授权 uniapp中h5网页微信公众号授权 主要代码 获取code返回的code截取代码 uniapp中h5网页微信公众号授权 微信官方文档–>网页授权 uniapp中h5网 ...
最新文章
- 数据结构与算法 第二次实验报告堆栈队列
- 为什么linux进程名匹配最多只支持15个字符?
- 数据库分组选 第一条
- 从单机应用到微服务,用户认证走几步?
- android qq输入法表情,QQ输入法如何输入表情
- 【weblogic部署应用】
- PID控制电机输出作为电机PWM占空比输入的理解
- 矩阵和向量的范式(Norms for Vectors and Matrices)
- unity物体自身轴旋转_unity3d如何实现物体自动旋转-unity3d物体自动旋转的设置教程 - 河东软件园...
- prometheus入门实例
- 三维动画渲染用什么软件好?
- 【map】高德地图点聚合—按索引聚合
- python里面如何实现元组和列表的转换_python列表和元组相互转换
- oracle分区表创建——实现重新定义普通表为分区表
- 浣溪沙·残雪凝辉冷画屏
- 刀具寿命预测特征处理方法、刀具磨损机理
- java什么是显示类型转换_【Java的显示转换方法是什么?这种数据类型转换就要这样操作】- 环球网校...
- JS——运用JSONP技术完成百度关键字搜索
- 《生命不息,折腾不止》
- 维克森林大学计算机科学专业好不好,维克森林大学计算机科学系
热门文章
- Qt翻译文件ts中type理解
- 申请注册公司需要什么材料
- Atitit glb 3tie city lst 三线城市列表 数据目录1. 全球范围内约90个城市 三线 12. 世界性三线城市全球共
- Indie Maker 一个人的创业
- 28 字符串排列(还差点,list包含、Arrays、Collections的sort)全排列
- 与另一台计算机建立ipc,利用IPC$开启他人电脑远程桌面
- 微软100题(91)智力题
- 大数据产品价值主张_大数据背景下新零售商业模式探究
- BA-业务架构_优化“价值主张画布”,助力企业数字化赋能
- 数论 II(组合数学)