├── README.md ├── package.json └── pay.js /README.md: -------------------------------------------------------------------------------- 1 | # Wechaty Pay - 让线上没有难做的生意 2 | 3 | ## TLDR 4 | 本文主要面对没有营业执照,想使用微信或支付宝在线收款的中小企业或者个人开发者,日收入在5K以下(菠菜类或者想偷税者请绕道)。 5 | 6 | 自从使用了Wechaty,资金及时到账,收款后立即通知。即开即用,高并发,超稳定不掉单。 7 | 8 | 让线上没有难做的生意 9 | 10 | 11 | 12 | ## 背景 13 | 随处可见微信和支付宝的支付二维码,已经让超市水果店和煎饼果子摊贩没有难做的生意。然而在线支付可就没那么容易了。接口大部分需要企业资质认证,或者需要备案域名以及开通权限,对于中小型商户门槛非常高。大部分人会在收到款后,手动确认订单,经常出现订单延误或者遗漏。然而市面上的解决方案都差强人意。那么这个线上支付的流程如何利用Wechaty优化一下呢? 14 | 15 | ![各种支付方案对比](https://cdn.mugglepay.com/docs/pics/paycompare.png) 16 | 17 | ## 技术实现 18 | 19 | 整个收款过程分为3步: 20 | 1. 用户选择支付金额后,付款页面打开对应的付款码(支付宝可自定义金额),用户扫码付款 21 | 2. 确认收款后(```onMessage```),跟后端发送回调收款金额及收款时间(```sendPayment```) 22 | 3. 后台根据金额以及时间,把对应的订单自动标记 23 | 24 | 下面的例子以第2步为例,如何在后台确认收款,以及跟后端发送回调。 25 | 26 | ``` 27 | // Wechaty 经典启动 28 | const bot = new Wechaty() 29 | bot.on('scan', onScan) 30 | bot.on('login', onLogin) 31 | bot.on('message', onMessage) 32 | bot.start() 33 | 34 | // 微信收款的消息提示 35 | async function onMessage (msg) { 36 | if ( msg.type() !== bot.Message.Type.Attachment && !msg.self() 37 | || contact.name() !== '微信支付') { 38 | return 39 | } 40 | const strs = msg.text().split('元') 41 | if (strs.length >= 1) { 42 | const prices = strs[0].split('微信支付收款') 43 | if (prices.length >= 1) { 44 | const priceStr = prices[1] 45 | sendPayment(parseFloat(priceStr), msg.date().getTime()) 46 | } 47 | } 48 | } 49 | 50 | // 收到金额之后,进行确认订单回调 51 | function sendPayment (priceAmount, timestamp) { 52 | const options = { 53 | method: 'POST', 54 | url: 'https://api.callbackaddress.com/api/admin/callback', 55 | headers: { 'content-type': 'application/json', 'token': 'XXXXXX'}, 56 | body: {'amount': priceAmount, 'timestamp': timestamp }, 57 | json: true 58 | }; 59 | request(options, function (error, response, body) { 60 | if (error) throw new Error(error); 61 | }); 62 | } 63 | ``` 64 | 65 | 支付宝道理类似,不过目前产品包装没有Wechaty这么优秀的代码库。半自动操作如下: 66 | 1. 扫码登录支付宝账号,在Headers中获取Cookie。操作类似于``` bot.on('scan', onScan) ``` 67 | 2. 轮询获取订单列表,如果有新支付订单,,跟后端发送回调收款金额及收款时间(```sendPayment```) 68 | 3. 由于此处使用了轮询的方式,为了防止频繁访问被支付宝风控,仅当有待支付订单才会高频访问订单接口。 69 | 70 | 71 | ## 效果预览 72 | 73 | 终于可以一站式的管理所有微信和支付宝的订单了!每笔订单的时间,金额,还有每天收入统计一览无余。 74 | 75 | ![后台订单管理](https://cdn.mugglepay.com/docs/pics/paymentsx.jpg) 76 | 77 | ## 产品实现 78 | 79 | 如果还是觉得步骤有点繁琐?那可以试一下这款基于[桔子互动](https://www.botorange.com/)的云端服务哦。 80 | 81 | * 支持微信扫码托管(基于桔子互动服务) 82 | * 支持支付宝扫码托管 83 | * 保障安全性,不记录个人账户密码 84 | * 资金实时到账,不经过第三方 85 | 86 | 桔子互动 87 | 88 | 89 | 本文仅供技术产品交流参考,建议使用官方认证接口。请勿使用此项目做违反微信、支付宝规定或者其他违法事情! 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechatpay", 3 | "version": "0.1.0", 4 | "description": "Personal Payment Solution", 5 | "main": "pay.js", 6 | "engines": { 7 | "node": ">= 10" 8 | }, 9 | "scripts": { 10 | "postinstall": "check-node-version --node \">= 10\"", 11 | "start": "node pay.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/coderwhocode/wechaty-pay.git" 16 | }, 17 | "keywords": [], 18 | "author": "Huan LI ", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/coderwhocode/wechaty-pay/issues" 22 | }, 23 | "homepage": "https://github.com/coderwhocode/wechaty-pay#readme", 24 | "dependencies": { 25 | "qrcode-terminal": "^0.12.0", 26 | "request": "^2.88.0", 27 | "wechaty": "^0.22.6" 28 | }, 29 | "devDependencies": { 30 | "check-node-version": "^3.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pay.js: -------------------------------------------------------------------------------- 1 | const { Wechaty } = require('wechaty') 2 | const request = require("request") 3 | 4 | function onScan (qrcode, status) { 5 | require('qrcode-terminal').generate(qrcode, { small: true }) // show qrcode on console 6 | 7 | const qrcodeImageUrl = [ 8 | 'https://api.qrserver.com/v1/create-qr-code/?data=', 9 | encodeURIComponent(qrcode), 10 | ].join('') 11 | 12 | console.log(qrcodeImageUrl) 13 | } 14 | 15 | function onLogin (user) { 16 | console.log(`${user} login`) 17 | } 18 | 19 | function onLogout(user) { 20 | console.log(`${user} logout`) 21 | } 22 | 23 | // sendPayment(0.01, 1562577831000) 24 | function sendPayment (priceAmount, timestamp) { 25 | console.log(priceAmount, timestamp); 26 | 27 | // const options = { 28 | // method: 'POST', 29 | // url: 'https://api.callbackurl.com/callback', 30 | // headers: 31 | // { 32 | // 'content-type': 'application/json', 33 | // token: 'usertoken-callback' }, 34 | // body: { 35 | // price_amount: priceAmount, 36 | // timestamp: timestamp 37 | // }, 38 | // json: true 39 | // }; 40 | 41 | // request(options, function (error, response, body) { 42 | // if (error) throw new Error(error); 43 | // }); 44 | } 45 | 46 | // 消息来自 “微信支付”,信息格式为“微信支付收款0.01元” 47 | async function onMessage (msg) { 48 | if (msg.age() > 300) { 49 | // console.log('Message discarded because its TOO OLD(than 5 minute)') 50 | return 51 | } 52 | 53 | const contact = msg.from() 54 | const text = msg.text() 55 | const msgDate = msg.date() 56 | 57 | if ( msg.type() !== bot.Message.Type.Attachment 58 | && !msg.self() 59 | ) { 60 | // console.log('Message discarded because it does not match Payment Attachment') 61 | return 62 | } 63 | 64 | if ( contact.name() !== '微信支付' 65 | ) { 66 | // console.log('Message is not from wechat pay - from ', contact.name()) 67 | return 68 | } 69 | 70 | const strs = text.split('元') 71 | if (strs.length >= 1) { 72 | const str= strs[0] 73 | const strs2 = str.split('微信支付收款') 74 | if (strs2.length >= 1) { 75 | const priceStr = strs2[1] 76 | sendPayment(parseFloat(priceStr), msgDate.getTime()) 77 | } 78 | } 79 | } 80 | 81 | const bot = new Wechaty() 82 | 83 | bot.on('scan', onScan) 84 | bot.on('login', onLogin) 85 | bot.on('logout', onLogout) 86 | bot.on('message', onMessage) 87 | 88 | bot.start() 89 | .then(() => console.log('Starter Bot Started.')) 90 | .catch(e => console.error(e)) 91 | --------------------------------------------------------------------------------