Current section

29 Versions

Jump to

Compare versions

19 files changed
+332 additions
-167 deletions
  @@ -7,15 +7,23 @@ Wechat API wrapper in Elixir.
7 7 ![Hex.pm](https://img.shields.io/hexpm/dt/wechat.svg)
8 8
9 9 ## Installation
10 +
10 11 ```elixir
11 12 def deps do
12 13 [{:wechat, "~> 0.4.0"}]
13 14 end
14 15 ```
15 16
16 - Warning: 0.4.0 is a rewrite for APIs, don't upgrade if you are using 0.3.0.
17 + ## Configuration (optional)
18 +
19 + ```elixir
20 + config :wechat,
21 + adapter_opts: {Wechat.Adapters.Redis, ["redis://localhost:6379/0"]},
22 + httpoison_opts: [recv_timeout: 300_000]
23 + ```
24 +
25 + ## Create a client to call APIs
17 26
18 - ## Usage
19 27 ```elixir
20 28 iex(1)> client = Wechat.Client.new(%{appid: "WECHAT_APPID", secret: "WECHAT_SECRET"})
21 29 %Wechat.Client{
  @@ -23,92 +31,138 @@ iex(1)> client = Wechat.Client.new(%{appid: "WECHAT_APPID", secret: "WECHAT_SECR
23 31 secret: "WECHAT_SECRET",
24 32 endpoint: "https://api.weixin.qq.com/"
25 33 }
34 +
26 35 iex(2)> Wechat.User.get(client)
27 36 {:ok,
28 - %{
29 - "count" => 1,
30 - "data" => %{"openid" => ["oi00OuKAhA8bm5okpaIDs7WmUZr4"]},
31 - "next_openid" => "oi00OuKAhA8bm5okpaIDs7WmUZr4",
32 - "total" => 1
33 - }}
37 + %{
38 + "count" => 1,
39 + "data" => %{"openid" => ["oi00OuKAhA8bm5okpaIDs7WmUZr4"]},
40 + "next_openid" => "oi00OuKAhA8bm5okpaIDs7WmUZr4",
41 + "total" => 1
42 + }}
34 43 ```
35 44
36 - ## Config
45 + ## Create a Wechat implementation
46 +
47 + You can implement the `Wechat` module to simplify the usage.
48 +
49 + First, create an implementation by `use Wechat` :
50 +
37 51 ```elixir
38 - config :wechat,
39 - adapter_opts: {Wechat.Adapters.Redis, ["redis://localhost:6379/0"]},
40 - httpoison_opts: [recv_timeout: 300_000]
52 + defmodule MyApp.Wechat do
53 + use Wechat, otp_app: :my_app
54 +
55 + def users do
56 + client() |> Wechat.User.get()
57 + end
58 + end
41 59 ```
42 60
43 - ## Plugs
61 + Config the implementation with Wechat credentials:
44 62
45 - ### Parse wechat message (in Phonenix controller)
63 + ```elixir
64 + config :my_app, MyApp.Wechat,
65 + appid: "APP_ID",
66 + secret: "APP_SECRET",
67 + token: "TOKEN",
68 + encoding_aes_key: "ENCODING_AES_KEY" # Required if you enabled the encrypt mode
69 + ```
46 70
47 - * config.exs
71 + ## Wechat implementation examples
48 72
49 - ```elixir
50 - config :my_app, MyApp.Wechat,
51 - appid: "APP_ID",
52 - secret: "APP_SECRET",
53 - token: "TOKEN",
54 - encoding_aes_key: "ENCODING_AES_KEY" # Required if you enabled the encrypt mode
55 - ```
73 + ### JS-SDK
56 74
57 - * my_app/wechat.ex
75 + [https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html)
58 76
59 - ```elixir
60 - defmodule MyApp.Wechat do
61 - use Wechat, otp_app: :beaver
77 + ```html
78 + <script type="text/javascript" src="//res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
79 + <%= raw MyApp.Wechat.wechat_config_js(@conn, debug: false, api: ~w(previewImage closeWindow)) %>
62 80
63 - def users do
64 - client() |> Wechat.User.get()
81 + <script>
82 + $(function() {
83 + var urls = [];
84 + $('img').map(function(){
85 + url = window.location.origin + $(this).attr('src'),
86 + urls.push(url);
87 + });
88 +
89 + $('img').click(function(e) {
90 + wx.previewImage({
91 + current: window.location.origin + $(this).attr('src'),
92 + urls: urls
93 + });
94 + })
95 + });
96 + </script>
97 + ```
98 +
99 + ### Process message in Phoenix
100 +
101 + [https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html](https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html)
102 +
103 + - router.ex
104 +
105 + ```elixir
106 + defmodule MyApp.Router do
107 + scope "/wechat", MyApp do
108 + resources "/", WechatController, [:index, :create]
109 + end
110 + end
111 + ```
112 +
113 + - wechat_controller.ex
114 +
115 + ```elixir
116 + defmodule MyApp.WechatController do
117 + use MyApp.Web, :controller
118 +
119 + # Validate signature param
120 + plug Wechat.Plugs.RequestValidator, module: MyApp.Wechat
121 +
122 + # Parse message
123 + plug Wechat.Plugs.MessageParser, [module: MyApp.Wechat] when action in [:create]
124 +
125 + def index(conn, %{"echostr" => echostr}) do
126 + text conn, echostr
127 + end
128 +
129 + def create(conn, _params) do
130 + %{"ToUserName" => to, "FromUserName" => from, "Content" => content} = conn.body_params
131 + reply = %{from: to, to: from, content: content}
132 +
133 + msg = Phoenix.View.render_to_string(EvercamWechatWeb.WechatView, "text.xml", reply: reply)
134 +
135 + # Return encrypted message if possible
136 + case Wechat.encrypt_message(msg) do
137 + {:ok, reply} ->
138 + render(conn, "encrypt.xml", reply: reply)
139 +
140 + {:error, _} ->
141 + text(conn, msg)
65 142 end
66 143 end
67 - ```
144 + end
145 + ```
68 146
69 - * router.ex
147 + - text.xml.eex
70 148
71 - ```elixir
72 - defmodule MyApp.Router do
73 - scope "/wechat", MyApp do
74 - resources "/", WechatController, [:index, :create]
75 - end
76 - end
77 - ```
149 + ```xml
150 + <xml>
151 + <MsgType><![CDATA[text]]></MsgType>
152 + <Content><![CDATA[<%= @reply.content %>]]></Content>
153 + <ToUserName><![CDATA[<%= @reply.to %>]]></ToUserName>
154 + <FromUserName><![CDATA[<%= @reply.from %>]]></FromUserName>
155 + <CreateTime><%= DateTime.to_unix(DateTime.utc_now) %></CreateTime>
156 + </xml>
157 + ```
78 158
79 - * wechat_controller.ex
159 + - encrypt.xml.eex
80 160
81 - ```elixir
82 - defmodule MyApp.WechatController do
83 - use MyApp.Web, :controller
84 -
85 - plug Wechat.Plugs.RequestValidator, module: MyApp.Wechat
86 - plug Wechat.Plugs.MessageParser, [module: MyApp.Wechat] when action in [:create]
87 -
88 - def index(conn, %{"echostr" => echostr}) do
89 - text conn, echostr
90 - end
91 -
92 - def create(conn, _params) do
93 - msg = conn.body_params
94 - reply = echo_reply(msg, msg["Content"])
95 - render conn, "text.xml", reply: reply
96 - end
97 -
98 - defp echo_reply(%{"ToUserName" => to, "FromUserName" => from}, content) do
99 - %{from: to, to: from, content: content}
100 - end
101 - end
102 - ```
103 -
104 - * text.xml.eex
105 -
106 - ```xml
107 - <xml>
108 - <MsgType><![CDATA[text]]></MsgType>
109 - <Content><![CDATA[<%= @reply.content %>]]></Content>
110 - <ToUserName><![CDATA[<%= @reply.to %>]]></ToUserName>
111 - <FromUserName><![CDATA[<%= @reply.from %>]]></FromUserName>
112 - <CreateTime><%= DateTime.to_unix(DateTime.utc_now) %></CreateTime>
113 - </xml>
114 - ```
161 + ```xml
162 + <xml>
163 + <Encrypt><![CDATA[<%= @reply.msg_encrypt %>]]></Encrypt>
164 + <MsgSignature><![CDATA[<%= @reply.msg_signature %>]]></MsgSignature>
165 + <TimeStamp><%= @reply.timestamp %></TimeStamp>
166 + <Nonce><![CDATA[<%= @reply.nonce %>]]></Nonce>
167 + </xml>
168 + ```
  @@ -1,7 +1,7 @@
1 1 {<<"app">>,<<"wechat">>}.
2 2 {<<"build_tools">>,[<<"mix">>]}.
3 3 {<<"description">>,<<"Wechat API wrapper in Elixir.">>}.
4 - {<<"elixir">>,<<"~> 1.6">>}.
4 + {<<"elixir">>,<<"~> 1.7">>}.
5 5 {<<"files">>,
6 6 [<<"lib">>,<<"lib/wechat">>,<<"lib/wechat/adapter.ex">>,
7 7 <<"lib/wechat/util.ex">>,<<"lib/wechat/client.ex">>,
  @@ -15,9 +15,9 @@
15 15 <<"lib/wechat/utils/message_encryptor.ex">>,<<"lib/wechat/adapters">>,
16 16 <<"lib/wechat/adapters/sandbox.ex">>,<<"lib/wechat/adapters/redis.ex">>,
17 17 <<"lib/wechat/plugs">>,<<"lib/wechat/plugs/request_validator.ex">>,
18 - <<"lib/wechat/plugs/message_parser.ex">>,<<"lib/wechat/base.ex">>,
19 - <<"lib/wechat/menu.ex">>,<<"lib/wechat/application.ex">>,
20 - <<"lib/wechat/helpers">>,<<"lib/wechat.ex">>,<<"lib/wechat_bypass">>,
18 + <<"lib/wechat/plugs/message_parser.ex">>,<<"lib/wechat/menu.ex">>,
19 + <<"lib/wechat/application.ex">>,<<"lib/wechat/helpers">>,
20 + <<"lib/wechat.ex">>,<<"lib/wechat_bypass">>,
21 21 <<"lib/wechat_bypass/assertion.ex">>,<<"lib/wechat_bypass/case.ex">>,
22 22 <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE.txt">>]}.
23 23 {<<"licenses">>,[<<"MIT">>]}.
  @@ -44,4 +44,4 @@
44 44 {<<"optional">>,true},
45 45 {<<"repository">>,<<"hexpm">>},
46 46 {<<"requirement">>,<<">= 0.0.0">>}]]}.
47 - {<<"version">>,<<"0.4.5">>}.
47 + {<<"version">>,<<"0.4.6">>}.
  @@ -21,7 +21,7 @@ defmodule Wechat do
21 21 end
22 22 """
23 23
24 - alias Wechat.Client
24 + alias Wechat.{Client, Request}
25 25
26 26 @callback client() :: Client.t()
27 27
  @@ -55,31 +55,24 @@ defmodule Wechat do
55 55
56 56 if Code.ensure_loaded?(Phoenix.Controller) do
57 57 def wechat_config_js(conn, opts \\ []) do
58 - client = client()
59 -
60 58 import Phoenix.Controller, only: [current_url: 1]
61 - page_url = current_url(conn)
62 59
63 - debug = Keyword.get(opts, :debug, false)
64 - js_api_list = opts |> Keyword.get(:api, []) |> Enum.join(",")
65 -
66 - %{timestamp: timestamp, noncestr: nonce, signature: signature} =
67 - Wechat.Client.sign_jsapi(client, page_url)
68 -
69 - """
70 - <script type="text/javascript">
71 - wx.config({
72 - debug: #{debug},
73 - jsApiList: ['#{js_api_list}'],
74 - appId: '#{client.appid}',
75 - timestamp: '#{timestamp}',
76 - nonceStr: '#{nonce}',
77 - signature: '#{signature}'
78 - });
79 - </script>
80 - """
60 + Wechat.Client.wechat_config_js(client(), current_url(conn), opts)
81 61 end
82 62 end
83 63 end
84 64 end
65 +
66 + @doc """
67 + Get access token.
68 + """
69 + def token(client) do
70 + params = [
71 + appid: client.appid,
72 + secret: client.secret,
73 + grant_type: :client_credential
74 + ]
75 +
76 + Request.raw_get(client, "cgi-bin/token", params: params)
77 + end
85 78 end
  @@ -1,15 +0,0 @@
1 - defmodule Wechat.Base do
2 - @moduledoc false
3 -
4 - alias Wechat.Request
5 -
6 - def token(client) do
7 - params = [
8 - appid: client.appid,
9 - secret: client.secret,
10 - grant_type: :client_credential
11 - ]
12 -
13 - Request.raw_get(client, "cgi-bin/token", params: params)
14 - end
15 - end
  @@ -3,7 +3,7 @@ defmodule Wechat.Client do
3 3 `Client` represents an API client.
4 4 """
5 5
6 - alias Wechat.{AccessToken, Base, Config, Error}
6 + alias Wechat.{AccessToken, Config, Error}
7 7 alias Wechat.Utils.{MessageEncryptor, SignatureVerifier}
8 8
9 9 @endpoint "https://api.weixin.qq.com/"
  @@ -28,7 +28,7 @@ defmodule Wechat.Client do
28 28 @doc """
29 29 Create client to consume Wechat API with config.
30 30
31 - ## Examples
31 + ## Example
32 32
33 33 iex> Wechat.Client.new(appid: "my_appid", secret: "my_secret")
34 34 %Wechat.Client{
  @@ -65,7 +65,7 @@ defmodule Wechat.Client do
65 65
66 66 @spec get_token(t) :: {:ok, t} | {:error, Error.t()}
67 67 def get_token(client) do
68 - case Base.token(client) do
68 + case Wechat.token(client) do
69 69 {:ok, body} ->
70 70 token = AccessToken.new(body)
71 71 :ok = Config.adapter().write_token(client.appid, token)
  @@ -140,4 +140,26 @@ defmodule Wechat.Client do
140 140 {:error, msg}
141 141 end
142 142 end
143 +
144 + @doc """
145 + Configure JS-SDK.
146 + """
147 + def wechat_config_js(client, url, opts) do
148 + debug = Keyword.get(opts, :debug, false)
149 + js_api_list = opts |> Keyword.get(:api, []) |> Enum.join(",")
150 + %{timestamp: timestamp, noncestr: nonce, signature: signature} = sign_jsapi(client, url)
151 +
152 + """
153 + <script type="text/javascript">
154 + wx.config({
155 + debug: #{debug},
156 + jsApiList: ['#{js_api_list}'],
157 + appId: '#{client.appid}',
158 + timestamp: '#{timestamp}',
159 + nonceStr: '#{nonce}',
160 + signature: '#{signature}'
161 + });
162 + </script>
163 + """
164 + end
143 165 end
Loading more files…