defmodule Kandis.Payment.Klarna do @behaviour Kandis.Payment use Phoenix.HTML @providername "klarna" def create_payment_attempt({_amount, _curr}, order_nr, orderitems, ordervars) do # total_price = orderitems.stats.total_price payload = get_klarna_payload(orderitems, ordervars) data = create_klarna_session(payload) %Kandis.PaymentAttempt{ provider: @providername, data: data, id: data["session_id"], order_nr: order_nr } end def update_payment_attempt_if_needed( %Kandis.PaymentAttempt{} = attempt, {_amount, _curr}, _order_nr, orderitems, ordervars ) do payload = get_klarna_payload(orderitems, ordervars) update_klarna_session(payload, attempt.id) attempt end def process_payment(vid, order) when is_binary(vid) and is_map(order) do get_latest_authorization_event(vid) |> case do %{authorization_token: authorization_token, attempt_id: tid} -> place_order(authorization_token, order) |> case do data when is_map(data) -> Kandis.Payment.log_event(data, vid, tid, "placed_klarna_order") |> case do _event = %{data: %{"fraud_status" => "ACCEPTED", "order_id" => order_id}} -> {:ok, order_id} _event = %{ data: %{ "error_messages" => [ "The order has been already completed with different request body" ] } } -> {:ok, "n/a"} event -> {:error, %{"msg" => "klarna could not process order", "data" => event}} end _ -> {:error, %{"msg" => "klarna could not receive order"}} end _ -> {:error, %{"msg" => "no authorization event found"}} end end def place_order(authorization_token, order) when is_map(order) and is_binary(authorization_token) do payload = get_klarna_payload(order.orderitems, order.ordervars) |> Map.merge(%{ "merchant_reference1" => order.order_nr, "merchant_reference2" => order.cart_id }) make_request("/payments/v1/authorizations/#{authorization_token}/order", payload) end # order= Order.get_current_order_for_vid(vid) def get_authorization_events(vid) when is_binary(vid) do Kandis.Payment.get_all_payment_events(vid) |> Enum.map(fn a -> a |> Map.put(:authorization_token, a.data["authorization_token"]) |> Map.put(:approved, a.data["approved"]) |> Map.drop([:data, :__struct__, :msg]) end) |> Enum.filter(fn a -> Kandis.KdHelpers.present?(a.authorization_token) end) end def get_latest_authorization_event(vid) when is_binary(vid) do get_authorization_events(vid) |> Enum.at(0) end def create_klarna_session(payload) when is_map(payload) do make_request("/payments/v1/sessions", payload) end def update_klarna_session(payload, session_id) when is_binary(session_id) and is_map(payload) do make_request("/payments/v1/sessions/#{session_id}", payload) end def release_klarna_authorization(authorization_token) when is_binary(authorization_token) do make_request("/payments/v1/authorizations/#{authorization_token}", :delete) end def get_klarna_session(session_id) when is_binary(session_id) do make_request("/payments/v1/sessions/#{session_id}", :get) end def get_taxamount_from_orderitems(orderitems) when is_map(orderitems) do orderitems.stats.taxrates |> Map.to_list() |> Enum.reduce("0", fn {_taxrate, tax_stats}, acc -> Decimal.add(acc, tax_stats.tax) end) end def centify(decimalval) do decimalval |> Decimal.mult(100) |> Decimal.round() |> Decimal.to_integer() end def get_order_lines_for_klarna(lineitems) when is_list(lineitems) do lineitems |> Enum.filter(fn l -> Enum.member?(~w(product addon), l.type) end) |> Enum.map(fn l -> rec = case l.type do "product" -> %{ "type" => "physical", "reference" => l.sku, "name" => l.title <> " " <> l.subtitle, "quantity" => l.amount, "unit_price" => centify(l.single_price), "tax_rate" => centify(l.taxrate), "total_amount" => centify(l.total_price), "total_discount_amount" => 0 # "image_url" => "https://www.exampleobjects.com/logo.png", # "product_url" => "https://www.estore.com/products/f2a8d7e34" } "addon" -> %{ "type" => "shipping_fee", "quantity" => 1, "name" => l.title, "unit_price" => centify(l.total_price), "tax_rate" => centify(l.taxrate), "total_amount" => centify(l.total_price) } end total_tax_amount = if rec["tax_rate"] == 0 do 0 else Kernel.round( rec["total_amount"] - rec["total_amount"] * 10000 / (10000 + rec["tax_rate"]) ) end Map.put(rec, "total_tax_amount", total_tax_amount) end) end def get_klarna_payload(orderitems, ordervars) do taxamount = get_taxamount_from_orderitems(orderitems) %{ "purchase_country" => "AT", "purchase_currency" => "EUR", "locale" => "de-AT", "order_amount" => centify(orderitems.stats.total_price), "order_tax_amount" => centify(taxamount), "order_lines" => get_order_lines_for_klarna(orderitems.lineitems), "billing_address" => %{ "given_name" => ordervars[:first_name], "family_name" => ordervars[:last_name], "email" => ordervars[:email], "street_address" => ordervars[:street], # "street_address2" => nil, "postal_code" => ordervars[:zip], "city" => ordervars[:city], "phone" => ordervars[:phone], "country" => ordervars[:country] } } end def make_request(url, method) when is_atom(method) do base_url = Application.get_env(:kandis, :klarna)[:base_url] |> String.trim_trailing("/") username = Application.get_env(:kandis, :klarna)[:username] password = Application.get_env(:kandis, :klarna)[:password] url = "#{base_url}#{url}" |> Kandis.KdHelpers.log("make Klarna-Request ", :info) HTTPoison.request(method, url, _body = "", _headers = [], hackney: [basic_auth: {username, password}] ) |> case do {:ok, response} -> body = response |> Map.get(:body) case body do nil -> body "" -> body _ -> body |> Jason.decode() |> case do {:ok, str} -> str |> Kandis.KdHelpers.log("klarna RESPONSE", :info) {:error, _} -> %{"error" => body} end end _ -> nil end end def make_request(url, payload) when is_map(payload) do base_url = Application.get_env(:kandis, :klarna)[:base_url] |> String.trim_trailing("/") username = Application.get_env(:kandis, :klarna)[:username] password = Application.get_env(:kandis, :klarna)[:password] url = "#{base_url}#{url}" |> Kandis.KdHelpers.log("KLARNA REQUEST URL", :info) body = payload |> Jason.encode!() |> Kandis.KdHelpers.log("Klarna payload 2020-08-20_11:08 ", :info) HTTPoison.post(url, body, [{"Content-Type", "application/json"}], hackney: [basic_auth: {username, password}] ) |> case do {:ok, response} -> body = response |> Map.get(:body) case body do nil -> body "" -> body _ -> body |> Jason.decode() |> case do {:ok, str} -> str |> Kandis.KdHelpers.log("KLARNA ➜ RESPONSE", :info) {:error, _} -> %{"error" => body} end end _ -> nil end end end 1