Current section

29 Versions

Jump to

Compare versions

22 files changed
+566 additions
-165 deletions
  @@ -16,10 +16,8 @@ Warning: 0.4.0 is a rewrite for APIs, don't upgrade if you are using 0.3.0.
16 16 ```elixir
17 17 iex(1)> client = Wechat.Client.new(%{appid: "WECHAT_APPID", secret: "WECHAT_SECRET"})
18 18 %Wechat.Client{
19 - auth: %{
20 - appid: "WECHAT_APPID",
21 - secret: "WECHAT_SECRET"
22 - },
19 + appid: "WECHAT_APPID",
20 + secret: "WECHAT_SECRET",
23 21 endpoint: "https://api.weixin.qq.com/"
24 22 }
25 23 iex(2)> Wechat.User.get(client)
  @@ -32,3 +30,75 @@ iex(2)> Wechat.User.get(client)
32 30 }}
33 31 ```
34 32
33 + ## Plugs
34 +
35 + ### Parse wechat message (in Phonenix controller)
36 +
37 + * config.exs
38 +
39 + ```elixir
40 + config :my_app, MyApp.Wechat,
41 + appid: "APP_ID",
42 + secret: "APP_SECRET",
43 + token: "TOKEN",
44 + encoding_aes_key: "ENCODING_AES_KEY" # Required if you enabled the encrypt mode
45 + ```
46 +
47 + * my_app/wechat.ex
48 +
49 + ```elixir
50 + defmodule MyApp.Wechat do
51 + use Wechat, otp_app: :beaver
52 +
53 + def users do
54 + client() |> Wechat.User.get()
55 + end
56 + end
57 + ```
58 +
59 + * router.ex
60 +
61 + ```elixir
62 + defmodule MyApp.Router do
63 + scope "/wechat", MyApp do
64 + resources "/", WechatController, [:index, :create]
65 + end
66 + end
67 + ```
68 +
69 + * wechat_controller.ex
70 +
71 + ```elixir
72 + defmodule MyApp.WechatController do
73 + use MyApp.Web, :controller
74 +
75 + plug Wechat.Plugs.RequestValidator, module: MyApp.Wechat
76 + plug Wechat.Plugs.MessageParser, [module: MyApp.Wechat] when action in [:create]
77 +
78 + def index(conn, %{"echostr" => echostr}) do
79 + text conn, echostr
80 + end
81 +
82 + def create(conn, _params) do
83 + msg = conn.body_params
84 + reply = echo_reply(msg, msg["Content"])
85 + render conn, "text.xml", reply: reply
86 + end
87 +
88 + defp echo_reply(%{"ToUserName" => to, "FromUserName" => from}, content) do
89 + %{from: to, to: from, content: content}
90 + end
91 + end
92 + ```
93 +
94 + * text.xml.eex
95 +
96 + ```xml
97 + <xml>
98 + <MsgType><![CDATA[text]]></MsgType>
99 + <Content><![CDATA[<%= @reply.content %>]]></Content>
100 + <ToUserName><![CDATA[<%= @reply.to %>]]></ToUserName>
101 + <FromUserName><![CDATA[<%= @reply.from %>]]></FromUserName>
102 + <CreateTime><%= DateTime.to_unix(DateTime.utc_now) %></CreateTime>
103 + </xml>
104 + ```
  @@ -5,15 +5,19 @@
5 5 {<<"files">>,
6 6 [<<"lib">>,<<"lib/wechat">>,<<"lib/wechat/adapter.ex">>,
7 7 <<"lib/wechat/util.ex">>,<<"lib/wechat/client.ex">>,
8 - <<"lib/wechat/message.ex">>,<<"lib/wechat/.DS_Store">>,
8 + <<"lib/wechat/message.ex">>,<<"lib/wechat/request.ex">>,
9 9 <<"lib/wechat/qrcode.ex">>,<<"lib/wechat/ticket.ex">>,
10 10 <<"lib/wechat/media.ex">>,<<"lib/wechat/access_token.ex">>,
11 11 <<"lib/wechat/user.ex">>,<<"lib/wechat/config.ex">>,
12 - <<"lib/wechat/error.ex">>,<<"lib/wechat/wxa.ex">>,<<"lib/wechat/adapter">>,
12 + <<"lib/wechat/error.ex">>,<<"lib/wechat/wxa.ex">>,<<"lib/wechat/utils">>,
13 + <<"lib/wechat/utils/xml_parser.ex">>,
14 + <<"lib/wechat/utils/signature_verifier.ex">>,
15 + <<"lib/wechat/utils/message_encryptor.ex">>,<<"lib/wechat/adapter">>,
13 16 <<"lib/wechat/adapter/sandbox.ex">>,<<"lib/wechat/adapter/redis.ex">>,
14 - <<"lib/wechat/tags">>,<<"lib/wechat/tags/.DS_Store">>,
15 - <<"lib/wechat/plugs">>,<<"lib/wechat/base.ex">>,<<"lib/wechat/menu.ex">>,
16 - <<"lib/wechat/application.ex">>,<<"lib/wechat.ex">>,<<"lib/wechat_bypass">>,
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.ex">>,<<"lib/wechat_bypass">>,
17 21 <<"lib/wechat_bypass/assertion.ex">>,<<"lib/wechat_bypass/case.ex">>,
18 22 <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE.txt">>]}.
19 23 {<<"licenses">>,[<<"MIT">>]}.
  @@ -24,15 +28,20 @@
24 28 {<<"name">>,<<"httpoison">>},
25 29 {<<"optional">>,false},
26 30 {<<"repository">>,<<"hexpm">>},
27 - {<<"requirement">>,<<"~> 0.13">>}],
31 + {<<"requirement">>,<<"~> 1.0">>}],
28 32 [{<<"app">>,<<"jason">>},
29 33 {<<"name">>,<<"jason">>},
30 34 {<<"optional">>,false},
31 35 {<<"repository">>,<<"hexpm">>},
32 36 {<<"requirement">>,<<"~> 1.0">>}],
37 + [{<<"app">>,<<"plug">>},
38 + {<<"name">>,<<"plug">>},
39 + {<<"optional">>,false},
40 + {<<"repository">>,<<"hexpm">>},
41 + {<<"requirement">>,<<"~> 1.8">>}],
33 42 [{<<"app">>,<<"redix">>},
34 43 {<<"name">>,<<"redix">>},
35 44 {<<"optional">>,true},
36 45 {<<"repository">>,<<"hexpm">>},
37 46 {<<"requirement">>,<<">= 0.0.0">>}]]}.
38 - {<<"version">>,<<"0.4.0">>}.
47 + {<<"version">>,<<"0.4.1">>}.
  @@ -1,111 +1,49 @@
1 1 defmodule Wechat do
2 - @moduledoc false
2 + @moduledoc """
3 + Use this module to build your own API wrapper for a client.
3 4
4 - alias Wechat.{Client, Error}
5 + ## Config
5 6
6 - @access_token_errcodes [40001, 40014, 41001, 42001]
7 + config :my_app, MyApp.Wechat,
8 + appid: "APP_ID",
9 + secret: "APP_SECRET",
10 + token: "TOKEN",
11 + encoding_aes_key: "ENCODING_AES_KEY" # Required if you enabled the encrypt mode
7 12
8 - def get(client, path, opts \\ []) do
9 - do_request(client, :get, path, "", opts)
10 - end
13 + ## Example
11 14
12 - def raw_get(client, path, opts \\ []) do
13 - raw_request(client, :get, path, "", opts)
14 - end
15 + def MyApp.Wechat do
16 + use Wechat, otp_app: :my_app
15 17
16 - def post(client, path, body, opts \\ [])
18 + def users do
19 + client() |> Wechat.User.get()
20 + end
21 + end
22 + """
17 23
18 - def post(client, path, body, opts) when is_map(body) do
19 - body = Jason.encode!(body)
20 - do_request(client, :post, path, body, opts)
21 - end
24 + alias Wechat.Client
22 25
23 - def post(client, path, body, opts),
24 - do: do_request(client, :post, path, body, opts)
26 + @callback client() :: Client.t()
25 27
26 - defp raw_request(client, method, path, body, opts) do
27 - url = client.endpoint <> path
28 + @callback config() :: Keyword.t()
28 29
29 - HTTPoison.request!(method, url, body, [], opts)
30 - |> process_response()
31 - end
30 + defmacro __using__(opts) do
31 + quote bind_quoted: [opts: opts] do
32 + @behaviour Wechat
32 33
33 - defp do_request(client, method, path, body, opts, retries \\ 3) do
34 - url = client.endpoint <> path
35 - opts = with_auth(opts, client.auth)
34 + otp_app = Keyword.fetch!(opts, :otp_app)
36 35
37 - retry? =
38 - case client.auth do
39 - %{appid: _} -> retries > 0
40 - _ -> false
36 + @otp_app otp_app
37 +
38 + @impl true
39 + def config do
40 + Application.get_env(@otp_app, __MODULE__, [])
41 41 end
42 42
43 - result =
44 - HTTPoison.request!(method, url, body, [], opts)
45 - |> process_response()
46 -
47 - case result do
48 - {:ok, body} ->
49 - {:ok, body}
50 -
51 - {:error, %Error{code: code}} when code in @access_token_errcodes and retry? ->
52 - client
53 - |> Client.get_token!()
54 - |> do_request(method, path, body, opts, retries - 1)
55 -
56 - {:error, error} ->
57 - {:error, error}
58 - end
59 - end
60 -
61 - defp with_auth(opts, %{access_token: access_token}),
62 - do: add_param_to_opts(opts, :access_token, access_token)
63 -
64 - defp with_auth(opts, %{appid: _} = auth) do
65 - access_token = Client.access_token(auth)
66 - add_param_to_opts(opts, :access_token, access_token)
67 - end
68 -
69 - defp add_param_to_opts(opts, name, value) do
70 - if Keyword.has_key?(opts, :params) do
71 - update_in(opts[:params], &Keyword.put(&1, name, value))
72 - else
73 - Keyword.put(opts, :params, [{name, value}])
74 - end
75 - end
76 -
77 - defp process_response(%{status_code: 200, body: body, headers: headers} = response) do
78 - case handle_body(body, headers) do
79 - %{"errcode" => errcode, "errmsg" => errmsg} when errcode > 0 ->
80 - {:error, %Error{type: :api, code: errcode, reason: errmsg, response: response}}
81 -
82 - body ->
83 - {:ok, body}
84 - end
85 - end
86 -
87 - defp process_response(%{status_code: status_code} = response),
88 - do: raise(%Error{type: :http, code: status_code, response: response})
89 -
90 - defp handle_body(body, headers) when is_list(headers) do
91 - content_type = content_type(headers)
92 - handle_body(body, content_type)
93 - end
94 -
95 - defp handle_body(body, "application/json"), do: Jason.decode!(body)
96 - defp handle_body(body, "text/plain"), do: Jason.decode!(body)
97 - defp handle_body(body, _), do: body
98 -
99 - defp content_type([]), do: ""
100 -
101 - defp content_type([{key, value} | t]) do
102 - cond do
103 - String.match?(key, ~r/content-type/i) ->
104 - [content_type | _] = String.split(value, ";")
105 - content_type
106 -
107 - true ->
108 - content_type(t)
43 + @impl true
44 + def client do
45 + Wechat.Client.new(config())
46 + end
109 47 end
110 48 end
111 49 end
  @@ -1,15 +1,15 @@
1 1 defmodule Wechat.Base do
2 2 @moduledoc false
3 3
4 - import Wechat
4 + alias Wechat.Request
5 5
6 6 def token(client) do
7 7 params = [
8 - appid: client.auth.appid,
9 - secret: client.auth.secret,
8 + appid: client.appid,
9 + secret: client.secret,
10 10 grant_type: :client_credential
11 11 ]
12 12
13 - raw_get(client, "cgi-bin/token", params: params)
13 + Request.raw_get(client, "cgi-bin/token", params: params)
14 14 end
15 15 end
Loading more files…