1
1
defmodule Algora.Payments.Jobs.ExecutePendingTransfers do
2
2
@ moduledoc false
3
- use Oban.Worker , queue: :transfers
3
+ use Oban.Worker ,
4
+ queue: :transfers ,
5
+ max_attempts: 1
4
6
5
7
import Ecto.Changeset
6
8
import Ecto.Query
@@ -15,6 +17,17 @@ defmodule Algora.Payments.Jobs.ExecutePendingTransfers do
15
17
16
18
@ impl Oban.Worker
17
19
def perform ( % Oban.Job { args: % { user_id: user_id , group_id: group_id } } ) do
20
+ pending_amount = get_pending_amount ( user_id )
21
+
22
+ with { :ok , account } <- Repo . fetch_by ( Account , user_id: user_id , provider: :stripe , payouts_enabled: true ) ,
23
+ true <- Money . positive? ( pending_amount ) do
24
+ initialize_and_execute_transfer ( user_id , group_id , pending_amount , account )
25
+ else
26
+ _ -> :ok
27
+ end
28
+ end
29
+
30
+ defp get_pending_amount ( user_id ) do
18
31
total_credits =
19
32
Repo . one (
20
33
from ( t in Transaction ,
@@ -35,50 +48,63 @@ defmodule Algora.Payments.Jobs.ExecutePendingTransfers do
35
48
)
36
49
) || Money . zero ( :USD )
37
50
38
- pending_amount = Money . sub! ( total_credits , total_transfers )
51
+ Money . sub! ( total_credits , total_transfers )
52
+ end
39
53
40
- with { :ok , account } <- Repo . fetch_by ( Account , user_id: user_id , provider: :stripe , payouts_enabled: true ) ,
41
- true <- Money . positive? ( pending_amount ) do
42
- { :ok , transaction } =
43
- % Transaction { }
44
- |> change ( % {
45
- provider: "stripe" ,
46
- type: :credit ,
47
- status: :initialized ,
48
- user_id: user_id ,
49
- gross_amount: pending_amount ,
50
- net_amount: pending_amount ,
51
- total_fee: Money . zero ( :USD ) ,
52
- group_id: group_id
53
- } )
54
- |> Algora.Validations . validate_positive ( :gross_amount )
55
- |> Algora.Validations . validate_positive ( :net_amount )
56
- |> foreign_key_constraint ( :user_id )
57
- |> Repo . insert ( )
54
+ defp initialize_and_execute_transfer ( user_id , group_id , pending_amount , account ) do
55
+ with { :ok , transaction } <- initialize_transfer ( user_id , group_id , pending_amount ) ,
56
+ { :ok , transfer } <- execute_transfer ( transaction , account ) do
57
+ { :ok , transfer }
58
+ else
59
+ error ->
60
+ Logger . error ( "Failed to execute transfer: #{ inspect ( error ) } " )
61
+ error
62
+ end
63
+ end
58
64
59
- Repo . transact ( fn ->
60
- # TODO: set other params
61
- # TODO: provide idempotency key
62
- { :ok , transfer } =
63
- Stripe.Transfer . create ( % {
64
- amount: MoneyUtils . to_minor_units ( pending_amount ) ,
65
- currency: to_string ( pending_amount . currency ) ,
66
- destination: account . stripe_account_id
67
- } )
65
+ defp initialize_transfer ( user_id , group_id , pending_amount ) do
66
+ % Transaction { }
67
+ |> change ( % {
68
+ provider: "stripe" ,
69
+ type: :credit ,
70
+ status: :initialized ,
71
+ user_id: user_id ,
72
+ gross_amount: pending_amount ,
73
+ net_amount: pending_amount ,
74
+ total_fee: Money . zero ( :USD ) ,
75
+ group_id: group_id
76
+ } )
77
+ |> Algora.Validations . validate_positive ( :gross_amount )
78
+ |> Algora.Validations . validate_positive ( :net_amount )
79
+ |> foreign_key_constraint ( :user_id )
80
+ |> Repo . insert ( )
81
+ end
68
82
69
- { :ok , transaction } =
70
- transaction
71
- |> change ( % {
72
- status: if ( transfer . status == :succeeded , do: :succeeded , else: :failed ) ,
73
- provider_id: transfer . id ,
74
- provider_meta: Util . normalize_struct ( transfer )
75
- } )
76
- |> Repo . update ( )
83
+ defp execute_transfer ( transaction , account ) do
84
+ # TODO: set other params
85
+ # TODO: provide idempotency key
86
+ case Stripe.Transfer . create ( % {
87
+ amount: MoneyUtils . to_minor_units ( transaction . net_amount ) ,
88
+ currency: to_string ( transaction . net_amount . currency ) ,
89
+ destination: account . stripe_account_id
90
+ } ) do
91
+ { :ok , transfer } ->
92
+ # it's fine if this fails since we'll receive a webhook
93
+ _result = try_update_transaction ( transaction , transfer )
94
+ { :ok , transfer }
77
95
78
- { :ok , transaction }
79
- end )
80
- else
81
- _ -> :ok
96
+ { :error , error } ->
97
+ { :error , error }
82
98
end
83
99
end
100
+
101
+ defp try_update_transaction ( transaction , transfer ) do
102
+ transaction
103
+ |> change ( % {
104
+ status: if ( transfer . status == :succeeded , do: :succeeded , else: :failed ) ,
105
+ provider_id: transfer . id ,
106
+ provider_meta: Util . normalize_struct ( transfer )
107
+ } )
108
+ |> Repo . update ( )
109
+ end
84
110
end
0 commit comments