HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

支付宝APP支付开通教程

支付宝 支付宝小程序

一、如何开通支付宝app支付
正常来说,按照官方的指引要求填写相关资料,即可开通支付宝app支付。但是,更多的时候我们的申请都会碰到一些阻力,常见的阻力就是“系统综合评估签约条件不满足,谢谢您的支持”或者“经系统检测您的账户不符合国家相关法律法规或《支付宝用户服务协议》约定,暂时无法签约当前产品”!

二、如何过风险,签约支付宝App支付呢?为此我们找到了以下方法来进行解决!
①支付宝账户是否通过实名认证。
②申请主体是否是企业或个体工商户,因为纯个人不能申请。
③提供的营业执照,与支付宝账户名称是否属于同一主体。
④账户初次申请成功便申请支付,系统认定账号不安全,需修改支付宝密码(很大部分是此原因)。
⑤企业是否曾受过行政类处罚;账号是否绑定独立手机、邮箱是否绑定过其他支付宝。
⑥法人是否曾有失信记录。
⑦方法是有了,但是有些账户完全按照上面的解决方案操作了之后,仍旧提示“系统综合评估签约条件不满足,谢谢您的支持”或者“经系统检测您的账户不符合国家相关法律法规或《支付宝用户服务协议》约定,暂时无法签约当前产品”!那么,你就需要进入公_众_号“网创商盟”获取更多信息,才能过签约开通支付宝APP支付产品权限。
三、开通案例展示

继续阅读 »

一、如何开通支付宝app支付
正常来说,按照官方的指引要求填写相关资料,即可开通支付宝app支付。但是,更多的时候我们的申请都会碰到一些阻力,常见的阻力就是“系统综合评估签约条件不满足,谢谢您的支持”或者“经系统检测您的账户不符合国家相关法律法规或《支付宝用户服务协议》约定,暂时无法签约当前产品”!

二、如何过风险,签约支付宝App支付呢?为此我们找到了以下方法来进行解决!
①支付宝账户是否通过实名认证。
②申请主体是否是企业或个体工商户,因为纯个人不能申请。
③提供的营业执照,与支付宝账户名称是否属于同一主体。
④账户初次申请成功便申请支付,系统认定账号不安全,需修改支付宝密码(很大部分是此原因)。
⑤企业是否曾受过行政类处罚;账号是否绑定独立手机、邮箱是否绑定过其他支付宝。
⑥法人是否曾有失信记录。
⑦方法是有了,但是有些账户完全按照上面的解决方案操作了之后,仍旧提示“系统综合评估签约条件不满足,谢谢您的支持”或者“经系统检测您的账户不符合国家相关法律法规或《支付宝用户服务协议》约定,暂时无法签约当前产品”!那么,你就需要进入公_众_号“网创商盟”获取更多信息,才能过签约开通支付宝APP支付产品权限。
三、开通案例展示

收起阅读 »

nvue实现全局挂载

vuex 全局变量

前言
nvue的无法手动扩展vue.prototype导致有时想用全局挂载就无从下手。

实现思路
由于nvue中的全局变量我们能找到的比较好的就是store和globalData,所以考虑在这两个上挂载。

globaldata
每次初始化时直接getApp({allowdefault:true})然后扩展一堆方法,使用时,页面内直接get出来用就好,我这里是作为全局请求api方法,实现类似vue项目中的 this.$api.getlist().then().catch()类似的效果,使用时就是getApp().api.login.getXX().then().catch();

store
同样的,直接在state上挂载静态方法在nvue中是完全没有问题的,this.$store.state.test.gettttt().then().catch(); 依然好用。

反思
目前使用这两种方法都可以实现,但是仔细想想稍有不妥。
store作为状态管理我们管理了静态方法,是不是用偏了;globaldata每次使用必须getApp获取整个app实例,getapp()...的链式写法未免有些奇怪;再globaldata是否有大小限制,或者我们挂载的东西太多会不会影响到app实例;store和globaldata我使用哪个比较合适呢?

请问有谁实现过或者在这方面有经验,帮我解答最后这几个问题!!!

merry christmas 收到下边兄弟的回复,才发现uni本身就是一个全局的对象,使用uni.XXX = function(){};然后页面中随处使用,确实好用。

那么问题又来了,我现在想需要一个可配置的接口地址,使用什么方案比较合适呢?个人还是觉得store比较好直接使用action请求配置服务器,更换每一个接口的地址就可以了。

继续阅读 »

前言
nvue的无法手动扩展vue.prototype导致有时想用全局挂载就无从下手。

实现思路
由于nvue中的全局变量我们能找到的比较好的就是store和globalData,所以考虑在这两个上挂载。

globaldata
每次初始化时直接getApp({allowdefault:true})然后扩展一堆方法,使用时,页面内直接get出来用就好,我这里是作为全局请求api方法,实现类似vue项目中的 this.$api.getlist().then().catch()类似的效果,使用时就是getApp().api.login.getXX().then().catch();

store
同样的,直接在state上挂载静态方法在nvue中是完全没有问题的,this.$store.state.test.gettttt().then().catch(); 依然好用。

反思
目前使用这两种方法都可以实现,但是仔细想想稍有不妥。
store作为状态管理我们管理了静态方法,是不是用偏了;globaldata每次使用必须getApp获取整个app实例,getapp()...的链式写法未免有些奇怪;再globaldata是否有大小限制,或者我们挂载的东西太多会不会影响到app实例;store和globaldata我使用哪个比较合适呢?

请问有谁实现过或者在这方面有经验,帮我解答最后这几个问题!!!

merry christmas 收到下边兄弟的回复,才发现uni本身就是一个全局的对象,使用uni.XXX = function(){};然后页面中随处使用,确实好用。

那么问题又来了,我现在想需要一个可配置的接口地址,使用什么方案比较合适呢?个人还是觉得store比较好直接使用action请求配置服务器,更换每一个接口的地址就可以了。

收起阅读 »

uniapp 调用echarts 占用内存过大不释放问题终于解决了 移动端支持echarts 5.0

uniapp 调用echarts 这个问题整了四天终于过了
总结下经验:
最开始做图表本来想用以前flutter上那款,结果没发现vue版本,最后退而求其次选echarts,什么uchart这些根本就没入法眼,界面没得echarts好看二个功能上没得这个强,那就开干

  1. 最开始信了官方的鬼话用renderjs,说性能有多强大,调是调了,也显出来了,最后发现一个重大问题,renderjs操作dom元素会不释放内存,就算调了disponse方法也没用,只把实例给剁了,加载到页面上的那个js文件还在内存中没得到释放,这个东东起码搞了我两天,最后放弃renderjs

  2. 下来又找,最后发现mpvue-echarts 这个东东试了还可以,内存也能及时回收,速度也够快, 不过这个东东停止维护了,示例上边的echarts.js文件有点老还是个simple的阉割版,样式没有5.0好看,官方下了个5.0上来,一来就报一堆错,以为不兼容新版,又找结果还真有改进版的,拿上去还是不对,版本从3.7一直下到5.0全试了一个遍,还是没对,晚上下搞到早上8点过才搞通,最后才发现原来echarts官方那个在线定制功能惹的祸,那个编译出来的包拿这上边根本过不了.最后下了个5.0整个项目包,dist目录下的文件拿过来一试就OK.

苹果7真机上边app测试完美通过,
H5上边报错 需要把echarts的js文件中 两处操作dom元素的
addEventListener语句删除
因为这个不是直接操作dom元素而是cavnas画板渲染,不需要这两处监听

两处分别为:
&& !!window.addEventListener
el.addEventListener(name, handler, opt); 压缩版 t.addEventListener(n,e,i)改为null

基本操作就是:
https://github.com/dcloudio/hello-uniapp/blob/master/components/mpvue-echarts/src/echarts.vue 上边拿 mpvue-echarts 目录下src下边两个文件
echarts.vue 要做点小修改:
删除error事件
删除mounted 和 onReady 的H5条件编译 不然手机显示白屏

上代码:

<template>  
    <view class="content">  
        <mpvue-echarts class="ec-canvas"   
    @onInit="lineInit" canvasId="line" ref="lineChart" />  
    </view>  
</template>  
<script>  
import * as echarts from '../../components/mpvue-echarts/echarts.min.js';   //官方下载的5.0库  
import mpvueEcharts from '../../components/mpvue-echarts/echarts.vue';  
//-------------------------------------------------------------  
 //官方演示option,这个地方方便从官方上拷示例把 let隔了一行  
let   

option = {  
    title: {  
        text: '某地区蒸发量和降水量',  
        subtext: '纯属虚构'  
    },  
    tooltip: {  
        trigger: 'axis'  
    },  
    legend: {  
        data: ['蒸发量', '降水量']  
    },  
    toolbox: {  
        show: true,  
        feature: {  
            dataView: {show: true, readOnly: false},  
            magicType: {show: true, type: ['line', 'bar']},  
            restore: {show: true},  
            saveAsImage: {show: true}  
        }  
    },  
    calculable: true,  
    xAxis: [  
        {  
            type: 'category',  
            data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']  
        }  
    ],  
    yAxis: [  
        {  
            type: 'value'  
        }  
    ],  
    series: [  
        {  
            name: '蒸发量',  
            type: 'bar',  
            data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3],  
            markPoint: {  
                data: [  
                    {type: 'max', name: '最大值'},  
                    {type: 'min', name: '最小值'}  
                ]  
            },  
            markLine: {  
                data: [  
                    {type: 'average', name: '平均值'}  
                ]  
            }  
        },  
        {  
            name: '降水量',  
            type: 'bar',  
            data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],  
            markPoint: {  
                data: [  
                    {name: '年最高', value: 182.2, xAxis: 7, yAxis: 183},  
                    {name: '年最低', value: 2.3, xAxis: 11, yAxis: 3}  
                ]  
            },  
            markLine: {  
                data: [  
                    {type: 'average', name: '平均值'}  
                ]  
            }  
        }  
    ]  
};  

//------------------------------------------  

export default {  
    //导入mpvue的mpvueEcharts组件。  
    components: {  
        mpvueEcharts  
    },  
    data() {  
        return {};  
    },  
    onLoad() {},  
    methods: {  
        lineInit(e) {  
            let { width, height } = e;  
            let canvas = this.$refs.lineChart.canvas;  
            echarts.setCanvasCreator(() => canvas);  
                        //这步很关键能拿到对象就万事大吉  
            let lineChart = echarts.init(canvas, null, {  
                width: width,  
                height: height  
            });  
            canvas.setChart(lineChart);  
            lineChart.setOption(option);  
            this.$refs.lineChart.setChart(lineChart);  
        }  
    }  
};  
</script>  

<style>  
//容器必须设高度宽度否则无法显示  
.content {  
    height: 1200rpx;  
    width: 100%;  
}  
//画板宽高  
.ec-canvas{  
    height: 1200rpx;  
    width: 100%;  
}  
</style>
继续阅读 »

uniapp 调用echarts 这个问题整了四天终于过了
总结下经验:
最开始做图表本来想用以前flutter上那款,结果没发现vue版本,最后退而求其次选echarts,什么uchart这些根本就没入法眼,界面没得echarts好看二个功能上没得这个强,那就开干

  1. 最开始信了官方的鬼话用renderjs,说性能有多强大,调是调了,也显出来了,最后发现一个重大问题,renderjs操作dom元素会不释放内存,就算调了disponse方法也没用,只把实例给剁了,加载到页面上的那个js文件还在内存中没得到释放,这个东东起码搞了我两天,最后放弃renderjs

  2. 下来又找,最后发现mpvue-echarts 这个东东试了还可以,内存也能及时回收,速度也够快, 不过这个东东停止维护了,示例上边的echarts.js文件有点老还是个simple的阉割版,样式没有5.0好看,官方下了个5.0上来,一来就报一堆错,以为不兼容新版,又找结果还真有改进版的,拿上去还是不对,版本从3.7一直下到5.0全试了一个遍,还是没对,晚上下搞到早上8点过才搞通,最后才发现原来echarts官方那个在线定制功能惹的祸,那个编译出来的包拿这上边根本过不了.最后下了个5.0整个项目包,dist目录下的文件拿过来一试就OK.

苹果7真机上边app测试完美通过,
H5上边报错 需要把echarts的js文件中 两处操作dom元素的
addEventListener语句删除
因为这个不是直接操作dom元素而是cavnas画板渲染,不需要这两处监听

两处分别为:
&& !!window.addEventListener
el.addEventListener(name, handler, opt); 压缩版 t.addEventListener(n,e,i)改为null

基本操作就是:
https://github.com/dcloudio/hello-uniapp/blob/master/components/mpvue-echarts/src/echarts.vue 上边拿 mpvue-echarts 目录下src下边两个文件
echarts.vue 要做点小修改:
删除error事件
删除mounted 和 onReady 的H5条件编译 不然手机显示白屏

上代码:

<template>  
    <view class="content">  
        <mpvue-echarts class="ec-canvas"   
    @onInit="lineInit" canvasId="line" ref="lineChart" />  
    </view>  
</template>  
<script>  
import * as echarts from '../../components/mpvue-echarts/echarts.min.js';   //官方下载的5.0库  
import mpvueEcharts from '../../components/mpvue-echarts/echarts.vue';  
//-------------------------------------------------------------  
 //官方演示option,这个地方方便从官方上拷示例把 let隔了一行  
let   

option = {  
    title: {  
        text: '某地区蒸发量和降水量',  
        subtext: '纯属虚构'  
    },  
    tooltip: {  
        trigger: 'axis'  
    },  
    legend: {  
        data: ['蒸发量', '降水量']  
    },  
    toolbox: {  
        show: true,  
        feature: {  
            dataView: {show: true, readOnly: false},  
            magicType: {show: true, type: ['line', 'bar']},  
            restore: {show: true},  
            saveAsImage: {show: true}  
        }  
    },  
    calculable: true,  
    xAxis: [  
        {  
            type: 'category',  
            data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']  
        }  
    ],  
    yAxis: [  
        {  
            type: 'value'  
        }  
    ],  
    series: [  
        {  
            name: '蒸发量',  
            type: 'bar',  
            data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3],  
            markPoint: {  
                data: [  
                    {type: 'max', name: '最大值'},  
                    {type: 'min', name: '最小值'}  
                ]  
            },  
            markLine: {  
                data: [  
                    {type: 'average', name: '平均值'}  
                ]  
            }  
        },  
        {  
            name: '降水量',  
            type: 'bar',  
            data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],  
            markPoint: {  
                data: [  
                    {name: '年最高', value: 182.2, xAxis: 7, yAxis: 183},  
                    {name: '年最低', value: 2.3, xAxis: 11, yAxis: 3}  
                ]  
            },  
            markLine: {  
                data: [  
                    {type: 'average', name: '平均值'}  
                ]  
            }  
        }  
    ]  
};  

//------------------------------------------  

export default {  
    //导入mpvue的mpvueEcharts组件。  
    components: {  
        mpvueEcharts  
    },  
    data() {  
        return {};  
    },  
    onLoad() {},  
    methods: {  
        lineInit(e) {  
            let { width, height } = e;  
            let canvas = this.$refs.lineChart.canvas;  
            echarts.setCanvasCreator(() => canvas);  
                        //这步很关键能拿到对象就万事大吉  
            let lineChart = echarts.init(canvas, null, {  
                width: width,  
                height: height  
            });  
            canvas.setChart(lineChart);  
            lineChart.setOption(option);  
            this.$refs.lineChart.setChart(lineChart);  
        }  
    }  
};  
</script>  

<style>  
//容器必须设高度宽度否则无法显示  
.content {  
    height: 1200rpx;  
    width: 100%;  
}  
//画板宽高  
.ec-canvas{  
    height: 1200rpx;  
    width: 100%;  
}  
</style>
收起阅读 »

小团队承接H5、小程序、APP等外包。

外包 微信小程序 h5 移动APP

经验丰富,做过多种类型项目,有案例可看;
开发流程、目录搭建、代码都很规范(二次开发也很容易上手);
整个项目外包可以找我(小团队接单,面向客户、产品);
只需要前端部分也可以找我(个人接单,面向服务端合作伙伴);
wechat、tel(13995636759);

继续阅读 »

经验丰富,做过多种类型项目,有案例可看;
开发流程、目录搭建、代码都很规范(二次开发也很容易上手);
整个项目外包可以找我(小团队接单,面向客户、产品);
只需要前端部分也可以找我(个人接单,面向服务端合作伙伴);
wechat、tel(13995636759);

收起阅读 »

关于UNIAPP缓存存储获取失效问题

缓存

uni.setStorageSync官方标注不能存储对象类型
但是IOS可以存储对象类型,亲测有效
就是因为测试有效导致好多问题,MMP。。。。。。
安卓存储对象类型无效确如官方所说!
所以有小伙伴遇到IOS可以存储数据,安卓无效的时候可以考虑一下是否存储的是对象类型

继续阅读 »

uni.setStorageSync官方标注不能存储对象类型
但是IOS可以存储对象类型,亲测有效
就是因为测试有效导致好多问题,MMP。。。。。。
安卓存储对象类型无效确如官方所说!
所以有小伙伴遇到IOS可以存储数据,安卓无效的时候可以考虑一下是否存储的是对象类型

收起阅读 »

KeyChain(钥匙串),保存的信息不会因App被删除而丢失(ios)

KeyChain(钥匙串),保存的信息不会因App被删除而丢失(ios):https://ext.dcloud.net.cn/plugin?id=3708

KeyChain(钥匙串),保存的信息不会因App被删除而丢失(ios):https://ext.dcloud.net.cn/plugin?id=3708

个人承接vue、uniapp项目,有案例,联系vx:web9688,不接受外地工作,可付费提供技术支持。添加vx时务必备注来意以及添加渠道,渠道可以写:`uniapp`

vue.js uniapp插件 uni_app uniapp

个人承接vue、uniapp项目,有案例,联系vx:web9688,不接受外地工作,可付费提供技术支持。添加vx时务必备注来意以及添加渠道,渠道可以写:uniapp

个人承接vue、uniapp项目,有案例,联系vx:web9688,不接受外地工作,可付费提供技术支持。添加vx时务必备注来意以及添加渠道,渠道可以写:uniapp

adb.exe1.0.31实测可用(测试机:meizu x8)解决真机运行检测不到手机的问题

adb下载:https://wwe.lanzous.com/ilOAPjld4da 密码:co6d

使用:
手机对adb的版本有特定要求(遇到一些魅族手机有此问题),此时需要更换HBuilder的adb版本。

替换版本前,将默认版本的adb.exe备份下。
然后把1.0.31版的adb.exe拷贝出来替换主目录下的exe。

HBuilder的adb目录位置:tools/adbs目录(MAC下为HBuilder.app/Contents/tools/adbs目录)

HBuilderX的adb目录位置:plugins/launcher/tools/adbs目录(MAC下为/Applications/HBuilderX-Alpha.app/Contents/HBuilderX/plugins/launcher/tools/adbs目录)

HBuilder安装目录下带了多个版本的adb。
(补充:HBuiderX版本一般只有一个版本的adb)
以上参考了深井冰_01发布于2014-11-07 22:35 的《HBuilderX真机运行、手机运行、真机联调常见问题》
链接:https://ask.dcloud.net.cn/article/97

继续阅读 »

adb下载:https://wwe.lanzous.com/ilOAPjld4da 密码:co6d

使用:
手机对adb的版本有特定要求(遇到一些魅族手机有此问题),此时需要更换HBuilder的adb版本。

替换版本前,将默认版本的adb.exe备份下。
然后把1.0.31版的adb.exe拷贝出来替换主目录下的exe。

HBuilder的adb目录位置:tools/adbs目录(MAC下为HBuilder.app/Contents/tools/adbs目录)

HBuilderX的adb目录位置:plugins/launcher/tools/adbs目录(MAC下为/Applications/HBuilderX-Alpha.app/Contents/HBuilderX/plugins/launcher/tools/adbs目录)

HBuilder安装目录下带了多个版本的adb。
(补充:HBuiderX版本一般只有一个版本的adb)
以上参考了深井冰_01发布于2014-11-07 22:35 的《HBuilderX真机运行、手机运行、真机联调常见问题》
链接:https://ask.dcloud.net.cn/article/97

收起阅读 »

长期提供uniapp 插件/组件/页面定制开发

iOS Android nvue uniapp插件

长期提供uniapp 插件/组件/页面定制,vue/nvue均可。请看更多插件

欢迎各位老板进群叨扰 1091229222,加群请备注: uniapp社区

长期提供uniapp 插件/组件/页面定制,vue/nvue均可。请看更多插件

欢迎各位老板进群叨扰 1091229222,加群请备注: uniapp社区

uni-app uniCloud node.js支付宝网页支付开发心得

支付宝 uniCloud

支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建form表单对支付宝发起POST请求->支付宝支付成功POST异步通知开发者的服务器。

支付宝用到的是RSA加密,搞懂RSA加密的原理有助于理解支付宝支付流程。推荐李永乐老师讲非对称加密的视频。

这是已经写好的测试例子:https://static-b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/static/alipay-demo.html

代码很简单,直接看代码就理解了,注意点写在注释里。

请求支付参数的服务端代码:

'use strict'  
const NodeRSA = require('node-rsa') // 需要执行npm install node-rsa才能调用  
const querystring = require('querystring') // node自带,无需安装,直接调用  
exports.main = async (event, context) => {  
    const appId = '支付宝的appId'  
    let merchantPrivateKey = '支付宝商家公钥'  

    let ua = ''  
    try {  
        ua = event.headers['user-agent']  
    }catch(e){}  
    let productCode = 'FAST_INSTANT_TRADE_PAY'  
    let method = 'alipay.trade.page.pay'  
    if (ua.indexOf('Mobile') > -1) {  
        productCode = 'QUICK_WAP_WAY'  
        method = 'alipay.trade.wap.pay'  
    }  
    let bodyObj = querystring.parse(event.body) // url请求参数字符串转object。uni-app云函数实例化后,POST的请求参数在body里  
    if (Object.keys(bodyObj).length) {  
        const passbackParams = JSON.stringify({mobile: '18888888888', sku: 'year'}) // 开发者想要传递的参数,字符串,支付宝异步通知会带上这个  
        let bizContent = JSON.stringify({  
            subject: '支付宝测试-'+bodyObj.price+'元',  
            out_trade_no: (new Date()).getTime(),  
            total_amount: bodyObj.price,  
            product_code: productCode,  
            quit_url: 'https://imgbed.cn/static/alipay-return.html', // 手机网页支付放弃支付时返回的网址  
            passback_params: passbackParams  
        })  
        let queryObject = ksort({  
            app_id: appId,  
            biz_content: bizContent,  
            charset: 'UTF-8',  
            method: method,  
            notify_url: '支付宝异步通知地址',  
            return_url: 'https://imgbed.cn/static/alipay-return.html',  
            sign_type: 'RSA2',  
            timestamp: time2date((new Date()).getTime()), // Y-m-d H:i:s格式的字符串  
            version: '1.0'  
        })  
        const query = querystring.unescape(querystring.stringify(queryObject)) // 要再套一层querystring.unescape,否则query被转义,会导致签名的字符串跟支付宝不一致  
        const key = new NodeRSA(merchantPrivateKey, 'pkcs8-private')  
        const sign = key.sign(Buffer.from(query)).toString('base64')  
        queryObject.sign = sign  
        return {code:0, alipayParams: queryObject}  
    } else {  
        return {code: 1, msg: 'event.body is empty'}  
    }  

    /****************************************************************************************************/  
    // 毫秒时间戳转Y-m-d H:i:s或者Y-m-d  
    function add0(m){return m<10?'0'+m:m }  
    function time2date(shijianchuo, onlyDate) {  
        var time = new Date(shijianchuo);  
        var y = time.getFullYear();  
        var m = time.getMonth()+1;  
        var d = time.getDate();  
        var h = time.getHours();  
        var mm = time.getMinutes();  
        var s = time.getSeconds();  
        let returnStr = y+'-'+add0(m)+'-'+add0(d)  
        if (!onlyDate) {  
            returnStr += ' '+add0(h)+':'+add0(mm)+':'+add0(s)  
        }  
        return returnStr  
    }  

    // 对object的key进行排序  
    function ksort(params) {  
        let keys = Object.keys(params).sort();  
        let newParams = {};  
        keys.forEach((key) => {  
            newParams[key] = params[key];  
        });  
        return newParams;  
    }  
}

前端支付代码,为了方便,我用了vue+vant:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, viewport-fit=cover">  
        <title>支付宝支付演示</title>  
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.11/lib/index.css"/>  
    </head>  
    <body>  
        <div id='app' style="display: none; padding: 100px 15px 15px 15px;">  
            <div style="max-width: 512px; margin: 0 auto;">  
                <p><img height="44px" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/dca221bb-2a37-4527-bea7-6fcb92945c18.png"></p>  
                <p v-for="price in prices"><van-button @click="alipay(price)" type="info" block>{{price}}元</van-button></p>  
            </div>  
            <div id="form-pay"></div>  

            <van-overlay :show="showLoading">  
                <div style="display: flex; align-items: center; justify-content: center; height: 100%">  
                    <van-loading size="24px" vertical>加载中...</van-loading>  
                </div>  
            </van-overlay>  
        </div>  
        <script src="https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>  
        <script src="https://cdn.jsdelivr.net/npm/vant@2.11/lib/vant.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js"></script>  
        <script>  
            const vm = new Vue({  
                el: '#app',  
                data() {  
                    return {  
                        prices: [0.01, 1, 10, 100],  
                        showLoading: false  
                    }  
                },  
                methods: {  
                    alipay(price) {  
                        if (navigator.userAgent.indexOf('MicroMessenger')>-1) {  
                            vant.Dialog({ message: '点击微信右上角···,选择“用浏览器打开”' })  
                        } else {  
                            this.showLoading = true  
                            axios({  
                                url: 'https://b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/http/alipay-params-demo', // uni-app云函数URL实例化的api  
                                data: Qs.stringify({price: price}),  
                                method: 'POST'  
                            }).then(res=>{  
                                this.showLoading = false  
                                if (0===res.data.code) {  
                                    const obj = res.data.alipayParams  
                                    const keys = Object.keys(obj)  
                                    let formHtml = ''  
                                    formHtml += '<meta charset="utf-8">'  
                                    formHtml += '<form id="alipaysubmit" method="POST" name="alipaysubmit" action="https://openapi.alipay.com/gateway.do?charset=UTF-8">'  
                                    for (i=0; i<keys.length; i++) {  
                                        formHtml += '<input type="hidden" name="'+keys[i]+'" value=\''+obj[keys[i]]+'\'>'  
                                    }  
                                    // formHtml += '<input type="submit">' // 手动提交表单  
                                    formHtml += '</form>'  
                                    $('#form-pay').html(formHtml)  
                                    document.forms["alipaysubmit"].submit() // 自动提交表单  
                                } else {  
                                    console.error(res.data.msg)  
                                }  
                            }).catch(err=>{  
                                this.showLoading = false  
                                console.error(err)  
                            })  
                        }  
                    }  
                }  
            })  
            $(document).ready(function(){  
                $('#app').show()  
            })  
        </script>  
    </body>  
</html>

支付成功后,支付宝会异步通知,开发者接收异步通知,验签,代码如下:

'use strict'  
const querystring = require('querystring')  
const NodeRSA = require('node-rsa')  
exports.main = async (event, context) => {  
    const alipayPublicKey = '支付宝公钥'  

    let bodyObj = ksort(querystring.parse(event.body))  
    if ('TRADE_SUCCESS'===bodyObj.trade_status) { // 支付失败也有可能会收到异步通知,所以这里要判断TRADE_SUCCESS  
        const outTradeNo = bodyObj.out_trade_no  
        if (outTradeNo是否已经存在于数据库) { // 异步通知可能会收到多次,判断out_trade_no是否存过数据库来判断重复通知  
            const sign = bodyObj.sign  
            delete bodyObj.sign  
            delete bodyObj.sign_type  
            const body = querystring.unescape(querystring.stringify(bodyObj, '&', '='))  
            const key = new NodeRSA(alipayPublicKey, 'pkcs8-public')  
            if (key.verify(Buffer.from(body), sign, 'utf8', 'base64')) { // 验签通过,继续执行业务代码  
                // 用户处理订单的代码  
            } else {  
                console.error('验签失败')  
            }  
        } else {  
            console.error('订单号已存在')  
        }  
    } else {  
        console.error('trade_status', bodyObj.trade_status)  
    }  

    return 'success' // 一定要返回'success'给支付宝,否则会重复多次通知  
}  

// 对object的key进行排序  
function ksort(params) {  
    let keys = Object.keys(params).sort();  
    let newParams = {};  
    keys.forEach((key) => {  
        newParams[key] = params[key];  
    });  
    return newParams;  
}

转载自:https://coding3.com/archives/unicloud-alipay.html

继续阅读 »

支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建form表单对支付宝发起POST请求->支付宝支付成功POST异步通知开发者的服务器。

支付宝用到的是RSA加密,搞懂RSA加密的原理有助于理解支付宝支付流程。推荐李永乐老师讲非对称加密的视频。

这是已经写好的测试例子:https://static-b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/static/alipay-demo.html

代码很简单,直接看代码就理解了,注意点写在注释里。

请求支付参数的服务端代码:

'use strict'  
const NodeRSA = require('node-rsa') // 需要执行npm install node-rsa才能调用  
const querystring = require('querystring') // node自带,无需安装,直接调用  
exports.main = async (event, context) => {  
    const appId = '支付宝的appId'  
    let merchantPrivateKey = '支付宝商家公钥'  

    let ua = ''  
    try {  
        ua = event.headers['user-agent']  
    }catch(e){}  
    let productCode = 'FAST_INSTANT_TRADE_PAY'  
    let method = 'alipay.trade.page.pay'  
    if (ua.indexOf('Mobile') > -1) {  
        productCode = 'QUICK_WAP_WAY'  
        method = 'alipay.trade.wap.pay'  
    }  
    let bodyObj = querystring.parse(event.body) // url请求参数字符串转object。uni-app云函数实例化后,POST的请求参数在body里  
    if (Object.keys(bodyObj).length) {  
        const passbackParams = JSON.stringify({mobile: '18888888888', sku: 'year'}) // 开发者想要传递的参数,字符串,支付宝异步通知会带上这个  
        let bizContent = JSON.stringify({  
            subject: '支付宝测试-'+bodyObj.price+'元',  
            out_trade_no: (new Date()).getTime(),  
            total_amount: bodyObj.price,  
            product_code: productCode,  
            quit_url: 'https://imgbed.cn/static/alipay-return.html', // 手机网页支付放弃支付时返回的网址  
            passback_params: passbackParams  
        })  
        let queryObject = ksort({  
            app_id: appId,  
            biz_content: bizContent,  
            charset: 'UTF-8',  
            method: method,  
            notify_url: '支付宝异步通知地址',  
            return_url: 'https://imgbed.cn/static/alipay-return.html',  
            sign_type: 'RSA2',  
            timestamp: time2date((new Date()).getTime()), // Y-m-d H:i:s格式的字符串  
            version: '1.0'  
        })  
        const query = querystring.unescape(querystring.stringify(queryObject)) // 要再套一层querystring.unescape,否则query被转义,会导致签名的字符串跟支付宝不一致  
        const key = new NodeRSA(merchantPrivateKey, 'pkcs8-private')  
        const sign = key.sign(Buffer.from(query)).toString('base64')  
        queryObject.sign = sign  
        return {code:0, alipayParams: queryObject}  
    } else {  
        return {code: 1, msg: 'event.body is empty'}  
    }  

    /****************************************************************************************************/  
    // 毫秒时间戳转Y-m-d H:i:s或者Y-m-d  
    function add0(m){return m<10?'0'+m:m }  
    function time2date(shijianchuo, onlyDate) {  
        var time = new Date(shijianchuo);  
        var y = time.getFullYear();  
        var m = time.getMonth()+1;  
        var d = time.getDate();  
        var h = time.getHours();  
        var mm = time.getMinutes();  
        var s = time.getSeconds();  
        let returnStr = y+'-'+add0(m)+'-'+add0(d)  
        if (!onlyDate) {  
            returnStr += ' '+add0(h)+':'+add0(mm)+':'+add0(s)  
        }  
        return returnStr  
    }  

    // 对object的key进行排序  
    function ksort(params) {  
        let keys = Object.keys(params).sort();  
        let newParams = {};  
        keys.forEach((key) => {  
            newParams[key] = params[key];  
        });  
        return newParams;  
    }  
}

前端支付代码,为了方便,我用了vue+vant:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, viewport-fit=cover">  
        <title>支付宝支付演示</title>  
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.11/lib/index.css"/>  
    </head>  
    <body>  
        <div id='app' style="display: none; padding: 100px 15px 15px 15px;">  
            <div style="max-width: 512px; margin: 0 auto;">  
                <p><img height="44px" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/dca221bb-2a37-4527-bea7-6fcb92945c18.png"></p>  
                <p v-for="price in prices"><van-button @click="alipay(price)" type="info" block>{{price}}元</van-button></p>  
            </div>  
            <div id="form-pay"></div>  

            <van-overlay :show="showLoading">  
                <div style="display: flex; align-items: center; justify-content: center; height: 100%">  
                    <van-loading size="24px" vertical>加载中...</van-loading>  
                </div>  
            </van-overlay>  
        </div>  
        <script src="https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>  
        <script src="https://cdn.jsdelivr.net/npm/vant@2.11/lib/vant.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>  
        <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js"></script>  
        <script>  
            const vm = new Vue({  
                el: '#app',  
                data() {  
                    return {  
                        prices: [0.01, 1, 10, 100],  
                        showLoading: false  
                    }  
                },  
                methods: {  
                    alipay(price) {  
                        if (navigator.userAgent.indexOf('MicroMessenger')>-1) {  
                            vant.Dialog({ message: '点击微信右上角···,选择“用浏览器打开”' })  
                        } else {  
                            this.showLoading = true  
                            axios({  
                                url: 'https://b1ebbd3c-ca49-405b-957b-effe60782276.bspapp.com/http/alipay-params-demo', // uni-app云函数URL实例化的api  
                                data: Qs.stringify({price: price}),  
                                method: 'POST'  
                            }).then(res=>{  
                                this.showLoading = false  
                                if (0===res.data.code) {  
                                    const obj = res.data.alipayParams  
                                    const keys = Object.keys(obj)  
                                    let formHtml = ''  
                                    formHtml += '<meta charset="utf-8">'  
                                    formHtml += '<form id="alipaysubmit" method="POST" name="alipaysubmit" action="https://openapi.alipay.com/gateway.do?charset=UTF-8">'  
                                    for (i=0; i<keys.length; i++) {  
                                        formHtml += '<input type="hidden" name="'+keys[i]+'" value=\''+obj[keys[i]]+'\'>'  
                                    }  
                                    // formHtml += '<input type="submit">' // 手动提交表单  
                                    formHtml += '</form>'  
                                    $('#form-pay').html(formHtml)  
                                    document.forms["alipaysubmit"].submit() // 自动提交表单  
                                } else {  
                                    console.error(res.data.msg)  
                                }  
                            }).catch(err=>{  
                                this.showLoading = false  
                                console.error(err)  
                            })  
                        }  
                    }  
                }  
            })  
            $(document).ready(function(){  
                $('#app').show()  
            })  
        </script>  
    </body>  
</html>

支付成功后,支付宝会异步通知,开发者接收异步通知,验签,代码如下:

'use strict'  
const querystring = require('querystring')  
const NodeRSA = require('node-rsa')  
exports.main = async (event, context) => {  
    const alipayPublicKey = '支付宝公钥'  

    let bodyObj = ksort(querystring.parse(event.body))  
    if ('TRADE_SUCCESS'===bodyObj.trade_status) { // 支付失败也有可能会收到异步通知,所以这里要判断TRADE_SUCCESS  
        const outTradeNo = bodyObj.out_trade_no  
        if (outTradeNo是否已经存在于数据库) { // 异步通知可能会收到多次,判断out_trade_no是否存过数据库来判断重复通知  
            const sign = bodyObj.sign  
            delete bodyObj.sign  
            delete bodyObj.sign_type  
            const body = querystring.unescape(querystring.stringify(bodyObj, '&', '='))  
            const key = new NodeRSA(alipayPublicKey, 'pkcs8-public')  
            if (key.verify(Buffer.from(body), sign, 'utf8', 'base64')) { // 验签通过,继续执行业务代码  
                // 用户处理订单的代码  
            } else {  
                console.error('验签失败')  
            }  
        } else {  
            console.error('订单号已存在')  
        }  
    } else {  
        console.error('trade_status', bodyObj.trade_status)  
    }  

    return 'success' // 一定要返回'success'给支付宝,否则会重复多次通知  
}  

// 对object的key进行排序  
function ksort(params) {  
    let keys = Object.keys(params).sort();  
    let newParams = {};  
    keys.forEach((key) => {  
        newParams[key] = params[key];  
    });  
    return newParams;  
}

转载自:https://coding3.com/archives/unicloud-alipay.html

收起阅读 »

初见鸿蒙,我觉得吧,嘿嘿。

鸿蒙采用了JS和JAVA混合开发模式。
惊喜的发现鸿蒙的编写方式和UNI都很像。
我觉得吧,未来还会有更多的惊喜的。
包括这里。哈哈。

鸿蒙采用了JS和JAVA混合开发模式。
惊喜的发现鸿蒙的编写方式和UNI都很像。
我觉得吧,未来还会有更多的惊喜的。
包括这里。哈哈。

微信小程序预览图片,因版权问题,不让用户下载,保存

解决方法:uni.previewImage({ current: src, // 当前显示图片的http链接 urls: [src], showmenu: false, })
起初看微信小程序,官网给的api 定义 wx.previewImage(Object object, boolean showmenu),这个是问题的, boolean showmenu 这个也是要写在对象里面的

uniapp 中也可以使用这个方法,但是文档还没有更新,希望尽快更新

继续阅读 »

解决方法:uni.previewImage({ current: src, // 当前显示图片的http链接 urls: [src], showmenu: false, })
起初看微信小程序,官网给的api 定义 wx.previewImage(Object object, boolean showmenu),这个是问题的, boolean showmenu 这个也是要写在对象里面的

uniapp 中也可以使用这个方法,但是文档还没有更新,希望尽快更新

收起阅读 »