@@ -284,86 +284,123 @@ defmodule Algora.Bounties do
284
284
)
285
285
end
286
286
287
- @ spec create_payment_session (
288
- % { creator: User . t ( ) , amount: Money . t ( ) , description: String . t ( ) } ,
287
+ # TODO: move to separate module
288
+ defmodule LineItem do
289
+ @ moduledoc false
290
+ defstruct [ :amount , :title , :description , :image , :type ]
291
+
292
+ @ type t :: % __MODULE__ {
293
+ amount: Money . t ( ) ,
294
+ title: String . t ( ) ,
295
+ description: String . t ( ) | nil ,
296
+ image: String . t ( ) | nil ,
297
+ type: :payment | :fee
298
+ }
299
+
300
+ def to_stripe ( line_item ) do
301
+ % {
302
+ price_data: % {
303
+ unit_amount: MoneyUtils . to_minor_units ( line_item . amount ) ,
304
+ currency: to_string ( line_item . amount . currency ) ,
305
+ product_data: % {
306
+ name: line_item . title ,
307
+ description: line_item . description ,
308
+ images: if ( line_item . image , do: [ line_item . image ] )
309
+ }
310
+ } ,
311
+ quantity: 1
312
+ }
313
+ end
314
+
315
+ def gross_amount ( line_items ) do
316
+ Enum . reduce ( line_items , Money . zero ( :USD ) , fn item , acc -> Money . add! ( acc , item . amount ) end )
317
+ end
318
+
319
+ def total_fee ( line_items ) do
320
+ Enum . reduce ( line_items , Money . zero ( :USD ) , fn item , acc ->
321
+ if item . type == :fee , do: Money . add! ( acc , item . amount ) , else: acc
322
+ end )
323
+ end
324
+ end
325
+
326
+ @ spec generate_line_items (
327
+ % { amount: Money . t ( ) } ,
289
328
opts :: [
290
329
ticket_ref: % { owner: String . t ( ) , repo: String . t ( ) , number: integer ( ) } ,
291
- tip_id: String . t ( ) ,
292
- bounty_id: String . t ( ) ,
293
330
claims: [ Claim . t ( ) ] ,
294
331
recipient: User . t ( )
295
332
]
296
333
) ::
297
- { :ok , String . t ( ) } | { :error , atom ( ) }
298
- def create_payment_session ( % { creator: creator , amount: amount , description: description } , opts \\ [ ] ) do
334
+ [ LineItem . t ( ) ]
335
+ def generate_line_items ( % { amount: amount } , opts \\ [ ] ) do
299
336
ticket_ref = opts [ :ticket_ref ]
300
337
recipient = opts [ :recipient ]
301
338
claims = opts [ :claims ]
302
339
303
- tx_group_id = Nanoid . generate ( )
340
+ description = if ( ticket_ref , do: " #{ ticket_ref [ :owner ] } / #{ ticket_ref [ :repo ] } # #{ ticket_ref [ :number ] } " )
304
341
305
- # Calculate fees
306
- currency = to_string ( amount . currency )
307
342
platform_fee_pct = FeeTier . calculate_fee_percentage ( Money . zero ( :USD ) )
308
343
transaction_fee_pct = Payments . get_transaction_fee_pct ( )
309
344
310
- platform_fee = Money . mult! ( amount , platform_fee_pct )
311
- transaction_fee = Money . mult! ( amount , transaction_fee_pct )
312
- total_fee = Money . add! ( platform_fee , transaction_fee )
313
- gross_amount = Money . add! ( amount , total_fee )
345
+ if recipient do
346
+ [
347
+ % LineItem {
348
+ amount: amount ,
349
+ title: "Payment to @#{ recipient . provider_login } " ,
350
+ description: description ,
351
+ image: recipient . avatar_url ,
352
+ type: :payment
353
+ }
354
+ ]
355
+ else
356
+ [ ]
357
+ end ++
358
+ Enum . map ( claims , fn claim ->
359
+ % LineItem {
360
+ # TODO: ensure shares are normalized
361
+ amount: Money . mult! ( amount , claim . group_share ) ,
362
+ title: "Payment to @#{ claim . user . provider_login } " ,
363
+ description: description ,
364
+ image: claim . user . avatar_url ,
365
+ type: :payment
366
+ }
367
+ end ) ++
368
+ [
369
+ % LineItem {
370
+ amount: Money . mult! ( amount , platform_fee_pct ) ,
371
+ title: "Algora platform fee (#{ Util . format_pct ( platform_fee_pct ) } )" ,
372
+ type: :fee
373
+ } ,
374
+ % LineItem {
375
+ amount: Money . mult! ( amount , transaction_fee_pct ) ,
376
+ title: "Transaction fee (#{ Util . format_pct ( transaction_fee_pct ) } )" ,
377
+ type: :fee
378
+ }
379
+ ]
380
+ end
381
+
382
+ @ spec create_payment_session (
383
+ % { creator: User . t ( ) , amount: Money . t ( ) , description: String . t ( ) } ,
384
+ opts :: [
385
+ ticket_ref: % { owner: String . t ( ) , repo: String . t ( ) , number: integer ( ) } ,
386
+ tip_id: String . t ( ) ,
387
+ bounty_id: String . t ( ) ,
388
+ claims: [ Claim . t ( ) ] ,
389
+ recipient: User . t ( )
390
+ ]
391
+ ) ::
392
+ { :ok , String . t ( ) } | { :error , atom ( ) }
393
+ def create_payment_session ( % { creator: creator , amount: amount , description: description } , opts \\ [ ] ) do
394
+ tx_group_id = Nanoid . generate ( )
314
395
315
396
line_items =
316
- if recipient do
317
- [
318
- % {
319
- price_data: % {
320
- unit_amount: MoneyUtils . to_minor_units ( amount ) ,
321
- currency: currency ,
322
- product_data: % {
323
- name: "Payment to @#{ recipient . provider_login } " ,
324
- description: if ( ticket_ref , do: "#{ ticket_ref [ :owner ] } /#{ ticket_ref [ :repo ] } ##{ ticket_ref [ :number ] } " ) ,
325
- images: [ recipient . avatar_url ]
326
- }
327
- } ,
328
- quantity: 1
329
- }
330
- ]
331
- else
332
- [ ]
333
- end ++
334
- Enum . map ( claims , fn claim ->
335
- % {
336
- price_data: % {
337
- # TODO: ensure shares are normalized
338
- unit_amount: amount |> Money . mult! ( claim . group_share ) |> MoneyUtils . to_minor_units ( ) ,
339
- currency: currency ,
340
- product_data: % {
341
- name: "Payment to @#{ claim . user . provider_login } " ,
342
- description: if ( ticket_ref , do: "#{ ticket_ref [ :owner ] } /#{ ticket_ref [ :repo ] } ##{ ticket_ref [ :number ] } " ) ,
343
- images: [ claim . user . avatar_url ]
344
- }
345
- } ,
346
- quantity: 1
347
- }
348
- end ) ++
349
- [
350
- % {
351
- price_data: % {
352
- unit_amount: MoneyUtils . to_minor_units ( Money . mult! ( amount , platform_fee_pct ) ) ,
353
- currency: currency ,
354
- product_data: % { name: "Algora platform fee (#{ Util . format_pct ( platform_fee_pct ) } )" }
355
- } ,
356
- quantity: 1
357
- } ,
358
- % {
359
- price_data: % {
360
- unit_amount: MoneyUtils . to_minor_units ( Money . mult! ( amount , transaction_fee_pct ) ) ,
361
- currency: currency ,
362
- product_data: % { name: "Transaction fee (#{ Util . format_pct ( transaction_fee_pct ) } )" }
363
- } ,
364
- quantity: 1
365
- }
366
- ]
397
+ generate_line_items ( % { amount: amount } ,
398
+ ticket_ref: opts [ :ticket_ref ] ,
399
+ recipient: opts [ :recipient ] ,
400
+ claims: opts [ :claims ]
401
+ )
402
+
403
+ gross_amount = LineItem . gross_amount ( line_items )
367
404
368
405
Repo . transact ( fn ->
369
406
with { :ok , _charge } <-
@@ -375,7 +412,7 @@ defmodule Algora.Bounties do
375
412
user_id: creator . id ,
376
413
gross_amount: gross_amount ,
377
414
net_amount: amount ,
378
- total_fee: total_fee ,
415
+ total_fee: Money . sub! ( gross_amount , amount ) ,
379
416
line_items: line_items ,
380
417
group_id: tx_group_id
381
418
} ) ,
@@ -389,7 +426,7 @@ defmodule Algora.Bounties do
389
426
group_id: tx_group_id
390
427
} ) ,
391
428
{ :ok , session } <-
392
- Payments . create_stripe_session ( line_items , % {
429
+ Payments . create_stripe_session ( LineItem . to_stripe ( line_items ) , % {
393
430
description: description ,
394
431
metadata: % { "version" => "2" , "group_id" => tx_group_id }
395
432
} ) do
0 commit comments