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
« 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
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
32__all__ = ["StripeAccountParams"]
35class StripeAccountParams:
36 __slots__ = (
37 "email",
38 "first_name",
39 "last_name",
40 "uid",
41 )
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")
55 self.uid = uid
56 self.email = email
57 self.first_name = first_name
58 self.last_name = last_name
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:
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 )
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 )
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 )
99 @property
100 def name(self) -> str:
101 return f"{self.first_name} {self.last_name}"
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 )
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
121 @property
122 def default_mcc(self) -> str:
123 return STRIPE_DEFAULT_MCC
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
131 @property
132 def support_url(self) -> str:
133 return COMPANY_SUPPORT_URL
135 @property
136 def business_url(self) -> str:
137 return AppBackendLink.USER.link(doc_id=self.uid)
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
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
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
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
166 @property
167 def shipping_name(self) -> str:
168 return self.name # this should match the registered name.
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)
176 @property
177 def product_description(self) -> str:
178 return Template(STRIPE_PRODUCT_DESC_TMPL).substitute(NAME=self.name)