Current section

29 Versions

Jump to

Compare versions

19 files changed
+281 additions
-116 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,6 +31,7 @@ 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 37 %{
  @@ -33,18 +42,23 @@ iex(2)> Wechat.User.get(client)
33 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
44 -
45 - ### Parse wechat message (in Phonenix controller)
46 -
47 - * config.exs
61 + Config the implementation with Wechat credentials:
48 62
49 63 ```elixir
50 64 config :my_app, MyApp.Wechat,
  @@ -54,19 +68,39 @@ config :wechat,
54 68 encoding_aes_key: "ENCODING_AES_KEY" # Required if you enabled the encrypt mode
55 69 ```
56 70
57 - * my_app/wechat.ex
71 + ## Wechat implementation examples
58 72
59 - ```elixir
60 - defmodule MyApp.Wechat do
61 - use Wechat, otp_app: :beaver
73 + ### JS-SDK
62 74
63 - def users do
64 - client() |> Wechat.User.get()
65 - end
66 - end
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)
76 +
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)) %>
80 +
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>
67 97 ```
68 98
69 - * router.ex
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
70 104
71 105 ```elixir
72 106 defmodule MyApp.Router do
  @@ -76,13 +110,16 @@ config :wechat,
76 110 end
77 111 ```
78 112
79 - * wechat_controller.ex
113 + - wechat_controller.ex
80 114
81 115 ```elixir
82 116 defmodule MyApp.WechatController do
83 117 use MyApp.Web, :controller
84 118
119 + # Validate signature param
85 120 plug Wechat.Plugs.RequestValidator, module: MyApp.Wechat
121 +
122 + # Parse message
86 123 plug Wechat.Plugs.MessageParser, [module: MyApp.Wechat] when action in [:create]
87 124
88 125 def index(conn, %{"echostr" => echostr}) do
  @@ -90,18 +127,24 @@ config :wechat,
90 127 end
91 128
92 129 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
130 + %{"ToUserName" => to, "FromUserName" => from, "Content" => content} = conn.body_params
131 + reply = %{from: to, to: from, content: content}
97 132
98 - defp echo_reply(%{"ToUserName" => to, "FromUserName" => from}, content) do
99 - %{from: to, to: from, content: content}
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)
142 + end
100 143 end
101 144 end
102 145 ```
103 146
104 - * text.xml.eex
147 + - text.xml.eex
105 148
106 149 ```xml
107 150 <xml>
  @@ -112,3 +155,14 @@ config :wechat,
112 155 <CreateTime><%= DateTime.to_unix(DateTime.utc_now) %></CreateTime>
113 156 </xml>
114 157 ```
158 +
159 + - encrypt.xml.eex
160 +
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)
60 + Wechat.Client.wechat_config_js(client(), current_url(conn), opts)
61 + end
62 + end
63 + end
64 + end
68 65
66 + @doc """
67 + Get access token.
69 68 """
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 - """
81 - end
82 - end
83 - end
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)
84 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…