Skip to content

Commit 76008eb

Browse files
authored
add abstraction layer for payment service provider interactions (#55)
1 parent 57b8bad commit 76008eb

File tree

16 files changed

+179
-104
lines changed

16 files changed

+179
-104
lines changed

lib/algora/admin/admin.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ defmodule Algora.Admin do
7878
with account_id when is_binary(account_id) <- Algora.config([:stripe, :test_account_id]),
7979
{:ok, user} <- Repo.fetch_by(User, handle: user_handle),
8080
{:ok, acct} <- Payments.create_account(user, "US"),
81-
{:ok, stripe_acct} <- Stripe.Account.retrieve(account_id, []) do
81+
{:ok, stripe_acct} <- Algora.PSP.Account.retrieve(account_id) do
8282
Payments.update_account(acct, stripe_acct)
8383
end
8484
end

lib/algora/bounties/bounties.ex

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ defmodule Algora.Bounties do
1616
alias Algora.Organizations.Member
1717
alias Algora.Payments
1818
alias Algora.Payments.Transaction
19+
alias Algora.PSP
1920
alias Algora.Repo
2021
alias Algora.Util
2122
alias Algora.Workspace
@@ -649,7 +650,7 @@ defmodule Algora.Bounties do
649650
recipient: User.t()
650651
]
651652
) ::
652-
{:ok, Stripe.Invoice.t()} | {:error, atom()}
653+
{:ok, PSP.invoice()} | {:error, atom()}
653654
def create_invoice(%{owner: owner, amount: amount}, opts \\ []) do
654655
tx_group_id = Nanoid.generate()
655656

@@ -685,15 +686,15 @@ defmodule Algora.Bounties do
685686
}),
686687
{:ok, customer} <- Payments.fetch_or_create_customer(owner),
687688
{:ok, invoice} <-
688-
Algora.Stripe.Invoice.create(%{
689+
PSP.Invoice.create(%{
689690
auto_advance: false,
690691
customer: customer.provider_id
691692
}),
692693
{:ok, _line_items} <-
693694
line_items
694695
|> Enum.map(&LineItem.to_invoice_item(&1, invoice, customer))
695696
|> Enum.reduce_while({:ok, []}, fn params, {:ok, acc} ->
696-
case Algora.Stripe.Invoiceitem.create(params) do
697+
case PSP.Invoiceitem.create(params) do
697698
{:ok, item} -> {:cont, {:ok, [item | acc]}}
698699
{:error, error} -> {:halt, {:error, error}}
699700
end

lib/algora/contracts/contracts.ex

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ defmodule Algora.Contracts do
1111
alias Algora.Payments
1212
alias Algora.Payments.Account
1313
alias Algora.Payments.Transaction
14+
alias Algora.PSP.Invoice
1415
alias Algora.Repo
15-
alias Algora.Stripe
1616
alias Algora.Util
1717

1818
require Algora.SQL
@@ -395,7 +395,7 @@ defmodule Algora.Contracts do
395395
defp maybe_generate_invoice(contract, charge) do
396396
invoice_params = %{auto_advance: false, customer: contract.client.customer.provider_id}
397397

398-
with {:ok, invoice} <- Stripe.Invoice.create(invoice_params),
398+
with {:ok, invoice} <- Invoice.create(invoice_params),
399399
{:ok, _line_items} <- create_line_items(contract, invoice, charge.line_items) do
400400
{:ok, invoice}
401401
end
@@ -442,7 +442,7 @@ defmodule Algora.Contracts do
442442

443443
defp create_line_items(contract, invoice, line_items) do
444444
Enum.reduce_while(line_items, {:ok, []}, fn line_item, {:ok, acc} ->
445-
case Stripe.Invoiceitem.create(%{
445+
case Algora.PSP.Invoiceitem.create(%{
446446
invoice: invoice.id,
447447
customer: contract.client.customer.provider_id,
448448
amount: MoneyUtils.to_minor_units(line_item.amount),
@@ -460,7 +460,7 @@ defmodule Algora.Contracts do
460460
defp maybe_pay_invoice(contract, invoice, txs) do
461461
pm_id = contract.client.customer.default_payment_method.provider_id
462462

463-
case Stripe.Invoice.pay(invoice.id, %{off_session: true, payment_method: pm_id}) do
463+
case Invoice.pay(invoice.id, %{off_session: true, payment_method: pm_id}) do
464464
{:ok, stripe_invoice} ->
465465
if stripe_invoice.paid, do: release_funds(contract, stripe_invoice, txs)
466466
{:ok, stripe_invoice}
@@ -482,7 +482,7 @@ defmodule Algora.Contracts do
482482
defp transfer_funds(contract, %Transaction{type: :transfer} = transaction) when transaction.status != :succeeded do
483483
with {:ok, account} <- Repo.fetch_by(Account, user_id: transaction.user_id),
484484
{:ok, stripe_transfer} <-
485-
Stripe.Transfer.create(%{
485+
Algora.PSP.Transfer.create(%{
486486
amount: MoneyUtils.to_minor_units(transaction.net_amount),
487487
currency: to_string(transaction.net_amount.currency),
488488
destination: account.provider_id

lib/algora/integrations/stripe/stripe.ex

Lines changed: 0 additions & 47 deletions
This file was deleted.

lib/algora/payments/payments.ex

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ defmodule Algora.Payments do
1111
alias Algora.Payments.Jobs
1212
alias Algora.Payments.PaymentMethod
1313
alias Algora.Payments.Transaction
14+
alias Algora.PSP
1415
alias Algora.Repo
15-
alias Algora.Stripe.ConnectCountries
1616
alias Algora.Util
1717

1818
require Logger
@@ -28,12 +28,12 @@ defmodule Algora.Payments do
2828
end
2929

3030
@spec create_stripe_session(
31-
line_items :: [Stripe.Session.line_item_data()],
32-
payment_intent_data :: Stripe.Session.payment_intent_data()
31+
line_items :: [PSP.Session.line_item_data()],
32+
payment_intent_data :: PSP.Session.payment_intent_data()
3333
) ::
34-
{:ok, Stripe.Session.t()} | {:error, Stripe.Error.t()}
34+
{:ok, PSP.session()} | {:error, PSP.error()}
3535
def create_stripe_session(line_items, payment_intent_data) do
36-
Algora.Stripe.Session.create(%{
36+
PSP.Session.create(%{
3737
mode: "payment",
3838
billing_address_collection: "required",
3939
line_items: line_items,
@@ -59,7 +59,7 @@ defmodule Algora.Payments do
5959
end
6060

6161
def get_provider_fee_from_invoice(%{id: id}) do
62-
case Stripe.Invoice.retrieve(id, expand: ["charge.balance_transaction"]) do
62+
case PSP.Invoice.retrieve(id, expand: ["charge.balance_transaction"]) do
6363
{:ok, invoice} ->
6464
get_provider_fee_from_balance_transaction(invoice.charge.balance_transaction)
6565

@@ -71,7 +71,7 @@ defmodule Algora.Payments do
7171
# TODO: This is not used anymore
7272
def get_provider_fee_from_payment_intent(pi) do
7373
with [ch] <- pi.charges.data,
74-
{:ok, txn} <- Stripe.BalanceTransaction.retrieve(ch.balance_transaction) do
74+
{:ok, txn} <- PSP.BalanceTransaction.retrieve(ch.balance_transaction) do
7575
get_provider_fee_from_balance_transaction(txn)
7676
else
7777
_ -> nil
@@ -153,7 +153,7 @@ defmodule Algora.Payments do
153153
end
154154

155155
@spec fetch_or_create_customer(user :: User.t()) ::
156-
{:ok, Customer.t()} | {:error, Ecto.Changeset.t()} | {:error, Stripe.Error.t()}
156+
{:ok, Customer.t()} | {:error, Ecto.Changeset.t()} | {:error, PSP.error()}
157157
def fetch_or_create_customer(user) do
158158
case fetch_customer_by(user_id: user.id) do
159159
{:ok, customer} -> {:ok, customer}
@@ -162,9 +162,9 @@ defmodule Algora.Payments do
162162
end
163163

164164
@spec create_customer(user :: User.t()) ::
165-
{:ok, Customer.t()} | {:error, Ecto.Changeset.t()} | {:error, Stripe.Error.t()}
165+
{:ok, Customer.t()} | {:error, Ecto.Changeset.t()} | {:error, PSP.error()}
166166
def create_customer(user) do
167-
with {:ok, stripe_customer} <- Stripe.Customer.create(%{name: user.name}) do
167+
with {:ok, stripe_customer} <- PSP.Customer.create(%{name: user.name}) do
168168
%Customer{}
169169
|> Customer.changeset(%{
170170
provider: "stripe",
@@ -177,7 +177,7 @@ defmodule Algora.Payments do
177177
end
178178
end
179179

180-
@spec create_payment_method(customer :: Customer.t(), payment_method :: Stripe.PaymentMethod.t()) ::
180+
@spec create_payment_method(customer :: Customer.t(), payment_method :: PSP.payment_method()) ::
181181
{:ok, PaymentMethod.t()} | {:error, Ecto.Changeset.t()}
182182
def create_payment_method(customer, payment_method) do
183183
%PaymentMethod{}
@@ -193,9 +193,9 @@ defmodule Algora.Payments do
193193
end
194194

195195
@spec create_stripe_setup_session(customer :: Customer.t(), success_url :: String.t(), cancel_url :: String.t()) ::
196-
{:ok, Stripe.Session.t()} | {:error, Stripe.Error.t()}
196+
{:ok, PSP.session()} | {:error, PSP.error()}
197197
def create_stripe_setup_session(customer, success_url, cancel_url) do
198-
Stripe.Session.create(%{
198+
PSP.Session.create(%{
199199
billing_address_collection: "required",
200200
mode: "setup",
201201
payment_method_types: ["card"],
@@ -223,7 +223,7 @@ defmodule Algora.Payments do
223223
@spec create_account(user :: User.t(), country :: String.t()) ::
224224
{:ok, Account.t()} | {:error, Ecto.Changeset.t()}
225225
def create_account(user, country) do
226-
type = ConnectCountries.account_type(country)
226+
type = PSP.ConnectCountries.account_type(country)
227227

228228
with {:ok, stripe_account} <- create_stripe_account(%{country: country, type: type}) do
229229
attrs = %{
@@ -242,18 +242,18 @@ defmodule Algora.Payments do
242242
end
243243

244244
@spec create_stripe_account(attrs :: map()) ::
245-
{:ok, Stripe.Account.t()} | {:error, Stripe.Error.t()}
245+
{:ok, PSP.account()} | {:error, PSP.error()}
246246
defp create_stripe_account(%{country: country, type: type}) do
247-
case Stripe.Account.create(%{country: country, type: to_string(type)}) do
247+
case PSP.Account.create(%{country: country, type: to_string(type)}) do
248248
{:ok, account} -> {:ok, account}
249-
{:error, _reason} -> Stripe.Account.create(%{type: to_string(type)})
249+
{:error, _reason} -> PSP.Account.create(%{type: to_string(type)})
250250
end
251251
end
252252

253253
@spec create_account_link(account :: Account.t(), base_url :: String.t()) ::
254-
{:ok, Stripe.AccountLink.t()} | {:error, Stripe.Error.t()}
254+
{:ok, PSP.account_link()} | {:error, PSP.error()}
255255
def create_account_link(account, base_url) do
256-
Stripe.AccountLink.create(%{
256+
PSP.AccountLink.create(%{
257257
account: account.provider_id,
258258
refresh_url: "#{base_url}/callbacks/stripe/refresh",
259259
return_url: "#{base_url}/callbacks/stripe/return",
@@ -262,12 +262,12 @@ defmodule Algora.Payments do
262262
end
263263

264264
@spec create_login_link(account :: Account.t()) ::
265-
{:ok, Stripe.LoginLink.t()} | {:error, Stripe.Error.t()}
265+
{:ok, PSP.login_link()} | {:error, PSP.error()}
266266
def create_login_link(account) do
267-
Stripe.LoginLink.create(account.provider_id, %{})
267+
PSP.LoginLink.create(account.provider_id)
268268
end
269269

270-
@spec update_account(account :: Account.t(), stripe_account :: Stripe.Account.t()) ::
270+
@spec update_account(account :: Account.t(), stripe_account :: PSP.account()) ::
271271
{:ok, Account.t()} | {:error, Ecto.Changeset.t()}
272272
def update_account(account, stripe_account) do
273273
account
@@ -288,10 +288,10 @@ defmodule Algora.Payments do
288288
end
289289

290290
@spec refresh_stripe_account(user :: User.t()) ::
291-
{:ok, Account.t()} | {:error, Ecto.Changeset.t()} | {:error, :not_found} | {:error, Stripe.Error.t()}
291+
{:ok, Account.t()} | {:error, Ecto.Changeset.t()} | {:error, :not_found} | {:error, PSP.error()}
292292
def refresh_stripe_account(user) do
293293
with {:ok, account} <- fetch_account(user),
294-
{:ok, stripe_account} <- Stripe.Account.retrieve(account.provider_id, []),
294+
{:ok, stripe_account} <- PSP.Account.retrieve(account.provider_id),
295295
{:ok, updated_account} <- update_account(account, stripe_account) do
296296
user = Accounts.get_user(account.user_id)
297297

@@ -303,25 +303,25 @@ defmodule Algora.Payments do
303303
end
304304
end
305305

306-
@spec get_service_agreement(account :: Stripe.Account.t()) :: String.t()
306+
@spec get_service_agreement(account :: PSP.account()) :: String.t()
307307
defp get_service_agreement(%{tos_acceptance: %{service_agreement: agreement}} = _account) when not is_nil(agreement) do
308308
agreement
309309
end
310310

311-
@spec get_service_agreement(account :: Stripe.Account.t()) :: String.t()
311+
@spec get_service_agreement(account :: PSP.account()) :: String.t()
312312
defp get_service_agreement(%{capabilities: capabilities}) do
313313
if is_nil(capabilities[:card_payments]), do: "recipient", else: "full"
314314
end
315315

316316
@spec delete_account(account :: Account.t()) :: {:ok, Account.t()} | {:error, Ecto.Changeset.t()}
317317
def delete_account(account) do
318-
with {:ok, _stripe_account} <- Stripe.Account.delete(account.provider_id) do
318+
with {:ok, _stripe_account} <- PSP.Account.delete(account.provider_id) do
319319
Repo.delete(account)
320320
end
321321
end
322322

323323
@spec execute_pending_transfer(credit_id :: String.t()) ::
324-
{:ok, Stripe.Transfer.t()} | {:error, :not_found} | {:error, :duplicate_transfer_attempt}
324+
{:ok, PSP.transfer()} | {:error, :not_found} | {:error, :duplicate_transfer_attempt}
325325
def execute_pending_transfer(credit_id) do
326326
with {:ok, credit} <- Repo.fetch_by(Transaction, id: credit_id, type: :credit, status: :succeeded) do
327327
transfers =
@@ -391,7 +391,7 @@ defmodule Algora.Payments do
391391
end
392392
end
393393

394-
@spec initialize_and_execute_transfer(credit :: Transaction.t()) :: {:ok, Stripe.Transfer.t()} | {:error, term()}
394+
@spec initialize_and_execute_transfer(credit :: Transaction.t()) :: {:ok, PSP.transfer()} | {:error, term()}
395395
defp initialize_and_execute_transfer(%Transaction{} = credit) do
396396
case fetch_active_account(credit.user_id) do
397397
{:ok, account} ->
@@ -452,7 +452,7 @@ defmodule Algora.Payments do
452452
|> Map.merge(if charge && charge.provider_id, do: %{source_transaction: charge.provider_id}, else: %{})
453453

454454
# TODO: provide idempotency key
455-
case Algora.Stripe.Transfer.create(transfer_params) do
455+
case PSP.Transfer.create(transfer_params) do
456456
{:ok, transfer} ->
457457
# it's fine if this fails since we'll receive a webhook
458458
transaction

lib/algora/payments/schemas/account.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ defmodule Algora.Payments.Account do
33
use Algora.Schema
44

55
alias Algora.Activities.Activity
6-
alias Algora.Stripe
76

87
@derive {Inspect, except: [:provider_meta]}
98
typed_schema "accounts" do
@@ -60,7 +59,7 @@ defmodule Algora.Payments.Account do
6059
:user_id
6160
])
6261
|> validate_inclusion(:type, [:standard, :express])
63-
|> validate_inclusion(:country, Stripe.ConnectCountries.list_codes())
62+
|> validate_inclusion(:country, Algora.PSP.ConnectCountries.list_codes())
6463
|> foreign_key_constraint(:user_id)
6564
|> generate_id()
6665
end

lib/algora/integrations/stripe/connect_countries.ex renamed to lib/algora/psp/connect_countries.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
defmodule Algora.Stripe.ConnectCountries do
1+
defmodule Algora.PSP.ConnectCountries do
22
@moduledoc false
33

44
@spec list() :: [{String.t(), String.t()}]

0 commit comments

Comments
 (0)