题目里说的个人收款指的不是普通的扫个码, 而是说那种可以支持回调的, 例如网上商城支付之后, 商城可以知道支付状态并且自动修改订单的状态为已支付这种支付的形式, 无论是微信支付宝还是银联, 目前都是不对个人开放的, 必须有企业资质才能申请但是对于很多开发者而言, 有时候就是一个小小的验证性应用, 想要拥有支付功能, 而自己又没有企业资质, 自然没法申请到微信支付宝这种接口, 甚至连第三方的聚合支付 (Ping++) 也是无法申请的本文就介绍一种利用个人支付宝 (微信也是可以的) 自己实现支付功能的思路, 成本是一部旧的安卓手机, 其他的都是完全免费的, 配合支付宝的收款码(提现免费), 可以做到零费率
一基本思路
这个方案的基本思路是非常简单的, 跟之前大家常用的用爬虫爬取网页账单数据类似, 但是这里我们用的是手机 App 相对来说, 截取手机 App 的推送消息更为简单, 不需要应为微信支付宝的各种反爬措施; 但是缺点是能够获取到的信息较少, 没有诸如流水号付款人之类的信息, 只有一个金额
所以, 我们的思路就是:
创建一个订单, 将二维码 (定额或者非定额都可以) 展示给用户
用户支付后, 商家手机 App 上收到支付宝的付款推送
安卓 App 截取支付宝的付款推送, 然后将付款信息发送给服务器
服务器根据付款金额, 确定到底是哪一笔订单, 然后将该订单标记为已付款, 然后根据需要进行回调通知之类的操作
二关键问题及其解决方案
这个方案里的关键问题有以下几个:
1. 支付宝 App 的通知截取
这个问题其实网上已经有很多的解决方案了, 其利用的是 Android 中的
NotificationListenerService
这个类, 通过注册这个 Listener, 可以在推送通知弹出来的时候, 获取到其发送的 App 标题内容等信息我们最关心的就是 App 和推送内容
判断发送 App 的包为支付宝的包, 然后再从推送的内容中获取到具体的内容, 即可得到付款金额
示例代码如下:
- public class AlipayNotificationListenerService extends NotificationListenerService {
- public AlipayNotificationListenerService() {
- }
- @Override
- public void onNotificationPosted(StatusBarNotification sbn) {
- // 这里可以拿到包名, 可以按照需要判断
- String packageName = sbn.getPackageName();
- Notification notification = sbn.getNotification();
- if (notification == null) {
- return;
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- Log.e("SevenNLS","in 1");
- Bundle extras = notification.extras;
- if (extras != null) {
- // 这里是具体的 title 和 content, 可以从中提取金额
- String title = extras.getString(Notification.EXTRA_TITLE, "");
- String content = extras.getString(Notification.EXTRA_TEXT, "");
- Log.d("Zachary", "title:" + title + "content:" + content);
- }
- }
- }
- @Override
- public void onListenerConnected()
- {
- Log.e("Zachary","connected");
- }
- }
当然, 为了让这个 App 能够顺利进行, 还要给它获取通知的权限, 保证它不被清理等等, 需要做一些相应的保护措施
2. 订单的确定
刚才我们说过, 服务器收到 App 发来的收款信息之后, 还需要找到对应的订单这一步是相对比较难的一步, 因为我们知道相同金额的订单可能有很多, 到底哪一个才是刚刚支付的订单呢?
这里, 我们可以再详细思考一下, 其实这个订单不仅仅是由这个金额确定的, 而是一个多元组共同确定的最简单的一种实现方式就是 (订单金额 - 支付状态)通过这个二元组可以确定一个订单其含义是, 如果这个订单已经支付过了, 那么我在查找订单的时候, 就可以不用理会它了, 我只需要查找 (指定金额 - 未支付) 的订单就可以了
这样可以基本解决这个问题但是, 我们考虑到除了正常支付外, 还有可能会有另外一些情况比如用户创建了订单之后, 突然不想支付了, 没有进行接下来的操作或者说, 有人恶意在网站上创建了大量的订单并且不支付 这样的后果是, 这些订单的状态永远都是未支付, 当你想要继续创建订单的时候, 就会受到限制, 不能创建跟这些订单相同金额的订单, 否则你的系统将无法分辨到底是哪一笔订单被支付了
为了应对这种情况, 我们想到其实很多的支付都是有时间限制的, 也就是说, 订单是有有效期的, 一旦过了有效期, 订单就不能被支付了所以我们也可以给订单加一个有效时间的限制, 比如 5 分钟, 一旦五分钟内没有被支付, 就认为这个订单已经失效了这时, 订单的确定方式就变成了一个三元组 (订单金额 - 支付状态 - 是否过期) 查找的时候, 只需要查找 (指定金额 - 未支付 - 未过期) 的订单就可以了也就是说, 任意一个订单, 最多只会占用这个金额 5 分钟, 一旦超过五分钟, 不管支付与否, 你都可以继续创建相同金额的订单了
但是这样我们还是觉得不满意, 特别是对于某些支付金额相对单一的情况, 可能每次都需要创建相同金额的订单, 这样的话, 再最坏情况下我们只能每隔五分钟处理一个订单, 这个效率可以说是非常低效了
在这里, 我们提出了一种 trade-off 的解决方法一般的正常支付是不会使用这种方式的, 也难以接受, 但是对于我们来说, 为了避免企业资质的认证和手续费, 在一定程度上是可以接受的
这种方式就是, 当目前系统中已经有了某一金额的订单的时候, 如果我们要继续创建相同金额的订单, 那么我们就在指定金额上进行上下浮动, 比如下浮一分钱, 这样金额就可以和之前的订单区分开来, 避免出现不能同时支付的情况这样, 虽然我们在高并发情况下可能会有一定的损失(同时支付的人越多, 差距越大), 但是满足了我们的高并发要求
友情提示: 如果金额发生浮动, 可以告诉用户这是随机立减, 一定程度上可以避免定价和实际支付金额的差距带来的问题(这种情况下就只能下浮, 不能上浮, 不然就变成随机立加了)
三总结
总体上来说, 我认为这种方案对于普通的个人用户来说, 是一种可以接受的方案其优缺点总结如下:
优点:
不需要企业资质
没有手续费
不对支付宝进行任何操作, 没有被支付宝进行风控的风险
缺点:
需要有一部手机一直运行, 且要求网络条件良好, 否则会丢失支付数据(可以有人工解决方案)
高并发时, 订单金额会产生浮动
如果金额浮动策略不合理, 并且被人探索出规律, 可能造成财产损失!!(例如短时间内创建大量订单, 这样订单价格会不断下降, 需要针对这种情况做出防范)
参考:
PaysApi: www.paysapi.com
来源: https://juejin.im/entry/5aa4c36df265da2397067a81