django-cfg 1.2.31__py3-none-any.whl → 1.3.3__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 (264) 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/cleanup_expired_data.py +419 -0
  39. django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
  40. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  41. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  42. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  43. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  44. django_cfg/apps/payments/middleware/__init__.py +3 -1
  45. django_cfg/apps/payments/middleware/api_access.py +329 -222
  46. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  47. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  48. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  49. django_cfg/apps/payments/models/__init__.py +13 -18
  50. django_cfg/apps/payments/models/api_keys.py +121 -43
  51. django_cfg/apps/payments/models/balance.py +153 -115
  52. django_cfg/apps/payments/models/base.py +68 -15
  53. django_cfg/apps/payments/models/currencies.py +172 -148
  54. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  55. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  56. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  57. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  58. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  59. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  60. django_cfg/apps/payments/models/payments.py +235 -285
  61. django_cfg/apps/payments/models/subscriptions.py +257 -177
  62. django_cfg/apps/payments/models/tariffs.py +147 -40
  63. django_cfg/apps/payments/services/__init__.py +209 -56
  64. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  65. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  66. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  67. django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
  68. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  69. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  70. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  71. django_cfg/apps/payments/services/core/__init__.py +10 -6
  72. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  73. django_cfg/apps/payments/services/core/base.py +166 -0
  74. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  75. django_cfg/apps/payments/services/core/payment_service.py +371 -465
  76. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  77. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  78. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  79. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  80. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  81. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  82. django_cfg/apps/payments/services/providers/base.py +234 -174
  83. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  84. django_cfg/apps/payments/services/providers/registry.py +367 -301
  85. django_cfg/apps/payments/services/types/__init__.py +78 -0
  86. django_cfg/apps/payments/services/types/data.py +177 -0
  87. django_cfg/apps/payments/services/types/requests.py +150 -0
  88. django_cfg/apps/payments/services/types/responses.py +156 -0
  89. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  90. django_cfg/apps/payments/signals/__init__.py +33 -8
  91. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  92. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  93. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  94. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  95. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  96. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  97. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  98. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  99. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  100. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  101. django_cfg/apps/payments/urls.py +45 -48
  102. django_cfg/apps/payments/urls_admin.py +33 -42
  103. django_cfg/apps/payments/views/api/__init__.py +101 -0
  104. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  105. django_cfg/apps/payments/views/api/balances.py +381 -0
  106. django_cfg/apps/payments/views/api/base.py +298 -0
  107. django_cfg/apps/payments/views/api/currencies.py +402 -0
  108. django_cfg/apps/payments/views/api/payments.py +415 -0
  109. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  110. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  111. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  112. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  113. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  114. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  115. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  116. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  117. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  118. django_cfg/config.py +1 -1
  119. django_cfg/core/config.py +40 -4
  120. django_cfg/core/generation.py +25 -4
  121. django_cfg/core/integration/README.md +363 -0
  122. django_cfg/core/integration/__init__.py +47 -0
  123. django_cfg/core/integration/commands_collector.py +239 -0
  124. django_cfg/core/integration/display/__init__.py +15 -0
  125. django_cfg/core/integration/display/base.py +157 -0
  126. django_cfg/core/integration/display/ngrok.py +164 -0
  127. django_cfg/core/integration/display/startup.py +815 -0
  128. django_cfg/core/integration/url_integration.py +123 -0
  129. django_cfg/core/integration/version_checker.py +160 -0
  130. django_cfg/management/commands/auto_generate.py +4 -0
  131. django_cfg/management/commands/check_settings.py +6 -0
  132. django_cfg/management/commands/clear_constance.py +5 -2
  133. django_cfg/management/commands/create_token.py +6 -0
  134. django_cfg/management/commands/list_urls.py +6 -0
  135. django_cfg/management/commands/migrate_all.py +6 -0
  136. django_cfg/management/commands/migrator.py +3 -0
  137. django_cfg/management/commands/rundramatiq.py +6 -0
  138. django_cfg/management/commands/runserver_ngrok.py +51 -29
  139. django_cfg/management/commands/script.py +6 -0
  140. django_cfg/management/commands/show_config.py +12 -2
  141. django_cfg/management/commands/show_urls.py +4 -0
  142. django_cfg/management/commands/superuser.py +6 -0
  143. django_cfg/management/commands/task_clear.py +4 -1
  144. django_cfg/management/commands/task_status.py +3 -1
  145. django_cfg/management/commands/test_email.py +3 -0
  146. django_cfg/management/commands/test_telegram.py +6 -0
  147. django_cfg/management/commands/test_twilio.py +6 -0
  148. django_cfg/management/commands/tree.py +6 -0
  149. django_cfg/management/commands/validate_config.py +155 -149
  150. django_cfg/models/constance.py +31 -11
  151. django_cfg/models/payments.py +175 -492
  152. django_cfg/modules/django_logger.py +160 -146
  153. django_cfg/modules/django_unfold/dashboard.py +64 -16
  154. django_cfg/registry/core.py +1 -0
  155. django_cfg/template_archive/django_sample.zip +0 -0
  156. django_cfg/utils/smart_defaults.py +227 -570
  157. django_cfg/utils/toolkit.py +51 -11
  158. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
  159. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
  160. django_cfg/apps/payments/__init__.py +0 -8
  161. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  162. django_cfg/apps/payments/config/module.py +0 -70
  163. django_cfg/apps/payments/config/providers.py +0 -105
  164. django_cfg/apps/payments/config/settings.py +0 -96
  165. django_cfg/apps/payments/config/utils.py +0 -52
  166. django_cfg/apps/payments/decorators.py +0 -291
  167. django_cfg/apps/payments/management/commands/README.md +0 -146
  168. django_cfg/apps/payments/managers/__init__.py +0 -23
  169. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  170. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  171. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  172. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  173. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  174. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  175. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  176. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  177. django_cfg/apps/payments/models/events.py +0 -73
  178. django_cfg/apps/payments/serializers/__init__.py +0 -57
  179. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  180. django_cfg/apps/payments/serializers/balance.py +0 -59
  181. django_cfg/apps/payments/serializers/currencies.py +0 -63
  182. django_cfg/apps/payments/serializers/payments.py +0 -62
  183. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  184. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  185. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  186. django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
  187. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  188. django_cfg/apps/payments/services/internal_types.py +0 -461
  189. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  190. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  191. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  192. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  193. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  194. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  195. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  196. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  197. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  198. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  199. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  200. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  201. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  202. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  203. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  204. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  205. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  206. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  207. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  208. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  209. django_cfg/apps/payments/services/security/__init__.py +0 -34
  210. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  211. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  212. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  213. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  214. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  215. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  216. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  217. django_cfg/apps/payments/tasks/__init__.py +0 -12
  218. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  219. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  220. django_cfg/apps/payments/templates/payments/base.html +0 -182
  221. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  222. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  223. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  224. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  225. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  226. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  227. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  228. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  229. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  230. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  231. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  232. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  233. django_cfg/apps/payments/templates/payments/test.html +0 -213
  234. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  235. django_cfg/apps/payments/utils/__init__.py +0 -43
  236. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  237. django_cfg/apps/payments/utils/config_utils.py +0 -239
  238. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  239. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  240. django_cfg/apps/payments/views/__init__.py +0 -63
  241. django_cfg/apps/payments/views/api_key_views.py +0 -164
  242. django_cfg/apps/payments/views/balance_views.py +0 -75
  243. django_cfg/apps/payments/views/currency_views.py +0 -122
  244. django_cfg/apps/payments/views/payment_views.py +0 -149
  245. django_cfg/apps/payments/views/subscription_views.py +0 -135
  246. django_cfg/apps/payments/views/tariff_views.py +0 -131
  247. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  248. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  249. django_cfg/apps/payments/views/templates/base.py +0 -212
  250. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  251. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  252. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  253. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  254. django_cfg/apps/payments/views/templates/stats.py +0 -244
  255. django_cfg/apps/payments/views/templates/utils.py +0 -181
  256. django_cfg/apps/payments/views/webhook_views.py +0 -266
  257. django_cfg/apps/payments/viewsets.py +0 -66
  258. django_cfg/core/integration.py +0 -160
  259. django_cfg/template_archive/.gitignore +0 -1
  260. django_cfg/template_archive/__init__.py +0 -0
  261. django_cfg/urls.py +0 -33
  262. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
  263. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
  264. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,174 @@
1
+ """
2
+ Balance Signals for the Universal Payment System v2.0.
3
+
4
+ Minimal signals focused on cache invalidation and notifications.
5
+ Business logic stays in UserBalanceManager and TransactionManager.
6
+ """
7
+
8
+ from django.db.models.signals import post_save, post_delete, pre_save
9
+ from django.dispatch import receiver
10
+ from django.core.cache import cache
11
+ from django.utils import timezone
12
+
13
+ from ..models import UserBalance, Transaction
14
+ from django_cfg.modules.django_logger import get_logger
15
+
16
+ logger = get_logger("balance_signals")
17
+
18
+
19
+ @receiver(pre_save, sender=UserBalance)
20
+ def store_original_balance(sender, instance: UserBalance, **kwargs):
21
+ """Store original balance for change detection."""
22
+ if instance.pk:
23
+ try:
24
+ original = UserBalance.objects.get(pk=instance.pk)
25
+ instance._original_balance = original.balance_usd
26
+ except UserBalance.DoesNotExist:
27
+ instance._original_balance = None
28
+ else:
29
+ instance._original_balance = None
30
+
31
+
32
+ @receiver(post_save, sender=UserBalance)
33
+ def handle_balance_change(sender, instance: UserBalance, created: bool, **kwargs):
34
+ """
35
+ Handle balance changes - only cache clearing and notifications.
36
+
37
+ Business logic (analytics, calculations) stays in managers.
38
+ """
39
+ if created:
40
+ logger.info(f"New balance created", extra={
41
+ 'user_id': instance.user.id,
42
+ 'initial_balance': instance.balance_usd
43
+ })
44
+ else:
45
+ # Check if balance changed
46
+ if hasattr(instance, '_original_balance'):
47
+ old_balance = instance._original_balance or 0.0
48
+ new_balance = instance.balance_usd
49
+
50
+ if old_balance != new_balance:
51
+ balance_change = new_balance - old_balance
52
+
53
+ logger.info(f"Balance changed", extra={
54
+ 'user_id': instance.user.id,
55
+ 'old_balance': old_balance,
56
+ 'new_balance': new_balance,
57
+ 'change_amount': balance_change
58
+ })
59
+
60
+ # Check for low balance warning (notification only)
61
+ if new_balance < 10.0 and old_balance >= 10.0:
62
+ _trigger_low_balance_warning(instance)
63
+
64
+ # Check for zero balance (notification only)
65
+ if new_balance <= 0.0 and old_balance > 0.0:
66
+ _handle_zero_balance(instance)
67
+
68
+ # Clear balance-related caches
69
+ _clear_balance_caches(instance.user.id)
70
+
71
+
72
+ @receiver(post_save, sender=Transaction)
73
+ def handle_transaction_creation(sender, instance: Transaction, created: bool, **kwargs):
74
+ """
75
+ Handle transaction creation - only logging and cache clearing.
76
+
77
+ Business logic (analytics, balance updates) stays in managers.
78
+ """
79
+ if created:
80
+ logger.info(f"New transaction created", extra={
81
+ 'transaction_id': str(instance.id),
82
+ 'user_id': instance.user.id,
83
+ 'transaction_type': instance.transaction_type,
84
+ 'amount': instance.amount_usd,
85
+ 'payment_id': str(instance.payment_id) if instance.payment_id else None
86
+ })
87
+
88
+ # Clear related caches
89
+ _clear_balance_caches(instance.user.id)
90
+
91
+
92
+ @receiver(post_delete, sender=Transaction)
93
+ def handle_transaction_deletion(sender, instance: Transaction, **kwargs):
94
+ """Handle transaction deletion (should be rare)."""
95
+ logger.warning(f"Transaction deleted", extra={
96
+ 'transaction_id': str(instance.id),
97
+ 'user_id': instance.user.id,
98
+ 'transaction_type': instance.transaction_type,
99
+ 'amount': instance.amount,
100
+ 'deletion_timestamp': timezone.now().isoformat()
101
+ })
102
+
103
+ # Clear caches
104
+ _clear_balance_caches(instance.user.id)
105
+
106
+
107
+ # Helper functions (notifications and caching only)
108
+
109
+ def _trigger_low_balance_warning(balance: UserBalance):
110
+ """Trigger low balance warning for user (notification only)."""
111
+ try:
112
+ logger.warning(f"Low balance warning", extra={
113
+ 'user_id': balance.user.id,
114
+ 'current_balance': balance.balance_usd,
115
+ 'threshold': 10.0
116
+ })
117
+
118
+ # Set warning flag in cache for frontend
119
+ cache.set(
120
+ f"low_balance_warning:{balance.user.id}",
121
+ {
122
+ 'balance': balance.balance_usd,
123
+ 'timestamp': timezone.now().isoformat(),
124
+ 'threshold': 10.0
125
+ },
126
+ timeout=86400 # 24 hours
127
+ )
128
+
129
+ except Exception as e:
130
+ logger.error(f"Failed to trigger low balance warning: {e}")
131
+
132
+
133
+ def _handle_zero_balance(balance: UserBalance):
134
+ """Handle zero balance situation (notification only)."""
135
+ try:
136
+ logger.warning(f"Zero balance reached", extra={
137
+ 'user_id': balance.user.id,
138
+ 'previous_balance': getattr(balance, '_original_balance', 'unknown')
139
+ })
140
+
141
+ # Set zero balance flag in cache
142
+ cache.set(
143
+ f"zero_balance:{balance.user.id}",
144
+ {
145
+ 'timestamp': timezone.now().isoformat(),
146
+ 'previous_balance': getattr(balance, '_original_balance', 0.0)
147
+ },
148
+ timeout=86400 * 7 # 7 days
149
+ )
150
+
151
+ except Exception as e:
152
+ logger.error(f"Failed to handle zero balance: {e}")
153
+
154
+
155
+ def _clear_balance_caches(user_id: int):
156
+ """Clear all balance-related cache entries for user."""
157
+ try:
158
+ cache_keys = [
159
+ f"user_balance:{user_id}",
160
+ f"user_transactions:{user_id}",
161
+ f"balance_summary:{user_id}",
162
+ f"balance_history:{user_id}",
163
+ f"transaction_stats:{user_id}",
164
+ ]
165
+
166
+ cache.delete_many(cache_keys)
167
+
168
+ logger.debug(f"Cleared balance caches", extra={
169
+ 'user_id': user_id,
170
+ 'cache_keys_cleared': len(cache_keys)
171
+ })
172
+
173
+ except Exception as e:
174
+ logger.warning(f"Failed to clear balance caches: {e}")
@@ -1,134 +1,159 @@
1
1
  """
2
- 🔄 Universal Payment Signals
2
+ Payment Signals for the Universal Payment System v2.0.
3
3
 
4
- Automatic payment processing and balance management via Django signals.
4
+ Minimal signals focused on cache invalidation and notifications.
5
+ Business logic stays in PaymentManager.
5
6
  """
6
7
 
7
8
  from django.db.models.signals import post_save, pre_save
8
9
  from django.dispatch import receiver
9
- from django.db import transaction
10
+ from django.core.cache import cache
10
11
  from django.utils import timezone
11
- from django_cfg.modules.django_logger import get_logger
12
12
 
13
- from ..models import UniversalPayment, UserBalance, Transaction
14
- from ..services.cache import SimpleCache
15
- from django.core.cache import cache
13
+ from ..models import UniversalPayment
14
+ from django_cfg.modules.django_logger import get_logger
16
15
 
17
16
  logger = get_logger("payment_signals")
18
17
 
19
18
 
20
19
  @receiver(pre_save, sender=UniversalPayment)
21
- def store_original_payment_status(sender, instance, **kwargs):
22
- """Store original payment status for change detection."""
20
+ def store_original_status(sender, instance: UniversalPayment, **kwargs):
21
+ """Store original status for change detection."""
23
22
  if instance.pk:
24
23
  try:
25
- old_instance = UniversalPayment.objects.get(pk=instance.pk)
26
- instance._original_status = old_instance.status
24
+ original = UniversalPayment.objects.get(pk=instance.pk)
25
+ instance._original_status = original.status
27
26
  except UniversalPayment.DoesNotExist:
28
27
  instance._original_status = None
28
+ else:
29
+ instance._original_status = None
29
30
 
30
31
 
31
32
  @receiver(post_save, sender=UniversalPayment)
32
- def process_payment_status_changes(sender, instance, created, **kwargs):
33
- """Process payment status changes and update user balance."""
34
- if created:
35
- logger.info(f"New payment created: {instance.internal_payment_id} for user {instance.user.email}")
36
- return
33
+ def handle_payment_changes(sender, instance: UniversalPayment, created: bool, **kwargs):
34
+ """
35
+ Handle payment changes - only cache clearing and notifications.
37
36
 
38
- # Check if status changed to completed
39
- if hasattr(instance, '_original_status'):
40
- old_status = instance._original_status
41
- new_status = instance.status
42
-
43
- if old_status != new_status:
44
- logger.info(
45
- f"Payment status changed: {instance.internal_payment_id} "
46
- f"for user {instance.user.email} - {old_status} → {new_status}"
47
- )
37
+ Business logic (balance updates, etc.) handled by managers.
38
+ """
39
+ if created:
40
+ logger.info(f"New payment created", extra={
41
+ 'payment_id': str(instance.id),
42
+ 'user_id': instance.user.id,
43
+ 'amount_usd': instance.amount_usd,
44
+ 'provider': instance.provider,
45
+ 'status': instance.status
46
+ })
47
+ else:
48
+ # Check for status changes
49
+ if hasattr(instance, '_original_status'):
50
+ old_status = instance._original_status
51
+ new_status = instance.status
48
52
 
49
- # Process completed payment
50
- if new_status == UniversalPayment.PaymentStatus.COMPLETED and old_status != new_status:
51
- _process_completed_payment(instance)
53
+ if old_status != new_status:
54
+ logger.info(f"Payment status changed", extra={
55
+ 'payment_id': str(instance.id),
56
+ 'user_id': instance.user.id,
57
+ 'old_status': old_status,
58
+ 'new_status': new_status
59
+ })
60
+
61
+ # Handle completed payment
62
+ if new_status == 'completed' and old_status != 'completed':
63
+ _handle_payment_completed(instance)
64
+
65
+ # Handle failed payment
66
+ elif new_status in ['failed', 'expired', 'cancelled'] and old_status not in ['failed', 'expired', 'cancelled']:
67
+ _handle_payment_failed(instance)
68
+
69
+ # Clear payment-related caches
70
+ _clear_payment_caches(instance)
52
71
 
53
72
 
54
- def _process_completed_payment(payment: UniversalPayment):
55
- """Process completed payment and add funds to user balance."""
73
+ def _handle_payment_completed(payment: UniversalPayment):
74
+ """
75
+ Handle completed payment - delegate to manager for business logic.
76
+ """
56
77
  try:
57
- with transaction.atomic():
58
- # Get or create user balance
59
- balance, created = UserBalance.objects.get_or_create(
60
- user=payment.user,
61
- defaults={
62
- 'amount_usd': 0,
63
- 'reserved_usd': 0
64
- }
65
- )
66
-
67
- # Add funds to balance
68
- old_balance = balance.amount_usd
69
- balance.amount_usd += payment.amount_usd
70
- balance.save()
71
-
72
- # Create transaction record
73
- Transaction.objects.create(
74
- user=payment.user,
75
- transaction_type=Transaction.TransactionType.PAYMENT,
76
- amount_usd=payment.amount_usd,
77
- balance_before=old_balance,
78
- balance_after=balance.amount_usd,
79
- description=f"Payment completed: {payment.internal_payment_id}",
80
- payment=payment,
81
- metadata={
82
- 'provider': payment.provider,
83
- 'provider_payment_id': payment.provider_payment_id,
84
- 'amount_usd': str(payment.amount_usd),
85
- 'currency_code': payment.currency_code
86
- }
87
- )
88
-
89
- # Mark payment as processed
90
- payment.processed_at = timezone.now()
91
- payment.save(update_fields=['processed_at'])
92
-
93
- # Clear Redis cache for user
94
- try:
95
- # Invalidate user cache using Django cache
96
- user_cache_pattern = f"payments:user:{payment.user.id}:*"
97
- # Note: Django cache doesn't support pattern deletion, so we clear specific keys
98
- cache.delete_many([
99
- f"payments:user:{payment.user.id}:balance",
100
- f"payments:user:{payment.user.id}:api_keys",
101
- f"payments:user:{payment.user.id}:subscriptions"
102
- ])
103
- except Exception as e:
104
- logger.warning(f"Failed to clear Redis cache for user {payment.user.id}: {e}")
105
-
106
- logger.info(
107
- f"Payment {payment.internal_payment_id} processed successfully. "
108
- f"User {payment.user.email} balance: ${balance.amount_usd}"
109
- )
110
-
78
+ # Use manager method which has all the business logic
79
+ from ..models import UserBalance
80
+
81
+ transaction_record = UserBalance.objects.add_funds_to_user(
82
+ user=payment.user,
83
+ amount=payment.amount_usd,
84
+ transaction_type='payment',
85
+ description=f"Payment completed: {payment.id}",
86
+ payment_id=payment.id
87
+ )
88
+
89
+ # Mark payment as processed
90
+ payment.completed_at = timezone.now()
91
+ payment.save(update_fields=['completed_at', 'updated_at'])
92
+
93
+ logger.info(f"Payment completed and processed", extra={
94
+ 'payment_id': str(payment.id),
95
+ 'user_id': payment.user.id,
96
+ 'amount_usd': payment.amount_usd,
97
+ 'transaction_id': str(transaction_record.id)
98
+ })
99
+
111
100
  except Exception as e:
112
- logger.error(f"Error processing completed payment {payment.internal_payment_id}: {e}")
113
- raise
101
+ logger.error(f"Failed to process completed payment", extra={
102
+ 'payment_id': str(payment.id),
103
+ 'user_id': payment.user.id,
104
+ 'error': str(e)
105
+ })
114
106
 
115
107
 
116
- @receiver(post_save, sender=UniversalPayment)
117
- def log_payment_webhook_data(sender, instance, created, **kwargs):
118
- """Log webhook data for audit purposes."""
119
- if not created and instance.webhook_data:
120
- logger.info(
121
- f"Webhook data received for payment {instance.internal_payment_id}: "
122
- f"status={instance.status}, provider={instance.provider}"
108
+ def _handle_payment_failed(payment: UniversalPayment):
109
+ """
110
+ Handle failed payment - just logging and notifications.
111
+ """
112
+ try:
113
+ logger.warning(f"Payment failed", extra={
114
+ 'payment_id': str(payment.id),
115
+ 'user_id': payment.user.id,
116
+ 'amount_usd': payment.amount_usd,
117
+ 'status': payment.status,
118
+ 'provider': payment.provider
119
+ })
120
+
121
+ # Set failure notification in cache
122
+ cache.set(
123
+ f"payment_failed:{payment.user.id}:{payment.id}",
124
+ {
125
+ 'payment_id': str(payment.id),
126
+ 'amount_usd': payment.amount_usd,
127
+ 'status': payment.status,
128
+ 'timestamp': timezone.now().isoformat()
129
+ },
130
+ timeout=86400 * 7 # 7 days
123
131
  )
132
+
133
+ except Exception as e:
134
+ logger.error(f"Failed to handle payment failure", extra={
135
+ 'payment_id': str(payment.id),
136
+ 'error': str(e)
137
+ })
124
138
 
125
139
 
126
- @receiver(post_save, sender=Transaction)
127
- def log_transaction_creation(sender, instance, created, **kwargs):
128
- """Log transaction creation for audit trail."""
129
- if created:
130
- logger.info(
131
- f"New transaction: {instance.transaction_type} "
132
- f"${instance.amount_usd} for user {instance.user.email} "
133
- f"(balance: ${instance.balance_after})"
134
- )
140
+ def _clear_payment_caches(payment: UniversalPayment):
141
+ """Clear payment-related cache entries."""
142
+ try:
143
+ cache_keys = [
144
+ f"user_payments:{payment.user.id}",
145
+ f"payment_stats:{payment.user.id}",
146
+ f"payment_summary:{payment.user.id}",
147
+ f"provider_stats:{payment.provider}",
148
+ ]
149
+
150
+ cache.delete_many(cache_keys)
151
+
152
+ logger.debug(f"Cleared payment caches", extra={
153
+ 'payment_id': str(payment.id),
154
+ 'user_id': payment.user.id,
155
+ 'cache_keys_cleared': len(cache_keys)
156
+ })
157
+
158
+ except Exception as e:
159
+ logger.warning(f"Failed to clear payment caches: {e}")