WIP: Qonto payment links #1

Draft
lateralus23 wants to merge 1 commit from qonto into main
17 changed files with 602 additions and 2 deletions

View file

@ -29,6 +29,9 @@ config :logger, :default_formatter,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
config :oauth2, adapter: Tesla.Adapter.Mint
config :oauth2, middleware: [{Tesla.Middleware.FollowRedirects, max_redirects: 3}, {Tesla.Middleware.Logger, debug: true}]
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs"

View file

@ -51,3 +51,9 @@ config :phoenix, :stacktrace_depth, 20
# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
config :oauth2, debug: true
config :camp_api, :qonto_url,
staging: true,
base_url: "https://thirdparty-sandbox.staging.qonto.co"

View file

@ -14,7 +14,8 @@ defmodule CampApi.Application do
# Start a worker by calling: CampApi.Worker.start_link(arg)
# {CampApi.Worker, arg},
# Start to serve requests, typically the last entry
CampApiWeb.Endpoint
CampApiWeb.Endpoint,
CampApi.PaymentLinks.Qonto
]
# See https://hexdocs.pm/elixir/Supervisor.html

View file

@ -0,0 +1,110 @@
defmodule CampApi.PaymentLinks do
@moduledoc """
The PaymentLinks context.
"""
# import Ecto.Query, warn: false
alias CampApi.Repo
alias CampApi.PaymentLinks.Link
alias CampApi.PaymentLinks.Qonto
def connect() do
body = %{
"partner_callback_url" => "http://localhost:4000",
"user_bank_account_id" => "01982d1e-6813-7bdf-a96f-0c583e6fa063",
"user_phone_number" => "+33612345678",
"user_website_url" => "http://localhost:4000",
"business_description" => "This needs a long long long description. This needs a long long long description."
}
Qonto.request(:post, "/v2/payment_links/connections", body)
end
@doc """
Returns the list of links.
## Examples
iex> list_links()
[%Link{}, ...]
"""
def list_links do
raise "TODO"
end
@doc """
Gets a single link.
Raises if the Link does not exist.
## Examples
iex> get_link!(123)
%Link{}
"""
def get_link!(id), do: raise "TODO"
@doc """
Creates a link.
## Examples
iex> create_link(%{field: value})
{:ok, %Link{}}
iex> create_link(%{field: bad_value})
{:error, ...}
"""
def create_link(attrs) do
raise "TODO"
end
@doc """
Updates a link.
## Examples
iex> update_link(link, %{field: new_value})
{:ok, %Link{}}
iex> update_link(link, %{field: bad_value})
{:error, ...}
"""
def update_link(%Link{} = link, attrs) do
raise "TODO"
end
@doc """
Deletes a Link.
## Examples
iex> delete_link(link)
{:ok, %Link{}}
iex> delete_link(link)
{:error, ...}
"""
def delete_link(%Link{} = link) do
raise "TODO"
end
@doc """
Returns a data structure for tracking link changes.
## Examples
iex> change_link(link)
%Todo{...}
"""
def change_link(%Link{} = link, _attrs \\ %{}) do
raise "TODO"
end
end

View file

@ -0,0 +1,3 @@
defmodule CampApi.PaymentLinks.Link do
defstruct name: "John", age: 27
end

View file

@ -0,0 +1,151 @@
defmodule CampApi.PaymentLinks.Qonto do
use GenServer
use CampApiWeb, :verified_routes
def base_url() do
if Application.get_env(CampApi, :qonto, :staging) do
"https://thirdparty-sandbox.staging.qonto.co"
else
""
end
end
def oauth_url() do
if Application.get_env(CampApi, :qonto, :staging) do
"https://oauth-sandbox.staging.qonto.co"
else
"https://oauth.qonto.com"
end
end
def client_id() do
if Application.get_env(CampApi, :qonto, :staging) do
"d78920aa-e35f-4bfd-97df-e457f8337e2d"
else
""
end
end
def client_secret() do
if Application.get_env(CampApi, :qonto, :staging) do
"EqrS29dZYPy1Nfg8V8Bt6aRR1u"
else
""
end
end
def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def request(method, url, body) do
GenServer.call(__MODULE__, {:request, method, url, body})
end
def init(%{}) do
oauth_client =
OAuth2.Client.new(
strategy: OAuth2.Strategy.AuthCode,
client_id: client_id(),
client_secret: client_secret(),
site: oauth_url(),
redirect_uri: url(~p"/api/qonto_auth"),
authorize_url: "/oauth2/auth",
token_url: "/oauth2/token"
)
|> OAuth2.Client.put_serializer("application/json;charset=UTF-8", Jason)
|> OAuth2.Client.put_serializer("application/json", Jason)
{:ok, %{status: :disconnected, auth_state: nil, oauth_client: oauth_client}, {:continue, nil}}
end
def handle_continue(_, state) do
auth_state = :crypto.strong_rand_bytes(24) |> Base.url_encode64(padding: false)
# auth_url = %URI{
# # scheme: "https",
# # host: oauth_url(),
# URI.parse(oauth_url()) |
# path: "/oauth2/auth",
# query:
# URI.encode_query(%{
# client_id: client_id(),
# redirect_uri: url(~p"/api/qonto_auth"),
# scope: "payment_link.write payment_link.read",
# response_type: "code",
# state: auth_state
# # organization_id: TODO
# })
# }
auth_url = OAuth2.Client.authorize_url!(state.oauth_client, state: auth_state, scope: "payment_link.write payment_link.read")
IO.puts("Plz go to the following URL:")
IO.puts(auth_url)
{:noreply, %{state | status: :pending, auth_state: auth_state}}
end
def create_token(code, auth_state) do
GenServer.call(__MODULE__, {:create_token, code, auth_state})
end
def handle_call({:create_token, code, auth_state}, _from, state) do
# with state.status == :pending && state.auth_state == auth_state,
# {:ok, %{body: %{"json" => %{"access_token" => access_token, "refresh_token" => refresh_token}}}} <- Req.post(base_url: oauth_url(), url: "/outh2/token",
# {:reply, :ok, %{state | status: :connected,
#
headers =
if Application.get_env(CampApi, :qonto, :staging) do
[
{"x-qonto-staging-token", "0CxYRit4ZV7uwjoUluN93XgQvOt7bke96Nn0x5MUg2w="},
{"authorization", ""}
]
else
[]
end
with state.status == :pending && state.auth_state == auth_state,
{:ok, oauth_client} <- OAuth2.Client.get_token(state.oauth_client, [code: code, client_secret: state.oauth_client.client_secret], headers) do
{:reply, :ok, %{state | status: :connected, oauth_client: oauth_client}}
else
_ ->
{:reply, :error, state}
end
end
# def handle_call({:request, method, url, body}, _from, state) do
# access_token =
# headers =
# if Application.get_env(CampApi, :qonto, :staging) do
# [
# authorization: "Bearer #{state.access_token}",
# x_qonto_staging_token: "0CxYRit4ZV7uwjoUluN93XgQvOt7bke96Nn0x5MUg2w="
# ]
# else
# []
# end
#
# Req.request(method: method, base_url: base_url(), url: url, headers: headers, json: body)
# end
def handle_call({:request, method, url, body}, _from, state) do
url = base_url() <> url
headers =
if Application.get_env(CampApi, :qonto, :staging) do
[
# x_qonto_staging_token: "0CxYRit4ZV7uwjoUluN93XgQvOt7bke96Nn0x5MUg2w="
{"x-qonto-staging-token", "0CxYRit4ZV7uwjoUluN93XgQvOt7bke96Nn0x5MUg2w="}
]
else
[]
end
case OAuth2.Request.request(method, state.oauth_client, url, Jason.encode!(body), headers, []) do
{:ok, resp} -> {:reply, {:ok, resp}, state}
_ -> {:reply, :error, state}
end
end
end

View file

@ -0,0 +1,25 @@
defmodule CampApiWeb.ChangesetJSON do
@doc """
Renders changeset errors.
"""
def error(%{changeset: changeset}) do
# When encoded, the changeset returns its errors
# as a JSON object. So we just pass it forward.
%{errors: Ecto.Changeset.traverse_errors(changeset, &translate_error/1)}
end
defp translate_error({msg, opts}) do
# You can make use of gettext to translate error messages by
# uncommenting and adjusting the following code:
# if count = opts[:count] do
# Gettext.dngettext(CampApiWeb.Gettext, "errors", msg, msg, count, opts)
# else
# Gettext.dgettext(CampApiWeb.Gettext, "errors", msg, opts)
# end
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end)
end)
end
end

View file

@ -0,0 +1,16 @@
defmodule CampApiWeb.FallbackController do
@moduledoc """
Translates controller action results into valid `Plug.Conn` responses.
See `Phoenix.Controller.action_fallback/1` for more details.
"""
use CampApiWeb, :controller
# This clause is an example of how to handle resources that cannot be found.
def call(conn, {:error, :not_found}) do
conn
|> put_status(:not_found)
|> put_view(html: CampApiWeb.ErrorHTML, json: CampApiWeb.ErrorJSON)
|> render(:"404")
end
end

View file

@ -0,0 +1,58 @@
defmodule CampApiWeb.PaymentLinkController do
use CampApiWeb, :controller
alias CampApi.PaymentLinks
alias CampApi.PaymentLinks.Link
alias CampApi.PaymentLinks.Qonto
action_fallback CampApiWeb.FallbackController
def qonto_auth(conn, %{"code" => code, "state" => state}) do
case Qonto.create_token(code, state) do
:ok ->
conn
# |> put_resp_content_type
|> send_resp(200, "connected")
:error ->
conn
# |> put_resp_content_type
|> send_resp(500, "error")
end
end
# def index(conn, _params) do
# links = PaymentLinks.list_links()
# render(conn, :index, links: links)
# end
def create(conn, %{"link" => link_params}) do
with {:ok, %Link{} = link} <- PaymentLinks.create_link(link_params) do
conn
|> put_status(:created)
|> put_resp_header("location", ~p"/api/links/#{link}")
|> render(:show, link: link)
end
end
# def show(conn, %{"id" => id}) do
# link = PaymentLinks.get_link!(id)
# render(conn, :show, link: link)
# end
# def update(conn, %{"id" => id, "link" => link_params}) do
# link = PaymentLinks.get_link!(id)
#
# with {:ok, %Link{} = link} <- PaymentLinks.update_link(link, link_params) do
# render(conn, :show, link: link)
# end
# end
# def delete(conn, %{"id" => id}) do
# link = PaymentLinks.get_link!(id)
#
# with {:ok, %Link{}} <- PaymentLinks.delete_link(link) do
# send_resp(conn, :no_content, "")
# end
# end
end

View file

@ -0,0 +1,24 @@
defmodule CampApiWeb.PaymentLinkJSON do
alias CampApi.PaymentLinks.Link
@doc """
Renders a list of links.
"""
def index(%{links: links}) do
%{data: for(link <- links, do: data(link))}
end
@doc """
Renders a single link.
"""
def show(%{link: link}) do
%{data: data(link)}
end
defp data(%Link{} = link) do
%{
id: link.id,
price: link.price
}
end
end

View file

@ -7,6 +7,10 @@ defmodule CampApiWeb.Router do
scope "/api", CampApiWeb do
pipe_through :api
resources "/payment_links", PaymentLinkController, only: [:create]
get "/qonto_auth", PaymentLinkController, :qonto_auth
end
# Enable LiveDashboard in development

View file

@ -45,7 +45,9 @@ defmodule CampApi.MixProject do
{:telemetry_poller, "~> 1.0"},
{:jason, "~> 1.2"},
{:dns_cluster, "~> 0.2.0"},
{:bandit, "~> 1.5"}
{:bandit, "~> 1.5"},
{:req, "~> 0.5.0"},
{:oauth2, "~> 2.0"}
]
end

View file

@ -1,9 +1,14 @@
%{
"bandit": {:hex, :bandit, "1.9.0", "6dc1ff2c30948dfecf32db574cc3447c7b9d70e0b61140098df3818870b01b76", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "2538aaa1663b40ca9cbd8ca1f8a540cb49e5baf34c6ffef068369cc45f9146f2"},
"dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"},
"finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"},
"phoenix": {:hex, :phoenix, "1.8.3", "49ac5e485083cb1495a905e47eb554277bdd9c65ccb4fc5100306b350151aa95", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "36169f95cc2e155b78be93d9590acc3f462f1e5438db06e6248613f27c80caec"},
"phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
@ -12,9 +17,11 @@
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"req": {:hex, :req, "0.5.16", "99ba6a36b014458e52a8b9a0543bfa752cb0344b2a9d756651db1281d4ba4450", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "974a7a27982b9b791df84e8f6687d21483795882a7840e8309abdbe08bb06f09"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
"tesla": {:hex, :tesla, "1.15.3", "3a2b5c37f09629b8dcf5d028fbafc9143c0099753559d7fe567eaabfbd9b8663", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "98bb3d4558abc67b92fb7be4cd31bb57ca8d80792de26870d362974b58caeda7"},
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.9", "43dc3ba6d89ef5dec5b1d0a39698436a1e856d000d84bf31a3149862b01a287f", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5534d5c9adad3c18a0f58a9371220d75a803bf0b9a3d87e6fe072faaeed76a08"},

27
script.sh Normal file
View file

@ -0,0 +1,27 @@
curl --request POST \
--url https://thirdparty-sandbox.staging.qonto.co/v2/payment_links \
--header 'Authorization: Bearer ory_at_l82B_-dFKvmqmw2wZ3qVuDb85M2jKDUmcX9qUIBu1Bw.SxFi-0Cs2L-kjN5pg9_VeEhHUNo52s3bcqaDsnq2gqs' \
--header 'Content-Type: application/json' \
--header 'X-Qonto-Staging-Token: 0CxYRit4ZV7uwjoUluN93XgQvOt7bke96Nn0x5MUg2w=' \
--data '
{
"payment_link": {
"items": [{
"title": "Participation",
"quantity": 1,
"unit_price": {
"value": "10.99",
"currency": "EUR"
},
"vat_rate": "20.0",
"type": "service",
"description": "Participation",
"measure_unit": "unit"
}],
"potential_payment_methods": [
"credit_card"
],
"reusable": false
}
}
'

View file

@ -0,0 +1,59 @@
defmodule CampApi.PaymentLinksTest do
use CampApi.DataCase
alias CampApi.PaymentLinks
describe "links" do
alias CampApi.PaymentLinks.Link
import CampApi.PaymentLinksFixtures
@invalid_attrs %{price: nil}
test "list_links/0 returns all links" do
link = link_fixture()
assert PaymentLinks.list_links() == [link]
end
test "get_link!/1 returns the link with given id" do
link = link_fixture()
assert PaymentLinks.get_link!(link.id) == link
end
test "create_link/1 with valid data creates a link" do
valid_attrs = %{price: "some price"}
assert {:ok, %Link{} = link} = PaymentLinks.create_link(valid_attrs)
assert link.price == "some price"
end
test "create_link/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = PaymentLinks.create_link(@invalid_attrs)
end
test "update_link/2 with valid data updates the link" do
link = link_fixture()
update_attrs = %{price: "some updated price"}
assert {:ok, %Link{} = link} = PaymentLinks.update_link(link, update_attrs)
assert link.price == "some updated price"
end
test "update_link/2 with invalid data returns error changeset" do
link = link_fixture()
assert {:error, %Ecto.Changeset{}} = PaymentLinks.update_link(link, @invalid_attrs)
assert link == PaymentLinks.get_link!(link.id)
end
test "delete_link/1 deletes the link" do
link = link_fixture()
assert {:ok, %Link{}} = PaymentLinks.delete_link(link)
assert_raise Ecto.NoResultsError, fn -> PaymentLinks.get_link!(link.id) end
end
test "change_link/1 returns a link changeset" do
link = link_fixture()
assert %Ecto.Changeset{} = PaymentLinks.change_link(link)
end
end
end

View file

@ -0,0 +1,84 @@
defmodule CampApiWeb.LinkControllerTest do
use CampApiWeb.ConnCase
import CampApi.PaymentLinksFixtures
alias CampApi.PaymentLinks.Link
@create_attrs %{
price: "some price"
}
@update_attrs %{
price: "some updated price"
}
@invalid_attrs %{price: nil}
setup %{conn: conn} do
{:ok, conn: put_req_header(conn, "accept", "application/json")}
end
describe "index" do
test "lists all links", %{conn: conn} do
conn = get(conn, ~p"/api/links")
assert json_response(conn, 200)["data"] == []
end
end
describe "create link" do
test "renders link when data is valid", %{conn: conn} do
conn = post(conn, ~p"/api/links", link: @create_attrs)
assert %{"id" => id} = json_response(conn, 201)["data"]
conn = get(conn, ~p"/api/links/#{id}")
assert %{
"id" => ^id,
"price" => "some price"
} = json_response(conn, 200)["data"]
end
test "renders errors when data is invalid", %{conn: conn} do
conn = post(conn, ~p"/api/links", link: @invalid_attrs)
assert json_response(conn, 422)["errors"] != %{}
end
end
describe "update link" do
setup [:create_link]
test "renders link when data is valid", %{conn: conn, link: %Link{id: id} = link} do
conn = put(conn, ~p"/api/links/#{link}", link: @update_attrs)
assert %{"id" => ^id} = json_response(conn, 200)["data"]
conn = get(conn, ~p"/api/links/#{id}")
assert %{
"id" => ^id,
"price" => "some updated price"
} = json_response(conn, 200)["data"]
end
test "renders errors when data is invalid", %{conn: conn, link: link} do
conn = put(conn, ~p"/api/links/#{link}", link: @invalid_attrs)
assert json_response(conn, 422)["errors"] != %{}
end
end
describe "delete link" do
setup [:create_link]
test "deletes chosen link", %{conn: conn, link: link} do
conn = delete(conn, ~p"/api/links/#{link}")
assert response(conn, 204)
assert_error_sent 404, fn ->
get(conn, ~p"/api/links/#{link}")
end
end
end
defp create_link(_) do
link = link_fixture()
%{link: link}
end
end

View file

@ -0,0 +1,20 @@
defmodule CampApi.PaymentLinksFixtures do
@moduledoc """
This module defines test helpers for creating
entities via the `CampApi.PaymentLinks` context.
"""
@doc """
Generate a link.
"""
def link_fixture(attrs \\ %{}) do
{:ok, link} =
attrs
|> Enum.into(%{
price: "some price"
})
|> CampApi.PaymentLinks.create_link()
link
end
end