django-cfg 1.2.29__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/api/health/views.py +4 -2
- django_cfg/apps/knowbase/config/settings.py +16 -15
- django_cfg/apps/payments/README.md +326 -0
- django_cfg/apps/payments/admin/__init__.py +20 -9
- django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
- django_cfg/apps/payments/admin/balance_admin.py +592 -297
- django_cfg/apps/payments/admin/currencies_admin.py +600 -108
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +470 -64
- django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
- django_cfg/apps/payments/admin_interface/__init__.py +18 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
- django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
- django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
- django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
- django_cfg/apps/payments/apps.py +34 -9
- django_cfg/apps/payments/config/__init__.py +28 -51
- django_cfg/apps/payments/config/constance/__init__.py +22 -0
- django_cfg/apps/payments/config/constance/config_service.py +123 -0
- django_cfg/apps/payments/config/constance/fields.py +69 -0
- django_cfg/apps/payments/config/constance/settings.py +160 -0
- django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
- django_cfg/apps/payments/config/helpers.py +130 -0
- django_cfg/apps/payments/management/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
- django_cfg/apps/payments/middleware/__init__.py +3 -1
- django_cfg/apps/payments/middleware/api_access.py +329 -222
- django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
- django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +16 -20
- django_cfg/apps/payments/models/api_keys.py +121 -43
- django_cfg/apps/payments/models/balance.py +150 -115
- django_cfg/apps/payments/models/base.py +68 -15
- django_cfg/apps/payments/models/currencies.py +207 -67
- django_cfg/apps/payments/models/managers/__init__.py +44 -0
- django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
- django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
- django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
- django_cfg/apps/payments/models/payments.py +235 -284
- django_cfg/apps/payments/models/subscriptions.py +257 -177
- django_cfg/apps/payments/models/tariffs.py +147 -40
- django_cfg/apps/payments/services/__init__.py +209 -56
- django_cfg/apps/payments/services/cache/__init__.py +6 -6
- django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
- django_cfg/apps/payments/services/core/__init__.py +10 -6
- django_cfg/apps/payments/services/core/balance_service.py +435 -360
- django_cfg/apps/payments/services/core/base.py +166 -0
- django_cfg/apps/payments/services/core/currency_service.py +478 -0
- django_cfg/apps/payments/services/core/payment_service.py +344 -468
- django_cfg/apps/payments/services/core/subscription_service.py +425 -484
- django_cfg/apps/payments/services/core/webhook_service.py +410 -0
- django_cfg/apps/payments/services/integrations/__init__.py +29 -0
- django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
- django_cfg/apps/payments/services/providers/__init__.py +9 -14
- django_cfg/apps/payments/services/providers/base.py +232 -71
- django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
- django_cfg/apps/payments/services/providers/registry.py +429 -80
- django_cfg/apps/payments/services/types/__init__.py +78 -0
- django_cfg/apps/payments/services/types/data.py +177 -0
- django_cfg/apps/payments/services/types/requests.py +150 -0
- django_cfg/apps/payments/services/types/responses.py +156 -0
- django_cfg/apps/payments/services/types/webhooks.py +232 -0
- django_cfg/apps/payments/signals/__init__.py +33 -8
- django_cfg/apps/payments/signals/api_key_signals.py +211 -130
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +129 -98
- django_cfg/apps/payments/signals/subscription_signals.py +195 -143
- django_cfg/apps/payments/static/payments/css/components.css +380 -0
- django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
- django_cfg/apps/payments/static/payments/js/components.js +545 -0
- django_cfg/apps/payments/static/payments/js/utils.js +412 -0
- django_cfg/apps/payments/templatetags/__init__.py +1 -1
- django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
- django_cfg/apps/payments/urls.py +46 -47
- django_cfg/apps/payments/urls_admin.py +49 -0
- django_cfg/apps/payments/views/api/__init__.py +101 -0
- django_cfg/apps/payments/views/api/api_keys.py +387 -0
- django_cfg/apps/payments/views/api/balances.py +381 -0
- django_cfg/apps/payments/views/api/base.py +298 -0
- django_cfg/apps/payments/views/api/currencies.py +402 -0
- django_cfg/apps/payments/views/api/payments.py +415 -0
- django_cfg/apps/payments/views/api/subscriptions.py +475 -0
- django_cfg/apps/payments/views/api/webhooks.py +476 -0
- django_cfg/apps/payments/views/serializers/__init__.py +99 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
- django_cfg/apps/payments/views/serializers/balances.py +300 -0
- django_cfg/apps/payments/views/serializers/currencies.py +335 -0
- django_cfg/apps/payments/views/serializers/payments.py +387 -0
- django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
- django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
- django_cfg/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +75 -4
- django_cfg/core/generation.py +25 -4
- django_cfg/core/integration/README.md +363 -0
- django_cfg/core/integration/__init__.py +47 -0
- django_cfg/core/integration/commands_collector.py +239 -0
- django_cfg/core/integration/display/__init__.py +15 -0
- django_cfg/core/integration/display/base.py +157 -0
- django_cfg/core/integration/display/ngrok.py +164 -0
- django_cfg/core/integration/display/startup.py +815 -0
- django_cfg/core/integration/url_integration.py +123 -0
- django_cfg/core/integration/version_checker.py +160 -0
- django_cfg/management/commands/auto_generate.py +4 -0
- django_cfg/management/commands/check_settings.py +6 -0
- django_cfg/management/commands/clear_constance.py +5 -2
- django_cfg/management/commands/create_token.py +6 -0
- django_cfg/management/commands/list_urls.py +6 -0
- django_cfg/management/commands/migrate_all.py +6 -0
- django_cfg/management/commands/migrator.py +3 -0
- django_cfg/management/commands/rundramatiq.py +6 -0
- django_cfg/management/commands/runserver_ngrok.py +51 -29
- django_cfg/management/commands/script.py +6 -0
- django_cfg/management/commands/show_config.py +12 -2
- django_cfg/management/commands/show_urls.py +4 -0
- django_cfg/management/commands/superuser.py +6 -0
- django_cfg/management/commands/task_clear.py +4 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -0
- django_cfg/management/commands/test_telegram.py +6 -0
- django_cfg/management/commands/test_twilio.py +6 -0
- django_cfg/management/commands/tree.py +6 -0
- django_cfg/management/commands/validate_config.py +155 -149
- django_cfg/models/constance.py +31 -11
- django_cfg/models/payments.py +175 -498
- django_cfg/modules/django_currency/__init__.py +16 -11
- django_cfg/modules/django_currency/clients/__init__.py +4 -4
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
- django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
- django_cfg/modules/django_currency/core/__init__.py +1 -7
- django_cfg/modules/django_currency/core/converter.py +18 -23
- django_cfg/modules/django_currency/core/models.py +122 -11
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +190 -309
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +65 -12
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/admin/components/action_grid.html +9 -9
- django_cfg/templates/admin/components/metric_card.html +5 -5
- django_cfg/templates/admin/components/status_badge.html +2 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
- django_cfg/templates/admin/snippets/components/system_health.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
- django_cfg/apps/payments/__init__.py +0 -8
- django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
- django_cfg/apps/payments/config/module.py +0 -70
- django_cfg/apps/payments/config/providers.py +0 -105
- django_cfg/apps/payments/config/settings.py +0 -96
- django_cfg/apps/payments/config/utils.py +0 -52
- django_cfg/apps/payments/decorators.py +0 -291
- django_cfg/apps/payments/management/commands/README.md +0 -178
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
- django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
- django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
- django_cfg/apps/payments/managers/__init__.py +0 -22
- django_cfg/apps/payments/managers/api_key_manager.py +0 -35
- django_cfg/apps/payments/managers/balance_manager.py +0 -361
- django_cfg/apps/payments/managers/currency_manager.py +0 -83
- django_cfg/apps/payments/managers/payment_manager.py +0 -44
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -56
- django_cfg/apps/payments/serializers/api_keys.py +0 -51
- django_cfg/apps/payments/serializers/balance.py +0 -59
- django_cfg/apps/payments/serializers/currencies.py +0 -55
- django_cfg/apps/payments/serializers/payments.py +0 -62
- django_cfg/apps/payments/serializers/subscriptions.py +0 -71
- django_cfg/apps/payments/serializers/tariffs.py +0 -56
- django_cfg/apps/payments/services/billing/__init__.py +0 -8
- django_cfg/apps/payments/services/cache/base.py +0 -30
- django_cfg/apps/payments/services/core/fallback_service.py +0 -432
- django_cfg/apps/payments/services/internal_types.py +0 -297
- django_cfg/apps/payments/services/middleware/__init__.py +0 -8
- django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
- django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
- django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -637
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- django_cfg/apps/payments/static/payments/css/payments.css +0 -340
- django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
- django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
- django_cfg/apps/payments/static/payments/js/theme.js +0 -86
- django_cfg/apps/payments/tasks/__init__.py +0 -12
- django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
- django_cfg/apps/payments/templates/payments/base.html +0 -182
- django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/urls_templates.py +0 -52
- django_cfg/apps/payments/utils/__init__.py +0 -45
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -245
- django_cfg/apps/payments/utils/middleware_utils.py +0 -228
- django_cfg/apps/payments/utils/validation_utils.py +0 -94
- django_cfg/apps/payments/views/__init__.py +0 -62
- django_cfg/apps/payments/views/api_key_views.py +0 -164
- django_cfg/apps/payments/views/balance_views.py +0 -75
- django_cfg/apps/payments/views/currency_views.py +0 -111
- django_cfg/apps/payments/views/payment_views.py +0 -149
- django_cfg/apps/payments/views/subscription_views.py +0 -135
- django_cfg/apps/payments/views/tariff_views.py +0 -131
- django_cfg/apps/payments/views/templates/__init__.py +0 -25
- django_cfg/apps/payments/views/templates/ajax.py +0 -312
- django_cfg/apps/payments/views/templates/base.py +0 -204
- django_cfg/apps/payments/views/templates/dashboard.py +0 -60
- django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
- django_cfg/apps/payments/views/templates/payment_management.py +0 -164
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -240
- django_cfg/apps/payments/views/templates/utils.py +0 -181
- django_cfg/apps/payments/views/webhook_views.py +0 -266
- django_cfg/apps/payments/viewsets.py +0 -65
- django_cfg/core/integration.py +0 -160
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- django_cfg/template_archive/.gitignore +0 -1
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/urls.py +0 -33
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.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,
|
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,128 +1,159 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Payment Signals for the Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
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.
|
10
|
+
from django.core.cache import cache
|
10
11
|
from django.utils import timezone
|
11
|
-
import logging
|
12
12
|
|
13
|
-
from ..models import UniversalPayment
|
14
|
-
from
|
15
|
-
from django_cfg.core.redis import RedisService
|
13
|
+
from ..models import UniversalPayment
|
14
|
+
from django_cfg.modules.django_logger import get_logger
|
16
15
|
|
17
|
-
logger =
|
16
|
+
logger = get_logger("payment_signals")
|
18
17
|
|
19
18
|
|
20
19
|
@receiver(pre_save, sender=UniversalPayment)
|
21
|
-
def
|
22
|
-
"""Store original
|
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
|
-
|
26
|
-
instance._original_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
|
33
|
-
"""
|
34
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
55
|
-
"""
|
73
|
+
def _handle_payment_completed(payment: UniversalPayment):
|
74
|
+
"""
|
75
|
+
Handle completed payment - delegate to manager for business logic.
|
76
|
+
"""
|
56
77
|
try:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
redis_service = RedisService()
|
96
|
-
redis_service.invalidate_user_cache(payment.user.id)
|
97
|
-
except Exception as e:
|
98
|
-
logger.warning(f"Failed to clear Redis cache for user {payment.user.id}: {e}")
|
99
|
-
|
100
|
-
logger.info(
|
101
|
-
f"Payment {payment.internal_payment_id} processed successfully. "
|
102
|
-
f"User {payment.user.email} balance: ${balance.amount_usd}"
|
103
|
-
)
|
104
|
-
|
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
|
+
|
105
100
|
except Exception as e:
|
106
|
-
logger.error(f"
|
107
|
-
|
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
|
+
})
|
108
106
|
|
109
107
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
117
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
|
+
})
|
118
138
|
|
119
139
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
f"
|
126
|
-
f"
|
127
|
-
f"
|
128
|
-
|
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}")
|