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