HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

预加载只加载了一次,传值需要定时刷新

 mui.init();  
            mui.plusReady(function() {  
                mui("ul").on('tap', 'img', function() {  
                    var num = this.src; //获取点击该标签的值  
//                  alert(num); //用于测试,显示该值  
                    plus.storage.setItem('a', num);  
                    setTimeout(function() {  
                        var page = mui.preload({  
                            url: "second.html",  
                            id: "second",  
                        });  
                    }, 0.001);  
                    mui.openWindow({  
                        url: "second.html",  
                        id: "second",  
                    });  
                    plus.nativeUI.closeWaiting();  
                });  
            });
继续阅读 »
 mui.init();  
            mui.plusReady(function() {  
                mui("ul").on('tap', 'img', function() {  
                    var num = this.src; //获取点击该标签的值  
//                  alert(num); //用于测试,显示该值  
                    plus.storage.setItem('a', num);  
                    setTimeout(function() {  
                        var page = mui.preload({  
                            url: "second.html",  
                            id: "second",  
                        });  
                    }, 0.001);  
                    mui.openWindow({  
                        url: "second.html",  
                        id: "second",  
                    });  
                    plus.nativeUI.closeWaiting();  
                });  
            });
收起阅读 »

获取城市或者省份(已解决,另附上代码及说明),不用谢

function getPosBaidu() {  
                alert("获取百度定位位置信息:");  
                plus.geolocation.getCurrentPosition(function getinfo(position){  
                    var str = position.address.city; //获取城市信息  
                    document.getElementById("addr").innerHTML = str;  
                    alert(str);  
                }, function(e) {  
                    alert("获取百度定位位置信息失败:"   e.message);  
                }, {provider: 'baidu'});  
            }

参考的测试的数据格式

{"coordsType":"bd09ll","address":{"cityCode":"132","province":"重庆市","district":"渝北区","street":"红黄路","city":"重庆市","country":"中国"},"addresses":"中国重庆市渝北区红黄路xxx号","coords":{"latitude":29.594499,"longitude":106.545088,"accuracy":188,"altitude":5e-324,"heading":null,"speed":0,"altitudeAccuracy":0},"timestamp":1449220694000} at plus/geolocation.html:32
继续阅读 »
function getPosBaidu() {  
                alert("获取百度定位位置信息:");  
                plus.geolocation.getCurrentPosition(function getinfo(position){  
                    var str = position.address.city; //获取城市信息  
                    document.getElementById("addr").innerHTML = str;  
                    alert(str);  
                }, function(e) {  
                    alert("获取百度定位位置信息失败:"   e.message);  
                }, {provider: 'baidu'});  
            }

参考的测试的数据格式

{"coordsType":"bd09ll","address":{"cityCode":"132","province":"重庆市","district":"渝北区","street":"红黄路","city":"重庆市","country":"中国"},"addresses":"中国重庆市渝北区红黄路xxx号","coords":{"latitude":29.594499,"longitude":106.545088,"accuracy":188,"altitude":5e-324,"heading":null,"speed":0,"altitudeAccuracy":0},"timestamp":1449220694000} at plus/geolocation.html:32
收起阅读 »

未能创建视图

默认话题

更新了svn的版本后报错:
未能创建视图:
E:\参考\HBuilder\plugins\com.pandora.cef3.win32.x86_2.0.0.201511261800\os\win32\x86\jcef.dll: Can't find dependent

部分详细错误:
java.lang.UnsatisfiedLinkError: E:\参考\HBuilder\plugins\com.pandora.cef3.win32.x86_2.0.0.201511261800\os\win32\x86\jcef.dll: Can't find dependent libraries
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary1(Unknown Source)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)

继续阅读 »

更新了svn的版本后报错:
未能创建视图:
E:\参考\HBuilder\plugins\com.pandora.cef3.win32.x86_2.0.0.201511261800\os\win32\x86\jcef.dll: Can't find dependent

部分详细错误:
java.lang.UnsatisfiedLinkError: E:\参考\HBuilder\plugins\com.pandora.cef3.win32.x86_2.0.0.201511261800\os\win32\x86\jcef.dll: Can't find dependent libraries
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary1(Unknown Source)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)

收起阅读 »

手机验证60s后重发问题?要清除定时

var countdown = 60;  
            function timedown(obj) {  
                if (countdown == 0) {  
                    obj.removeAttribute("disabled");  
                    obj.value = "获取验证码";  
                    return clearTimeout();//清除定时,没有的话会导致后面每次减一越来越快  
                } else {  
                    obj.setAttribute("disabled", true);  
                    obj.value = "重新发送(" + countdown + ")";  
                    countdown--;  
                }  
                setTimeout(function() {  
                        timedown(obj);  
                    }, 1000);//定时每秒减一  
            }  
            //手机验证,判断60s之后再发送验证码  
            function settime(obj) {  
                var reg_phone_number = document.getElementById("reg_phone_number");  
                var reg_getcode = document.getElementById("reg_getcode");  
                var check_phone_number = /^1[3458]\d{9}$/;  
                if (reg_phone_number.value.length == 0) {  
                    plus.ui.toast("手机号不能为空");  
                    return;  
                }  
                if (reg_phone_number.value.length != 11) {  
                    plus.ui.toast("请输入有效的手机号!");  
                    return;  
                }  
                if (!reg_phone_number.value.match(check_phone_number)) {  
                    plus.ui.toast("请输入有效的手机号");  
                    return;  
                } else {  
                                        //短信发送  
                    /*                  var url = strWebServer + "/App_Fun/Login/Reg_Edit.ashx";  
                                        var _params = {  
                                            config: {  
                                                action: "getCode",  
                                                strTel: reg_phone_number.value  
                                            },  
                                            fun_Success: function(data) {  
                                                if (data.success == "true") {  
                                                    plus.ui.toast("短信已发送");  
                                                } else {  
                                                    plus.ui.toast(data.msg);  
                                                }  
                                            }  
                                        };  
                                        fun_ExcuteAjax(url, _params);*/  
                    countdown = 5;  
//                  alert("haha");//用于测试  
                    timedown(obj);  
                }  
            }
继续阅读 »
var countdown = 60;  
            function timedown(obj) {  
                if (countdown == 0) {  
                    obj.removeAttribute("disabled");  
                    obj.value = "获取验证码";  
                    return clearTimeout();//清除定时,没有的话会导致后面每次减一越来越快  
                } else {  
                    obj.setAttribute("disabled", true);  
                    obj.value = "重新发送(" + countdown + ")";  
                    countdown--;  
                }  
                setTimeout(function() {  
                        timedown(obj);  
                    }, 1000);//定时每秒减一  
            }  
            //手机验证,判断60s之后再发送验证码  
            function settime(obj) {  
                var reg_phone_number = document.getElementById("reg_phone_number");  
                var reg_getcode = document.getElementById("reg_getcode");  
                var check_phone_number = /^1[3458]\d{9}$/;  
                if (reg_phone_number.value.length == 0) {  
                    plus.ui.toast("手机号不能为空");  
                    return;  
                }  
                if (reg_phone_number.value.length != 11) {  
                    plus.ui.toast("请输入有效的手机号!");  
                    return;  
                }  
                if (!reg_phone_number.value.match(check_phone_number)) {  
                    plus.ui.toast("请输入有效的手机号");  
                    return;  
                } else {  
                                        //短信发送  
                    /*                  var url = strWebServer + "/App_Fun/Login/Reg_Edit.ashx";  
                                        var _params = {  
                                            config: {  
                                                action: "getCode",  
                                                strTel: reg_phone_number.value  
                                            },  
                                            fun_Success: function(data) {  
                                                if (data.success == "true") {  
                                                    plus.ui.toast("短信已发送");  
                                                } else {  
                                                    plus.ui.toast(data.msg);  
                                                }  
                                            }  
                                        };  
                                        fun_ExcuteAjax(url, _params);*/  
                    countdown = 5;  
//                  alert("haha");//用于测试  
                    timedown(obj);  
                }  
            }
收起阅读 »

[Bug反馈] Android plus.runtime.restart();重启失效

bug已确认 bug反馈 mui

HBhuilder版本: 6.8.1.201512032138
mui.min.js 版本:v2.7.0(复制最新,替换项目中同名文件,其他文件经过同样的操作替换。)
问题表现:Android 设备上,plus.runtime.restart(); 失效,应用没有重启。
Android 版本:5.0.1
IOS:表现正常

附带问题:应用启动时
Android平台提示
Uncaught TypeError: Cannot read property 'value' of undefined at js/mui.min.js:9;
ios 提示
[ERROR] : TypeError: undefined is not an object (evaluating 'a.input.value')
file name:js/mui.min.js
line no:9

初步排查定位:应用首页的图片轮播。(此时,在以下代码被注释掉的情况下轮播能正常使用)

//          var pageSlider = mui("#slider");  
//          pageSlider.slider({  
//              interval: 5000  
//          });

----------------------------感谢@DCloud_MUI_new的回答解惑,以下补充信息----------------------------------

用mui.js v2.7.0 定位错误:
Uncaught TypeError: Cannot read property 'value' of undefined at js/mui.min.js:7219
Uncaught TypeError: Cannot read property '0' of undefined at js/mui.min.js:4662

分别为:

首个错误,与数字输入框相关,现在数字输入框不能通过 + - 按钮更新数字了。只能input填写。
【已经解决:新的数字输入框 input 的class 现在是 mui-input-numbox,而不是原来的 mui-numbox-input 】

        /**  
         * 验证当前值是法合法  
         **/  
        checkValue: function() {  
            var self = this;  
            var val = self.input.value;//Cannot read property 'value' of undefined  
            if (val == null || val == '' || isNaN(val)) {  
                self.input.value = self.options.min || 0;  
                self.minus.disabled = self.options.min != null;  
            } else {  
                var val = parseInt(val);  
                     //以下略

提示2,轮播slider的问题

                    if (slideNumber > (this.itemLength - 1)) {  
                        slideNumber = 0;  
                        time = 0;  
                    } else if (slideNumber < 0) {  
                        slideNumber = this.itemLength - 1;  
                        time = 0;  
                    }  
                }  
                slideNumber = Math.min(Math.max(0, slideNumber), this.itemLength - 1);  
            }  
            return this.pages[slideNumber][0];//Cannot read property '0' of undefined  
        },

----------------------------------------------追加询问 / 反馈---------------------------------------------------------
ios平台( ver 8.1.3):plus.nativeUI.closeWating(); 无法关闭上一窗口遗留的waiting等待动画。请确认是否是Bug

继续阅读 »

HBhuilder版本: 6.8.1.201512032138
mui.min.js 版本:v2.7.0(复制最新,替换项目中同名文件,其他文件经过同样的操作替换。)
问题表现:Android 设备上,plus.runtime.restart(); 失效,应用没有重启。
Android 版本:5.0.1
IOS:表现正常

附带问题:应用启动时
Android平台提示
Uncaught TypeError: Cannot read property 'value' of undefined at js/mui.min.js:9;
ios 提示
[ERROR] : TypeError: undefined is not an object (evaluating 'a.input.value')
file name:js/mui.min.js
line no:9

初步排查定位:应用首页的图片轮播。(此时,在以下代码被注释掉的情况下轮播能正常使用)

//          var pageSlider = mui("#slider");  
//          pageSlider.slider({  
//              interval: 5000  
//          });

----------------------------感谢@DCloud_MUI_new的回答解惑,以下补充信息----------------------------------

用mui.js v2.7.0 定位错误:
Uncaught TypeError: Cannot read property 'value' of undefined at js/mui.min.js:7219
Uncaught TypeError: Cannot read property '0' of undefined at js/mui.min.js:4662

分别为:

首个错误,与数字输入框相关,现在数字输入框不能通过 + - 按钮更新数字了。只能input填写。
【已经解决:新的数字输入框 input 的class 现在是 mui-input-numbox,而不是原来的 mui-numbox-input 】

        /**  
         * 验证当前值是法合法  
         **/  
        checkValue: function() {  
            var self = this;  
            var val = self.input.value;//Cannot read property 'value' of undefined  
            if (val == null || val == '' || isNaN(val)) {  
                self.input.value = self.options.min || 0;  
                self.minus.disabled = self.options.min != null;  
            } else {  
                var val = parseInt(val);  
                     //以下略

提示2,轮播slider的问题

                    if (slideNumber > (this.itemLength - 1)) {  
                        slideNumber = 0;  
                        time = 0;  
                    } else if (slideNumber < 0) {  
                        slideNumber = this.itemLength - 1;  
                        time = 0;  
                    }  
                }  
                slideNumber = Math.min(Math.max(0, slideNumber), this.itemLength - 1);  
            }  
            return this.pages[slideNumber][0];//Cannot read property '0' of undefined  
        },

----------------------------------------------追加询问 / 反馈---------------------------------------------------------
ios平台( ver 8.1.3):plus.nativeUI.closeWating(); 无法关闭上一窗口遗留的waiting等待动画。请确认是否是Bug

收起阅读 »

今天升级HBuilder之后,出现 安装失败 return code=-402653104 临时解决办法

真机调试

由于升级包中的ios基座安装包损坏,造成无法真机调试
遇到这个问题


可先点击这个链接打开文件所在目录,

http://pan.baidu.com/s/1hqjmDSS在百度网盘中下载完整ipa包
替换之前的ipa
可临时解决

继续阅读 »

由于升级包中的ios基座安装包损坏,造成无法真机调试
遇到这个问题


可先点击这个链接打开文件所在目录,

http://pan.baidu.com/s/1hqjmDSS在百度网盘中下载完整ipa包
替换之前的ipa
可临时解决

收起阅读 »

[Wanz]已捕捉HB的websql电脑路径位置

websql

HB路径:
XP下
C:\Documents and Settings\Administrator\Local Settings\Temp\scoped_dir1560_13945\databases\http_127.0.0.1_8020
WIN7
C:\Users\Administrator\Local Settings\Temp\scoped_dir1560_13945\databases\http_127.0.0.1_8020

附送UC浏览器下的路径:
C:\Documents and Settings\Administrator\Local Settings\Application Data\UCBrowser\User Data\Default\databases

继续阅读 »

HB路径:
XP下
C:\Documents and Settings\Administrator\Local Settings\Temp\scoped_dir1560_13945\databases\http_127.0.0.1_8020
WIN7
C:\Users\Administrator\Local Settings\Temp\scoped_dir1560_13945\databases\http_127.0.0.1_8020

附送UC浏览器下的路径:
C:\Documents and Settings\Administrator\Local Settings\Application Data\UCBrowser\User Data\Default\databases

收起阅读 »

蓝牙搜索周围蓝牙设备

蓝牙 Bluetooth Native.JS
var main, BAdapter, BluetoothAdapter, BluetoothDevice;  
main = plus.android.runtimeMainActivity();  
BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");  
BAdapter = BluetoothAdapter.getDefaultAdapter();  
BluetoothDevice = plus.android.importClass('android.bluetooth.BluetoothDevice');  
//搜索设备接收器             
var foundreceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {  
    onReceive: function(context, intent) {  
        plus.android.importClass(intent);  
        console.log(intent.getAction());  
        var BleDevice = new BluetoothDevice();  
        BleDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
        console.log("蓝牙设备:" + BleDevice.getName() + BleDevice.getAddress());  
        main.unregisterReceiver(receiver);  
    }  
});  

//注册接收器  
var IntentFilterScan = plus.android.importClass('android.content.IntentFilter');  
var filterScan = new IntentFilterScan();  
filterScan.addAction(BluetoothDevice.ACTION_FOUND); //搜索设备                    
main.registerReceiver(foundreceiver, filterScan); //注册监听
继续阅读 »
var main, BAdapter, BluetoothAdapter, BluetoothDevice;  
main = plus.android.runtimeMainActivity();  
BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");  
BAdapter = BluetoothAdapter.getDefaultAdapter();  
BluetoothDevice = plus.android.importClass('android.bluetooth.BluetoothDevice');  
//搜索设备接收器             
var foundreceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {  
    onReceive: function(context, intent) {  
        plus.android.importClass(intent);  
        console.log(intent.getAction());  
        var BleDevice = new BluetoothDevice();  
        BleDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
        console.log("蓝牙设备:" + BleDevice.getName() + BleDevice.getAddress());  
        main.unregisterReceiver(receiver);  
    }  
});  

//注册接收器  
var IntentFilterScan = plus.android.importClass('android.content.IntentFilter');  
var filterScan = new IntentFilterScan();  
filterScan.addAction(BluetoothDevice.ACTION_FOUND); //搜索设备                    
main.registerReceiver(foundreceiver, filterScan); //注册监听
收起阅读 »

微信、支付宝支付那点事

支付 微信 支付宝

公司要在MUI开发的APP里添加上支付功能,然后爬坑开始了。。

因为公司用的是Java语言开发的服务端,所以就要找Java版本的支付代码了
首先在dcloud的问答里搜索看有没有相关文章,找到了下面两篇有用的

第一篇是配置支付宝支付的,第二篇是我在下面发了一个回复求微信支付的代码(所以代码在回复里)

dcloud官方给的php代码地址:https://github.com/dcloudio/H5P.Server

下面说说我在爬坑的时候碰到的障碍

  1. Java语言输出方法有print(),println() 切记一定不能用println() 这个方法输出后会换行,所以一直是失败状态,我开发的时候是支付宝报的错ALI10
  2. 微信支付,从APP里请求到服务端接口,在接口里会调用一次微信的接口,此时涉及到一次签名(sign),切记在签名前发送给微信服务器的参数要按照a-z排列好,然后在去签名,完成之后请求微信支付接口,微信给返回一些XML数据,其中只有prepay_id有用,其他需要的参数基本上都是在微信的配置类里配置好了的,此时转换成的json格式数据写出的还有一次签名(sign),这个签名跟上面第一次的签名不一样,要记得在签名参数最后带上key
  3. 支付宝支付的参数,地址类的字符串比如:notify_url等,不需要URLEncoder.encode(),参数都要加上""
  4. xcode里,如果APP跳转到支付宝打开的是网页版的,而不是支付宝APP,就需要在xcode里配置plist文件添加下面这些代码
    <key>LSApplicationQueriesSchemes</key>  
    <array>  
    <string>weixin</string>  
    <string>wechat</string>  
    <string>alipay</string>  
    <string>sinaweibo</string>  
    <string>weibosdk</string>  
    <string>tencentweiboSdkv2</string>  
    <string>weibosdk2.5</string>  
    <string>mqq</string>  
    <string>mqqOpensdkSSoLogin</string>  
    <string>mqqopensdkapiV2</string>  
    <string>mqqwpa</string>  
    <string>mqqopensdkapiV3</string>  
    <string>wtloginmqq2</string>  
    </array>

下面是我Java服务端的代码,给大家分享一下
支付宝(使用了支付宝商户SDK)

public void alipayapi(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    response.setContentType("text/plain; charset=UTF-8");  
    PrintWriter out = response.getWriter();  
    ////////////////////////////////////请求参数//////////////////////////////////////  

    //支付类型  
    String payment_type = "1";  
    //必填,不能修改  
    //服务器异步通知页面路径  
    String notify_url = ApplicationListener.getBasePath() + "pay/sdk/alipay/notify";  
    //需http://格式的完整路径,不能加?id=123这类自定义参数  

    //页面跳转同步通知页面路径  
//        String return_url = URLEncoder.encode(ApplicationListener.getBasePath() + "pay/wap/alipay/return");  
    //需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/  

    //商户订单号  
    String out_trade_no = request.getParameter("orderId");  
    //商户网站订单系统中唯一订单号,必填  

    //订单名称  
    String subject = "支付预定金";  
    //必填  

    //付款金额  
    String total_fee = request.getParameter("total_fee");  
    //必填  

    //商品展示地址  
    String show_url = ApplicationListener.getBasePath();  
    //必填,需以http://开头的完整路径,例如:http://www.商户网址.com/myorder.html  

    //订单描述  
    String body = "订单支付定金";  
    //选填  

    //超时时间  
    String it_b_pay = "1d";  
    //选填  

    //把请求参数打包成数组  
    Map<String, String> sParaTemp = new HashMap<String, String>();  
    sParaTemp.put("service", "mobile.securitypay.pay");  
    sParaTemp.put("partner", AlipayConfig.partner);  
    sParaTemp.put("seller_id", AlipayConfig.seller_id);  
    sParaTemp.put("_input_charset", AlipayConfig.input_charset);  
    sParaTemp.put("payment_type", payment_type);  
    sParaTemp.put("notify_url", notify_url);  
    sParaTemp.put("out_trade_no", out_trade_no);  
    sParaTemp.put("subject", subject);  
    sParaTemp.put("total_fee", total_fee);  
    sParaTemp.put("show_url", show_url);  
    sParaTemp.put("body", body);  
    sParaTemp.put("it_b_pay", it_b_pay);  
    String sHtmlText = "";  
    for(Iterator iter = sParaTemp.keySet().iterator(); iter.hasNext();) {  
        String name = (String) iter.next();  
        String value = sParaTemp.get(name);  
        sHtmlText += name + "=\"" + value + "\"&";  
    }  
    //建立请求  
    System.out.println(sHtmlText);  
    sHtmlText = sHtmlText.substring(0, sHtmlText.length()-1);  
    String sign = RSA.sign(sHtmlText, AlipayConfig.PRIVATE, AlipayConfig.input_charset);  
    String outText = sHtmlText + "&sign=\"" + URLEncoder.encode(sign, "UTF-8") + "\"&sign_type=\""+AlipayConfig.sign_type+"\"";  
    out.print(outText);  
}

微信支付(用到了微信Java版SDK,下载地址
RequestData.java

public class RequestData {  
    private String appid;  
    private String body;  
    private String mch_id;  
    private String nonce_str;  
    private String notify_url;  
    private String out_trade_no;  
    private String sign;  
    private String spbill_create_ip;  
    private String total_fee;  
    private String trade_type;  

    //getter,setter  
}
public void alipayapi(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    response.setContentType("text/plain; charset=UTF-8");  
    PrintWriter out = response.getWriter();  

    //微信分配的公众账号ID(企业号corpid即为此appId)  
    String appid = Configure.getAppid();  
    //必填  

    //商品或支付单简要描述  
    String body = "预定金支付";  
    //必填  

    //微信支付分配的商户号  
    String mch_id = Configure.getMchid();  
    //必填  

    //随机字符串,不长于32位。推荐随机数生成算法  
    String nonce_str = RandomStringGenerator.getRandomStringByLength(32);  
    //必填  

    //接收微信支付异步通知回调地址  
    String notify_url = ApplicationListener.getBasePath() + "pay/sdk/wxpay/notify";  
    //必填  

    //商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号  
    String out_trade_no = request.getParameter("orderId");  
    //必填  

    //签名,详见签名生成算法  
    String sign = "";  
    //必填  

    //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。  
    String spbill_create_ip = IpUtil.getIpAddr(request);  
    //必填  

    //订单总金额,单位为分,详见支付金额  
    Double total_fee_d = Double.parseDouble(request.getParameter("total_fee"));  
    Double total_fee_s = total_fee_d * 100;  
    String total_fee = total_fee_s.intValue() + "";  
    //必填  

    //取值如下:JSAPI,NATIVE,APP,详细说明见参数规定  
    String trade_type = request.getParameter("trade_type");  
    //必填  

    //=============================以下参数 非必填 ===============================  

    //商品名称明细列表  
    String detail = "";  
    //非必填  

    //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据  
    String attach = "";  
    //非必填  

    //符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型  
    String fee_type = "";  
    //非必填  

    //终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"  
    String device_info = "";  
    //非必填  

    //商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠  
    String goods_tag = "";  
    //非必填  

    //订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则  
    String time_start = "";  
    //非必填  

    //订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则  
    //注意:最短失效时间间隔必须大于5分钟  
    String time_expire = "";  
    //非必填  

    //trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。  
    String product_id = "";  
    //非必填  

    //no_credit--指定不能使用信用卡支付  
    String limit_pay = "";  
    //非必填  

    //trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。  
    //企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换  
    String openid = "";  
    //非必填  

    RequestData reqData = new RequestData();  
    reqData.setAppid(appid);  
    reqData.setBody(body);  
    reqData.setMch_id(mch_id);  
    reqData.setNonce_str(nonce_str);  
    reqData.setNotify_url(notify_url);  
    reqData.setOut_trade_no(out_trade_no);  
    reqData.setSpbill_create_ip(spbill_create_ip);  
    reqData.setTotal_fee(total_fee);  
    reqData.setTrade_type(trade_type);  
    sign = Signature.getSign(reqData);  
    reqData.setSign(sign);  
    String result = new HttpsRequest().sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", reqData);  

    System.out.println(result);  

    Map map = XMLParser.getMapFromXML(result);  
    String prepay_id = (String) map.get("prepay_id");  
    System.out.println(prepay_id);  

    String timestamp = String.valueOf(System.currentTimeMillis() / 1000);  
    String s="appid="+appid+"&noncestr="+nonce_str+"&package=Sign=WXPay"+"&partnerid="+  
            mch_id+"&prepayid="+prepay_id+"&timestamp="+timestamp+"&key=" + Configure.getKey();  
    String newSign = MD5.MD5Encode(s).toUpperCase();  
    StringBuffer json = new StringBuffer();  
    json.append("{\"appid\":\"");  
    json.append(appid);  
    json.append("\",\"noncestr\":\"");  
    json.append(nonce_str);  
    json.append("\",\"package\":\"");  
    json.append("Sign=WXPay");  
    json.append("\",\"partnerid\":\"");  
    json.append(mch_id);  
    json.append("\",\"prepayid\":\"");  
    json.append(prepay_id);  
    json.append("\",\"timestamp\":\"");  
    json.append(timestamp);  
    json.append("\",\"sign\":\"");  
    json.append(newSign);  
    json.append("\"}");  
    System.out.println(json.toString());  
    out.print(json.toString());  
}
继续阅读 »

公司要在MUI开发的APP里添加上支付功能,然后爬坑开始了。。

因为公司用的是Java语言开发的服务端,所以就要找Java版本的支付代码了
首先在dcloud的问答里搜索看有没有相关文章,找到了下面两篇有用的

第一篇是配置支付宝支付的,第二篇是我在下面发了一个回复求微信支付的代码(所以代码在回复里)

dcloud官方给的php代码地址:https://github.com/dcloudio/H5P.Server

下面说说我在爬坑的时候碰到的障碍

  1. Java语言输出方法有print(),println() 切记一定不能用println() 这个方法输出后会换行,所以一直是失败状态,我开发的时候是支付宝报的错ALI10
  2. 微信支付,从APP里请求到服务端接口,在接口里会调用一次微信的接口,此时涉及到一次签名(sign),切记在签名前发送给微信服务器的参数要按照a-z排列好,然后在去签名,完成之后请求微信支付接口,微信给返回一些XML数据,其中只有prepay_id有用,其他需要的参数基本上都是在微信的配置类里配置好了的,此时转换成的json格式数据写出的还有一次签名(sign),这个签名跟上面第一次的签名不一样,要记得在签名参数最后带上key
  3. 支付宝支付的参数,地址类的字符串比如:notify_url等,不需要URLEncoder.encode(),参数都要加上""
  4. xcode里,如果APP跳转到支付宝打开的是网页版的,而不是支付宝APP,就需要在xcode里配置plist文件添加下面这些代码
    <key>LSApplicationQueriesSchemes</key>  
    <array>  
    <string>weixin</string>  
    <string>wechat</string>  
    <string>alipay</string>  
    <string>sinaweibo</string>  
    <string>weibosdk</string>  
    <string>tencentweiboSdkv2</string>  
    <string>weibosdk2.5</string>  
    <string>mqq</string>  
    <string>mqqOpensdkSSoLogin</string>  
    <string>mqqopensdkapiV2</string>  
    <string>mqqwpa</string>  
    <string>mqqopensdkapiV3</string>  
    <string>wtloginmqq2</string>  
    </array>

下面是我Java服务端的代码,给大家分享一下
支付宝(使用了支付宝商户SDK)

public void alipayapi(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    response.setContentType("text/plain; charset=UTF-8");  
    PrintWriter out = response.getWriter();  
    ////////////////////////////////////请求参数//////////////////////////////////////  

    //支付类型  
    String payment_type = "1";  
    //必填,不能修改  
    //服务器异步通知页面路径  
    String notify_url = ApplicationListener.getBasePath() + "pay/sdk/alipay/notify";  
    //需http://格式的完整路径,不能加?id=123这类自定义参数  

    //页面跳转同步通知页面路径  
//        String return_url = URLEncoder.encode(ApplicationListener.getBasePath() + "pay/wap/alipay/return");  
    //需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/  

    //商户订单号  
    String out_trade_no = request.getParameter("orderId");  
    //商户网站订单系统中唯一订单号,必填  

    //订单名称  
    String subject = "支付预定金";  
    //必填  

    //付款金额  
    String total_fee = request.getParameter("total_fee");  
    //必填  

    //商品展示地址  
    String show_url = ApplicationListener.getBasePath();  
    //必填,需以http://开头的完整路径,例如:http://www.商户网址.com/myorder.html  

    //订单描述  
    String body = "订单支付定金";  
    //选填  

    //超时时间  
    String it_b_pay = "1d";  
    //选填  

    //把请求参数打包成数组  
    Map<String, String> sParaTemp = new HashMap<String, String>();  
    sParaTemp.put("service", "mobile.securitypay.pay");  
    sParaTemp.put("partner", AlipayConfig.partner);  
    sParaTemp.put("seller_id", AlipayConfig.seller_id);  
    sParaTemp.put("_input_charset", AlipayConfig.input_charset);  
    sParaTemp.put("payment_type", payment_type);  
    sParaTemp.put("notify_url", notify_url);  
    sParaTemp.put("out_trade_no", out_trade_no);  
    sParaTemp.put("subject", subject);  
    sParaTemp.put("total_fee", total_fee);  
    sParaTemp.put("show_url", show_url);  
    sParaTemp.put("body", body);  
    sParaTemp.put("it_b_pay", it_b_pay);  
    String sHtmlText = "";  
    for(Iterator iter = sParaTemp.keySet().iterator(); iter.hasNext();) {  
        String name = (String) iter.next();  
        String value = sParaTemp.get(name);  
        sHtmlText += name + "=\"" + value + "\"&";  
    }  
    //建立请求  
    System.out.println(sHtmlText);  
    sHtmlText = sHtmlText.substring(0, sHtmlText.length()-1);  
    String sign = RSA.sign(sHtmlText, AlipayConfig.PRIVATE, AlipayConfig.input_charset);  
    String outText = sHtmlText + "&sign=\"" + URLEncoder.encode(sign, "UTF-8") + "\"&sign_type=\""+AlipayConfig.sign_type+"\"";  
    out.print(outText);  
}

微信支付(用到了微信Java版SDK,下载地址
RequestData.java

public class RequestData {  
    private String appid;  
    private String body;  
    private String mch_id;  
    private String nonce_str;  
    private String notify_url;  
    private String out_trade_no;  
    private String sign;  
    private String spbill_create_ip;  
    private String total_fee;  
    private String trade_type;  

    //getter,setter  
}
public void alipayapi(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    response.setContentType("text/plain; charset=UTF-8");  
    PrintWriter out = response.getWriter();  

    //微信分配的公众账号ID(企业号corpid即为此appId)  
    String appid = Configure.getAppid();  
    //必填  

    //商品或支付单简要描述  
    String body = "预定金支付";  
    //必填  

    //微信支付分配的商户号  
    String mch_id = Configure.getMchid();  
    //必填  

    //随机字符串,不长于32位。推荐随机数生成算法  
    String nonce_str = RandomStringGenerator.getRandomStringByLength(32);  
    //必填  

    //接收微信支付异步通知回调地址  
    String notify_url = ApplicationListener.getBasePath() + "pay/sdk/wxpay/notify";  
    //必填  

    //商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号  
    String out_trade_no = request.getParameter("orderId");  
    //必填  

    //签名,详见签名生成算法  
    String sign = "";  
    //必填  

    //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。  
    String spbill_create_ip = IpUtil.getIpAddr(request);  
    //必填  

    //订单总金额,单位为分,详见支付金额  
    Double total_fee_d = Double.parseDouble(request.getParameter("total_fee"));  
    Double total_fee_s = total_fee_d * 100;  
    String total_fee = total_fee_s.intValue() + "";  
    //必填  

    //取值如下:JSAPI,NATIVE,APP,详细说明见参数规定  
    String trade_type = request.getParameter("trade_type");  
    //必填  

    //=============================以下参数 非必填 ===============================  

    //商品名称明细列表  
    String detail = "";  
    //非必填  

    //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据  
    String attach = "";  
    //非必填  

    //符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型  
    String fee_type = "";  
    //非必填  

    //终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"  
    String device_info = "";  
    //非必填  

    //商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠  
    String goods_tag = "";  
    //非必填  

    //订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则  
    String time_start = "";  
    //非必填  

    //订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则  
    //注意:最短失效时间间隔必须大于5分钟  
    String time_expire = "";  
    //非必填  

    //trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。  
    String product_id = "";  
    //非必填  

    //no_credit--指定不能使用信用卡支付  
    String limit_pay = "";  
    //非必填  

    //trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。  
    //企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换  
    String openid = "";  
    //非必填  

    RequestData reqData = new RequestData();  
    reqData.setAppid(appid);  
    reqData.setBody(body);  
    reqData.setMch_id(mch_id);  
    reqData.setNonce_str(nonce_str);  
    reqData.setNotify_url(notify_url);  
    reqData.setOut_trade_no(out_trade_no);  
    reqData.setSpbill_create_ip(spbill_create_ip);  
    reqData.setTotal_fee(total_fee);  
    reqData.setTrade_type(trade_type);  
    sign = Signature.getSign(reqData);  
    reqData.setSign(sign);  
    String result = new HttpsRequest().sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", reqData);  

    System.out.println(result);  

    Map map = XMLParser.getMapFromXML(result);  
    String prepay_id = (String) map.get("prepay_id");  
    System.out.println(prepay_id);  

    String timestamp = String.valueOf(System.currentTimeMillis() / 1000);  
    String s="appid="+appid+"&noncestr="+nonce_str+"&package=Sign=WXPay"+"&partnerid="+  
            mch_id+"&prepayid="+prepay_id+"&timestamp="+timestamp+"&key=" + Configure.getKey();  
    String newSign = MD5.MD5Encode(s).toUpperCase();  
    StringBuffer json = new StringBuffer();  
    json.append("{\"appid\":\"");  
    json.append(appid);  
    json.append("\",\"noncestr\":\"");  
    json.append(nonce_str);  
    json.append("\",\"package\":\"");  
    json.append("Sign=WXPay");  
    json.append("\",\"partnerid\":\"");  
    json.append(mch_id);  
    json.append("\",\"prepayid\":\"");  
    json.append(prepay_id);  
    json.append("\",\"timestamp\":\"");  
    json.append(timestamp);  
    json.append("\",\"sign\":\"");  
    json.append(newSign);  
    json.append("\"}");  
    System.out.println(json.toString());  
    out.print(json.toString());  
}
收起阅读 »

url中文字符串建议转码

调用到plus.runtime.openURL、plus.webview.currentWebview().loadURL()等用到url的地方,必须注意调用接口时url中存在中文字符参数的建议先转码后调用,否则可能会出现的调用失败异常。

调用到plus.runtime.openURL、plus.webview.currentWebview().loadURL()等用到url的地方,必须注意调用接口时url中存在中文字符参数的建议先转码后调用,否则可能会出现的调用失败异常。

【分享】跨 webview 的 js 函数调用

源码 技术分享 HBuilder

webview 是 5+ SDK 的重要组成部分,也是 HBuilder 区别于其它“H5 混合模式”移动端开发方案的一个重要特色和利器。

在实际使用中,webview 也会给程序设计带来一些麻烦。因为一个 webview 实例就相当于桌面浏览器里的一个窗口页签,每个 webview 的 js 上下文都是相互独立的,并不能互相访问。

幸好 HTML5+ 规范中定义了 WebviewObject,evalJS() 这个接口,为两个平行宇宙之间的互相通信提供了可能。MUI 就利用这个接口实现了“自定义事件”功能,可以用来跨 webview 传递消息。

不过,evalJS() 只是底层实现的一个单向通信机制,MUI 的自定义事件也是单向消息,没有返回值。虽然这已经解决了很多问题,但还是有不少应用场景更适合用接近“函数调用”的方式来实现。

有鉴于此,我做了这个 hbuilder-rpc。分享在这里,希望能有用。如果这段代码将来有机会成为 MUI 的一部分,我将尤感欣慰。

演示界面截图:

相关程序代码:

/**  
 * 以当前的 WebView 为媒介,向其它 WebView 中的 js 提供服务接口。  
 */  
window.RpcServer = {  
    /**  
     * 服务提供者调用此函数注册一个服务接口。  
     * @param {String} service_name 接口名称  
     * @param {Function} fnService 注册的服务函数,具有如下形式:  
     *      function(params, finish) {  
     *          // params 是调用参数。  
     *          // finish 是回调函数,应该在服务完成后调用,并传入唯一参数表示服务执行结果。  
     *          //      即使服务出错,也要确保回调函数被调用,并用传入参数来表示错误状态。  
     *      }  
     */  
    expose: function(service_name, fnService) {  
        var me = this;  
        if (me.exposed[service_name] != undefined) {  
            throw new Error('RpcServer.expose: service already exists: ' + service_name);  
        }  
        me.exposed[service_name] = fnService;  
    },  

    exposed: {}, // 注册的服务接口  

    /**  
     * RpcClient 通过 evalJS() 调用此函数,访问服务接口。  
     * @param {String} service_name 服务接口名称  
     * @param {Mixed} params 入口参数  
     * @param {String} vw_id 调用源的 webview id  
     * @param {String} cb_id 调用源的 callback id  
     */  
    invoke: function(service_name, params, vw_id, cb_id) {  
        var fn = this.exposed[service_name];  
        if (typeof fn != 'function') {  
            throw new Error('RpcServer.invoke: service not found: ' + service_name);  
        }  
        fn(params, function(ret) {  
            var vw = plus.webview.getWebviewById(vw_id);  
            if (!vw) return;  
            var js = 'RpcClient.callback(' + JSON.stringify(cb_id);  
            js += ',' + JSON.stringify(ret);  
            js += ')';  
            vw.evalJS(js);  
        });  
    }  
};
/**  
 * 远程访问 RpcServer 提供的服务接口。  
 */  
window.RpcClient = {  
    /**  
     * 调用一个远程服务接口。  
     * @param {String} server_id rpc server 的 webview id。  
     * @param {String} service_name 服务接口名称。  
     * @param {Mixed} params 服务入口参数。  
     * @param {Function} callback 回调函数,用于回传服务执行结果。  
     */  
    invoke: function(server_id, service_name, params, callback) {  
        var me = this;  
        var cs = plus.webview.getWebviewById(server_id);  
        if (!cs) throw new Error('RpcServer view not found: ' + server_id);  
        var js = 'RpcServer.invoke(' + JSON.stringify(service_name);  
        js += ',' + JSON.stringify(params);  
        if (typeof callback == 'function') {  
            js += ',' + JSON.stringify(plus.webview.currentWebview().id);  
            js += ',' + me.next_callback_id;  
            me.callbacks[me.next_callback_id] = callback;  
            me.next_callback_id ++;  
        }  
        js += ')';  
        cs.evalJS(js);  
    },  

    next_callback_id: 1,  
    callbacks: {},  
    callback: function(cb_id, ret) {  
        var me = this;  
        var cb = me.callbacks[cb_id];  
        if (typeof cb != 'function') return;  
        cb.call(undefined, ret);  
        delete me.callbacks[cb_id];  
    }  
};
// 通过 RpcServer.expose() 暴露一个服务函数供其它 WebView 中的 js 调用  
RpcServer.expose('demo-rpc-service', function(params, finish) {  
    // 入口参数  
    mui('#rpc-call-params')[0].innerText = JSON.stringify(params, undefined, '    ');  

    // 服务功能完成后,调用 finish() 把结果发回给调用者  
    finish({  
        success: true,  
        result: {  
            reply: 'hi, ' + params.from + '.',  
            num: window._call_num = (window._call_num || 0) + 1  
        }  
    });  
});
// 通过 RpcClient.invoke() 调用另一个 WebView 中的服务函数  
RpcClient.invoke('demo-rpc-server', 'demo-rpc-service', {  
    greeting: 'hi !',  
    from: plus.webview.currentWebview().id,  
    num: window._call_num = (window._call_num || 0) + 1  
}, function(resp) {  
    // resp 是服务执行结果  
    mui('#rpc-call-resp')[0].innerText = JSON.stringify(resp, undefined, '    ');  
});

演示项目源代码可通过附件下载。

继续阅读 »

webview 是 5+ SDK 的重要组成部分,也是 HBuilder 区别于其它“H5 混合模式”移动端开发方案的一个重要特色和利器。

在实际使用中,webview 也会给程序设计带来一些麻烦。因为一个 webview 实例就相当于桌面浏览器里的一个窗口页签,每个 webview 的 js 上下文都是相互独立的,并不能互相访问。

幸好 HTML5+ 规范中定义了 WebviewObject,evalJS() 这个接口,为两个平行宇宙之间的互相通信提供了可能。MUI 就利用这个接口实现了“自定义事件”功能,可以用来跨 webview 传递消息。

不过,evalJS() 只是底层实现的一个单向通信机制,MUI 的自定义事件也是单向消息,没有返回值。虽然这已经解决了很多问题,但还是有不少应用场景更适合用接近“函数调用”的方式来实现。

有鉴于此,我做了这个 hbuilder-rpc。分享在这里,希望能有用。如果这段代码将来有机会成为 MUI 的一部分,我将尤感欣慰。

演示界面截图:

相关程序代码:

/**  
 * 以当前的 WebView 为媒介,向其它 WebView 中的 js 提供服务接口。  
 */  
window.RpcServer = {  
    /**  
     * 服务提供者调用此函数注册一个服务接口。  
     * @param {String} service_name 接口名称  
     * @param {Function} fnService 注册的服务函数,具有如下形式:  
     *      function(params, finish) {  
     *          // params 是调用参数。  
     *          // finish 是回调函数,应该在服务完成后调用,并传入唯一参数表示服务执行结果。  
     *          //      即使服务出错,也要确保回调函数被调用,并用传入参数来表示错误状态。  
     *      }  
     */  
    expose: function(service_name, fnService) {  
        var me = this;  
        if (me.exposed[service_name] != undefined) {  
            throw new Error('RpcServer.expose: service already exists: ' + service_name);  
        }  
        me.exposed[service_name] = fnService;  
    },  

    exposed: {}, // 注册的服务接口  

    /**  
     * RpcClient 通过 evalJS() 调用此函数,访问服务接口。  
     * @param {String} service_name 服务接口名称  
     * @param {Mixed} params 入口参数  
     * @param {String} vw_id 调用源的 webview id  
     * @param {String} cb_id 调用源的 callback id  
     */  
    invoke: function(service_name, params, vw_id, cb_id) {  
        var fn = this.exposed[service_name];  
        if (typeof fn != 'function') {  
            throw new Error('RpcServer.invoke: service not found: ' + service_name);  
        }  
        fn(params, function(ret) {  
            var vw = plus.webview.getWebviewById(vw_id);  
            if (!vw) return;  
            var js = 'RpcClient.callback(' + JSON.stringify(cb_id);  
            js += ',' + JSON.stringify(ret);  
            js += ')';  
            vw.evalJS(js);  
        });  
    }  
};
/**  
 * 远程访问 RpcServer 提供的服务接口。  
 */  
window.RpcClient = {  
    /**  
     * 调用一个远程服务接口。  
     * @param {String} server_id rpc server 的 webview id。  
     * @param {String} service_name 服务接口名称。  
     * @param {Mixed} params 服务入口参数。  
     * @param {Function} callback 回调函数,用于回传服务执行结果。  
     */  
    invoke: function(server_id, service_name, params, callback) {  
        var me = this;  
        var cs = plus.webview.getWebviewById(server_id);  
        if (!cs) throw new Error('RpcServer view not found: ' + server_id);  
        var js = 'RpcServer.invoke(' + JSON.stringify(service_name);  
        js += ',' + JSON.stringify(params);  
        if (typeof callback == 'function') {  
            js += ',' + JSON.stringify(plus.webview.currentWebview().id);  
            js += ',' + me.next_callback_id;  
            me.callbacks[me.next_callback_id] = callback;  
            me.next_callback_id ++;  
        }  
        js += ')';  
        cs.evalJS(js);  
    },  

    next_callback_id: 1,  
    callbacks: {},  
    callback: function(cb_id, ret) {  
        var me = this;  
        var cb = me.callbacks[cb_id];  
        if (typeof cb != 'function') return;  
        cb.call(undefined, ret);  
        delete me.callbacks[cb_id];  
    }  
};
// 通过 RpcServer.expose() 暴露一个服务函数供其它 WebView 中的 js 调用  
RpcServer.expose('demo-rpc-service', function(params, finish) {  
    // 入口参数  
    mui('#rpc-call-params')[0].innerText = JSON.stringify(params, undefined, '    ');  

    // 服务功能完成后,调用 finish() 把结果发回给调用者  
    finish({  
        success: true,  
        result: {  
            reply: 'hi, ' + params.from + '.',  
            num: window._call_num = (window._call_num || 0) + 1  
        }  
    });  
});
// 通过 RpcClient.invoke() 调用另一个 WebView 中的服务函数  
RpcClient.invoke('demo-rpc-server', 'demo-rpc-service', {  
    greeting: 'hi !',  
    from: plus.webview.currentWebview().id,  
    num: window._call_num = (window._call_num || 0) + 1  
}, function(resp) {  
    // resp 是服务执行结果  
    mui('#rpc-call-resp')[0].innerText = JSON.stringify(resp, undefined, '    ');  
});

演示项目源代码可通过附件下载。

收起阅读 »