
支付宝APP支付开通教程

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

nvue实现全局挂载
前言
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好看二个功能上没得这个强,那就开干
-
最开始信了官方的鬼话用renderjs,说性能有多强大,调是调了,也显出来了,最后发现一个重大问题,renderjs操作dom元素会不释放内存,就算调了disponse方法也没用,只把实例给剁了,加载到页面上的那个js文件还在内存中没得到释放,这个东东起码搞了我两天,最后放弃renderjs
-
下来又找,最后发现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好看二个功能上没得这个强,那就开干
-
最开始信了官方的鬼话用renderjs,说性能有多强大,调是调了,也显出来了,最后发现一个重大问题,renderjs操作dom元素会不释放内存,就算调了disponse方法也没用,只把实例给剁了,加载到页面上的那个js文件还在内存中没得到释放,这个东东起码搞了我两天,最后放弃renderjs
-
下来又找,最后发现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>
收起阅读 »

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、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

uni-app uniCloud node.js支付宝网页支付开发心得
支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建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;
}
支付宝网页支付的流程:前端请求支付宝支付表单参数->后端生成支付表单参数给前端->前端根据支付参数构建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
收起阅读 »
微信小程序预览图片,因版权问题,不让用户下载,保存
解决方法: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 中也可以使用这个方法,但是文档还没有更新,希望尽快更新
收起阅读 »