0%

Elixir 微信(WeChat) 支付SDK 使用指南

最近给 wechat sdk 增加了支付相关的支持,我们来看看怎么使用

初始化

首先,添加依赖:

1
2
3
4
5
6
7
8
defp deps do  
[
{:wechat, "~> 0.14", hex: :wechat_sdk},
{:saxy, "~> 1.2"},
{:x509, "~> 0.8"},
{:cachex, "~> 3.6"}
]
end

然后定义 Client:

1
2
3
4
5
6
7
8
9
10
defmodule WechatPayDemo.PayClient do  
@moduledoc false

use WeChat.Pay,
mch_id: "mch_id",
api_secret_key: :runtime_env,
api_secret_v2_key: :runtime_env,
client_serial_no: "client_serial_no",
client_key: {:app_dir, :wechat_pay_demo, "priv/cert/apiclient_key.pem"}
end

上面这些参数都可以从微信支付商户后台中获取到,证书和私钥文件可以通过微信支付官方工具下载。

然后将 v2 & v3 的 API秘钥 添加到配置文件runtime.exs

1
2
3
config :wechat, WechatPayDemo.PayClient,  
api_secret_key: "api_secret_v3_key",
api_secret_v2_key: "api_secret_v2_key"

注意:部分 v2 的接口请求时需要用到证书,如:撤销订单,因此如果有使用到这部分接口,必须添加下面的配置

1
2
3
4
5
6
7
8
  
config :wechat, WechatPayDemo.PayClient,
api_secret_key: "api_secret_key",
api_secret_v2_key: "api_secret_v2_key",
v2_ssl: [
certfile: {:app_dir, :wechat_pay_demo, "priv/cert/apiclient_cert.pem"},
keyfile: {:app_dir, :wechat_pay_demo, "priv/cert/apiclient_key.pem"}
]

最后别忘将 WechatPayDemo.PayClient 启动起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def start(_type, _args) do  
children = [
# Start the Telemetry supervisor
WechatPayDemoWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: WechatPayDemo.PubSub},
# 启动微信支付
WechatPayDemo.PayClient,
# Start the Endpoint (http/https)
WechatPayDemoWeb.Endpoint
]

opts = [strategy: :one_for_one, name: WechatPayDemo.Supervisor]
Supervisor.start_link(children, opts)
end

使用接口

启动之后,就可以开始调用 微信支付的接口了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@doc "小程序/jsapi 下单"  
def jsapi(appid, description, out_trade_no, notify_url, amount, payer) do
with {:ok, %{status: 200, body: %{"prepay_id" => prepay_id}}} <-
Transactions.jsapi(
@client,
appid,
description,
out_trade_no,
notify_url,
amount,
payer
) do
Transactions.request_payment_args(@client, appid, prepay_id)
end
end

@doc "付款码支付"
def pay_by_scan(appid, description, out_trade_no, amount, ip, auth_code) do
Transactions.pay_by_scan(@client, %{
"appid" => appid,
"mch_id" => @client.mch_id(),
"nonce_str" => random_string(),
"sign_type" => "HMAC-SHA256",
"body" => description,
"out_trade_no" => out_trade_no,
"total_fee" => amount,
"fee_type" => "CNY",
"spbill_create_ip" => ip,
"auth_code" => auth_code
})
end

@doc "撤销订单"
def revoke(appid, out_trade_no) do
Transactions.revoke(@client, %{
"appid" => appid,
"mch_id" => @client.mch_id(),
"out_trade_no" => out_trade_no,
"nonce_str" => random_string(),
"sign_type" => "HMAC-SHA256"
})end

设置回调功能

有部分接口会用到 微信支付回调 功能,因此 还需要几步 进行设置

新建建 路由模块 WechatPayDemoWeb.PayRouter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defmodule WechatPayDemoWeb.PayRouter do  
use Plug.Router

plug :match
plug :dispatch

Code.ensure_compiled!(WechatPayDemo.PayClient)

post "/api/pay/callback",
to: WeChat.Pay.EventHandler,
init_opts: [
client: WechatPayDemo.PayClient,
event_handler: &WechatPayDemo.PayEvent.handle_event/3
]

match _, do: conn
end

新建 事件处理器 WechatPayDemo.PayEvent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
defmodule WechatPayDemo.PayEvent do  
@moduledoc false
require Logger

# 处理 回调消息
def handle_event(_client, _conn, message) do
Logger.info("Got message: #{inspect(message)}")
id = Map.get(message, "id")
# 排重处理
case Cachex.get(:event_id_cache, id) do
{:ok, nil} ->
# handle message here
Cachex.put(:event_id_cache, id, :ok)
:ok

# already handled
{:ok, v} ->
v
end
end
end

我们用到了 Cachex 库进行排重处理,所以 还需要 先启动它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def start(_type, _args) do  
children = [
# Start the Telemetry supervisor
WechatPayDemoWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: WechatPayDemo.PubSub},
# 启动 事件id 缓存,事件排重用到
{Cachex,
[
name: :event_id_cache,
expiration: expiration(default: :timer.hours(25), interval: :timer.minutes(1))
]},
# 启动微信支付
WechatPayDemo.PayClient,
# Start the Endpoint (http/https)
WechatPayDemoWeb.Endpoint
]

opts = [strategy: :one_for_one, name: WechatPayDemo.Supervisor]
Supervisor.start_link(children, opts)
end

最后,我们将 路由模块 放入 Endpoint :

1
2
3
4
5
6
plug WechatPayDemoWeb.PayRouter  

plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()

注意:
WechatPayDemoWeb.PayRouter 的位置比较关键
位于 plug Plug.Parsers 的上一行
PayRouter 内部需要未解析的 body,所以必须在解析器解析body之前

大功告成

至此,微信支付功能 集成就完成了,希望你使用愉快
如遇到问题,可以 在 Repo 下留言或提交 PR

梦想基金
feng19 微信

微信

feng19 支付宝

支付宝

欢迎关注我的其它发布渠道