今年利用业余时间搞了个web应用,在支付环节卡住了,因为支付宝和微信都不支持个人收款,必须要有公司资质,没办法,只能google下看看是否有人和我遇到相同问题,果然有很多解决方案,研究了一个多礼拜,发现个人收款都是收费的,而且还不稳定,服务也非常差,问个问题半天甚至几天后才有回答。最后没办法只能自己各种研究,搞出了一种真正免费的个人免签支付方案(不需了解Andriod,不需了解后端开发,甚至不需要写一行代码),不敢独享,现分享给大家。
终极原理很简单,就一句话:Android端应用将收到的个人微信的收款信息通知后端服务,后端服务分析出谁支付了多少钱等信息,然后通知开发者支付完成并自动发货。
1. 开发者上传微信的个人收款码到平台。
2. 用户发起请求支付服务。
3. 平台返回相匹配的收款二维码并展示给用户。
4. 用户完成扫码支付。
5. 安装有收款微信的Android手机收到用户完成支付的通知。
6. Android端应用将收款信息通知后端服务。
7. 后端服务分析并校验无误后,通知开发者用户已经完成支付,可以自动发货了。
下面介绍利用「冰狐智能辅助」实现免费的个人免签支付的方案,您只需要详细案以下步骤操作,不需要写一行代码即可实现免费的个人免签支付。分两部分实现,一部分是移动端,负责捕获收款信息;另一部分是后端服务,负责解析并通知开发者支付结果。在之前请先免费注册一个用户。
function main() {
while (true) {
}
}
function cbNotification(textList, className, packageName, event) {
for (var item of textList) {
if (item.includes('微信支付')) {
var arr = item.fetchNumber();
if (arr.length > 0) {
var ret = callMicroService('pay_notify', [rsUUID, 1, arr[arr.length - 1]]);
console.log('call ret:' + ret);
}
return true;
}
}
return false;
}
微服务脚本用于实现后端的业务逻辑,比如:上传和查看支付二维码,查看支付订单,通知开发者支付结果等功能。
打开web页面「微服务」/「微服务脚本」新建如下脚本,并将对应的JS源码粘贴进去:
function main(uuid, type, price) {
var ret = false;
price = parseInt(price*100);
var key = uuid + price;
var orderId = cacheGet(key);
if (orderId) {
console.log('匹配到价格,可以通知了:' + orderId);
var data = dbQuery('order', '*', ['id=' + orderId]);
if (data && data.length == 1) {
var item = data[0];
// 通知 调用者
var bodyParams = {outTradeNo: item.outTradeNo, price: item.price / 100.0, realPrice: item.realPrice / 100.0};
var result = 2, postResult = httpPost(item.notifyUrl, bodyParams);
if (postResult && postResult.length > 0) {
if (postResult.status == 200 || postResult.status == 302) {
result = 1;
}
}
// change status
ret = dbUpdate('order', ['status=' + result], ['id=' + orderId]);
if (!ret) {
console.error('更新状态失败');
}
cacheRemove(key);
}
} else {
console.error('invalidate price:' + price);
}
return ret;
}
function main(payType, price, deviceId, file) {
price = parseInt(price*100);
var ret = false, qrCode = qrDecode(file.inputStream);
if (qrCode != '') {
var qRet = dbQuery('price', 'count(id) as count', [`userId='${rsOpenId}'`, `price=${price}`, `payType=${payType}`, `deviceId='${deviceId}'`]);
if (qRet[0].count > 0) {
ret = dbUpdate('price', [`qrCode='${qrCode}'`], [`price=${price}`, `payType=${payType}`, `deviceId='${deviceId}'`]);
} else {
var iRet = dbInsert('price', {userId:rsOpenId, price:price, payType:payType, deviceId:deviceId, qrCode:qrCode});
ret = iRet > 0;
}
}
return ret;
}
function main(fetchCountOnly, conditions, startIndex, itemCount) {
var jsonConditions = JSON.parse(conditions), params = [`userId='${rsOpenId}'`];
for (var item of jsonConditions) {
if (item.name == 'query_type') {
var type = parseInt(item.value);
if (type > 0) {
params.push('payType=' + type);
}
}
}
if (fetchCountOnly) {
var qRet = dbQuery('price', 'count(id) as count', params);
return qRet[0].count;
} else {
var qRet = dbQuery('price', '*', params, '', startIndex, itemCount);
for (var item of qRet) {
item.price /= 100.0;
if (item.payType == 1) {
item.payType = '微信';
} else {
item.payType = '支付宝';
}
}
return qRet;
}
}
function main(fetchCountOnly, conditions, startIndex, itemCount) {
var jsonConditions = JSON.parse(conditions);
var params = [`userId='${rsOpenId}'`];
for (var item of jsonConditions) {
var deviceId = item.value;
if (item.name == 'deviceId' && deviceId != '') {
params.push(`deviceId='${deviceId}'`);
}
}
if (fetchCountOnly) {
var qRet = dbQuery('order', 'count(id) as count', params);
return qRet[0].count;
} else {
var qRet = dbQuery('order', '*', params, 'order by time desc', startIndex, itemCount);
for (var item of qRet) {
item.price /= 100.0;
item.realPrice /= 100.0;
if (item.payType == 1) {
item.payType = '微信';
} else {
item.payType = '支付宝';
}
if (item.status == 0) {
item.status = '等待支付';
} else if (item.status == 1) {
item.status = '交易完成';
} else if (item.status == 2) {
item.status = '通知失败';
} else if (item.status == -1) {
item.status = '订单过期';
}
var datetime = new Date();
datetime.setTime(item.time);
var year = datetime.getYear();
var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
var hour = datetime.getHours()< 10 ? "0" + datetime.getHours() : datetime.getHours();
var minute = datetime.getMinutes()< 10 ? "0" + datetime.getMinutes() : datetime.getMinutes();
var second = datetime.getSeconds()< 10 ? "0" + datetime.getSeconds() : datetime.getSeconds();
var t = year + "-" + month + "-" + date+" "+hour+":"+minute+":"+second;
item.time = t;
}
return qRet;
}
}
function main(payType, price, outTradeNo, notifyUrl, timeout = 120000) {
price = parseInt(price*100);
var ret = null, qrImage = '', realPrice = price;
var maxCount = 5, i = 0, anyRet = null, findAny = false;
while (i < maxCount) {
realPrice = price - i;
var qRet = dbQuery('price', '*', [`userId='${rsOpenId}'`, `payType=${payType}`, `price=${realPrice}`]);
if (qRet.length <= 0) {
if (!findAny) {
anyRet = dbQuery('price', '*', [`userId='${rsOpenId}'`, `payType=${payType}`, 'price=0']);
findAny = true;
}
qRet = anyRet;
}
if (qRet.length > 0) {
for (var item of qRet) {
var key = item.deviceId + realPrice;
var cache = cacheGet(key);
if (null == cache) {
lock('malloc_price');
cache = cacheGet(key);
if (null == cache) {
// 可以分配了
ret = dbInsert('order', {
userId: rsOpenId,
price: price,
realPrice: realPrice,
outTradeNo: outTradeNo,
deviceId: item.deviceId,
qrCode: item.qrCode,
notifyUrl: notifyUrl
});
}
if (null != ret) {
var image = qrEncode(item.qrCode);
qrImage = base64Encode(image);
cachePut(key, ret, 60000);
}
unlock('malloc_price');
if (null != ret) {
break;
}
}
}
}
if (null != ret) {
break;
}
++i;
}
if (null == ret) {
console.error('unified failed');
} else {
return {payTye: payType, price: price, realPrice: realPrice, qrImageData: qrImage, timeout: timeout};
}
return {};
}
function deletePrice(item) {
var id = item.id;
return dbDelete('price', [`id=${id}`]);
}
function deleteOrder(item) {
var id = item.id;
return dbDelete('order', [`id=${id}`]);
}
function getDevice(includeAll=false) {
var list = deviceList(rsOpenId);
var result = [];
if (includeAll) {
result.push({name: '全部', value: ''});
}
for (var item of list) {
result.push({name: item.name, value: item.uuid});
}
return result;
}
移动端接口用于「冰狐智能辅助」APP端脚本调用,在这里主要用于手机端通知后端服务支付相关信息。参考文档
服务端接口用于第三方服务调用「冰狐智能辅助」的后端功能,这里主要用于开发者在自己的后端服务中调用生成订单的接口。参考文档
价格数据库用于存放开发者上传的价格二维码信息。
[{
"type": "tinyint",
"name": "payType",
"default": "1",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}, {
"name": "unsigned",
"value": false,
"description": "无符号"
}]
}, {
"type": "varchar(45)",
"name": "deviceId",
"default": "''",
"options": [{
"name": "not null",
"value": true,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "varchar(128)",
"name": "qrCode",
"default": "''",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "int",
"name": "price",
"default": "0",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}, {
"name": "unsigned",
"value": false,
"description": "无符号"
}]
}, {
"type": "varchar(45)",
"name": "userId",
"default": "''",
"options": [{
"name": "not null",
"value": true,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
},{
"name": "支付类型",
"id": "payType",
"type": "select",
"defaultValue": "微信:1,支付宝:2",
"description": ""
}, {
"name": "价格",
"id": "price",
"type": "float",
"defaultValue": "",
"description": ""
}, {
"name": "设备",
"id": "deviceId",
"type": "select",
"defaultValue": "{\"name\":\"get_device\", \"params\":[], \"isDev\":true}",
"description": ""
}, {
"name": "二维码图片",
"id": "file",
"type": "file",
"defaultValue": "",
"description": ""
}]
[{
"indexName": "userId",
"columnsName": "userId"
}]
订单数据库用于存放订单数据。
[{
"type": "char(45)",
"name": "userId",
"default": "''",
"options": [{
"name": "not null",
"value": true,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "char(45)",
"name": "deviceId",
"default": "''",
"options": [{
"name": "not null",
"value": true,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "char(128)",
"name": "outTradeNo",
"default": "''",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "tinyint",
"name": "payType",
"default": "1",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}, {
"name": "unsigned",
"value": false,
"description": "无符号"
}]
}, {
"type": "int",
"name": "price",
"default": "",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}, {
"name": "unsigned",
"value": false,
"description": "无符号"
}]
}, {
"type": "int",
"name": "realPrice",
"default": "",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}, {
"name": "unsigned",
"value": false,
"description": "无符号"
}]
}, {
"type": "char(128)",
"name": "notifyUrl",
"default": "''",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "char(128)",
"name": "qrCode",
"default": "''",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}, {
"type": "tinyint",
"name": "status",
"default": "0",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}, {
"name": "unsigned",
"value": false,
"description": "无符号"
}]
}, {
"type": "timestamp",
"name": "time",
"default": "CURRENT_TIMESTAMP",
"options": [{
"name": "not null",
"value": false,
"description": "不为空"
}, {
"name": "unique",
"value": false,
"description": "不重复"
}]
}]
[{
"indexName": "userId",
"columnsName": "userId"
}, {
"indexName": "deviceId",
"columnsName": "deviceId"
}]
用于开发者录入价格二维码。
[{
"name": "支付类型",
"id": "payType",
"type": "select",
"defaultValue": "微信:1,支付宝:2",
"description": ""
}, {
"name": "价格",
"id": "price",
"type": "float",
"defaultValue": "",
"description": ""
}, {
"name": "设备",
"id": "deviceId",
"type": "select",
"defaultValue": "{\"scriptName\":\"common\",\"functionName\":\"getDevice\"}",
"description": ""
}, {
"name": "二维码图片",
"id": "file",
"type": "file",
"defaultValue": "",
"description": ""
}]
用于开发者查询已经录入的价格二维码信息。
[{
"name": "支付类型",
"id": "query_type",
"type": "select",
"defaultValue": "微信:1,支付宝:2",
"description": ""
}]
[{
"name": "支付类型",
"id": "payType"
}, {
"name": "价格",
"id": "price"
}, {
"name": "设备ID",
"id": "deviceId"
}, {
"name": "二维码",
"id": "qrCode"
}]
[{
"name": "删除",
"id": "delete",
"scriptId": "5",
"scriptName": "common",
"functionName": "deletePrice"
}]
用于开发者查询历史订单信息。
[{
"name": "设备",
"id": "deviceId",
"type": "select",
"defaultValue": "{\"scriptName\":\"common\",\"functionName\":\"getDevice\", \"params\":[true]}\t",
"description": ""
}]
[{
"name": "设备",
"id": "deviceId"
}, {
"name": "交易NO",
"id": "outTradeNo"
}, {
"name": "支付类型",
"id": "payType"
}, {
"name": "价格",
"id": "price"
}, {
"name": "支付价格",
"id": "realPrice"
}, {
"name": "状态",
"id": "status"
}, {
"name": "时间",
"id": "time"
}]
[{
"name": "删除",
"id": "delete",
"scriptId": "5",
"scriptName": "common",
"functionName": "deleteOrder"
}]
打开手机端「冰狐智能辅助」APP,点击“点击这里启动设备”启动设备。
本文介绍了一种基于「冰狐智能辅助」系统实现的、不需了解Andriod、不需了解后端开发、甚至不需要编写一行代码、免费的、个人免签支付方案和具体实现细节。有任何问题,欢迎评论留言,大家一起讨论!