Skip to content

Instantly share code, notes, and snippets.

@imetallica
Created January 22, 2016 19:28
Show Gist options
  • Save imetallica/b0fd851f94ee5967318a to your computer and use it in GitHub Desktop.
Save imetallica/b0fd851f94ee5967318a to your computer and use it in GitHub Desktop.
defmodule Merchant.Gateways.PayPal do
@moduledoc """
The PayPal api. It connects with the PayPal's REST API.
"""
use HTTPoison.Base
use Merchant.Gateway
alias Merchant.Gateways.PayPal
@sandbox_api "https://api.sandbox.paypal.com/"
@live_api "https://api.paypal.com/"
@token_api_path "v1/oauth2/token"
@grant_type_client_credentials "grant_type=client_credentials"
defmodule Credentials do
@moduledoc """
Credentials used by PayPal module.
"""
defstruct username: "", password: "", test: ""
end
defmodule PayerVisa do
@moduledoc """
A Payer structure. All *fields* are required:
- *payment_method*: String. (We only support "credit_card")
- *funding_instruments* : [
- *credit_card*: Merchant.Card
]
"""
defstruct payment_method: "credit_card",
funding_instruments: [
credit_card: %Merchant.Cards.Visa{}
]
end
defmodule PayerMC do
@moduledoc """
A Payer structure. All *fields* are required:
- *payment_method*: String. (We only support "credit_card")
- *funding_instruments* : [
- *credit_card*: Merchant.Card
]
"""
defstruct payment_method: "credit_card",
funding_instruments: [
credit_card: %Merchant.Cards.MasterCard{}
]
end
defmodule Transaction do
@moduledoc """
A Transaction structure. All *fields* are required:
- description: String. (Give a description to the transaction.)
- *ammount*:
- *currency*: String. (A 3-letter currency code. All supported
currencies are listed below.)
- *total*: String. (Total to be charged. 10 characters max with
support for 2 decimal places.)
- details:
- shipping: String. (Amount charged for shipping. 10 characters
max with support for 2 decimal places.)
- subtotal: String. (Amount of the subtotal of the items.
Required if line items are specified. 10
characters max, with support for 2 decimal
places.)
- tax: String. (Amount charged for tax. 10 characters max
with support for 2 decimal places.)
## Supported currencies Currency Code
Australian dollar AUD
Brazilian real** BRL
Canadian dollar CAD
Czech koruna CZK
Danish krone DKK
Euro EUR
Hong Kong dollar HKD
Hungarian forint* HUF
Israeli new shekel ILS
Japanese yen* JPY
Malaysian ringgit** MYR
Mexican peso MXN
New Taiwan dollar* TWD
New Zealand dollar NZD
Norwegian krone NOK
Philippine peso PHP
Polish złoty PLN
Pound sterling GBP
Russian ruble RUB
Singapore dollar SGD
Swedish krona SEK
Swiss franc CHF
Thai baht THB
Turkish lira** TRY
United States dollar USD
* This currency does not support decimals. Passing a decimal amount will
result in an error.
** This currency is supported as a payment currency and a currency balance
for in-country PayPal accounts only.
"""
defstruct description: "",
ammount: [
currency: "USD",
total: "",
details: [
shipping: "",
subtotal: "",
tax: ""
]
]
end
def get_paypal_creds do
configs = Application.get_env!(:merchant, :paypal)
%Credentials{
username: configs.username,
password: configs.password,
test: configs.test}
end
@doc """
This function sends the credentials to the PayPal's oauth2 server and it
expects either the data or throws an error.
"""
def get_token!(cred) do
case cred.test do
true ->
response = PayPal.post!(
@sandbox_api <> @token_api_path,
@grant_type_client_credentials,
[{"Accept", "application/json"},
{"Content-Type", "application/x-www-form-urlencoded"},
generate_paypal_request_id()],
[hackney:
[basic_auth: {cred.username, cred.password}]])
|> check_response |> Map.get("access_token")
false -> Paypal.get!(@live_api <> @token_api_path, [],
[hackney: [basic_auth: {cred.username, cred.password}]])
_ -> raise "Not configured."
end
end
# Request ID is to allow requests to be unique in paypal. It encodes in base64.
# Finally it returns a tuple, that can be used directly into paypal's headers.
defp generate_paypal_request_id do
str_system_time = Integer.to_string :erlang.system_time(:milli_seconds)
str_random_numb = Float.to_string :rand.uniform()
rnd = :base64.encode(str_system_time <> str_random_numb)
{"PayPal-Request-Id", rnd}
end
defp check_response(response) do
IO.inspect(response.status_code)
body = Poison.decode!(response.body)
case response.status_code do
x when x >= 200 and x < 300 ->
IO.inspect body
body
x when x >= 400 and x < 500 ->
error = Map.get(body, "error")
error_description = Map.get(body, "error_description")
raise "#{x}. #{error}: #{error_description}"
x when x >= 500 and x < 600 ->
raise "#{x}. PayPal is misbehaving. Please try again"
end
end
defimpl AvailableCards, for: Merchant.Cards.MasterCard do
def support_card?(_card), do: true
def normalize_card(card) do
Map.update!(card, :type, fn x ->
x = "mastercard"
end)
end
end
defimpl AvailableCards, for: Merchant.Cards.Visa do
def support_card?(_card), do: true
def normalize_card(card) do
Map.update!(card, :type, fn x ->
x = "visa"
end)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment