Coverage for functions \ flipdare \ payments \ core \ stripe_account_params.py: 99%

71 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-05-08 12:22 +1000

1#!/usr/bin/env python 

2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved. 

3# 

4# This file is part of Flipdare's proprietary software and contains 

5# modification, distribution, or use of this file is strictly 

6# prohibited without prior written permission from Flipdare Pty Ltd. 

7# 

8# This software includes third-party components licensed under MIT, 

9# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details. 

10# 

11from __future__ import annotations 

12from string import Template 

13from typing import Self 

14 

15from flipdare.app_globals import is_letters_present, truncate_string_exclude 

16from flipdare.constants import ( 

17 COMPANY_SUPPORT_URL, 

18 STRIPE_DEFAULT_MCC, 

19 STRIPE_DISPLAY_NAME_TMPL, 

20 STRIPE_DOING_BUSINESS_AS_TMPL, 

21 STRIPE_MAX_STATEMENT_LENGTH, 

22 STRIPE_PRODUCT_DESC_TMPL, 

23 STRIPE_STATEMENT_DESCRIPTOR, 

24 STRIPE_STATEMENT_EXCLUSION_CHARS, 

25 STRIPE_STATEMENT_PREFIX, 

26 STRIPE_SUPPORT_EMAIL, 

27) 

28from flipdare.core.app_backend_link import AppBackendLink 

29from flipdare.payments.core.stripe_invoice_prefix import StripeInvoicePrefix 

30from flipdare.payments.core.stripe_util import StripeUtil 

31 

32__all__ = ["StripeAccountParams"] 

33 

34 

35class StripeAccountParams: 

36 __slots__ = ( 

37 "email", 

38 "first_name", 

39 "last_name", 

40 "uid", 

41 ) 

42 

43 def __init__( 

44 self, 

45 *, 

46 _internal: bool, 

47 uid: str, 

48 email: str, 

49 first_name: str, 

50 last_name: str, 

51 ) -> None: 

52 if not _internal: 

53 raise ValueError("Use .from_name or .from_tokens to create StripeAccountParams") 

54 

55 self.uid = uid 

56 self.email = email 

57 self.first_name = first_name 

58 self.last_name = last_name 

59 

60 @classmethod 

61 def create( 

62 cls, 

63 uid: str, 

64 name: str, 

65 email: str, 

66 tokens: tuple[str, str] | None = None, 

67 ) -> Self: 

68 

69 return ( 

70 cls.from_tokens(uid=uid, email=email, tokens=tokens) 

71 if tokens 

72 else cls.from_name(uid=uid, name=name, email=email) 

73 ) 

74 

75 @classmethod 

76 def from_name(cls, uid: str, name: str, email: str) -> Self: 

77 first_name, last_name = StripeUtil.get_name_tokens(name=name, email=email) 

78 return cls( 

79 _internal=True, 

80 uid=uid, 

81 email=email, 

82 first_name=first_name, 

83 last_name=last_name, 

84 ) 

85 

86 @classmethod 

87 def from_tokens(cls, uid: str, email: str, tokens: tuple[str, str]) -> Self: 

88 first_name, last_name = tokens 

89 if not is_letters_present(first_name) or not is_letters_present(last_name): 

90 raise ValueError("Tokens must contain at least one letter each.") 

91 return cls( 

92 _internal=True, 

93 uid=uid, 

94 email=email, 

95 first_name=first_name, 

96 last_name=last_name, 

97 ) 

98 

99 @property 

100 def name(self) -> str: 

101 return f"{self.first_name} {self.last_name}" 

102 

103 @property 

104 def display_name(self) -> str: 

105 full = Template(STRIPE_DISPLAY_NAME_TMPL).substitute(NAME=self.name) 

106 # since we use template, we can ensure the display_name has letters 

107 # which is a requirement. 

108 return truncate_string_exclude( 

109 full, 

110 exclude=STRIPE_STATEMENT_EXCLUSION_CHARS, 

111 max_length=STRIPE_MAX_STATEMENT_LENGTH, 

112 ) 

113 

114 @property 

115 def invoice_prefix(self) -> str: 

116 return StripeInvoicePrefix.from_tokens( 

117 uid=self.uid, 

118 tokens=(self.first_name, self.last_name), 

119 ).prefix 

120 

121 @property 

122 def default_mcc(self) -> str: 

123 return STRIPE_DEFAULT_MCC 

124 

125 @property 

126 def support_email(self) -> str: 

127 # FIXME: this should include the uid so we can identify the account when a customer notifies. 

128 # i.e. the customer may think this is for the account itself, and may not include the account id/uid 

129 return STRIPE_SUPPORT_EMAIL 

130 

131 @property 

132 def support_url(self) -> str: 

133 return COMPANY_SUPPORT_URL 

134 

135 @property 

136 def business_url(self) -> str: 

137 return AppBackendLink.USER.link(doc_id=self.uid) 

138 

139 @property 

140 def statement_descriptor(self) -> str: 

141 # for merchant configuration 

142 # statement_descriptor.descriptor: 

143 # The full text for non-card payments 

144 # For card payments (if no prefix set), also used as the prefix 

145 # Requirements: 5-22 characters, at least one letter 

146 

147 # NOTE: RECOMMENDATIONS: 

148 # - Use marketplace brand (MYMARKETPLACE) - customers recognize your platform 

149 # - Don't use individual seller names - causes confusion and chargebacks 

150 # - Consistent across all sellers for brand recognition 

151 return STRIPE_STATEMENT_DESCRIPTOR 

152 

153 @property 

154 def statement_prefix(self) -> str: 

155 # Used only for card payments as a prefix 

156 # Combined with dynamic suffix: PREFIX* SUFFIX 

157 # Requirements: 2-10 characters, at least one letter 

158 return STRIPE_STATEMENT_PREFIX 

159 

160 @property 

161 def registered_name(self) -> str: 

162 # set to the individual name for tax purposes, but can be different from the display name. 

163 # the user can manually change if required. 

164 return self.name 

165 

166 @property 

167 def shipping_name(self) -> str: 

168 return self.name # this should match the registered name. 

169 

170 @property 

171 def doing_business_as(self) -> str: 

172 # set to the individual name for tax purposes, but can be different from the display name. 

173 # the user can manually change if required. 

174 return Template(STRIPE_DOING_BUSINESS_AS_TMPL).substitute(NAME=self.name) 

175 

176 @property 

177 def product_description(self) -> str: 

178 return Template(STRIPE_PRODUCT_DESC_TMPL).substitute(NAME=self.name)