HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

分享下android的websocket解决方案

HTML5+ Webview WEBSOCKET

分享下android的websocket结局方案
先占个坑
明天写


  • 2017.1.9
  • 最近在做一个外勤应用,里面要做即时通讯,最终选择的是自己写websocket封装协议,没有使用融云这些,原因是系统会给用户私有云部署,同时也不想太依赖于第三方
  • ios下面的websocket是没有任何问题的
  • 但是android上是有问题的 虽然官方说4.4以上支持,但是经测试 我的一台4.4的机器也是不支持,服务端报错是 protocol error
  • 我们做应用的 除非是公司品牌特别牛像微信支付宝这类,否则你放弃了4.4一下的用户,他们也就放弃了你
    网上找了很久,其中也搜到这里 http://ask.dcloud.net.cn/article/302
    也尝试了下不是很好
    最好只好自己尝试解决
    目标有三个
    1:使用第三方websocket js库 这样代码改动基本是最小的
    2:自己写一个dcloud插件

第一个方案 找了很多 包括jwebsocket socket.io
最后发现问题依旧,原因就不说了
开始尝试第二个方案
但是看了半天dcloud的文档,发现自己写一个插件的复杂度太大,主要是官方很多地方文档描述不清
随后静下心来,我本身就是在原生的基础上来加的h5+sdk,所以可以尝试原生与js混合通信的方式
写到这里 我再次吐槽一下,我也用过apicloud,里面我觉得最好的一个是apicloud与原生之间的一个事件通知,不需要使用webview,随手写随手通知

回归正题

原有的js中websocke的部分,需要完成3个事情:
1 websocket初始化连接
2 发送消息
3 接受消息

所以我们只需要在java中写一个辅助类
1在需要的时候初始化连接
2可以接受js的发送请求,将数据发出
3在收到消息时候通知js

下面贴代码
js部分

function createWs() {  
        var WsUrl = ws_host + getMemberId();  
        if(mui.os.ios) {  
            if('WebSocket' in window) {  
                debug("WebSocket")  
                wsClient = new WebSocket(WsUrl);  
            } else if('MozWebSocket' in window) {  
                debug("MozWebSocket")  
                wsClient = new MozWebSocket(WsUrl);  
            }  
            wsClient.onopen = function() {  
                onOpen()  
            };  
            wsClient.onclose = function() {  
                onClose()  
            };  
            wsClient.onmessage = function(evt) {  
                onMessage(evt)  
            };  
            wsClient.onerror = function() {  
                onError()  
            };  
        } else if(mui.os.android) {  
            var Toolkit = plus.android.importClass("com.HBuilder.integrate.WsHelper");  
            if(Toolkit) {  
                //var wifiInfo = new Toolkit();  
                Toolkit.webConnect(WsUrl);  
            } else {  
                plus.nativeUI.alert("IM服务器连接失败");  
            }  
        }  

    }

原理一看就明白,如果是ios 则使用html5的websocket,如果是案桌,就使用原生的方法去创建websocket
下面是java原生,java原生有很多websocket客户端库,我这里用的是AndroidAsync
java 实现初始化websocket连接,以及实现向js通讯消息

public static void webConnect(final String url) {  

        if (webSocket == null) {  
            try {  
                AsyncHttpClient.getDefaultInstance().websocket(url, null, new AsyncHttpClient.WebSocketConnectCallback() {  
                    @Override  
                    public void onCompleted(Exception ex, WebSocket _webSocket) {  
                        webSocket = _webSocket;  
//发送心跳  
                        webSocket.send("{\"messageType\":3}");  
                        webSocket.setStringCallback(new WebSocket.StringCallback() {  
                            @Override  
                            public void onStringAvailable(String s) {  
                                Log.d("qwe", "hello");  
                                //获取webview  
                                ArrayList<IWebview> ss = SDK.obtainAllIWebview();  
                                for (IWebview iWebview : ss) {  
                                    if (iWebview.getOriginalUrl().equals("messageList.html")) {  
                                        iWebview.evalJS("wsRecive('" + s + "')");  
                                        // JSUtil.broadcastWebviewEvent(iWebview, iWebview.getWebviewUUID(), "wsRecive", s);  
                                        //JSUtil.execCallback(iWebview, "wsRecive", s, JSUtil.OK, false);  
                                        break;  
                                    }  
                                }  
                            }  
                        });  
                        webSocket.setClosedCallback(new CompletedCallback() {  
                            @Override  
                            public void onCompleted(Exception ex) {  
                                try {  
                                    if (ex != null)  
                                        Log.e("WebSocket", "Error");  
                                } finally {  
                                    if (tryTime <= 3) {  
                                        tryTime += 1;  
                                        webConnect(url);  
                                    }  
                                }  
                            }  
                        });  
                    }  
                });  
            }  
            finally {  
                if (tryTime <= 3) {  
                    tryTime += 1;  
                    webConnect(url);  
                }  
            }  

        } else {  
            webSocket.send("{\"messageType\":3}");  
        }  
    }

这里又要吐槽一下,官方的h5+sdk文档实在是太糟糕,花了很长时间才摸索出来,主要是获取指定的webview 这点没有apicloud做得好 ,apicloud后台java发起一个事件,前面任何一个页面都可以监听,5+sdk我找了半天没有找到,可能也有但是不知道在哪里
首先查找所有的webview,然后调用这个页面的js方法

  ArrayList<IWebview> ss = SDK.obtainAllIWebview();  
                                for (IWebview iWebview : ss) {  
                                    if (iWebview.getOriginalUrl().equals("messageList.html")) {  
                                        iWebview.evalJS("wsRecive('" + s + "')");  
                                        break;  
                                    }  
                                }

下面是js发送消息

function sendMsg(msg) {  

        if(mui.os.ios) {  
            //缓存本地  
            if(wsClient.readyState == WebSocket.OPEN) {  
                wsClient.send(JSON.stringify(msg));  
                _msg = null;  
            } else {  
                _msg = msg;  
                createWs();  
            }  
        } else if(mui.os.android) {  
            var Toolkit = plus.android.importClass("com.HBuilder.integrate.WsHelper");  
            if(Toolkit) {  
                //var wifiInfo = new Toolkit();  
                Toolkit.sendMsg(JSON.stringify(msg));  
            } else {  
                plus.nativeUI.alert("IM服务器连接失败");  
            }  
        }  

    }

也是先要判断,如果ios就走h5直接发送,否则就走原生android发送

public static void sendMsg(String msg) {  
        if (webSocket != null && webSocket.isOpen()) {  
            webSocket.send(msg);  
        }  
    }

至此,android的websocket完美解决,无论多少版本,统一走底层
思路不一定是最好的 欢迎加我微信(ming-lsard)交流
希望有更好的解决思路一起分享
谢谢大家

本文未经许可,禁止转载,只发在dcloud官方论坛

继续阅读 »

分享下android的websocket结局方案
先占个坑
明天写


  • 2017.1.9
  • 最近在做一个外勤应用,里面要做即时通讯,最终选择的是自己写websocket封装协议,没有使用融云这些,原因是系统会给用户私有云部署,同时也不想太依赖于第三方
  • ios下面的websocket是没有任何问题的
  • 但是android上是有问题的 虽然官方说4.4以上支持,但是经测试 我的一台4.4的机器也是不支持,服务端报错是 protocol error
  • 我们做应用的 除非是公司品牌特别牛像微信支付宝这类,否则你放弃了4.4一下的用户,他们也就放弃了你
    网上找了很久,其中也搜到这里 http://ask.dcloud.net.cn/article/302
    也尝试了下不是很好
    最好只好自己尝试解决
    目标有三个
    1:使用第三方websocket js库 这样代码改动基本是最小的
    2:自己写一个dcloud插件

第一个方案 找了很多 包括jwebsocket socket.io
最后发现问题依旧,原因就不说了
开始尝试第二个方案
但是看了半天dcloud的文档,发现自己写一个插件的复杂度太大,主要是官方很多地方文档描述不清
随后静下心来,我本身就是在原生的基础上来加的h5+sdk,所以可以尝试原生与js混合通信的方式
写到这里 我再次吐槽一下,我也用过apicloud,里面我觉得最好的一个是apicloud与原生之间的一个事件通知,不需要使用webview,随手写随手通知

回归正题

原有的js中websocke的部分,需要完成3个事情:
1 websocket初始化连接
2 发送消息
3 接受消息

所以我们只需要在java中写一个辅助类
1在需要的时候初始化连接
2可以接受js的发送请求,将数据发出
3在收到消息时候通知js

下面贴代码
js部分

function createWs() {  
        var WsUrl = ws_host + getMemberId();  
        if(mui.os.ios) {  
            if('WebSocket' in window) {  
                debug("WebSocket")  
                wsClient = new WebSocket(WsUrl);  
            } else if('MozWebSocket' in window) {  
                debug("MozWebSocket")  
                wsClient = new MozWebSocket(WsUrl);  
            }  
            wsClient.onopen = function() {  
                onOpen()  
            };  
            wsClient.onclose = function() {  
                onClose()  
            };  
            wsClient.onmessage = function(evt) {  
                onMessage(evt)  
            };  
            wsClient.onerror = function() {  
                onError()  
            };  
        } else if(mui.os.android) {  
            var Toolkit = plus.android.importClass("com.HBuilder.integrate.WsHelper");  
            if(Toolkit) {  
                //var wifiInfo = new Toolkit();  
                Toolkit.webConnect(WsUrl);  
            } else {  
                plus.nativeUI.alert("IM服务器连接失败");  
            }  
        }  

    }

原理一看就明白,如果是ios 则使用html5的websocket,如果是案桌,就使用原生的方法去创建websocket
下面是java原生,java原生有很多websocket客户端库,我这里用的是AndroidAsync
java 实现初始化websocket连接,以及实现向js通讯消息

public static void webConnect(final String url) {  

        if (webSocket == null) {  
            try {  
                AsyncHttpClient.getDefaultInstance().websocket(url, null, new AsyncHttpClient.WebSocketConnectCallback() {  
                    @Override  
                    public void onCompleted(Exception ex, WebSocket _webSocket) {  
                        webSocket = _webSocket;  
//发送心跳  
                        webSocket.send("{\"messageType\":3}");  
                        webSocket.setStringCallback(new WebSocket.StringCallback() {  
                            @Override  
                            public void onStringAvailable(String s) {  
                                Log.d("qwe", "hello");  
                                //获取webview  
                                ArrayList<IWebview> ss = SDK.obtainAllIWebview();  
                                for (IWebview iWebview : ss) {  
                                    if (iWebview.getOriginalUrl().equals("messageList.html")) {  
                                        iWebview.evalJS("wsRecive('" + s + "')");  
                                        // JSUtil.broadcastWebviewEvent(iWebview, iWebview.getWebviewUUID(), "wsRecive", s);  
                                        //JSUtil.execCallback(iWebview, "wsRecive", s, JSUtil.OK, false);  
                                        break;  
                                    }  
                                }  
                            }  
                        });  
                        webSocket.setClosedCallback(new CompletedCallback() {  
                            @Override  
                            public void onCompleted(Exception ex) {  
                                try {  
                                    if (ex != null)  
                                        Log.e("WebSocket", "Error");  
                                } finally {  
                                    if (tryTime <= 3) {  
                                        tryTime += 1;  
                                        webConnect(url);  
                                    }  
                                }  
                            }  
                        });  
                    }  
                });  
            }  
            finally {  
                if (tryTime <= 3) {  
                    tryTime += 1;  
                    webConnect(url);  
                }  
            }  

        } else {  
            webSocket.send("{\"messageType\":3}");  
        }  
    }

这里又要吐槽一下,官方的h5+sdk文档实在是太糟糕,花了很长时间才摸索出来,主要是获取指定的webview 这点没有apicloud做得好 ,apicloud后台java发起一个事件,前面任何一个页面都可以监听,5+sdk我找了半天没有找到,可能也有但是不知道在哪里
首先查找所有的webview,然后调用这个页面的js方法

  ArrayList<IWebview> ss = SDK.obtainAllIWebview();  
                                for (IWebview iWebview : ss) {  
                                    if (iWebview.getOriginalUrl().equals("messageList.html")) {  
                                        iWebview.evalJS("wsRecive('" + s + "')");  
                                        break;  
                                    }  
                                }

下面是js发送消息

function sendMsg(msg) {  

        if(mui.os.ios) {  
            //缓存本地  
            if(wsClient.readyState == WebSocket.OPEN) {  
                wsClient.send(JSON.stringify(msg));  
                _msg = null;  
            } else {  
                _msg = msg;  
                createWs();  
            }  
        } else if(mui.os.android) {  
            var Toolkit = plus.android.importClass("com.HBuilder.integrate.WsHelper");  
            if(Toolkit) {  
                //var wifiInfo = new Toolkit();  
                Toolkit.sendMsg(JSON.stringify(msg));  
            } else {  
                plus.nativeUI.alert("IM服务器连接失败");  
            }  
        }  

    }

也是先要判断,如果ios就走h5直接发送,否则就走原生android发送

public static void sendMsg(String msg) {  
        if (webSocket != null && webSocket.isOpen()) {  
            webSocket.send(msg);  
        }  
    }

至此,android的websocket完美解决,无论多少版本,统一走底层
思路不一定是最好的 欢迎加我微信(ming-lsard)交流
希望有更好的解决思路一起分享
谢谢大家

本文未经许可,禁止转载,只发在dcloud官方论坛

收起阅读 »

使用Hbuilder开发的APP终于完工了,欢迎大家拍砖

移动APP

废了好大劲,终于完工了,使用Hbuilder开发了一款汽车4s店保养预约的APP,简直太不容易了,趟过了好多坑,不过还好有Hbuilder官方的大力支持,使得APP顺利完工,放几张截图:如需要查看详细完整的图片点击 更多截图查看,介绍了开发这个APP遇到的一些坑!这里上图图片太费劲了。下载地址也可以点击这个链接地址查看。

继续阅读 »

废了好大劲,终于完工了,使用Hbuilder开发了一款汽车4s店保养预约的APP,简直太不容易了,趟过了好多坑,不过还好有Hbuilder官方的大力支持,使得APP顺利完工,放几张截图:如需要查看详细完整的图片点击 更多截图查看,介绍了开发这个APP遇到的一些坑!这里上图图片太费劲了。下载地址也可以点击这个链接地址查看。

收起阅读 »

底部选项卡切换更换子页面Header内容

1:首先在默认首页中设置:
var subpage_style = {
top: '0px',
bottom: '51px'
};
2:注销:title.innerHTML = this.querySelector('.mui-tab-label').innerHTML;代码;
3:在子页面中设置相应的Header内容

完成以上操作,实现底部选项卡切换更换子页面Header内容。

继续阅读 »

1:首先在默认首页中设置:
var subpage_style = {
top: '0px',
bottom: '51px'
};
2:注销:title.innerHTML = this.querySelector('.mui-tab-label').innerHTML;代码;
3:在子页面中设置相应的Header内容

完成以上操作,实现底部选项卡切换更换子页面Header内容。

收起阅读 »

自定义icon经验

1:首先阅读文章:mui如何增加自定义icon图标,根据文章中的步骤完成所有操作;
2:自定义的iconfont.css文件中书写格式为:src:url('../fonts/iconfont.ttf') format('truetype');
注意:这里在阿里图标自动生成的文件会可能缺少src前缀,请自行补齐,并且结尾是逗号,修改为分号;
3:切记在页面中引入iconfont.css文件;
注意:HBuilder自动生成的link标签没有rel="stylesheet"属性,请自行补齐,否则有可能无法显示自定义icon;

完成以上操作,终于实现了自己想要的效果!

继续阅读 »

1:首先阅读文章:mui如何增加自定义icon图标,根据文章中的步骤完成所有操作;
2:自定义的iconfont.css文件中书写格式为:src:url('../fonts/iconfont.ttf') format('truetype');
注意:这里在阿里图标自动生成的文件会可能缺少src前缀,请自行补齐,并且结尾是逗号,修改为分号;
3:切记在页面中引入iconfont.css文件;
注意:HBuilder自动生成的link标签没有rel="stylesheet"属性,请自行补齐,否则有可能无法显示自定义icon;

完成以上操作,终于实现了自己想要的效果!

收起阅读 »

点lable标签input没有获得焦点

与Picker选择器冲突了,导致点lable标签input没有获得焦点。
解决办法: 触发Picker.show();时 用click 代替tap

源代码:

<div class="mui-content">
<form id="recommendShop" action="" method="post">
<div class="mui-input-row">
<label for="ss-mch">名称</label>
<input type="text" class="mui-input-clear" id="ss-mch" placeholder="请填写商家名称">
</div>
<div class="mui-input-row">
<label for="ss-lxdh">电话</label>
<input type="tel" class="mui-input-clear" id="ss-lxdh" placeholder="请填写商家电话">
</div>
<div class="mui-input-row">
<label for="ss-lxdzh">地址</label>
<input type="text" class="mui-input-clear" id="ss-lxdzh" placeholder="请填写商家地址">
</div>
<div class="mui-input-row">
<label for="ss-fzr">负责人</label>
<input type="tel" class="mui-input-clear" id="ss-fzr" placeholder="请填写商家负责人电话">
</div>
<div class="area">
<h3>区域</h3>
<p>江苏省/无锡市/惠山区</p>
</div>
<div class="creditsRate">
<h3>积分比率</h3>
<p>5%</p>
</div>
<div class="IDcardZ">
<h3>身份证正面</h3>
<img class="wshitupian" src="../images/banner2.jpg"/>
</div>
<div class="IDcardZ">
<h3>身份证反面</h3>
<img class="wshitupian" src="../images/banner2.jpg"/>
</div>
<div class="IDcardZ">
<h3>营业执照</h3>
<img class="wshitupian" src="../images/banner2.jpg"/>
</div>
<div style="height: 65px;background-color: #fff; padding-top: 15px;">
<div class="submit">提交</div>
</div>
</form>
</div>

    <script src="../js/plugs/mui.picker.js"></script>  
    <script src="../js/plugs/mui.poppicker.js"></script>  
    <script type="text/javascript">  
        mui.init(  

        );        

        var creditsRatePicker = new mui.PopPicker();   //选择社群  
        creditsRatePicker.setData([{value:'0',text:'3%'},{value:'1',text:'5%'},{value:'2',text:'7%'},{value:'3',text:'10%'}]);  

        mui("#recommendShop").on('click','.creditsRate',function(){  
            creditsRatePicker.show(function (getSelectedItems) {      
                var js_creditsRate = document.querySelector(".creditsRate>p");  
                js_creditsRate.innerHTML = getSelectedItems[0].text;  
           });                
        });  

    </script>
继续阅读 »

与Picker选择器冲突了,导致点lable标签input没有获得焦点。
解决办法: 触发Picker.show();时 用click 代替tap

源代码:

<div class="mui-content">
<form id="recommendShop" action="" method="post">
<div class="mui-input-row">
<label for="ss-mch">名称</label>
<input type="text" class="mui-input-clear" id="ss-mch" placeholder="请填写商家名称">
</div>
<div class="mui-input-row">
<label for="ss-lxdh">电话</label>
<input type="tel" class="mui-input-clear" id="ss-lxdh" placeholder="请填写商家电话">
</div>
<div class="mui-input-row">
<label for="ss-lxdzh">地址</label>
<input type="text" class="mui-input-clear" id="ss-lxdzh" placeholder="请填写商家地址">
</div>
<div class="mui-input-row">
<label for="ss-fzr">负责人</label>
<input type="tel" class="mui-input-clear" id="ss-fzr" placeholder="请填写商家负责人电话">
</div>
<div class="area">
<h3>区域</h3>
<p>江苏省/无锡市/惠山区</p>
</div>
<div class="creditsRate">
<h3>积分比率</h3>
<p>5%</p>
</div>
<div class="IDcardZ">
<h3>身份证正面</h3>
<img class="wshitupian" src="../images/banner2.jpg"/>
</div>
<div class="IDcardZ">
<h3>身份证反面</h3>
<img class="wshitupian" src="../images/banner2.jpg"/>
</div>
<div class="IDcardZ">
<h3>营业执照</h3>
<img class="wshitupian" src="../images/banner2.jpg"/>
</div>
<div style="height: 65px;background-color: #fff; padding-top: 15px;">
<div class="submit">提交</div>
</div>
</form>
</div>

    <script src="../js/plugs/mui.picker.js"></script>  
    <script src="../js/plugs/mui.poppicker.js"></script>  
    <script type="text/javascript">  
        mui.init(  

        );        

        var creditsRatePicker = new mui.PopPicker();   //选择社群  
        creditsRatePicker.setData([{value:'0',text:'3%'},{value:'1',text:'5%'},{value:'2',text:'7%'},{value:'3',text:'10%'}]);  

        mui("#recommendShop").on('click','.creditsRate',function(){  
            creditsRatePicker.show(function (getSelectedItems) {      
                var js_creditsRate = document.querySelector(".creditsRate>p");  
                js_creditsRate.innerHTML = getSelectedItems[0].text;  
           });                
        });  

    </script>
收起阅读 »

【原创分享·支付宝支付】HBuilder打包APP调用支付宝客户端支付

支付宝

发现复制过来没用,先贴几个图片都贴不了,博客园和csdn都是我本人发布的,我贴个博客园的地址,如果不想去博客园看,就看下面的文字描述了.
http://www.cnblogs.com/nangong/p/5b5db8769efcd7e10647869fd5ee5539.html#3596715

前言

  最近有点空余时间,所以,就研究了一下APP支付。前面很早就搞完APP的微信支付了,但是由于时间上和应用上的情况,支付宝一直没空去研究。然后等我空了的时候,发现支付宝居然升级了支付逻辑,虽然目前还兼容老的方法,但是新的既然出来了,肯定研究新的了。但是网上几乎都是旧的方法,所以,唯有自己看官方的文档,慢慢一步一步研究了。在研究的过程中,发现,他跟微信支付的差别蛮大的。好了废话不多说了,下面直接来干货。
首先,你得去蚂蚁金服开放平台申请一个应用,地址:https://openhome.alipay.com注册一个应用,如下图:

  应用申请下来之后,需要申请功能,我们这里用到的是“APP支付”功能。如下图:

  如果需要查看相关的文档,那就点击“APP支付”就可以跳转到相关的文档,这里我直接给出APP需要看到的文档,地址:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.J9S7XU&treeId=204&articleId=105465&docType=1 如下图:

  我们在编写服务端的时候,需要用到两个参数,一个是APPID,这个上面的图里面有,还有一个就是密钥,这个是通过签名工具生成,可以通过下面这个地址下载工具,然后生成,地址:https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105351&docType=1

  下载这个工具,然后解压,双击“支付宝RAS密钥生成器SHAwithRSA1024_V1.0.bat”生成即可,这里要注意:TIPS:工具不支持含中文或空格的路径,请下载到英文目录下使用。
打开工具后,如下图:

  先“生成密钥”,然后再复制公钥,然后把公钥复制到平台,如下图:

  再保存,如下图:

  然后再验证公钥的正确性,这里,可以写个小工具来验证,方法如下:

        /// <summary>  
        /// 测试公钥是否对  
        /// </summary>  
        /// <returns></returns>  
        public string testsign()  
        {  
            string privtekey = Config.privtekey;//这个就是生成器里面的那个私钥,第一个大框框那里的.  
            string data = "a=123";//平台上提供的串  
            string sign = RSAFromPkcs8.sign(data, privtekey, "utf-8");  
            return sign;  
        }

然后再把这个sign的值,复制出来,然后再点击“验证公钥正确性”,如下图:

  然后输入你的“sign”的值:

  点击“验证”后,如果提示验证通过,那么你这个签名的方式就是对了,如下图:

  再点击“保存”即可。
接下来,我就写一下服务端生成相应的串的方法,全部贴出来,方便大家模仿吧,其实大家按照下面这个图,慢慢研究,也可以的,如下图:

  最后,我们要给回到APP的参数是这个,只要我们按照规则返回即可。下面,我把方法贴出:

    public class AliPayController : Controller  
    {  
        public Dictionary<string, string> PayInfo = new Dictionary<string, string>();  
        //  
        // GET: /AliPay/  
        public ActionResult Index()  
        {  
            testsign();  
            GetPayInfo("0.01");  
            return View();  
        }  

        /// <summary>  
        /// 测试公钥是否对  
        /// </summary>  
        /// <returns></returns>  
        public string testsign()  
        {  
            string privtekey = Config.privtekey;//这个就是生成器里面的那个私钥,第一个大框框那里的.  
            string data = "a=123";//平台上提供的串  
            string sign = RSAFromPkcs8.sign(data, privtekey, "utf-8");  
            return sign;  
        }  

        /// <summary>  
        /// 获取支付信息  
        /// </summary>  
        /// <param name="_amount"></param>  
        /// <returns></returns>  
        public string GetPayInfo(string _amount)//_amount:付款金额  
        {  
            string strJson = string.Empty;  
            try  
            {  
                string orderInfo = GetOrderInfoWithOutEncode(_amount);  
                // 对订单做RSA 签名  
                string sign = RSAFromPkcs8.sign(orderInfo, Config.privtekey, "utf-8");  
                //仅需对sign做URL编码  
                sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);  
                string payInfo = GetOrderInfoWithEncode() + "&sign=" + sign;  
                strJson = payInfo.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下  
                FileLog.WriteLog("支付宝串:" + strJson);  
            }  
            catch (Exception ex)  
            {  
                FileLog.WriteLog(ex.ToString());  

            }  
            return strJson;  
        }  
        /// <summary>  
        /// 不包含Encode的字符串拼接  
        /// </summary>  
        /// <param name="price"></param>  
        /// <returns></returns>  
        public string GetOrderInfoWithOutEncode(string price)  
        {  
            PayInfo.Add("app_id", Config.app_id);  
            PayInfo.Add("biz_content", GetBizContent(price));  
            PayInfo.Add("charset", "utf-8");  
            PayInfo.Add("format", "json");  
            PayInfo.Add("method", "alipay.trade.app.pay");  
            PayInfo.Add("notify_url", "http://wxpay.lmx.ren/ResultNotify");  
            PayInfo.Add("sign_type", "RSA");  
            PayInfo.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));  
            PayInfo.Add("version", "1.0");  
            string strUrl = BuildQueryWithOutEncode(PayInfo);  
            return strUrl;  
        }  
        /// <summary>  
        /// 包含Encode的字符串拼接  
        /// </summary>  
        /// <param name="price"></param>  
        /// <returns></returns>  
        public string GetOrderInfoWithEncode()  
        {  
            string strUrl = BuildQuery(PayInfo, "utf-8");  
            return strUrl;  
        }  
        /// <summary>  
        /// 获取支付内容详情  
        /// </summary>  
        /// <param name="total_amount"></param>  
        /// <returns></returns>  
        public string GetBizContent(string total_amount)  
        {  
            Dictionary<string, string> biz_content_info = new Dictionary<string, string>();  
            biz_content_info.Add("timeout_express", "30m");//该笔订单允许的最晚付款时间,逾期将关闭交易。  
            biz_content_info.Add("seller_id", "");//收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID  
            biz_content_info.Add("product_code", "QUICK_MSECURITY_PAY");//销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY  
            biz_content_info.Add("total_amount", "0.01");//订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]  
            biz_content_info.Add("subject", "Iphone7 128G");//商品的标题/交易标题/订单标题/订单关键字等。  
            biz_content_info.Add("body", "最新款的手机啦");//对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。  
            biz_content_info.Add("out_trade_no", DateTime.Now.ToString("yyyyMMddHHmmssffffff"));//商户网站唯一订单号  
            string strBizContent = JsonHelper.Serialize(biz_content_info);  
            return strBizContent;  
        }  

        /// <summary>  
        /// 组装普通文本请求参数(带Encode)。  
        /// </summary>  
        /// <param name="parameters">Key-Value形式请求参数字典</param>  
        /// <returns>URL编码后的请求数据</returns>  
        public static string BuildQuery(IDictionary<string, string> parameters, string charset)  
        {  
            StringBuilder postData = new StringBuilder();  
            bool hasParam = false;  

            IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();  
            while (dem.MoveNext())  
            {  
                string name = dem.Current.Key;  
                string value = dem.Current.Value;  
                // 忽略参数名或参数值为空的参数  
                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))  
                {  
                    if (hasParam)  
                    {  
                        postData.Append("&");  
                    }  

                    postData.Append(name);  
                    postData.Append("=");  

                    string encodedValue = HttpUtility.UrlEncode(value, Encoding.GetEncoding(charset));  

                    postData.Append(encodedValue);  
                    hasParam = true;  
                }  
            }  
            return postData.ToString();  
        }  
        /// <summary>  
        /// 组装普通文本请求参数(不带Encode)。  
        /// </summary>  
        /// <param name="parameters">Key-Value形式请求参数字典</param>  
        /// <returns>URL编码后的请求数据</returns>  
        public static string BuildQueryWithOutEncode(IDictionary<string, string> parameters)  
        {  
            StringBuilder postData = new StringBuilder();  
            bool hasParam = false;  

            IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();  
            while (dem.MoveNext())  
            {  
                string name = dem.Current.Key;  
                string value = dem.Current.Value;  
                // 忽略参数名或参数值为空的参数  
                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))  
                {  
                    if (hasParam)  
                    {  
                        postData.Append("&");  
                    }  

                    postData.Append(name);  
                    postData.Append("=");  

                    string encodedValue = value;  
                    postData.Append(encodedValue);  
                    hasParam = true;  
                }  
            }  
            return postData.ToString();  
        }  
        /// <summary>  
        /// 配置(请自行填上下面两个参数)  
        /// </summary>  
        public class Config  
        {  
            /// <summary>  
            /// 应用APPID  
            /// </summary>  
            public const string app_id = "";  
            /// <summary>  
            /// 私钥,通过工具生成  
            /// </summary>  
            public const string privtekey = "";  
        }  

    }  

  然后还有一个签名的文件,代码如下:

    /// <summary>  
    /// 类名:RSAFromPkcs8  
    /// 功能:RSA解密、签名、验签  
    /// 详细:该类对Java生成的密钥进行解密和签名以及验签专用类,不需要修改  
    /// 版本:2.0  
    /// 修改日期:2011-05-10  
    /// 说明:  
    /// 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。  
    /// 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。  
    /// </summary>  
    public sealed class RSAFromPkcs8  
    {  
        /// <summary>  
        /// 签名  
        /// </summary>  
        /// <param name="content">需要签名的内容</param>  
        /// <param name="privateKey">私钥</param>  
        /// <param name="input_charset">编码格式</param>  
        /// <returns></returns>  
        public static string sign(string content, string privateKey, string input_charset)  
        {  
            Encoding code = Encoding.GetEncoding(input_charset);  
            byte[] Data = code.GetBytes(content);  
            RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);  
            SHA1 sh = new SHA1CryptoServiceProvider();  
            byte[] signData = rsa.SignData(Data, sh);  
            return Convert.ToBase64String(signData);  
        }  

        /// <summary>  
        /// 验证签名  
        /// </summary>  
        /// <param name="content">需要验证的内容</param>  
        /// <param name="signedString">签名结果</param>  
        /// <param name="publicKey">公钥</param>  
        /// <param name="input_charset">编码格式</param>  
        /// <returns></returns>  
        public static bool verify(string content, string signedString, string publicKey, string input_charset)  
        {  
            bool result = false;  

            Encoding code = Encoding.GetEncoding(input_charset);  
            byte[] Data = code.GetBytes(content);  
            byte[] data = Convert.FromBase64String(signedString);  
            RSAParameters paraPub = ConvertFromPublicKey(publicKey);  
            RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();  
            rsaPub.ImportParameters(paraPub);  

            SHA1 sh = new SHA1CryptoServiceProvider();  
            result = rsaPub.VerifyData(Data, sh, data);  
            return result;  
        }  

        /// <summary>  
        /// 用RSA解密  
        /// </summary>  
        /// <param name="resData">待解密字符串</param>  
        /// <param name="privateKey">私钥</param>  
        /// <param name="input_charset">编码格式</param>  
        /// <returns>解密结果</returns>  
        public static string decryptData(string resData, string privateKey, string input_charset)  
        {  
            byte[] DataToDecrypt = Convert.FromBase64String(resData);  
            List<byte> result = new List<byte>();  

            for (int j = 0; j < DataToDecrypt.Length / 128; j++)  
            {  
                byte[] buf = new byte[128];  
                for (int i = 0; i < 128; i++)  
                {  
                    buf[i] = DataToDecrypt[i + 128 * j];  
                }  
                result.AddRange(decrypt(buf, privateKey, input_charset));  
            }  
            byte[] source = result.ToArray();  
            char[] asciiChars = new char[Encoding.GetEncoding(input_charset).GetCharCount(source, 0, source.Length)];  
            Encoding.GetEncoding(input_charset).GetChars(source, 0, source.Length, asciiChars, 0);  
            return new string(asciiChars);  
        }  

        private static byte[] decrypt(byte[] data, string privateKey, string input_charset)  
        {  
            RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);  
            SHA1 sh = new SHA1CryptoServiceProvider();  
            return rsa.Decrypt(data, false);  
        }  

        /// <summary>  
        /// 解析java生成的pem文件私钥  
        /// </summary>  
        /// <param name="pemstr"></param>  
        /// <returns></returns>  
        private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)  
        {  
            byte[] pkcs8privatekey;  
            pkcs8privatekey = Convert.FromBase64String(pemstr);  
            if (pkcs8privatekey != null)  
            {  

                RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);  
                return rsa;  
            }  
            else  
                return null;  
        }  

        private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)  
        {  

            byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };  
            byte[] seq = new byte[15];  

            MemoryStream mem = new MemoryStream(pkcs8);  
            int lenstream = (int)mem.Length;  
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
            byte bt = 0;  
            ushort twobytes = 0;  

            try  
            {  

                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8130)    //data read as little endian order (actual data order for Sequence is 30 81)  
                    binr.ReadByte();    //advance 1 byte  
                else if (twobytes == 0x8230)  
                    binr.ReadInt16();    //advance 2 bytes  
                else  
                    return null;  

                bt = binr.ReadByte();  
                if (bt != 0x02)  
                    return null;  

                twobytes = binr.ReadUInt16();  

                if (twobytes != 0x0001)  
                    return null;  

                seq = binr.ReadBytes(15);        //read the Sequence OID  
                if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct  
                    return null;  

                bt = binr.ReadByte();  
                if (bt != 0x04)    //expect an Octet string   
                    return null;  

                bt = binr.ReadByte();        //read next byte, or next 2 bytes is  0x81 or 0x82; otherwise bt is the byte count  
                if (bt == 0x81)  
                    binr.ReadByte();  
                else  
                    if (bt == 0x82)  
                        binr.ReadUInt16();  
                //------ at this stage, the remaining sequence should be the RSA private key  

                byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));  
                RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);  
                return rsacsp;  
            }  

            catch (Exception)  
            {  
                return null;  
            }  

            finally { binr.Close(); }  

        }  

        private static bool CompareBytearrays(byte[] a, byte[] b)  
        {  
            if (a.Length != b.Length)  
                return false;  
            int i = 0;  
            foreach (byte c in a)  
            {  
                if (c != b[i])  
                    return false;  
                i++;  
            }  
            return true;  
        }  

        private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)  
        {  
            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;  

            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------  
            MemoryStream mem = new MemoryStream(privkey);  
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
            byte bt = 0;  
            ushort twobytes = 0;  
            int elems = 0;  
            try  
            {  
                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8130)    //data read as little endian order (actual data order for Sequence is 30 81)  
                    binr.ReadByte();    //advance 1 byte  
                else if (twobytes == 0x8230)  
                    binr.ReadInt16();    //advance 2 bytes  
                else  
                    return null;  

                twobytes = binr.ReadUInt16();  
                if (twobytes != 0x0102)    //version number  
                    return null;  
                bt = binr.ReadByte();  
                if (bt != 0x00)  
                    return null;  

                //------  all private key components are Integer sequences ----  
                elems = GetIntegerSize(binr);  
                MODULUS = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                E = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                D = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                P = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                Q = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                DP = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                DQ = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                IQ = binr.ReadBytes(elems);  

                // ------- create RSACryptoServiceProvider instance and initialize with public key -----  
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();  
                RSAParameters RSAparams = new RSAParameters();  
                RSAparams.Modulus = MODULUS;  
                RSAparams.Exponent = E;  
                RSAparams.D = D;  
                RSAparams.P = P;  
                RSAparams.Q = Q;  
                RSAparams.DP = DP;  
                RSAparams.DQ = DQ;  
                RSAparams.InverseQ = IQ;  
                RSA.ImportParameters(RSAparams);  
                return RSA;  
            }  
            catch (Exception)  
            {  
                return null;  
            }  
            finally { binr.Close(); }  
        }  

        private static int GetIntegerSize(BinaryReader binr)  
        {  
            byte bt = 0;  
            byte lowbyte = 0x00;  
            byte highbyte = 0x00;  
            int count = 0;  
            bt = binr.ReadByte();  
            if (bt != 0x02)        //expect integer  
                return 0;  
            bt = binr.ReadByte();  

            if (bt == 0x81)  
                count = binr.ReadByte();    // data size in next byte  
            else  
                if (bt == 0x82)  
                {  
                    highbyte = binr.ReadByte();    // data size in next 2 bytes  
                    lowbyte = binr.ReadByte();  
                    byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };  
                    count = BitConverter.ToInt32(modint, 0);  
                }  
                else  
                {  
                    count = bt;        // we already have the data size  
                }  

            while (binr.ReadByte() == 0x00)  
            {    //remove high order zeros in data  
                count -= 1;  
            }  
            binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte  
            return count;  
        }  

        #region 解析.net 生成的Pem  
        private static RSAParameters ConvertFromPublicKey(string pemFileConent)  
        {  

            byte[] keyData = Convert.FromBase64String(pemFileConent);  
            if (keyData.Length < 162)  
            {  
                throw new ArgumentException("pem file content is incorrect.");  
            }  
            byte[] pemModulus = new byte[128];  
            byte[] pemPublicExponent = new byte[3];  
            Array.Copy(keyData, 29, pemModulus, 0, 128);  
            Array.Copy(keyData, 159, pemPublicExponent, 0, 3);  
            RSAParameters para = new RSAParameters();  
            para.Modulus = pemModulus;  
            para.Exponent = pemPublicExponent;  
            return para;  
        }  

        private static RSAParameters ConvertFromPrivateKey(string pemFileConent)  
        {  
            byte[] keyData = Convert.FromBase64String(pemFileConent);  
            if (keyData.Length < 609)  
            {  
                throw new ArgumentException("pem file content is incorrect.");  
            }  

            int index = 11;  
            byte[] pemModulus = new byte[128];  
            Array.Copy(keyData, index, pemModulus, 0, 128);  

            index += 128;  
            index += 2;//141  
            byte[] pemPublicExponent = new byte[3];  
            Array.Copy(keyData, index, pemPublicExponent, 0, 3);  

            index += 3;  
            index += 4;//148  
            byte[] pemPrivateExponent = new byte[128];  
            Array.Copy(keyData, index, pemPrivateExponent, 0, 128);  

            index += 128;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//279  
            byte[] pemPrime1 = new byte[64];  
            Array.Copy(keyData, index, pemPrime1, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//346  
            byte[] pemPrime2 = new byte[64];  
            Array.Copy(keyData, index, pemPrime2, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//412/413  
            byte[] pemExponent1 = new byte[64];  
            Array.Copy(keyData, index, pemExponent1, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//479/480  
            byte[] pemExponent2 = new byte[64];  
            Array.Copy(keyData, index, pemExponent2, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//545/546  
            byte[] pemCoefficient = new byte[64];  
            Array.Copy(keyData, index, pemCoefficient, 0, 64);  

            RSAParameters para = new RSAParameters();  
            para.Modulus = pemModulus;  
            para.Exponent = pemPublicExponent;  
            para.D = pemPrivateExponent;  
            para.P = pemPrime1;  
            para.Q = pemPrime2;  
            para.DP = pemExponent1;  
            para.DQ = pemExponent2;  
            para.InverseQ = pemCoefficient;  
            return para;  
        }  
        #endregion  

    }  

  服务端的完整代码就如上了。
下面我吧HBuilder里面的代码也写一下,就是选择好“支付宝”之后,执行的代码是:

                    plus.nativeUI.showWaiting();  
                    mui.post("http://wxpay.lmx.ren/AliPay/GetPayInfo", {  
                        _amount: 0.01  
                    }, function(data) {  
                        plus.nativeUI.closeWaiting();  
                        if (data) {  
                            plus.payment.request(payChanel, data, function(result) {  
                                console.log(JSON.stringify(result));  
                                mui.alert(JSON.stringify(result), title);  
                                mui.alert("付费成功", title);  
                            }, function(e) {  
                                console.log(JSON.stringify(e));  
                                alert(JSON.stringify(e));  
                                mui.alert("付费失败", title);  
                            });  
                        } else {  
                            plus.nativeUI.alert("支付失败");  
                        }  
                    });

  好了,就是如此简单。下面贴几张成功的图片,方便大家预览。



  好了,这次教程到此结束。如果代码有漏的,回复评论,我会上来看。如果需要讨论的,加群讨论,QQ个人好友已满,加不了了,抱歉。

  到这里,就大功告成啦,接下来的东西,就由大家自己去展开拓展了,本次经验分享到此结束,写过博客的人都知道,好好写一个博客,需要自己从头重新走一遍代码,所以,各种辛苦,只有自己能体会。所以您如果觉得写得不错,或者对你有帮助,请点“好文要顶”或者“关注我”,顺带也可以评论一两句,大家互相交流交流,转载请保留原作者地址以及姓名。  

我新建一个QQ群,如果有问题,可以在群里提。如果合适,也会根据大家提的比较多的问题,来写篇博文,帮助更多的人,群号:275523437
(点击链接加入群【.Net,MVC,EasyUI,MUI,Html,JS】)[http://jq.qq.com/?_wv=1027&k=2A0RbLd]

(如果有私活,或者一起合作的,也可以私信找我呀,嘿嘿);

作者:南宫萧尘
E-mail:314791147@qq.com
QQ:314791147
日期:2017-01-05

需要实时测试的,可以关注公众号,测试相关功能(根据实际情况,可能会不定时更新程序,如果需要最新程序的,可以加群联系,QQ群号在上面):

【原创分享·微信支付】C# 微信支付教程系列之现金红包
【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付
【原创分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
【原创分享·微信支付】C# MVC 微信支付之微信模板消息推送

继续阅读 »

发现复制过来没用,先贴几个图片都贴不了,博客园和csdn都是我本人发布的,我贴个博客园的地址,如果不想去博客园看,就看下面的文字描述了.
http://www.cnblogs.com/nangong/p/5b5db8769efcd7e10647869fd5ee5539.html#3596715

前言

  最近有点空余时间,所以,就研究了一下APP支付。前面很早就搞完APP的微信支付了,但是由于时间上和应用上的情况,支付宝一直没空去研究。然后等我空了的时候,发现支付宝居然升级了支付逻辑,虽然目前还兼容老的方法,但是新的既然出来了,肯定研究新的了。但是网上几乎都是旧的方法,所以,唯有自己看官方的文档,慢慢一步一步研究了。在研究的过程中,发现,他跟微信支付的差别蛮大的。好了废话不多说了,下面直接来干货。
首先,你得去蚂蚁金服开放平台申请一个应用,地址:https://openhome.alipay.com注册一个应用,如下图:

  应用申请下来之后,需要申请功能,我们这里用到的是“APP支付”功能。如下图:

  如果需要查看相关的文档,那就点击“APP支付”就可以跳转到相关的文档,这里我直接给出APP需要看到的文档,地址:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.J9S7XU&treeId=204&articleId=105465&docType=1 如下图:

  我们在编写服务端的时候,需要用到两个参数,一个是APPID,这个上面的图里面有,还有一个就是密钥,这个是通过签名工具生成,可以通过下面这个地址下载工具,然后生成,地址:https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105351&docType=1

  下载这个工具,然后解压,双击“支付宝RAS密钥生成器SHAwithRSA1024_V1.0.bat”生成即可,这里要注意:TIPS:工具不支持含中文或空格的路径,请下载到英文目录下使用。
打开工具后,如下图:

  先“生成密钥”,然后再复制公钥,然后把公钥复制到平台,如下图:

  再保存,如下图:

  然后再验证公钥的正确性,这里,可以写个小工具来验证,方法如下:

        /// <summary>  
        /// 测试公钥是否对  
        /// </summary>  
        /// <returns></returns>  
        public string testsign()  
        {  
            string privtekey = Config.privtekey;//这个就是生成器里面的那个私钥,第一个大框框那里的.  
            string data = "a=123";//平台上提供的串  
            string sign = RSAFromPkcs8.sign(data, privtekey, "utf-8");  
            return sign;  
        }

然后再把这个sign的值,复制出来,然后再点击“验证公钥正确性”,如下图:

  然后输入你的“sign”的值:

  点击“验证”后,如果提示验证通过,那么你这个签名的方式就是对了,如下图:

  再点击“保存”即可。
接下来,我就写一下服务端生成相应的串的方法,全部贴出来,方便大家模仿吧,其实大家按照下面这个图,慢慢研究,也可以的,如下图:

  最后,我们要给回到APP的参数是这个,只要我们按照规则返回即可。下面,我把方法贴出:

    public class AliPayController : Controller  
    {  
        public Dictionary<string, string> PayInfo = new Dictionary<string, string>();  
        //  
        // GET: /AliPay/  
        public ActionResult Index()  
        {  
            testsign();  
            GetPayInfo("0.01");  
            return View();  
        }  

        /// <summary>  
        /// 测试公钥是否对  
        /// </summary>  
        /// <returns></returns>  
        public string testsign()  
        {  
            string privtekey = Config.privtekey;//这个就是生成器里面的那个私钥,第一个大框框那里的.  
            string data = "a=123";//平台上提供的串  
            string sign = RSAFromPkcs8.sign(data, privtekey, "utf-8");  
            return sign;  
        }  

        /// <summary>  
        /// 获取支付信息  
        /// </summary>  
        /// <param name="_amount"></param>  
        /// <returns></returns>  
        public string GetPayInfo(string _amount)//_amount:付款金额  
        {  
            string strJson = string.Empty;  
            try  
            {  
                string orderInfo = GetOrderInfoWithOutEncode(_amount);  
                // 对订单做RSA 签名  
                string sign = RSAFromPkcs8.sign(orderInfo, Config.privtekey, "utf-8");  
                //仅需对sign做URL编码  
                sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);  
                string payInfo = GetOrderInfoWithEncode() + "&sign=" + sign;  
                strJson = payInfo.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下  
                FileLog.WriteLog("支付宝串:" + strJson);  
            }  
            catch (Exception ex)  
            {  
                FileLog.WriteLog(ex.ToString());  

            }  
            return strJson;  
        }  
        /// <summary>  
        /// 不包含Encode的字符串拼接  
        /// </summary>  
        /// <param name="price"></param>  
        /// <returns></returns>  
        public string GetOrderInfoWithOutEncode(string price)  
        {  
            PayInfo.Add("app_id", Config.app_id);  
            PayInfo.Add("biz_content", GetBizContent(price));  
            PayInfo.Add("charset", "utf-8");  
            PayInfo.Add("format", "json");  
            PayInfo.Add("method", "alipay.trade.app.pay");  
            PayInfo.Add("notify_url", "http://wxpay.lmx.ren/ResultNotify");  
            PayInfo.Add("sign_type", "RSA");  
            PayInfo.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));  
            PayInfo.Add("version", "1.0");  
            string strUrl = BuildQueryWithOutEncode(PayInfo);  
            return strUrl;  
        }  
        /// <summary>  
        /// 包含Encode的字符串拼接  
        /// </summary>  
        /// <param name="price"></param>  
        /// <returns></returns>  
        public string GetOrderInfoWithEncode()  
        {  
            string strUrl = BuildQuery(PayInfo, "utf-8");  
            return strUrl;  
        }  
        /// <summary>  
        /// 获取支付内容详情  
        /// </summary>  
        /// <param name="total_amount"></param>  
        /// <returns></returns>  
        public string GetBizContent(string total_amount)  
        {  
            Dictionary<string, string> biz_content_info = new Dictionary<string, string>();  
            biz_content_info.Add("timeout_express", "30m");//该笔订单允许的最晚付款时间,逾期将关闭交易。  
            biz_content_info.Add("seller_id", "");//收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID  
            biz_content_info.Add("product_code", "QUICK_MSECURITY_PAY");//销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY  
            biz_content_info.Add("total_amount", "0.01");//订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]  
            biz_content_info.Add("subject", "Iphone7 128G");//商品的标题/交易标题/订单标题/订单关键字等。  
            biz_content_info.Add("body", "最新款的手机啦");//对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。  
            biz_content_info.Add("out_trade_no", DateTime.Now.ToString("yyyyMMddHHmmssffffff"));//商户网站唯一订单号  
            string strBizContent = JsonHelper.Serialize(biz_content_info);  
            return strBizContent;  
        }  

        /// <summary>  
        /// 组装普通文本请求参数(带Encode)。  
        /// </summary>  
        /// <param name="parameters">Key-Value形式请求参数字典</param>  
        /// <returns>URL编码后的请求数据</returns>  
        public static string BuildQuery(IDictionary<string, string> parameters, string charset)  
        {  
            StringBuilder postData = new StringBuilder();  
            bool hasParam = false;  

            IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();  
            while (dem.MoveNext())  
            {  
                string name = dem.Current.Key;  
                string value = dem.Current.Value;  
                // 忽略参数名或参数值为空的参数  
                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))  
                {  
                    if (hasParam)  
                    {  
                        postData.Append("&");  
                    }  

                    postData.Append(name);  
                    postData.Append("=");  

                    string encodedValue = HttpUtility.UrlEncode(value, Encoding.GetEncoding(charset));  

                    postData.Append(encodedValue);  
                    hasParam = true;  
                }  
            }  
            return postData.ToString();  
        }  
        /// <summary>  
        /// 组装普通文本请求参数(不带Encode)。  
        /// </summary>  
        /// <param name="parameters">Key-Value形式请求参数字典</param>  
        /// <returns>URL编码后的请求数据</returns>  
        public static string BuildQueryWithOutEncode(IDictionary<string, string> parameters)  
        {  
            StringBuilder postData = new StringBuilder();  
            bool hasParam = false;  

            IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();  
            while (dem.MoveNext())  
            {  
                string name = dem.Current.Key;  
                string value = dem.Current.Value;  
                // 忽略参数名或参数值为空的参数  
                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))  
                {  
                    if (hasParam)  
                    {  
                        postData.Append("&");  
                    }  

                    postData.Append(name);  
                    postData.Append("=");  

                    string encodedValue = value;  
                    postData.Append(encodedValue);  
                    hasParam = true;  
                }  
            }  
            return postData.ToString();  
        }  
        /// <summary>  
        /// 配置(请自行填上下面两个参数)  
        /// </summary>  
        public class Config  
        {  
            /// <summary>  
            /// 应用APPID  
            /// </summary>  
            public const string app_id = "";  
            /// <summary>  
            /// 私钥,通过工具生成  
            /// </summary>  
            public const string privtekey = "";  
        }  

    }  

  然后还有一个签名的文件,代码如下:

    /// <summary>  
    /// 类名:RSAFromPkcs8  
    /// 功能:RSA解密、签名、验签  
    /// 详细:该类对Java生成的密钥进行解密和签名以及验签专用类,不需要修改  
    /// 版本:2.0  
    /// 修改日期:2011-05-10  
    /// 说明:  
    /// 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。  
    /// 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。  
    /// </summary>  
    public sealed class RSAFromPkcs8  
    {  
        /// <summary>  
        /// 签名  
        /// </summary>  
        /// <param name="content">需要签名的内容</param>  
        /// <param name="privateKey">私钥</param>  
        /// <param name="input_charset">编码格式</param>  
        /// <returns></returns>  
        public static string sign(string content, string privateKey, string input_charset)  
        {  
            Encoding code = Encoding.GetEncoding(input_charset);  
            byte[] Data = code.GetBytes(content);  
            RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);  
            SHA1 sh = new SHA1CryptoServiceProvider();  
            byte[] signData = rsa.SignData(Data, sh);  
            return Convert.ToBase64String(signData);  
        }  

        /// <summary>  
        /// 验证签名  
        /// </summary>  
        /// <param name="content">需要验证的内容</param>  
        /// <param name="signedString">签名结果</param>  
        /// <param name="publicKey">公钥</param>  
        /// <param name="input_charset">编码格式</param>  
        /// <returns></returns>  
        public static bool verify(string content, string signedString, string publicKey, string input_charset)  
        {  
            bool result = false;  

            Encoding code = Encoding.GetEncoding(input_charset);  
            byte[] Data = code.GetBytes(content);  
            byte[] data = Convert.FromBase64String(signedString);  
            RSAParameters paraPub = ConvertFromPublicKey(publicKey);  
            RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();  
            rsaPub.ImportParameters(paraPub);  

            SHA1 sh = new SHA1CryptoServiceProvider();  
            result = rsaPub.VerifyData(Data, sh, data);  
            return result;  
        }  

        /// <summary>  
        /// 用RSA解密  
        /// </summary>  
        /// <param name="resData">待解密字符串</param>  
        /// <param name="privateKey">私钥</param>  
        /// <param name="input_charset">编码格式</param>  
        /// <returns>解密结果</returns>  
        public static string decryptData(string resData, string privateKey, string input_charset)  
        {  
            byte[] DataToDecrypt = Convert.FromBase64String(resData);  
            List<byte> result = new List<byte>();  

            for (int j = 0; j < DataToDecrypt.Length / 128; j++)  
            {  
                byte[] buf = new byte[128];  
                for (int i = 0; i < 128; i++)  
                {  
                    buf[i] = DataToDecrypt[i + 128 * j];  
                }  
                result.AddRange(decrypt(buf, privateKey, input_charset));  
            }  
            byte[] source = result.ToArray();  
            char[] asciiChars = new char[Encoding.GetEncoding(input_charset).GetCharCount(source, 0, source.Length)];  
            Encoding.GetEncoding(input_charset).GetChars(source, 0, source.Length, asciiChars, 0);  
            return new string(asciiChars);  
        }  

        private static byte[] decrypt(byte[] data, string privateKey, string input_charset)  
        {  
            RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);  
            SHA1 sh = new SHA1CryptoServiceProvider();  
            return rsa.Decrypt(data, false);  
        }  

        /// <summary>  
        /// 解析java生成的pem文件私钥  
        /// </summary>  
        /// <param name="pemstr"></param>  
        /// <returns></returns>  
        private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)  
        {  
            byte[] pkcs8privatekey;  
            pkcs8privatekey = Convert.FromBase64String(pemstr);  
            if (pkcs8privatekey != null)  
            {  

                RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);  
                return rsa;  
            }  
            else  
                return null;  
        }  

        private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)  
        {  

            byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };  
            byte[] seq = new byte[15];  

            MemoryStream mem = new MemoryStream(pkcs8);  
            int lenstream = (int)mem.Length;  
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
            byte bt = 0;  
            ushort twobytes = 0;  

            try  
            {  

                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8130)    //data read as little endian order (actual data order for Sequence is 30 81)  
                    binr.ReadByte();    //advance 1 byte  
                else if (twobytes == 0x8230)  
                    binr.ReadInt16();    //advance 2 bytes  
                else  
                    return null;  

                bt = binr.ReadByte();  
                if (bt != 0x02)  
                    return null;  

                twobytes = binr.ReadUInt16();  

                if (twobytes != 0x0001)  
                    return null;  

                seq = binr.ReadBytes(15);        //read the Sequence OID  
                if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct  
                    return null;  

                bt = binr.ReadByte();  
                if (bt != 0x04)    //expect an Octet string   
                    return null;  

                bt = binr.ReadByte();        //read next byte, or next 2 bytes is  0x81 or 0x82; otherwise bt is the byte count  
                if (bt == 0x81)  
                    binr.ReadByte();  
                else  
                    if (bt == 0x82)  
                        binr.ReadUInt16();  
                //------ at this stage, the remaining sequence should be the RSA private key  

                byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));  
                RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);  
                return rsacsp;  
            }  

            catch (Exception)  
            {  
                return null;  
            }  

            finally { binr.Close(); }  

        }  

        private static bool CompareBytearrays(byte[] a, byte[] b)  
        {  
            if (a.Length != b.Length)  
                return false;  
            int i = 0;  
            foreach (byte c in a)  
            {  
                if (c != b[i])  
                    return false;  
                i++;  
            }  
            return true;  
        }  

        private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)  
        {  
            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;  

            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------  
            MemoryStream mem = new MemoryStream(privkey);  
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
            byte bt = 0;  
            ushort twobytes = 0;  
            int elems = 0;  
            try  
            {  
                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8130)    //data read as little endian order (actual data order for Sequence is 30 81)  
                    binr.ReadByte();    //advance 1 byte  
                else if (twobytes == 0x8230)  
                    binr.ReadInt16();    //advance 2 bytes  
                else  
                    return null;  

                twobytes = binr.ReadUInt16();  
                if (twobytes != 0x0102)    //version number  
                    return null;  
                bt = binr.ReadByte();  
                if (bt != 0x00)  
                    return null;  

                //------  all private key components are Integer sequences ----  
                elems = GetIntegerSize(binr);  
                MODULUS = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                E = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                D = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                P = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                Q = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                DP = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                DQ = binr.ReadBytes(elems);  

                elems = GetIntegerSize(binr);  
                IQ = binr.ReadBytes(elems);  

                // ------- create RSACryptoServiceProvider instance and initialize with public key -----  
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();  
                RSAParameters RSAparams = new RSAParameters();  
                RSAparams.Modulus = MODULUS;  
                RSAparams.Exponent = E;  
                RSAparams.D = D;  
                RSAparams.P = P;  
                RSAparams.Q = Q;  
                RSAparams.DP = DP;  
                RSAparams.DQ = DQ;  
                RSAparams.InverseQ = IQ;  
                RSA.ImportParameters(RSAparams);  
                return RSA;  
            }  
            catch (Exception)  
            {  
                return null;  
            }  
            finally { binr.Close(); }  
        }  

        private static int GetIntegerSize(BinaryReader binr)  
        {  
            byte bt = 0;  
            byte lowbyte = 0x00;  
            byte highbyte = 0x00;  
            int count = 0;  
            bt = binr.ReadByte();  
            if (bt != 0x02)        //expect integer  
                return 0;  
            bt = binr.ReadByte();  

            if (bt == 0x81)  
                count = binr.ReadByte();    // data size in next byte  
            else  
                if (bt == 0x82)  
                {  
                    highbyte = binr.ReadByte();    // data size in next 2 bytes  
                    lowbyte = binr.ReadByte();  
                    byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };  
                    count = BitConverter.ToInt32(modint, 0);  
                }  
                else  
                {  
                    count = bt;        // we already have the data size  
                }  

            while (binr.ReadByte() == 0x00)  
            {    //remove high order zeros in data  
                count -= 1;  
            }  
            binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte  
            return count;  
        }  

        #region 解析.net 生成的Pem  
        private static RSAParameters ConvertFromPublicKey(string pemFileConent)  
        {  

            byte[] keyData = Convert.FromBase64String(pemFileConent);  
            if (keyData.Length < 162)  
            {  
                throw new ArgumentException("pem file content is incorrect.");  
            }  
            byte[] pemModulus = new byte[128];  
            byte[] pemPublicExponent = new byte[3];  
            Array.Copy(keyData, 29, pemModulus, 0, 128);  
            Array.Copy(keyData, 159, pemPublicExponent, 0, 3);  
            RSAParameters para = new RSAParameters();  
            para.Modulus = pemModulus;  
            para.Exponent = pemPublicExponent;  
            return para;  
        }  

        private static RSAParameters ConvertFromPrivateKey(string pemFileConent)  
        {  
            byte[] keyData = Convert.FromBase64String(pemFileConent);  
            if (keyData.Length < 609)  
            {  
                throw new ArgumentException("pem file content is incorrect.");  
            }  

            int index = 11;  
            byte[] pemModulus = new byte[128];  
            Array.Copy(keyData, index, pemModulus, 0, 128);  

            index += 128;  
            index += 2;//141  
            byte[] pemPublicExponent = new byte[3];  
            Array.Copy(keyData, index, pemPublicExponent, 0, 3);  

            index += 3;  
            index += 4;//148  
            byte[] pemPrivateExponent = new byte[128];  
            Array.Copy(keyData, index, pemPrivateExponent, 0, 128);  

            index += 128;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//279  
            byte[] pemPrime1 = new byte[64];  
            Array.Copy(keyData, index, pemPrime1, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//346  
            byte[] pemPrime2 = new byte[64];  
            Array.Copy(keyData, index, pemPrime2, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//412/413  
            byte[] pemExponent1 = new byte[64];  
            Array.Copy(keyData, index, pemExponent1, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//479/480  
            byte[] pemExponent2 = new byte[64];  
            Array.Copy(keyData, index, pemExponent2, 0, 64);  

            index += 64;  
            index += ((int)keyData[index + 1] == 64 ? 2 : 3);//545/546  
            byte[] pemCoefficient = new byte[64];  
            Array.Copy(keyData, index, pemCoefficient, 0, 64);  

            RSAParameters para = new RSAParameters();  
            para.Modulus = pemModulus;  
            para.Exponent = pemPublicExponent;  
            para.D = pemPrivateExponent;  
            para.P = pemPrime1;  
            para.Q = pemPrime2;  
            para.DP = pemExponent1;  
            para.DQ = pemExponent2;  
            para.InverseQ = pemCoefficient;  
            return para;  
        }  
        #endregion  

    }  

  服务端的完整代码就如上了。
下面我吧HBuilder里面的代码也写一下,就是选择好“支付宝”之后,执行的代码是:

                    plus.nativeUI.showWaiting();  
                    mui.post("http://wxpay.lmx.ren/AliPay/GetPayInfo", {  
                        _amount: 0.01  
                    }, function(data) {  
                        plus.nativeUI.closeWaiting();  
                        if (data) {  
                            plus.payment.request(payChanel, data, function(result) {  
                                console.log(JSON.stringify(result));  
                                mui.alert(JSON.stringify(result), title);  
                                mui.alert("付费成功", title);  
                            }, function(e) {  
                                console.log(JSON.stringify(e));  
                                alert(JSON.stringify(e));  
                                mui.alert("付费失败", title);  
                            });  
                        } else {  
                            plus.nativeUI.alert("支付失败");  
                        }  
                    });

  好了,就是如此简单。下面贴几张成功的图片,方便大家预览。



  好了,这次教程到此结束。如果代码有漏的,回复评论,我会上来看。如果需要讨论的,加群讨论,QQ个人好友已满,加不了了,抱歉。

  到这里,就大功告成啦,接下来的东西,就由大家自己去展开拓展了,本次经验分享到此结束,写过博客的人都知道,好好写一个博客,需要自己从头重新走一遍代码,所以,各种辛苦,只有自己能体会。所以您如果觉得写得不错,或者对你有帮助,请点“好文要顶”或者“关注我”,顺带也可以评论一两句,大家互相交流交流,转载请保留原作者地址以及姓名。  

我新建一个QQ群,如果有问题,可以在群里提。如果合适,也会根据大家提的比较多的问题,来写篇博文,帮助更多的人,群号:275523437
(点击链接加入群【.Net,MVC,EasyUI,MUI,Html,JS】)[http://jq.qq.com/?_wv=1027&k=2A0RbLd]

(如果有私活,或者一起合作的,也可以私信找我呀,嘿嘿);

作者:南宫萧尘
E-mail:314791147@qq.com
QQ:314791147
日期:2017-01-05

需要实时测试的,可以关注公众号,测试相关功能(根据实际情况,可能会不定时更新程序,如果需要最新程序的,可以加群联系,QQ群号在上面):

【原创分享·微信支付】C# 微信支付教程系列之现金红包
【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付
【原创分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
【原创分享·微信支付】C# MVC 微信支付之微信模板消息推送

收起阅读 »

最近每次更新 安卓版本都会带来新的问题

新版本

每次版本更新都会带来新的问题 ,请下次更新的时候,测试仔细点,明明上一次可以运行正常的代码 ,更新以后莫名其妙的不可以。
这样非常让人头疼。
安卓本来就优化性能 现在不停的出现新的问题,增加许多的工作量

每次版本更新都会带来新的问题 ,请下次更新的时候,测试仔细点,明明上一次可以运行正常的代码 ,更新以后莫名其妙的不可以。
这样非常让人头疼。
安卓本来就优化性能 现在不停的出现新的问题,增加许多的工作量

video 全屏横屏时 show和hide概率性失效的解决方法

video

Andriod端

当video全屏横屏后,show和hide可能失效

解决办法
document.webkitCancelFullScreen();
这句退出video全屏必须放在
plus.navigator.setFullscreen( false );
plus.screen.lockOrientation( 'portrait-primary' );
之前

继续阅读 »

Andriod端

当video全屏横屏后,show和hide可能失效

解决办法
document.webkitCancelFullScreen();
这句退出video全屏必须放在
plus.navigator.setFullscreen( false );
plus.screen.lockOrientation( 'portrait-primary' );
之前

收起阅读 »

hbuilder在登录时卡在登录页面无法进入问题

HBuilder

如果你也出现上述问题,可以先选择暂不登录,直接进入编辑器界面,然后在右下角点击登录,会弹出个登录框,输入你的账号密码就可以正常登录了,亲测有用!

如果你也出现上述问题,可以先选择暂不登录,直接进入编辑器界面,然后在右下角点击登录,会弹出个登录框,输入你的账号密码就可以正常登录了,亲测有用!

h.js 发布了!!开源、永久免费!致力于优化mui的dom操,解决mui不提供dom操作的小遗憾,完美兼容mui,开启更高效的开发。

mui 开源 技术分享

h.js 发布了!!开源、永久免费!
h.js - dom 操作的小伙伴,极小 超快!
致力于优化mui的dom操,解决mui不提供dom操作的小遗憾,完美兼容mui,开启更高效的开发。
8k的js 极小、极快。
视频教程已经录制完毕 60分钟成为dom操作老司机! jQuery老司机直接上车!
官网 http://www.hcoder.net/h

希望大家帮忙顶贴 支持一下 谢谢了, 希望官方给个支持 谢谢

继续阅读 »

h.js 发布了!!开源、永久免费!
h.js - dom 操作的小伙伴,极小 超快!
致力于优化mui的dom操,解决mui不提供dom操作的小遗憾,完美兼容mui,开启更高效的开发。
8k的js 极小、极快。
视频教程已经录制完毕 60分钟成为dom操作老司机! jQuery老司机直接上车!
官网 http://www.hcoder.net/h

希望大家帮忙顶贴 支持一下 谢谢了, 希望官方给个支持 谢谢

收起阅读 »

提交苹果App store审核强制要求ATS,及访问非HTTPS服务器地址导致文件下载、上传、ajax无法正常工作 的问题

iOS打包

云打包已默认关闭ATS,无需在进行关闭配置, 如果应用完全支持https可以使用文章中的方法确保应用通讯的数据安全

iOS从9.0版本开始添加了对应用数据传输的安全性要求(ATS),为了保证向下兼容HBuilder云端打包做了统一配置应用关闭ATS限制开关。目前从公开的消息是从2017年开始App store审核将加强关闭ATS应用的审核(具体情况暂时还不明确,预计可能会导致应用无法通过审核)。
实际上苹果对应用的ATS是分等级控制,可以全局关闭ATS限制,也可对Webview访问资源关闭ATS限制,也可对特定的域名进行ATS策略控制(指定特定域名不受ATS限制),详情可参考网上资料(http://www.jianshu.com/p/36ddc5b009a7)。
为了避免关闭ATS限制导致应用无法通过审核的问题,HBuilder云端打包将调整默认策略为开启ATS限制(Alpha打包机从2016-12-29日开始,正式打包机计划从2017-1-1日开始),并添加以下配置策略:

  1. 关闭通过webview 发出的网络请求的ATS限制(Webview对象可以访问http资源);
  2. 关闭载入任意本地资源ATS限制(第三方SDK访问本地资源)。

如果开启ATS后,iOS9以下需要做兼容处理.,可通过判断系统版本请求不同的地址(plus.os.version)

调整默认配置后对普通5+应用运行不会有影响,在Webview中仍然可以访问http资源,但是以下5+ API请求的服务器地址必需符合ATS规范(使用https),影响的模块如下:

  1. Downloader(文件下载),影响API范围:plus.downloader.*;
  2. Uploader(文件上传),影响API范围:plus.uploader.*;
  3. XMLHtttpRequest(跨域网络连接),影响API:plus.new.XMLHttpRequest。
    如果应用中使用了以上5+ API访问非https服务器地址,将会导致无法正常工作,推荐将服务器地址修改为https来解决,也可以使用下面的指定特定域名关闭ATS限制方法来配置允许http访问特定域名。

如果应用需要自定义ATS策略,可通过以下方法修改:

设置全局关闭ATS限制

在manifest.json的“plus->distribute->apple”节点下添加plistcmds数据如下:

"plistcmds":["Add :NSAppTransportSecurity:NSAllowsArbitraryLoads bool true"]

如果应用完全支持https(请使用ATS检测工具测试地址是否满足 ATS 特性),可使用以下方法设置Webview开启ATS限制及载入本地资源ATS限制

对于服务器已经完全支持https的应用,推荐配置这种策略,可确保应用通讯的数据安全。
在manifest.json的“plus->distribute->apple”节点下添加plistcmds数据如下:

"plistcmds":["Set :NSAppTransportSecurity:NSAllowsArbitraryLoadsInWebContent NO",  
    "Set :NSAppTransportSecurity:NSAllowsLocalNetworking NO"  
]

指定特定域名关闭ATS限制

特定域名关闭ATS限制后可以通过http访问服务器,可能需要开发者提交App store审查时说明原因。
在manifest.json的“plus->distribute->apple”节点下添加plistcmds数据如下:

"plistcmds":["Add :NSAppTransportSecurity:NSExceptionDomains dict ",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:qq.com dict ",                      // 指定允许不使用ATS访问qq.com域名  
    "Add :NSAppTransportSecurity:NSExceptionDomains:qq.com:NSIncludesSubdomains bool true",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:qq.com:NSExceptionAllowsInsecureHTTPLoads bool true",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:dcloud.net.cn dict ",            // 指定允许不使用ATS访问dcloud.net.cn域名  
    "Add :NSAppTransportSecurity:NSExceptionDomains:dcloud.net.cn:NSIncludesSubdomains bool true",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:dcloud.net.cn:NSExceptionAllowsInsecureHTTPLoads bool true"  
]

云打包后生效

真机调试基座默认关闭ATS,但HBuilder (7.6.5)是开启的,需要使用此基座替换使用https://pan.baidu.com/s/1qXZD9SS其他低版本不受影响

目前还不能确定从2017年开始App store审核对于ATS要求的准确细则,有碰到相关问题导致审核失败的开发者可以反馈问题,我们将尽快跟进处理

继续阅读 »

云打包已默认关闭ATS,无需在进行关闭配置, 如果应用完全支持https可以使用文章中的方法确保应用通讯的数据安全

iOS从9.0版本开始添加了对应用数据传输的安全性要求(ATS),为了保证向下兼容HBuilder云端打包做了统一配置应用关闭ATS限制开关。目前从公开的消息是从2017年开始App store审核将加强关闭ATS应用的审核(具体情况暂时还不明确,预计可能会导致应用无法通过审核)。
实际上苹果对应用的ATS是分等级控制,可以全局关闭ATS限制,也可对Webview访问资源关闭ATS限制,也可对特定的域名进行ATS策略控制(指定特定域名不受ATS限制),详情可参考网上资料(http://www.jianshu.com/p/36ddc5b009a7)。
为了避免关闭ATS限制导致应用无法通过审核的问题,HBuilder云端打包将调整默认策略为开启ATS限制(Alpha打包机从2016-12-29日开始,正式打包机计划从2017-1-1日开始),并添加以下配置策略:

  1. 关闭通过webview 发出的网络请求的ATS限制(Webview对象可以访问http资源);
  2. 关闭载入任意本地资源ATS限制(第三方SDK访问本地资源)。

如果开启ATS后,iOS9以下需要做兼容处理.,可通过判断系统版本请求不同的地址(plus.os.version)

调整默认配置后对普通5+应用运行不会有影响,在Webview中仍然可以访问http资源,但是以下5+ API请求的服务器地址必需符合ATS规范(使用https),影响的模块如下:

  1. Downloader(文件下载),影响API范围:plus.downloader.*;
  2. Uploader(文件上传),影响API范围:plus.uploader.*;
  3. XMLHtttpRequest(跨域网络连接),影响API:plus.new.XMLHttpRequest。
    如果应用中使用了以上5+ API访问非https服务器地址,将会导致无法正常工作,推荐将服务器地址修改为https来解决,也可以使用下面的指定特定域名关闭ATS限制方法来配置允许http访问特定域名。

如果应用需要自定义ATS策略,可通过以下方法修改:

设置全局关闭ATS限制

在manifest.json的“plus->distribute->apple”节点下添加plistcmds数据如下:

"plistcmds":["Add :NSAppTransportSecurity:NSAllowsArbitraryLoads bool true"]

如果应用完全支持https(请使用ATS检测工具测试地址是否满足 ATS 特性),可使用以下方法设置Webview开启ATS限制及载入本地资源ATS限制

对于服务器已经完全支持https的应用,推荐配置这种策略,可确保应用通讯的数据安全。
在manifest.json的“plus->distribute->apple”节点下添加plistcmds数据如下:

"plistcmds":["Set :NSAppTransportSecurity:NSAllowsArbitraryLoadsInWebContent NO",  
    "Set :NSAppTransportSecurity:NSAllowsLocalNetworking NO"  
]

指定特定域名关闭ATS限制

特定域名关闭ATS限制后可以通过http访问服务器,可能需要开发者提交App store审查时说明原因。
在manifest.json的“plus->distribute->apple”节点下添加plistcmds数据如下:

"plistcmds":["Add :NSAppTransportSecurity:NSExceptionDomains dict ",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:qq.com dict ",                      // 指定允许不使用ATS访问qq.com域名  
    "Add :NSAppTransportSecurity:NSExceptionDomains:qq.com:NSIncludesSubdomains bool true",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:qq.com:NSExceptionAllowsInsecureHTTPLoads bool true",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:dcloud.net.cn dict ",            // 指定允许不使用ATS访问dcloud.net.cn域名  
    "Add :NSAppTransportSecurity:NSExceptionDomains:dcloud.net.cn:NSIncludesSubdomains bool true",  
    "Add :NSAppTransportSecurity:NSExceptionDomains:dcloud.net.cn:NSExceptionAllowsInsecureHTTPLoads bool true"  
]

云打包后生效

真机调试基座默认关闭ATS,但HBuilder (7.6.5)是开启的,需要使用此基座替换使用https://pan.baidu.com/s/1qXZD9SS其他低版本不受影响

目前还不能确定从2017年开始App store审核对于ATS要求的准确细则,有碰到相关问题导致审核失败的开发者可以反馈问题,我们将尽快跟进处理

收起阅读 »