Skip to content

Commit 89e89d6

Browse files
committed
implement authorization flow
1 parent a630041 commit 89e89d6

File tree

3 files changed

+76
-16
lines changed

3 files changed

+76
-16
lines changed

lib/algora/bounties/bounties.ex

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,27 @@ defmodule Algora.Bounties do
788788
)
789789
end
790790

791+
@spec authorize_payment(
792+
%{
793+
owner: User.t(),
794+
amount: Money.t(),
795+
bounty: Bounty.t(),
796+
claims: [Claim.t()]
797+
},
798+
opts :: [ticket_ref: %{owner: String.t(), repo: String.t(), number: integer()}, recipient: User.t()]
799+
) ::
800+
{:ok, String.t()} | {:error, atom()}
801+
def authorize_payment(%{owner: owner, amount: amount, bounty: bounty, claims: claims}, opts \\ []) do
802+
create_payment_session(
803+
%{owner: owner, amount: amount, description: "Bounty payment for OSS contributions"},
804+
ticket_ref: opts[:ticket_ref],
805+
bounty: bounty,
806+
claims: claims,
807+
recipient: opts[:recipient],
808+
capture_method: :manual
809+
)
810+
end
811+
791812
@spec generate_line_items(
792813
%{owner: User.t(), amount: Money.t()},
793814
opts :: [
@@ -861,7 +882,8 @@ defmodule Algora.Bounties do
861882
tip_id: String.t(),
862883
bounty: Bounty.t(),
863884
claims: [Claim.t()],
864-
recipient: User.t()
885+
recipient: User.t(),
886+
capture_method: :automatic | :automatic_async | :manual
865887
]
866888
) ::
867889
{:ok, String.t()} | {:error, atom()}
@@ -876,6 +898,18 @@ defmodule Algora.Bounties do
876898
bounty: opts[:bounty]
877899
)
878900

901+
payment_intent_data = %{
902+
description: description,
903+
metadata: %{"version" => Payments.metadata_version(), "group_id" => tx_group_id}
904+
}
905+
906+
payment_intent_data =
907+
if capture_method = opts[:capture_method] do
908+
Map.put(payment_intent_data, :capture_method, capture_method)
909+
else
910+
payment_intent_data
911+
end
912+
879913
gross_amount = LineItem.gross_amount(line_items)
880914

881915
bounty_id = if bounty = opts[:bounty], do: bounty.id
@@ -905,10 +939,7 @@ defmodule Algora.Bounties do
905939
group_id: tx_group_id
906940
}),
907941
{:ok, session} <-
908-
Payments.create_stripe_session(owner, Enum.map(line_items, &LineItem.to_stripe/1), %{
909-
description: description,
910-
metadata: %{"version" => Payments.metadata_version(), "group_id" => tx_group_id}
911-
}) do
942+
Payments.create_stripe_session(owner, Enum.map(line_items, &LineItem.to_stripe/1), payment_intent_data) do
912943
{:ok, session.url}
913944
end
914945
end)

lib/algora/payments/payments.ex

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,24 @@ defmodule Algora.Payments do
3636
{:ok, PSP.session()} | {:error, PSP.error()}
3737
def create_stripe_session(user, line_items, payment_intent_data) do
3838
with {:ok, customer} <- fetch_or_create_customer(user) do
39-
PSP.Session.create(%{
39+
opts = %{
4040
mode: "payment",
4141
customer: customer.provider_id,
4242
billing_address_collection: "required",
4343
line_items: line_items,
44-
invoice_creation: %{enabled: true},
4544
success_url: "#{AlgoraWeb.Endpoint.url()}/payment/success",
4645
cancel_url: "#{AlgoraWeb.Endpoint.url()}/payment/canceled",
4746
payment_intent_data: payment_intent_data
48-
})
47+
}
48+
49+
opts =
50+
if payment_intent_data[:capture_method] == :manual do
51+
opts
52+
else
53+
Map.put(opts, :invoice_creation, %{enabled: true})
54+
end
55+
56+
PSP.Session.create(opts)
4957
end
5058
end
5159

lib/algora_web/live/contract_live.ex

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ defmodule AlgoraWeb.ContractLive do
1818

1919
require Logger
2020

21-
defp tip_options, do: [{"None", 0}, {"10%", 10}, {"20%", 20}, {"50%", 50}]
21+
# defp tip_options, do: [{"None", 0}, {"10%", 10}, {"20%", 20}, {"50%", 50}]
2222

2323
defmodule RewardBountyForm do
2424
@moduledoc false
@@ -109,7 +109,7 @@ defmodule AlgoraWeb.ContractLive do
109109
ticket_body_html = Algora.Markdown.render(bounty.ticket.description)
110110

111111
reward_changeset =
112-
RewardBountyForm.changeset(%RewardBountyForm{}, %{tip_percentage: 0})
112+
RewardBountyForm.changeset(%RewardBountyForm{}, %{amount: bounty.amount})
113113

114114
{:ok, thread} = Chat.get_or_create_bounty_thread(bounty)
115115
messages = thread.id |> Chat.list_messages() |> Repo.preload(:sender)
@@ -233,6 +233,18 @@ defmodule AlgoraWeb.ContractLive do
233233
end
234234
end
235235

236+
@impl true
237+
def handle_event("authorize_with_stripe", _params, socket) do
238+
case authorize_payment(socket, socket.assigns.bounty) do
239+
{:ok, session_url} ->
240+
{:noreply, redirect(socket, external: session_url)}
241+
242+
{:error, reason} ->
243+
Logger.error("Failed to create payment session: #{inspect(reason)}")
244+
{:noreply, put_flash(socket, :error, "Something went wrong")}
245+
end
246+
end
247+
236248
@impl true
237249
def handle_event(_event, _params, socket) do
238250
{:noreply, socket}
@@ -508,13 +520,13 @@ defmodule AlgoraWeb.ContractLive do
508520
509521
<.drawer :if={@current_user} show={@show_reward_modal} on_cancel="close_drawer">
510522
<.drawer_header>
511-
<.drawer_title>Pay Contract</.drawer_title>
523+
<.drawer_title>Authorize payment</.drawer_title>
512524
<.drawer_description>
513-
You can pay any amount at any time.
525+
You will be charged once {@contractor.name} accepts the contract.
514526
</.drawer_description>
515527
</.drawer_header>
516528
<.drawer_content class="mt-4">
517-
<.form for={@reward_form} phx-change="validate_reward" phx-submit="pay_with_stripe">
529+
<.form for={@reward_form} phx-change="validate_reward" phx-submit="authorize_with_stripe">
518530
<div class="flex flex-col gap-8">
519531
<div class="grid grid-cols-2 gap-8">
520532
<.card>
@@ -527,9 +539,10 @@ defmodule AlgoraWeb.ContractLive do
527539
label="Amount"
528540
icon="tabler-currency-dollar"
529541
field={@reward_form[:amount]}
542+
disabled
530543
/>
531544
532-
<div>
545+
<%!-- <div>
533546
<.label>Tip</.label>
534547
<div class="mt-2">
535548
<.radio_group
@@ -538,7 +551,7 @@ defmodule AlgoraWeb.ContractLive do
538551
options={tip_options()}
539552
/>
540553
</div>
541-
</div>
554+
</div> --%>
542555
</div>
543556
</.card_content>
544557
</.card>
@@ -590,7 +603,7 @@ defmodule AlgoraWeb.ContractLive do
590603
Cancel
591604
</.button>
592605
<.button type="submit">
593-
Pay with Stripe <.icon name="tabler-arrow-right" class="-mr-1 ml-2 h-4 w-4" />
606+
Authorize with Stripe <.icon name="tabler-arrow-right" class="-mr-1 ml-2 h-4 w-4" />
594607
</.button>
595608
</div>
596609
</div>
@@ -625,6 +638,14 @@ defmodule AlgoraWeb.ContractLive do
625638
)
626639
end
627640

641+
defp authorize_payment(socket, bounty) do
642+
Bounties.authorize_payment(
643+
%{owner: bounty.owner, amount: bounty.amount, bounty: bounty, claims: []},
644+
ticket_ref: socket.assigns.ticket_ref,
645+
recipient: socket.assigns.contractor
646+
)
647+
end
648+
628649
defp calculate_final_amount(data_or_changeset) do
629650
tip_percentage = get_field(data_or_changeset, :tip_percentage) || Decimal.new(0)
630651
amount = get_field(data_or_changeset, :amount) || Money.zero(:USD, no_fraction_if_integer: true)

0 commit comments

Comments
 (0)