@@ -8,8 +8,11 @@ defmodule AlgoraWeb.Webhooks.GithubController do
88 alias Algora.Bounties
99 alias Algora.Bounties.Bounty
1010 alias Algora.Bounties.Claim
11+ alias Algora.Bounties.Tip
1112 alias Algora.Github
1213 alias Algora.Github.Webhook
14+ alias Algora.Payments.Customer
15+ alias Algora.PSP.Invoice
1316 alias Algora.Repo
1417 alias Algora.Workspace
1518 alias Algora.Workspace.CommandResponse
@@ -110,11 +113,7 @@ defmodule AlgoraWeb.Webhooks.GithubController do
110113 primary_claim = List . first ( claims )
111114
112115 installation =
113- Repo . one (
114- from i in Installation ,
115- where: i . provider == "github" ,
116- where: i . provider_id == ^ to_string ( payload [ "installation" ] [ "id" ] )
117- )
116+ Repo . get_by ( Installation , provider: "github" , provider_id: to_string ( payload [ "installation" ] [ "id" ] ) )
118117
119118 bounties =
120119 Repo . all (
@@ -158,7 +157,7 @@ defmodule AlgoraWeb.Webhooks.GithubController do
158157 claims: claims
159158 ) ,
160159 { :ok , _invoice } <-
161- Algora.PSP. Invoice. pay (
160+ Invoice . pay (
162161 invoice ,
163162 % {
164163 payment_method: autopayable_bounty . owner . customer . default_payment_method . provider_id ,
@@ -299,22 +298,114 @@ defmodule AlgoraWeb.Webhooks.GithubController do
299298 defp execute_command ( % Webhook { event_action: event_action , payload: payload } = webhook , { :tip , args } )
300299 when event_action in [ "issue_comment.created" , "issue_comment.edited" ] do
301300 amount = args [ :amount ]
302- # TODO: handle autopay with cooldown
301+
302+ ticket_ref = % {
303+ owner: payload [ "repository" ] [ "owner" ] [ "login" ] ,
304+ repo: payload [ "repository" ] [ "name" ] ,
305+ number: payload [ "issue" ] [ "number" ]
306+ }
307+
303308 case get_permissions ( webhook ) do
304309 { :ok , "admin" } ->
305- with { :ok , recipient } <- get_tip_recipient ( webhook , { :tip , args } ) do
306- Bounties . create_tip_intent (
307- % {
308- recipient: recipient ,
309- amount: amount ,
310- ticket_ref: % {
311- owner: payload [ "repository" ] [ "owner" ] [ "login" ] ,
312- repo: payload [ "repository" ] [ "name" ] ,
313- number: payload [ "issue" ] [ "number" ]
314- }
315- } ,
316- installation_id: payload [ "installation" ] [ "id" ]
310+ installation =
311+ Repo . get_by ( Installation , provider: "github" , provider_id: to_string ( payload [ "installation" ] [ "id" ] ) )
312+
313+ customer =
314+ Repo . one (
315+ from c in Customer ,
316+ left_join: p in assoc ( c , :default_payment_method ) ,
317+ where: c . user_id == ^ installation . connected_user_id ,
318+ select_merge: % { default_payment_method: p }
317319 )
320+
321+ { :ok , recipient } = get_tip_recipient ( webhook , { :tip , args } )
322+
323+ { :ok , token } = Github . get_installation_token ( payload [ "installation" ] [ "id" ] )
324+
325+ { :ok , ticket } = Workspace . ensure_ticket ( token , ticket_ref . owner , ticket_ref . repo , ticket_ref . number )
326+
327+ autopay_cooldown_expired? = fn ->
328+ from ( t in Tip ,
329+ join: recipient in assoc ( t , :recipient ) ,
330+ where: recipient . provider_login == ^ recipient ,
331+ where: t . ticket_id == ^ ticket . id ,
332+ where: t . status != :cancelled ,
333+ order_by: [ desc: t . inserted_at ] ,
334+ limit: 1
335+ )
336+ |> Repo . one ( )
337+ |> case do
338+ nil -> true
339+ tip -> DateTime . diff ( DateTime . utc_now ( ) , tip . inserted_at , :millisecond ) > :timer . hours ( 1 )
340+ end
341+ end
342+
343+ autopayable? =
344+ not is_nil ( installation ) and
345+ not is_nil ( customer ) and
346+ not is_nil ( customer . default_payment_method ) and
347+ not is_nil ( recipient ) and
348+ not is_nil ( amount ) and
349+ autopay_cooldown_expired? . ( )
350+
351+ idempotency_key = "tip-#{ recipient } -#{ webhook . delivery } "
352+
353+ autopay_result =
354+ if autopayable? do
355+ with { :ok , owner } <- Accounts . fetch_user_by ( id: installation . connected_user_id ) ,
356+ { :ok , creator } <- Workspace . ensure_user ( token , payload [ "repository" ] [ "owner" ] [ "login" ] ) ,
357+ { :ok , recipient } <- Workspace . ensure_user ( token , recipient ) ,
358+ { :ok , tip } <-
359+ Bounties . do_create_tip (
360+ % { creator: creator , owner: owner , recipient: recipient , amount: amount } ,
361+ ticket_ref: ticket_ref ,
362+ installation_id: payload [ "installation" ] [ "id" ]
363+ ) ,
364+ { :ok , invoice } <-
365+ Bounties . create_invoice (
366+ % {
367+ owner: owner ,
368+ amount: amount ,
369+ idempotency_key: idempotency_key
370+ } ,
371+ ticket_ref: ticket_ref ,
372+ tip_id: tip . id ,
373+ recipient: recipient
374+ ) ,
375+ { :ok , _invoice } <-
376+ Invoice . pay (
377+ invoice ,
378+ % {
379+ payment_method: customer . default_payment_method . provider_id ,
380+ off_session: true
381+ } ,
382+ % { idempotency_key: idempotency_key }
383+ ) do
384+ Logger . info ( "Autopay successful (#{ payload [ "repository" ] [ "full_name" ] } ##{ ticket_ref . number } - #{ amount } )." )
385+ { :ok , tip }
386+ else
387+ { :error , reason } ->
388+ Logger . error (
389+ "Autopay failed (#{ payload [ "repository" ] [ "full_name" ] } ##{ ticket_ref . number } - #{ amount } ): #{ inspect ( reason ) } "
390+ )
391+
392+ { :error , reason }
393+ end
394+ end
395+
396+ case autopay_result do
397+ { :ok , tip } ->
398+ { :ok , tip }
399+
400+ _ ->
401+ Bounties . create_tip_intent (
402+ % {
403+ recipient: recipient ,
404+ amount: amount ,
405+ ticket_ref: ticket_ref
406+ } ,
407+ installation_id: payload [ "installation" ] [ "id" ]
408+ )
318409 end
319410
320411 { :ok , _permission } ->
0 commit comments