[教程第1篇] Ruby on Rails 微信支付

大概花30分钟

[教程第1篇] Ruby on Rails 微信支付

这篇文章写什么?

这是一篇教程,教 Ruby on Rails 开发者在 20~40 分钟内搞定微信支付
写于2019年11月29号

目标读者

Ruby on Rails 开发者

说明

本文教电脑端扫二维码支付,微信叫这个 "NATIVE 支付"
其他方式的教程 (App, 小程序, 微信内网页端等)
以后如果有条件就写,现在没有。

二维码支付适合电脑端网站,如下图:

我们用模式二

举几个扫码支付的例子

UCloud 充值
迅雷买会员
百度网盘充值会员

先演示一下最终结果

本次的 demo 部署后,首页如下图:

这个网址你就不要访问了,继续往下看演示就行。
这篇文章写完了我会把这个例子下线的

点击"新建订单"。会看到如下画面,可以直接扫码支付1分钱

如果没支付就直接关掉了页面。
想重新支付,可以在首页找到订单,点击"去支付"

付款后会变成"已支付"

本文技术栈+用到的工具

  • Ruby on Rails 6
  • Heroku (用于部署)
  • 一个已备案的域名

在微信商户后台设置"回调地址"时,域名必须备案过.

在开始写代码之前 需要准备什么

  1. 一个公司(这样才能申请微信支付)
  2. 申请微信支付
  3. 一个已备案的域名(用于支付后的回调)

如果微信支付申请好了,你应该有如下4样信息,开发需要这4样信息:

  1. appid
  2. appsecret
  3. mch_id
  4. key

具体含义请看下方微信的文档:

https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=3_1

截图自微信文档

你也应该收到如下这封邮件:

接下来我详细讲解如何获取

  1. appid
  2. appsecret
  3. mch_id
  4. key

appid 和 appsecret 是成对的,
可以把 appid 想成是账号, appsecret 想成是密码.

既可以是公众号的appid+appsecret,可以是小程序的appid+appsecret,
实测没有区别,两个都可以用。因为在"商户平台"绑定了这两个 appid。

https://pay.weixin.qq.com/static/pay_setting/appid_protocol.shtml

1. 公众号的 appid 和 appsecret 如何获取:

登录公众平台 -> 基本配置 -> "公众号开发信息"

AppID 示例: wxdxxx9xxxd402bxxx
AppSecret 示例: eeexxx0e3dc3a0xxx3ede1xxxe4eaxxx
大概长这样, 为了保密把一些信息用 xxx 替换掉了.
但是长度是这样没错

可以确认一下这个公众号和商户号绑定了没有,如下图:

顺便一说这是"服务号"

2. 小程序的 appid 和 appsecret 如何获取:

登录小程序平台后 -> 开发 -> 开发设置

3. mch_id 如何获取:

上面那封绿色邮件里,有写"微信支付商户号",直接复制即可。
示例: 14942618xx
商户平台里也可以找到这个商户号。

4. key 如何获取:

商户平台 -> 账户中心 -> API 安全 -> 设置 API 密钥。

自己设置一个32位的 key,前面的 mch_id, appid 都是直接复制粘贴,这个是要自己设置。

如果4样信息都收集好了,看起来如下:

AppID: wxdxxx9xxxd402bxxx
AppSecret: eeexxx0e3dc3a0xxx3ede1xxxe4eaxxx
mch_id: 149xx71xxx
key: fff444GS444kiHrNqxxxBcF6bZkkkxxx

我们使用"统一下单"接口

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

来实现二维码扫码支付

文档内容就不过一遍了,简单来说就是:

  1. 用 XML 格式,带上必要信息,签个名,发到指定 url。
  2. 微信会返回各种数据,其中包括 code_url。我们用二维码显示这个 code_url 让用户扫码支付就可以了。

以下开始弄代码

分2个部分

第一部分。先不讲原理,直接告诉你 appid, key, mch_id 填哪里,然后部署。先把代码跑起来看到效果

第二部分。讲原理。

第一部分

这里是代码
https://github.com/1c7/demo_ruby_on_rails_wechat_pay

我们要做的操作比直接 git clone 稍微复杂一点,如下:

  1. 在 Github 上新建一个私有库 (注意是私有) (2019年现在在 Github 弄私有库是免费的了)
  2. 把代码复制一份 (git clone)
  3. 把代码推到刚新建的私有库

具体命令可参考: https://stackoverflow.com/questions/5181845/git-push-existing-repo-to-a-new-and-different-remote-repo-server

命令如下

[新建私有库]

# 复制代码到本地
git clone [email protected]:1c7/demo_ruby_on_rails_wechat_pay.git

# 改名 git remote 的地址
git remote rename origin upstream

# 设置新地址
git remote add origin [你的私有库链接, 如 [email protected]:[用户名]/[仓库名].git]

# 把代码推到这个新地址
git push origin master

这么做的理由是,我们希望 Github 上有一份私有代码(只有你能访问)。

需要代码在 Github 上,是因为待会要用 Heroku 部署。
需要代码私有,是因为待会会填写 appid, key, mch_id 等秘密信息,需要保密。

直接 fork 是没法转成私有代码的。所以做这样一个操作。

接下来我们填入配置信息,也就是前面说的 key, appid, mch_id

往 config/initializers/wx_pay.rb 里面填入必须信息

同样,也往 app/controllers/home_controller.rb 里面填入必要信息

代码只需要改这两处就可以了,很简单。

你可能发现了,这里只用上了 key, appid, mch_id,没有用上 appsecret。
这次二维码付款其实不需要 appsecret。

之所以前面还是拿了,是因为 appid 和 appsecret 是一对的。
以后如果你需要获取 access_token 就方便一点,不需要又打开公众号/小程序把这个 appsecret 拿一份。省点事。

当然,实际项目中可以抽到一个配置文件里。不把同样的东西写在2个地方。
demo 为了方便,就不搞太多抽象。

接下来讲如何部署:

我用的是 Heroku。
理由:

  1. 免费(仅针对小规模应用)
  2. Github 自动触发部署,方便

虽然 Heroku 需要翻墙访问,但因为是 demo,不用面向客户,不用担心这个问题

如果你不想用 Heroku 也可以,下面的部分可以跳过。
你可以自行部署到任意地方,阿里云,UCloud,腾讯云,Digital Ocean, Linode 等等。
目的是线上可访问,同时有一个已备案域名指向它。具体用什么工具不重要。

Heroku 操作步骤:登录后,在面板中选择右上角 new app

App name 名字你可以随便写一个,demo-for-wechat-pay 之类的,一眼认出来是什么就行了

Deployment method 部署方法选择 Github
然后选择你刚新建那个私有库

输入名字搜索到之后,选择 connect

然后点击两个黑色按钮。
一个是自动部署。
一个是选择部署分支。

这就可以了
你还需要一个 Heroku cli 把 db:migrate 跑一下。

heroku run rake db:migrate -a [App name]

现在来配置"已备案域名指向 Heroku"
在 app 面板里选择 Settings

向下滚动找到 Domain.
点击右边的 "Add domain"
我这里配置的是 heroku.[已备案域名]
配了个子域名。

接下来去域名的 DNS 服务商(我这里用的是阿里云)
(找到域名,点击"解析")

配置 CNAME。
记录值前面 heroku 有给,复制粘贴一下就行。

部署就此完成。

现在可以通过一个已备案的域名。访问到 Heroku 上的应用。
heroku.[你的域名].cn
(请把 [你的域名] 换成你的域名)

最后一步:配置支付回调地址

这一步的意义:用户支付后,微信会通过"支付回调地址"告知我们(发一个 POST 请求,带上必要的信息)
这样我们就可以把用户的订单从"未支付"变成"已支付"状态
(当然,变更订单状态之前,要做签名校验,确保的确是微信发来的请求)

商户平台 -> 产品中心 -> 开发配置

这里填写刚刚配置的域名。比如我需要填 heroku.[域名].cn
(请替换掉[域名]为你的域名)

可以测试了!

现在访问 heroku.[域名].cn
你看的画面应该和以下类似,只不过没有那么多订单就是了。

你可以点击"新建订单",然后"返回首页"。
应该会看到刚刚新建的订单,点击"去支付"
会显示二维码。
二维码扫码是要支付1分钱。支付完之后,那一分钱可以到你自己的商户平台里查看。

第一部分就讲完了。你有了一个实际可支付的例子。

第二部分:讲代码

这次用了2个 gem(可以打开 Gemfile 查看)

wx_pay 和 rest-client

wx_pay 是用来验证回调签名。
rest-client 用来调用"统一下单"接口。

来看一下路由,其实就4条。

  • 单个订单页
  • 首页
  • 下单页
  • 接受微信回调通知

二维码支付的核心代码在 home controller
app/controllers/home_controller.rb

def unifieorder 代码很短,解释一下

  1. 把必要的商品信息如商品名,价格(x分钱),回调地址,支付类型 NATIVE 写清楚(info = {})
  2. 写上 key, appid, mch_id (whole_params = {})
  3. RestClient 发一个 POST 请求,payload 是 xml 格式的

注意,最后结果存入了 @result 里。我们会在 view 里使用它。

home controller 里剩余代码如下:

def get_sign:生成 md5 的签名
def make_payload:把参数转成 XML 格式

接下来我们看看 home#unifieorder 的 view

其实就是把 code_url 用一个前端 js 库显示二维码。没有别的了。

接下来看看微信回调代码

  1. 验证签名
  2. 签名没问题了,找到订单,改为已支付状态。

总结

填入必要配置信息后(appid, key, mch_id)

unifieorder 接口下单,把二维码 (code_url)显示给用户,
让用户扫码付款即可。
用户付款后,notify 会收到通知,改成已支付状态。

文末再强调一遍

微信提供了多种支付方式,这次只是做了一种。"二维码扫码支付"。
没做的:(以后有机会再写)

  • App支付
  • 小程序支付
  • 公众号内网页支付
  • 退款
  • 发红包
  • 查询订单状态(异步付款通知出问题,需要主动去查)

最后

希望这篇文章对你有帮助
有问题可联系 guokrfans#gmail.com

我觉得这篇文章讲得比较清晰了,代码实际上也不多。加起来大概50行左右。
gem "wx_pay" 只用来在回调接口里验证签名,没有用 gem "wx_pay" 的 unifieorder,
因为我用这个东西碰到了一些问题。干脆就自己写 unifieorder 下单的那几行代码。

承接开发

如果你觉得以上太麻烦。太费时间。
希望花钱让别人来帮你开发,可以联系 guokrfans#gmail.com(#换成@)