什么是个人免签支付
个人免签支付就是给个人用的支付接口,一般的支付接口都需要营业执照才能申请,个人很难申请的到,或者是没有资质去申请,要和支付商进行签约的。免签,顾名思义就是不需要签约。那么个人免签支付就有市场了,就是为了解决个人无法轻易申请到支付接口的问题。
免签的方案有很多种
- APP监听收款码的支付结果,然后修改页面的支付结果。
- 二次清算。款先到拥有官方支付接口的商户中,由商户给你结算。
- Xposed微信插件实现全自动监听创建收款码、以及收款过程,容易封号。
方案其实有很多种,但是以上的方案都有不少的缺点,当然这些方案仍有不少人在用,没办法,确实是解决问题的一种办法。而本次文章我也是通过简单的技术开发实现第一种APP监听收款码的免签支付方式。
APP监听收款码的支付结果
整个过程很简单:
访问URL -> 检查数据库2分钟内未支付的订单金额 -> 如果2分钟内未支付的订单金额中存在当前订单一样的金额,需要在当前金额基础上+0.01元用于区分订单的唯一性 -> 用户扫码支付 -> 安卓手机APP监听到这笔订单的收款 -> 将收款金额异步发送到服务器 -> 服务器修改数据库该笔订单金额的支付状态 -> 扫码页面一直在轮询订单的支付状态,发现已支付就修改页面的支付结果 -> 完成支付。
视频演示
点击链接播放演示视频:https://share.weiyun.com/XliTd63d
上代码
index.php
该页面是用于创建订单的,其中 $order_price = 0.01;
就是创建一笔0.01元的订单。创建订单前,程序会查询数据库来确定当前金额是否存在未支付的订单,如果存在,需支付的金额会在当前订单金额基础上+0.01元。
为什么要这么做?因为APP监听只能监听到收到了多少钱,无法监听到订单号,所以无法区分这个金额是你支付的还是其他人支付的。
所以在当前订单有效期内,你需要注意两件事,第一,你这个程序的访问量一定不能太高,不允许高并发的情况出现,适合小众,小规模,小流量的业务使用,一旦人多了,短时间内订单量多了,很难做到精准的监听支付结果。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover">
<meta charset="utf-8">
<script src="./js/jquery.min.js"></script>
<link rel="stylesheet" href="./css/style.css">
<title>微信赞赏码免签约支付实现原理Demo</title>
</head>
<body onload="clock(120)">
<?php
// 数据库配置
include './Db.php';
// 实例化类
$db = new DB_API($config);
// 订单号
$order_num = date('Ymd').time();
// 订单金额
$order_price = 0.01;
// 获取未支付订单列表
$getOrderList = $db->set_table('mqpay_order')->findAll(['order_status' => 1]);
// 遍历订单
$orderNoExpire = array();
for ($i = 0; $i < count($getOrderList); $i++) {
// 订单时间
$order_time = json_decode(json_encode($getOrderList[$i]))->order_time;
// 订单金额
$order_money = json_decode(json_encode($getOrderList[$i]))->order_money;
// 获取2分钟未支付的订单
if(countTimes(time(),strtotime($order_time)) <= 2){
// 如果存在
$orderNoExpire[] = $order_money;
}
}
// 判断是否有2分钟未支付的订单
if(count($orderNoExpire) == 0){
$needPay = $order_price;
}else{
// 获取2分钟未支付的订单的最大金额+0.01
$needPay = max($orderNoExpire) + 0.01;
}
// 创建订单
creatOrder($order_num,$order_price,$needPay,$db);
// 创建订单
function creatOrder($order_num,$order_price,$needPay,$db){
// 订单参数
$createOrder = [
'order_num' => $order_num,
'order_price' => $order_price,
'order_money' => $needPay,
];
// 创建
$createOrderResult = $db->set_table('mqpay_order')->add($createOrder);
if($createOrderResult){
// 成功
echo '<div class="payInfoCard">
<div class="header">里客云科技</div>
<div class="moneyCard">
<div class="text">支付金额</div>
<div class="money"><span class="rmb">¥</span>'.$needPay.'</div>
<!--二维码-->
<img src="./img/zsm.jpg" id="zsmQrcode" class="zsmQrcode" />
<p class="payWarning">请识别上方赞赏码</p>
<p class="payWarning">点击<span class="blueFont">其他金额</span>输入'.$needPay.'元</p>
<p class="payWarningMini">输入的金额必须要完全一致</p>
<p id="orderExpireTime"></p>
<p id="orderNum" style="display:none;">'.$order_num.'</p>
<p id="needPay" style="display:none;">'.$needPay.'</p>
</div>
</div>
<!--提示-->
<p class="payTips">我们通过机器人监测本次支付<br/>支付后会立刻显示支付结果<br/>支付后没显示支付结果请联系人工处理<br/>人工微信号:sansure2016</p>';
}else{
// 失败
echo '<div class="payInfoCard">
<div class="header">里客云科技</div>
<div class="moneyCard" style="padding:20px 20px;">
创建订单失败!
</div>
</div>';
}
}
// 计算时间戳的差值
function countTimes($begin,$end){
$begintimes = $begin;
$endtimes = $end;
$timediff = abs($endtimes - $begintimes);
$days = intval($timediff / 86400);
$remain = $timediff % 86400;
$hours = intval($remain / 3600);
$remain = $remain % 3600;
$mins = intval($remain / 60);
$secs = $remain % 60;
return $mins;
}
?>
<script>
// 每2秒获取一次支付结果
var checkPayInterval = setInterval('checkPay()',2000);
// 获取支付结果
function checkPay(){
// 获取订单号和支付金额
var orderNum = $("#orderNum").text();
var needPay = $("#needPay").text();
$.ajax({
type: "GET",
url: "./checkPay.php?order_num="+orderNum+"&order_money="+needPay,
success: function(res){
// 判断支付结果
if(res.code == 200){
console.log('支付成功');
$("#zsmQrcode").prop("src","./img/success.png");
$('#orderExpireTime').css('display','none');
clearInterval(checkPayInterval);
}else{
console.log(res.msg);
}
}
});
}
// 倒计时
function clock(times){
// 获取时分秒
var h=parseInt(times/3600);
var m=parseInt((times%3600)/60);
var s=(times%3600)%60;
// 在页面中显示倒计时
$('#orderExpireTime').html(m+"分"+s+"秒后过期");
// 倒计时
if(times > 0){
times = times-1;
setTimeout(function (){
clock(times);
}, 1000);
}else{
// 显示订单过期
$("#zsmQrcode").prop("src","./img/expire.png");
$('#orderExpireTime').text('订单已过期,请刷新页面!');
// 结束轮询
clearInterval(checkPayInterval);
console.log('订单过期,停止监听');
}
}
</script>
</body>
</html>
checkPay.php
这个是用于订单页面实时监听支付结果的,每2秒就查一次数据库获取订单的支付结果,2分钟后未支付会停止查询。
<?php
// 页面编码
header("Content-type:application/json");
// 数据库配置
include './Db.php';
// 实例化类
$db = new DB_API($config);
// 获取订单号和支付金额
$order_num = $_GET['order_num'];
$order_money = $_GET['order_money'];
// 根据订单号和订单金额来查询支付结果
$getOrderPayStatus = $db->set_table('mqpay_order')->find(['order_num'=>$order_num,'order_money'=>$order_money]);
// 判断支付结果
if($getOrderPayStatus){
// 支付状态
$order_status = json_decode(json_encode($getOrderPayStatus))->order_status;
if($order_status == 2){
// 支付成功
$payResult = array(
'code' => 200,
'msg' => '支付成功'
);
}else{
// 未支付
$payResult = array(
'code' => 202,
'msg' => '未支付'
);
}
}else{
// 无结果
$payResult = array(
'code' => 201,
'msg' => '未支付'
);
}
// 返回JSON
echo json_encode($payResult,JSON_UNESCAPED_UNICODE);
?>
notify.php
这个是异步回调,在APP监听软件需要配置这个文件的URL和传递的参数,将监听到的金额POST给你的服务器然后修改数据库的支付结果,以实现支付回调的目的。
<?php
// 页面编码
header("Content-type:application/json");
// 原文
$orderMsg = $_GET['orderMsg'];
// 数据库配置
include './Db.php';
// 实例化类
$db = new DB_API($config);
// 订单金额、需支付的金额、通知原文、db实例
updateOrder($orderMsg,$db);
// 修改支付结果
function updateOrder($orderMsg,$db){
// 截取
// 示例:二维码赞赏到账1.00元
// 截取【到账】后面的
$money_1 = substr($orderMsg, strripos($orderMsg, "到账") + 6);
// 截取【元】前面的
$money_2 = substr($money_1, 0, strrpos($money_1, "元"));
// 更新订单
$updateOrderResult = $db->set_table('mqpay_order')->update(['order_status'=>1,'order_money'=>$money_2],['order_status'=>2,'order_paytime'=>time(),'order_msg'=>$orderMsg]);
if($updateOrderResult){
// 成功
$ret = array(
'code' => 200,
'msg' => '支付成功',
'order_num' => $order_num,
'order_money' => $money_2,
'order_msg' => $orderMsg
);
}else{
// 失败
$ret = array(
'code' => 200,
'msg' => '支付失败',
'order_num' => $order_num
);
}
}
// 返回JSON
echo json_encode($ret,JSON_UNESCAPED_UNICODE);
?>
完整代码
以上3个文件是核心代码,仅作技术分析实现原理。完整的代码涉及到样式、图片、数据库操作类、以及数据库表SQL语句、监听APP源码及安装包。
Demo
页面也是非常简单的,也是由本人设计,页面精简美观。
演示地址:http://demo.likeyunba.com/pay/zsm/ (仅开放一段时间,以后会撤下来)
监听APP配置
监听APP配置也挺简单的,只需要将notify.php的线上URL配置至APP即可。具体操作请看截图:
1、打开APP,选择【发送通道】点击【Webhook】,选择【GET请求】 ,输入notify.php 所在服务器的URL,下面的参数填写【orderMsg=[msg]】即可。
2、选择【转发规则】,点击【应用】,添加转发规则,【APP包名】,选择【是】,包名输入【com.tencent.mm】,选择的发送通道是上一步你创建的通道,下方的模板直接选择通知内容即可。
到这里,APP基本完成配置,然后将这个APP的自动启动开启,以及加入电池优化白名单,保证这个APP能一直在后台运行不被杀死。
赞赏码获取
为什么用赞赏码而不用收款码?因为收款码更容易被风控,收款码更加适合面对面扫码收款,而不适合线上远程收款,因为你的每一笔支付,都会记录付款ip地址,定位等信息,扫码次数多了,就会被系统判断远程付款,容易触发风控。赞赏码是用于网络上的赞赏使用,相对来说是比收款码安全的。
获得自己的赞赏码后,将赞赏码的那部分裁剪出来,替换掉源码中 zsm.jpg
这个文件就行了。赞赏码是可以设置赞赏的引导语的,可以将引导语修改为【请点击其他金额输入】,引导用户。
源码下载
这个源码只有单页面,没有后台,只适合个人单页部署使用或者研究、学习、二次开发,不适合开箱即用,当然其实你有点基础,也算是开箱即用,上传到服务器,修改数据库配置,导入数据库表,配置好赞赏码,配制好APP异步回调,也是可以用来做单页的收款的,只要你的金额是固定的,确实是开箱即用。
开源地址:https://github.com/likeyun/liKeYun_MqPay
微信电脑版回调监听
编写时间:2023/11/20
使用Python结合uiautomation编写了一个电脑版回调,代码如下:
import uiautomation as auto
import requests
import time
import re
# 用于存储上一个匹配的控件名称和记录的信息的变量
last_matching_control_name = None
recorded_info = []
def send_get_request(order_msg):
# 指定带有orderMsg参数的URL
url = f"https://xxx.com/notify_PC.php?orderMsg={order_msg}"
try:
# 发送GET请求
response = requests.get(url)
# 打印响应状态码
if response.status_code == 200:
print('已将收款信息发送给服务器')
print('---')
except requests.RequestException as e:
print(f"请求发送失败: {e}")
# 以指定深度和文本打印控件的函数
def print_controls_with_text_at_depth(control, target_depth, target_text, current_depth=0):
global last_matching_control_name # 使用全局变量
if current_depth == target_depth:
if target_text in control.Name:
control_name = control.Name
last_matching_control_name = control_name # 记录上一个匹配的控件名称
else:
for child in control.GetChildren():
print_controls_with_text_at_depth(child, target_depth, target_text, current_depth + 1)
# 无限循环,每次间隔2秒
while True:
# 示例用法
wxPay = auto.WindowControl(Name="微信支付", ClassName="ChatWnd")
payInfo = wxPay.ListControl(Name="消息")
# 打印深度为6且包含指定文本的控件的名称
target_depth = 6
target_text = "赞赏到账通知"
print_controls_with_text_at_depth(payInfo, target_depth, target_text)
# 在循环后使用last_matching_control_name发送GET请求
if last_matching_control_name is not None:
amount_match = re.search(r'收款金额¥([\d.]+)', last_matching_control_name)
time_match = re.search(r'到账时间(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', last_matching_control_name)
if amount_match and time_match:
amount = amount_match.group(1)
arrival_time = time_match.group(1)
# 检查信息是否已记录
if (amount, arrival_time) not in recorded_info:
# 记录信息
recorded_info.append((amount, arrival_time))
# 打印信息
print(f"收款金额: {amount}, 收款时间: {arrival_time}")
print('---')
# 将收款信息发送给服务器
send_get_request(last_matching_control_name)
else:
print("无法获取到收款信息...")
# 重置last_matching_control_name以便下一次迭代
last_matching_control_name = None
# 添加2秒延迟
time.sleep(2)
需修改
url = f"https://xxx.com/notify_PC.php?orderMsg={order_msg}"
在电脑版单独将微信支付这个界面抽出来。
这个脚本就会实时监听了。
技术原理:使用 uiautomation 自动化解析电脑版微信UI控件的信息,然后将收款信息发送到后端就实现了收款回调。
作者
TANKING
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。