django-cfg 1.2.31__py3-none-any.whl → 1.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -10
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +526 -222
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +465 -70
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  39. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  40. django_cfg/apps/payments/middleware/__init__.py +3 -1
  41. django_cfg/apps/payments/middleware/api_access.py +329 -222
  42. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  43. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +13 -18
  46. django_cfg/apps/payments/models/api_keys.py +121 -43
  47. django_cfg/apps/payments/models/balance.py +150 -115
  48. django_cfg/apps/payments/models/base.py +68 -15
  49. django_cfg/apps/payments/models/currencies.py +172 -148
  50. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  51. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  52. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  53. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  54. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  55. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  56. django_cfg/apps/payments/models/payments.py +235 -285
  57. django_cfg/apps/payments/models/subscriptions.py +257 -177
  58. django_cfg/apps/payments/models/tariffs.py +147 -40
  59. django_cfg/apps/payments/services/__init__.py +209 -56
  60. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  61. django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
  62. django_cfg/apps/payments/services/core/__init__.py +10 -6
  63. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  64. django_cfg/apps/payments/services/core/base.py +166 -0
  65. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  66. django_cfg/apps/payments/services/core/payment_service.py +346 -467
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  68. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  69. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  70. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  71. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  72. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  73. django_cfg/apps/payments/services/providers/base.py +234 -174
  74. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  75. django_cfg/apps/payments/services/providers/registry.py +367 -301
  76. django_cfg/apps/payments/services/types/__init__.py +78 -0
  77. django_cfg/apps/payments/services/types/data.py +177 -0
  78. django_cfg/apps/payments/services/types/requests.py +150 -0
  79. django_cfg/apps/payments/services/types/responses.py +156 -0
  80. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  81. django_cfg/apps/payments/signals/__init__.py +33 -8
  82. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  85. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  86. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  87. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  88. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  89. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  90. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  91. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  92. django_cfg/apps/payments/urls.py +45 -48
  93. django_cfg/apps/payments/urls_admin.py +33 -42
  94. django_cfg/apps/payments/views/api/__init__.py +101 -0
  95. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  96. django_cfg/apps/payments/views/api/balances.py +381 -0
  97. django_cfg/apps/payments/views/api/base.py +298 -0
  98. django_cfg/apps/payments/views/api/currencies.py +402 -0
  99. django_cfg/apps/payments/views/api/payments.py +415 -0
  100. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  101. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  102. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  103. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  104. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  105. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  106. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  107. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  108. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  109. django_cfg/config.py +1 -1
  110. django_cfg/core/config.py +40 -4
  111. django_cfg/core/generation.py +25 -4
  112. django_cfg/core/integration/README.md +363 -0
  113. django_cfg/core/integration/__init__.py +47 -0
  114. django_cfg/core/integration/commands_collector.py +239 -0
  115. django_cfg/core/integration/display/__init__.py +15 -0
  116. django_cfg/core/integration/display/base.py +157 -0
  117. django_cfg/core/integration/display/ngrok.py +164 -0
  118. django_cfg/core/integration/display/startup.py +815 -0
  119. django_cfg/core/integration/url_integration.py +123 -0
  120. django_cfg/core/integration/version_checker.py +160 -0
  121. django_cfg/management/commands/auto_generate.py +4 -0
  122. django_cfg/management/commands/check_settings.py +6 -0
  123. django_cfg/management/commands/clear_constance.py +5 -2
  124. django_cfg/management/commands/create_token.py +6 -0
  125. django_cfg/management/commands/list_urls.py +6 -0
  126. django_cfg/management/commands/migrate_all.py +6 -0
  127. django_cfg/management/commands/migrator.py +3 -0
  128. django_cfg/management/commands/rundramatiq.py +6 -0
  129. django_cfg/management/commands/runserver_ngrok.py +51 -29
  130. django_cfg/management/commands/script.py +6 -0
  131. django_cfg/management/commands/show_config.py +12 -2
  132. django_cfg/management/commands/show_urls.py +4 -0
  133. django_cfg/management/commands/superuser.py +6 -0
  134. django_cfg/management/commands/task_clear.py +4 -1
  135. django_cfg/management/commands/task_status.py +3 -1
  136. django_cfg/management/commands/test_email.py +3 -0
  137. django_cfg/management/commands/test_telegram.py +6 -0
  138. django_cfg/management/commands/test_twilio.py +6 -0
  139. django_cfg/management/commands/tree.py +6 -0
  140. django_cfg/management/commands/validate_config.py +155 -149
  141. django_cfg/models/constance.py +31 -11
  142. django_cfg/models/payments.py +175 -492
  143. django_cfg/modules/django_logger.py +160 -146
  144. django_cfg/modules/django_unfold/dashboard.py +64 -16
  145. django_cfg/registry/core.py +1 -0
  146. django_cfg/template_archive/django_sample.zip +0 -0
  147. django_cfg/utils/smart_defaults.py +222 -571
  148. django_cfg/utils/toolkit.py +51 -11
  149. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
  150. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
  151. django_cfg/apps/payments/__init__.py +0 -8
  152. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  153. django_cfg/apps/payments/config/module.py +0 -70
  154. django_cfg/apps/payments/config/providers.py +0 -105
  155. django_cfg/apps/payments/config/settings.py +0 -96
  156. django_cfg/apps/payments/config/utils.py +0 -52
  157. django_cfg/apps/payments/decorators.py +0 -291
  158. django_cfg/apps/payments/management/commands/README.md +0 -146
  159. django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
  160. django_cfg/apps/payments/managers/__init__.py +0 -23
  161. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  162. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  163. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  164. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  165. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  166. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  167. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  168. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  169. django_cfg/apps/payments/models/events.py +0 -73
  170. django_cfg/apps/payments/serializers/__init__.py +0 -57
  171. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  172. django_cfg/apps/payments/serializers/balance.py +0 -59
  173. django_cfg/apps/payments/serializers/currencies.py +0 -63
  174. django_cfg/apps/payments/serializers/payments.py +0 -62
  175. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  176. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  177. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  178. django_cfg/apps/payments/services/cache/base.py +0 -30
  179. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  180. django_cfg/apps/payments/services/internal_types.py +0 -461
  181. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  182. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  183. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  184. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  185. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  186. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  187. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  188. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  189. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  190. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  191. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  192. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  193. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  194. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  195. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  196. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  197. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  198. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  199. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  200. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  201. django_cfg/apps/payments/services/security/__init__.py +0 -34
  202. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  203. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  204. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  205. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  206. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  207. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  208. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  209. django_cfg/apps/payments/tasks/__init__.py +0 -12
  210. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  211. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  212. django_cfg/apps/payments/templates/payments/base.html +0 -182
  213. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  214. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  215. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  216. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  217. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  218. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  219. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  220. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  221. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  222. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  223. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  224. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  225. django_cfg/apps/payments/templates/payments/test.html +0 -213
  226. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  227. django_cfg/apps/payments/utils/__init__.py +0 -43
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -239
  230. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  231. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  232. django_cfg/apps/payments/views/__init__.py +0 -63
  233. django_cfg/apps/payments/views/api_key_views.py +0 -164
  234. django_cfg/apps/payments/views/balance_views.py +0 -75
  235. django_cfg/apps/payments/views/currency_views.py +0 -122
  236. django_cfg/apps/payments/views/payment_views.py +0 -149
  237. django_cfg/apps/payments/views/subscription_views.py +0 -135
  238. django_cfg/apps/payments/views/tariff_views.py +0 -131
  239. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  240. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  241. django_cfg/apps/payments/views/templates/base.py +0 -212
  242. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  243. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  244. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -244
  247. django_cfg/apps/payments/views/templates/utils.py +0 -181
  248. django_cfg/apps/payments/views/webhook_views.py +0 -266
  249. django_cfg/apps/payments/viewsets.py +0 -66
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/template_archive/.gitignore +0 -1
  252. django_cfg/template_archive/__init__.py +0 -0
  253. django_cfg/urls.py +0 -33
  254. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  255. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  256. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,30 +1,83 @@
1
1
  """
2
- Base model classes for the universal payments system.
2
+ Base models for the Universal Payment System v2.0.
3
+
4
+ Provides common functionality for all payment-related models.
3
5
  """
4
6
 
5
7
  import uuid
6
8
  from django.db import models
7
-
8
-
9
- class TimestampedModel(models.Model):
10
- """Base model with automatic timestamps."""
11
- created_at = models.DateTimeField(auto_now_add=True, db_index=True)
12
- updated_at = models.DateTimeField(auto_now=True)
13
-
14
- class Meta:
15
- abstract = True
9
+ from django.utils import timezone
16
10
 
17
11
 
18
12
  class UUIDTimestampedModel(models.Model):
19
- """Base model with UUID primary key and automatic timestamps."""
13
+ """
14
+ Abstract base model with UUID primary key and timestamps.
15
+
16
+ Provides:
17
+ - UUID primary key for security and scalability
18
+ - Created/updated timestamps with timezone awareness
19
+ - Common functionality for all payment models
20
+ """
21
+
20
22
  id = models.UUIDField(
21
23
  primary_key=True,
22
24
  default=uuid.uuid4,
23
25
  editable=False,
24
- help_text="Unique identifier"
26
+ help_text="Unique identifier for this record"
27
+ )
28
+
29
+ created_at = models.DateTimeField(
30
+ auto_now_add=True,
31
+ db_index=True,
32
+ help_text="When this record was created"
33
+ )
34
+
35
+ updated_at = models.DateTimeField(
36
+ auto_now=True,
37
+ help_text="When this record was last updated"
25
38
  )
26
- created_at = models.DateTimeField(auto_now_add=True, db_index=True)
27
- updated_at = models.DateTimeField(auto_now=True)
28
39
 
29
40
  class Meta:
30
- abstract = True
41
+ abstract = True
42
+ ordering = ['-created_at']
43
+
44
+ def __str__(self):
45
+ """String representation showing ID and creation time."""
46
+ return f"{self.__class__.__name__}({str(self.id)[:8]}...)"
47
+
48
+ def __repr__(self):
49
+ """Developer-friendly representation."""
50
+ return f"<{self.__class__.__name__}: {str(self.id)}>"
51
+
52
+ @property
53
+ def age_in_seconds(self) -> int:
54
+ """Get age of this record in seconds."""
55
+ return int((timezone.now() - self.created_at).total_seconds())
56
+
57
+ @property
58
+ def is_recent(self) -> bool:
59
+ """Check if record was created in the last hour."""
60
+ return self.age_in_seconds < 3600
61
+
62
+
63
+ class TimestampedModel(models.Model):
64
+ """
65
+ Abstract base model with auto-incrementing ID and timestamps.
66
+
67
+ Use this for models that don't need UUID (like configuration models).
68
+ """
69
+
70
+ created_at = models.DateTimeField(
71
+ auto_now_add=True,
72
+ db_index=True,
73
+ help_text="When this record was created"
74
+ )
75
+
76
+ updated_at = models.DateTimeField(
77
+ auto_now=True,
78
+ help_text="When this record was last updated"
79
+ )
80
+
81
+ class Meta:
82
+ abstract = True
83
+ ordering = ['-created_at']
@@ -1,254 +1,278 @@
1
1
  """
2
- Currency models for the universal payments system - KISS version.
2
+ Currency models for the Universal Payment System v2.0.
3
+
4
+ Handles multi-provider currency support with integration to django_currency module.
3
5
  """
4
6
 
5
7
  from django.db import models
8
+ from django.core.validators import MinLengthValidator, MaxLengthValidator
9
+ from django.core.exceptions import ValidationError
6
10
  from .base import TimestampedModel
7
11
 
8
- # Currency converter import moved inside methods to avoid circular imports
9
12
 
10
13
  class Currency(TimestampedModel):
11
- """Base currencies - clean, no provider-specific codes."""
14
+ """
15
+ Universal currency model supporting both fiat and crypto.
16
+
17
+ Integrates with django_currency module for rate conversion.
18
+ """
12
19
 
13
20
  class CurrencyType(models.TextChoices):
14
21
  FIAT = "fiat", "Fiat Currency"
15
22
  CRYPTO = "crypto", "Cryptocurrency"
16
23
 
17
- # Core fields - only essentials
18
24
  code = models.CharField(
19
25
  max_length=10,
20
26
  unique=True,
21
- help_text="Clean currency code: BTC, USDT, ETH, USD (NO network suffixes)"
27
+ validators=[MinLengthValidator(3), MaxLengthValidator(10)],
28
+ help_text="Currency code (e.g., BTC, USD, ETH)"
22
29
  )
30
+
23
31
  name = models.CharField(
24
32
  max_length=100,
25
- help_text="Currency name: Bitcoin, Tether USD, Ethereum"
33
+ help_text="Full currency name (e.g., Bitcoin, US Dollar)"
26
34
  )
35
+
27
36
  currency_type = models.CharField(
28
37
  max_length=10,
29
38
  choices=CurrencyType.choices,
30
- help_text="fiat or crypto"
39
+ help_text="Type of currency"
31
40
  )
32
41
 
33
- # USD rate caching - updated once per day
34
- usd_rate = models.DecimalField(
35
- max_digits=20,
36
- decimal_places=8,
37
- null=True,
42
+ symbol = models.CharField(
43
+ max_length=10,
38
44
  blank=True,
39
- help_text="Cached USD exchange rate (1 CURRENCY = X USD)"
45
+ help_text="Currency symbol (e.g., $, ₿, Ξ)"
40
46
  )
41
47
 
42
- rate_updated_at = models.DateTimeField(
43
- null=True,
48
+ decimal_places = models.PositiveSmallIntegerField(
49
+ default=8,
50
+ help_text="Number of decimal places for this currency"
51
+ )
52
+
53
+ is_active = models.BooleanField(
54
+ default=True,
55
+ help_text="Whether this currency is available for payments"
56
+ )
57
+
58
+ # Integration with django_currency
59
+ exchange_rate_source = models.CharField(
60
+ max_length=50,
44
61
  blank=True,
45
- help_text="When the USD rate was last updated"
62
+ help_text="Source for exchange rates (auto-detected by django_currency)"
46
63
  )
47
64
 
48
- # Import manager
49
- from ..managers.currency_manager import CurrencyManager
65
+ # Manager
66
+ from .managers.currency_managers import CurrencyManager
50
67
  objects = CurrencyManager()
51
68
 
52
69
  class Meta:
53
- db_table = 'payment_currencies'
54
- verbose_name = "Currency"
55
- verbose_name_plural = "Currencies"
70
+ db_table = 'payments_currencies'
71
+ verbose_name = 'Currency'
72
+ verbose_name_plural = 'Currencies'
56
73
  ordering = ['currency_type', 'code']
74
+ indexes = [
75
+ models.Index(fields=['currency_type', 'is_active']),
76
+ models.Index(fields=['code']),
77
+ ]
57
78
 
58
79
  def __str__(self):
59
- return f"{self.code} - {self.name}"
80
+ return f"{self.code} ({self.name})"
60
81
 
61
- @property
62
- def is_fiat(self) -> bool:
63
- return self.currency_type == self.CurrencyType.FIAT
82
+ def clean(self):
83
+ """Validate currency data."""
84
+ if self.code:
85
+ self.code = self.code.upper()
86
+
87
+ # Validate decimal places based on currency type
88
+ if self.currency_type == self.CurrencyType.FIAT and self.decimal_places > 4:
89
+ raise ValidationError("Fiat currencies should not have more than 4 decimal places")
90
+
91
+ if self.currency_type == self.CurrencyType.CRYPTO and self.decimal_places > 18:
92
+ raise ValidationError("Crypto currencies should not have more than 18 decimal places")
64
93
 
65
94
  @property
66
95
  def is_crypto(self) -> bool:
96
+ """Check if this is a cryptocurrency."""
67
97
  return self.currency_type == self.CurrencyType.CRYPTO
98
+
99
+ @property
100
+ def is_fiat(self) -> bool:
101
+ """Check if this is a fiat currency."""
102
+ return self.currency_type == self.CurrencyType.FIAT
68
103
 
69
104
 
70
105
  class Network(TimestampedModel):
71
- """Blockchain networks - code and name only."""
106
+ """
107
+ Blockchain network model for cryptocurrency payments.
108
+
109
+ Represents different networks like Ethereum, Bitcoin, Polygon, etc.
110
+ """
111
+
112
+ name = models.CharField(
113
+ max_length=50,
114
+ unique=True,
115
+ help_text="Network name (e.g., Ethereum, Bitcoin, Polygon)"
116
+ )
72
117
 
73
118
  code = models.CharField(
74
119
  max_length=20,
75
120
  unique=True,
76
- help_text="Network code: ethereum, bitcoin, tron, bsc"
121
+ help_text="Network code (e.g., ETH, BTC, MATIC)"
77
122
  )
78
- name = models.CharField(
79
- max_length=100,
80
- help_text="Network name: Ethereum, Bitcoin, TRON, BSC"
123
+
124
+ native_currency = models.ForeignKey(
125
+ Currency,
126
+ on_delete=models.PROTECT,
127
+ related_name='native_networks',
128
+ help_text="Native currency of this network"
129
+ )
130
+
131
+ block_explorer_url = models.URLField(
132
+ blank=True,
133
+ help_text="Block explorer URL template (use {tx} for transaction hash)"
81
134
  )
82
135
 
83
- # Import manager
84
- from ..managers.currency_manager import NetworkManager
85
- objects = NetworkManager()
136
+ is_active = models.BooleanField(
137
+ default=True,
138
+ help_text="Whether this network is available for payments"
139
+ )
140
+
141
+ # Network-specific settings
142
+ confirmation_blocks = models.PositiveIntegerField(
143
+ default=1,
144
+ help_text="Number of confirmations required for this network"
145
+ )
146
+
147
+ average_block_time = models.PositiveIntegerField(
148
+ default=600, # 10 minutes in seconds
149
+ help_text="Average block time in seconds"
150
+ )
86
151
 
87
152
  class Meta:
88
- db_table = 'payment_networks'
89
- verbose_name = "Network"
90
- verbose_name_plural = "Networks"
153
+ db_table = 'payments_networks'
154
+ verbose_name = 'Network'
155
+ verbose_name_plural = 'Networks'
91
156
  ordering = ['name']
157
+ indexes = [
158
+ models.Index(fields=['is_active']),
159
+ models.Index(fields=['code']),
160
+ ]
92
161
 
93
162
  def __str__(self):
94
163
  return f"{self.name} ({self.code})"
164
+
165
+ def clean(self):
166
+ """Validate network data."""
167
+ if self.code:
168
+ self.code = self.code.upper()
169
+
170
+ @property
171
+ def estimated_confirmation_time(self) -> int:
172
+ """Estimated time for confirmations in seconds."""
173
+ return self.confirmation_blocks * self.average_block_time
95
174
 
96
175
 
97
176
  class ProviderCurrency(TimestampedModel):
98
- """Provider-currency-network mapping - minimal."""
177
+ """
178
+ Provider-specific currency configuration.
99
179
 
100
- # Identification
101
- provider_name = models.CharField(
180
+ Maps currencies to specific providers and networks.
181
+ """
182
+
183
+ provider = models.CharField(
102
184
  max_length=50,
103
- help_text="Provider: nowpayments, stripe, cryptomus"
104
- )
105
- provider_currency_code = models.CharField(
106
- max_length=20,
107
- help_text="Provider code: USDTERC20, USDTBSC, usd"
185
+ help_text="Payment provider name (e.g., nowpayments)"
108
186
  )
109
187
 
110
- # Links to clean models
111
- base_currency = models.ForeignKey(
188
+ currency = models.ForeignKey(
112
189
  Currency,
113
190
  on_delete=models.CASCADE,
114
- related_name='provider_mappings',
115
- help_text="Base currency: BTC, USDT, ETH"
191
+ related_name='provider_configs'
116
192
  )
193
+
117
194
  network = models.ForeignKey(
118
195
  Network,
119
196
  on_delete=models.CASCADE,
197
+ related_name='provider_configs',
120
198
  null=True,
121
199
  blank=True,
122
- related_name='provider_currencies',
123
- help_text="Network for crypto (null for fiat)"
200
+ help_text="Network for crypto currencies (null for fiat)"
201
+ )
202
+
203
+ # Provider-specific settings
204
+ provider_currency_code = models.CharField(
205
+ max_length=20,
206
+ help_text="Currency code as used by the provider"
124
207
  )
125
208
 
126
- # Universal provider fields - common across providers
127
209
  min_amount = models.DecimalField(
128
210
  max_digits=20,
129
211
  decimal_places=8,
130
212
  null=True,
131
213
  blank=True,
132
- help_text="Minimum payment amount"
214
+ help_text="Minimum payment amount for this currency"
133
215
  )
216
+
134
217
  max_amount = models.DecimalField(
135
218
  max_digits=20,
136
219
  decimal_places=8,
137
220
  null=True,
138
221
  blank=True,
139
- help_text="Maximum payment amount (null = no limit)"
222
+ help_text="Maximum payment amount for this currency"
140
223
  )
141
224
 
142
- # Status and availability
143
225
  is_enabled = models.BooleanField(
144
226
  default=True,
145
- help_text="Enabled by provider"
146
- )
147
- available_for_payment = models.BooleanField(
148
- default=True,
149
- help_text="Can receive payments"
150
- )
151
- available_for_payout = models.BooleanField(
152
- default=True,
153
- help_text="Can send payouts"
227
+ help_text="Whether this currency is enabled for this provider"
154
228
  )
155
229
 
156
- # Classification for UI
157
- is_popular = models.BooleanField(
158
- default=False,
159
- help_text="Popular/recommended by provider"
160
- )
161
- is_stable = models.BooleanField(
162
- default=False,
163
- help_text="Stable coin (USDT, USDC, etc.)"
164
- )
165
- priority = models.IntegerField(
230
+ # Fee configuration
231
+ fee_percentage = models.DecimalField(
232
+ max_digits=5,
233
+ decimal_places=4,
166
234
  default=0,
167
- help_text="Display priority (higher = shown first)"
168
- )
169
- logo_url = models.URLField(
170
- blank=True,
171
- help_text="Currency logo/icon URL from provider"
235
+ help_text="Fee percentage (0.0250 = 2.5%)"
172
236
  )
173
237
 
174
- # Raw provider data - everything else goes here
175
- metadata = models.JSONField(
176
- default=dict,
177
- blank=True,
178
- help_text="All provider-specific data: logo_url, smart_contract, wallet_regex, commission_percent, etc."
238
+ fixed_fee = models.DecimalField(
239
+ max_digits=20,
240
+ decimal_places=8,
241
+ default=0,
242
+ help_text="Fixed fee amount in this currency"
179
243
  )
180
244
 
181
- # Import manager
182
- from ..managers.currency_manager import ProviderCurrencyManager
183
- objects = ProviderCurrencyManager()
184
-
185
245
  class Meta:
186
- db_table = 'payment_provider_currencies'
187
- verbose_name = "Provider Currency"
188
- verbose_name_plural = "Provider Currencies"
189
- unique_together = [
190
- ('provider_name', 'provider_currency_code'),
191
- ('provider_name', 'base_currency', 'network')
246
+ db_table = 'payments_provider_currencies'
247
+ verbose_name = 'Provider Currency'
248
+ verbose_name_plural = 'Provider Currencies'
249
+ unique_together = [['provider', 'currency', 'network']]
250
+ ordering = ['provider', 'currency__code']
251
+ indexes = [
252
+ models.Index(fields=['provider', 'is_enabled']),
253
+ models.Index(fields=['currency', 'is_enabled']),
192
254
  ]
193
- ordering = ['-priority', 'provider_name', 'base_currency__code']
194
255
 
195
256
  def __str__(self):
196
257
  network_part = f" ({self.network.code})" if self.network else ""
197
- return f"{self.provider_name}: {self.base_currency.code}{network_part}"
258
+ return f"{self.provider}: {self.currency.code}{network_part}"
198
259
 
199
- @property
200
- def display_name(self) -> str:
201
- """Human-readable name."""
202
- if self.network:
203
- return f"{self.base_currency.name} ({self.network.name})"
204
- return self.base_currency.name
205
-
206
- # Metadata helper properties
207
-
208
- def is_amount_valid(self, amount) -> bool:
209
- """Check if amount is within provider limits."""
210
- if not amount:
211
- return False
212
-
213
- if self.min_amount and amount < self.min_amount:
214
- return False
215
-
216
- if self.max_amount and amount > self.max_amount:
217
- return False
218
-
219
- return True
220
-
221
- def get_validation_errors(self, amount) -> list:
222
- """Get validation errors for amount."""
223
- errors = []
260
+ def clean(self):
261
+ """Validate provider currency configuration."""
262
+ # Crypto currencies must have a network
263
+ if self.currency and self.currency.is_crypto and not self.network:
264
+ raise ValidationError("Crypto currencies must specify a network")
265
+
266
+ # Fiat currencies should not have a network
267
+ if self.currency and self.currency.is_fiat and self.network:
268
+ raise ValidationError("Fiat currencies should not specify a network")
224
269
 
225
- if not amount:
226
- errors.append("Amount is required")
227
- return errors
228
-
229
- if self.min_amount and amount < self.min_amount:
230
- errors.append(f"Amount must be at least {self.min_amount}")
231
-
232
- if self.max_amount and amount > self.max_amount:
233
- errors.append(f"Amount must not exceed {self.max_amount}")
234
-
235
- return errors
270
+ # Validate amount limits
271
+ if self.min_amount and self.max_amount and self.min_amount > self.max_amount:
272
+ raise ValidationError("Minimum amount cannot be greater than maximum amount")
236
273
 
237
274
  @property
238
- def usd_rate(self) -> float:
239
- """Get USD rate for base currency (1 CURRENCY = X USD)."""
240
- return Currency.objects.get_usd_rate(self.base_currency.code)
241
-
242
- @property
243
- def tokens_per_usd(self) -> float:
244
- """Get how many tokens you can buy for 1 USD."""
245
- return Currency.objects.get_tokens_per_usd(self.base_currency.code)
246
-
247
- def convert_to_usd(self, amount: float) -> float:
248
- """Convert amount of this currency to USD."""
249
- return Currency.objects.convert_to_usd(amount, self.base_currency.code)
250
-
251
- def convert_from_usd(self, usd_amount: float) -> float:
252
- """Convert USD amount to this currency."""
253
- return Currency.objects.convert_from_usd(usd_amount, self.base_currency.code)
254
-
275
+ def display_name(self) -> str:
276
+ """Human-readable display name."""
277
+ network_part = f" on {self.network.name}" if self.network else ""
278
+ return f"{self.currency.name}{network_part}"
@@ -0,0 +1,44 @@
1
+ """
2
+ Django ORM Managers for the Universal Payment System v2.0.
3
+
4
+ Optimized managers and querysets for all payment-related models.
5
+ Follows the data typing requirements: Django ORM for database layer.
6
+ """
7
+
8
+ # Payment managers
9
+ from .payment_managers import PaymentQuerySet, PaymentManager
10
+
11
+ # Balance managers
12
+ from .balance_managers import UserBalanceManager, TransactionQuerySet, TransactionManager
13
+
14
+ # Subscription managers
15
+ from .subscription_managers import SubscriptionQuerySet, SubscriptionManager
16
+
17
+ # Currency managers
18
+ from .currency_managers import CurrencyQuerySet, CurrencyManager
19
+
20
+ # API Key managers
21
+ from .api_key_managers import APIKeyQuerySet, APIKeyManager
22
+
23
+ __all__ = [
24
+ # Payment managers
25
+ 'PaymentQuerySet',
26
+ 'PaymentManager',
27
+
28
+ # Balance managers
29
+ 'UserBalanceManager',
30
+ 'TransactionQuerySet',
31
+ 'TransactionManager',
32
+
33
+ # Subscription managers
34
+ 'SubscriptionQuerySet',
35
+ 'SubscriptionManager',
36
+
37
+ # Currency managers
38
+ 'CurrencyQuerySet',
39
+ 'CurrencyManager',
40
+
41
+ # API Key managers
42
+ 'APIKeyQuerySet',
43
+ 'APIKeyManager',
44
+ ]