Coverage for polar/payout/tasks.py: 40%

53 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 16:17 +0000

1import uuid 1a

2 

3import sentry_sdk 1a

4import stripe as stripe_lib 1a

5import structlog 1a

6 

7from polar.enums import AccountType 1a

8from polar.exceptions import PolarTaskError 1a

9from polar.logging import Logger 1a

10from polar.worker import AsyncSessionMaker, CronTrigger, TaskPriority, actor 1a

11 

12from .repository import PayoutRepository 1a

13from .service import PayoutAlreadyTriggered 1a

14from .service import payout as payout_service 1a

15 

16log: Logger = structlog.get_logger() 1a

17 

18 

19class PayoutTaskError(PolarTaskError): ... 1a

20 

21 

22class PayoutDoesNotExist(PayoutTaskError): 1a

23 def __init__(self, payout_id: uuid.UUID) -> None: 1a

24 self.payout_id = payout_id 

25 message = f"The payout with id {payout_id} does not exist." 

26 super().__init__(message) 

27 

28 

29@actor(actor_name="payout.created", priority=TaskPriority.LOW) 1a

30async def payout_created(payout_id: uuid.UUID) -> None: 1a

31 async with AsyncSessionMaker() as session: 

32 repository = PayoutRepository(session) 

33 payout = await repository.get_by_id( 

34 payout_id, options=repository.get_eager_options() 

35 ) 

36 if payout is None: 

37 raise PayoutDoesNotExist(payout_id) 

38 

39 if payout.processor == AccountType.stripe: 

40 await payout_service.transfer_stripe(session, payout) 

41 

42 

43@actor( 1a

44 actor_name="payout.trigger_stripe_payouts", 

45 cron_trigger=CronTrigger(minute=15), 

46 priority=TaskPriority.LOW, 

47) 

48async def trigger_stripe_payouts() -> None: 1a

49 async with AsyncSessionMaker() as session: 

50 await payout_service.trigger_stripe_payouts(session) 

51 

52 

53@actor(actor_name="payout.trigger_stripe_payout", priority=TaskPriority.LOW) 1a

54async def trigger_payout(payout_id: uuid.UUID) -> None: 1a

55 async with AsyncSessionMaker() as session: 

56 repository = PayoutRepository(session) 

57 payout = await repository.get_by_id( 

58 payout_id, options=repository.get_eager_options() 

59 ) 

60 if payout is None: 

61 raise PayoutDoesNotExist(payout_id) 

62 

63 try: 

64 await payout_service.trigger_stripe_payout(session, payout) 

65 except PayoutAlreadyTriggered: 

66 # Swallow it, since it's likely a task that's being retried 

67 # while the payout has already been triggered. 

68 pass 

69 except stripe_lib.InvalidRequestError as e: 

70 # Capture exception in Sentry for debugging purposes 

71 sentry_sdk.capture_exception( 

72 e, 

73 extras={"payout_id": str(payout_id)}, 

74 ) 

75 # Do not raise an error here: we know it happens often, because Stripe 

76 # has many hidden rules on payout creation that we cannot control. 

77 pass 

78 

79 

80@actor(actor_name="payout.invoice", priority=TaskPriority.LOW) 1a

81async def order_invoice(payout_id: uuid.UUID) -> None: 1a

82 async with AsyncSessionMaker() as session: 

83 repository = PayoutRepository(session) 

84 payout = await repository.get_by_id( 

85 payout_id, options=repository.get_eager_options() 

86 ) 

87 if payout is None: 

88 raise PayoutDoesNotExist(payout_id) 

89 

90 await payout_service.generate_invoice(session, payout)