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,357 @@
1
+ """
2
+ Process Pending Payments Management Command for Universal Payment System v2.0.
3
+
4
+ Automatically process pending payments, check statuses, and handle timeouts.
5
+ """
6
+
7
+ from datetime import timedelta
8
+ from typing import List, Dict, Any, Optional
9
+ from decimal import Decimal
10
+
11
+ from django.core.management.base import BaseCommand, CommandError
12
+ from django.utils import timezone
13
+ from django.db import transaction
14
+ from django.db.models import Q
15
+
16
+ from django_cfg.modules.django_logger import get_logger
17
+ from django_cfg.apps.payments.models import UniversalPayment
18
+ from django_cfg.apps.payments.services.providers.registry import get_provider_registry
19
+ from django_cfg.apps.payments.services.core.payment_service import PaymentService
20
+ from django_cfg.apps.payments.services.core.webhook_service import WebhookService
21
+
22
+ logger = get_logger("process_pending_payments")
23
+
24
+
25
+ class Command(BaseCommand):
26
+ """
27
+ Process pending payments and update their statuses.
28
+
29
+ Features:
30
+ - Check payment statuses with providers
31
+ - Handle expired payments
32
+ - Process confirmations
33
+ - Update balances for completed payments
34
+ - Comprehensive logging and error handling
35
+ """
36
+
37
+ help = 'Process pending payments and update their statuses'
38
+
39
+ def add_arguments(self, parser):
40
+ """Add command line arguments."""
41
+ parser.add_argument(
42
+ '--provider',
43
+ type=str,
44
+ help='Process payments for specific provider only'
45
+ )
46
+
47
+ parser.add_argument(
48
+ '--payment-id',
49
+ type=str,
50
+ help='Process specific payment by ID'
51
+ )
52
+
53
+ parser.add_argument(
54
+ '--max-age-hours',
55
+ type=int,
56
+ default=72,
57
+ help='Maximum age in hours for pending payments (default: 72)'
58
+ )
59
+
60
+ parser.add_argument(
61
+ '--batch-size',
62
+ type=int,
63
+ default=50,
64
+ help='Number of payments to process in each batch (default: 50)'
65
+ )
66
+
67
+ parser.add_argument(
68
+ '--dry-run',
69
+ action='store_true',
70
+ help='Show what would be processed without making changes'
71
+ )
72
+
73
+ parser.add_argument(
74
+ '--force-expired',
75
+ action='store_true',
76
+ help='Force expire payments older than max-age-hours'
77
+ )
78
+
79
+ parser.add_argument(
80
+ '--verbose',
81
+ action='store_true',
82
+ help='Show detailed processing information'
83
+ )
84
+
85
+ def handle(self, *args, **options):
86
+ """Execute the command."""
87
+ try:
88
+ self.options = options
89
+ self.dry_run = options['dry_run']
90
+ self.verbose = options['verbose']
91
+
92
+ self.show_header()
93
+
94
+ if options['payment_id']:
95
+ self.process_single_payment(options['payment_id'])
96
+ else:
97
+ self.process_pending_payments()
98
+
99
+ self.show_summary()
100
+
101
+ except Exception as e:
102
+ logger.error(f"Process pending payments command failed: {e}")
103
+ raise CommandError(f"Failed to process pending payments: {e}")
104
+
105
+ def show_header(self):
106
+ """Display command header."""
107
+ mode = "DRY RUN" if self.dry_run else "LIVE MODE"
108
+ self.stdout.write(
109
+ self.style.SUCCESS("=" * 60)
110
+ )
111
+ self.stdout.write(
112
+ self.style.SUCCESS(f"⚡ PROCESS PENDING PAYMENTS - {mode}")
113
+ )
114
+ self.stdout.write(
115
+ self.style.SUCCESS("=" * 60)
116
+ )
117
+ self.stdout.write(f"Started: {timezone.now().strftime('%Y-%m-%d %H:%M:%S UTC')}")
118
+ self.stdout.write("")
119
+
120
+ # Initialize counters
121
+ self.stats = {
122
+ 'processed': 0,
123
+ 'completed': 0,
124
+ 'failed': 0,
125
+ 'expired': 0,
126
+ 'errors': 0,
127
+ 'skipped': 0
128
+ }
129
+
130
+ def process_single_payment(self, payment_id: str):
131
+ """Process a single payment by ID."""
132
+ try:
133
+ payment = UniversalPayment.objects.get(id=payment_id)
134
+ self.stdout.write(f"Processing single payment: {payment.id}")
135
+
136
+ result = self.process_payment(payment)
137
+ if result:
138
+ self.stdout.write(
139
+ self.style.SUCCESS(f"✅ Payment processed successfully")
140
+ )
141
+ else:
142
+ self.stdout.write(
143
+ self.style.ERROR(f"❌ Failed to process payment")
144
+ )
145
+
146
+ except UniversalPayment.DoesNotExist:
147
+ raise CommandError(f"Payment with ID {payment_id} not found")
148
+
149
+ def process_pending_payments(self):
150
+ """Process all pending payments."""
151
+ # Build query filters
152
+ filters = Q(status__in=['pending', 'confirming', 'confirmed'])
153
+
154
+ # Provider filter
155
+ if self.options['provider']:
156
+ filters &= Q(provider=self.options['provider'])
157
+
158
+ # Age filter
159
+ max_age = timezone.now() - timedelta(hours=self.options['max_age_hours'])
160
+ if not self.options['force_expired']:
161
+ filters &= Q(created_at__gte=max_age)
162
+
163
+ # Get payments to process
164
+ payments = UniversalPayment.objects.filter(filters).select_related(
165
+ 'user', 'currency', 'network'
166
+ ).order_by('created_at')
167
+
168
+ total_payments = payments.count()
169
+ self.stdout.write(f"Found {total_payments} payments to process")
170
+
171
+ if total_payments == 0:
172
+ self.stdout.write(self.style.WARNING("No payments to process"))
173
+ return
174
+
175
+ # Process in batches
176
+ batch_size = self.options['batch_size']
177
+ processed = 0
178
+
179
+ for i in range(0, total_payments, batch_size):
180
+ batch = payments[i:i + batch_size]
181
+ self.stdout.write(f"\nProcessing batch {i//batch_size + 1} ({len(batch)} payments)...")
182
+
183
+ for payment in batch:
184
+ try:
185
+ self.process_payment(payment)
186
+ processed += 1
187
+
188
+ if self.verbose:
189
+ self.stdout.write(f" Processed: {payment.id} ({payment.status})")
190
+
191
+ except Exception as e:
192
+ self.stats['errors'] += 1
193
+ logger.error(f"Error processing payment {payment.id}: {e}")
194
+ if self.verbose:
195
+ self.stdout.write(
196
+ self.style.ERROR(f" Error: {payment.id} - {e}")
197
+ )
198
+
199
+ # Show progress
200
+ progress = (processed / total_payments) * 100
201
+ self.stdout.write(f"Progress: {processed}/{total_payments} ({progress:.1f}%)")
202
+
203
+ def process_payment(self, payment: UniversalPayment) -> bool:
204
+ """
205
+ Process a single payment.
206
+
207
+ Returns:
208
+ bool: True if payment was processed successfully
209
+ """
210
+ try:
211
+ # Check if payment is too old and should be expired
212
+ max_age = timezone.now() - timedelta(hours=self.options['max_age_hours'])
213
+ if payment.created_at < max_age and self.options['force_expired']:
214
+ return self.expire_payment(payment)
215
+
216
+ # Check if payment has explicit expiration
217
+ if payment.expires_at and payment.expires_at < timezone.now():
218
+ return self.expire_payment(payment)
219
+
220
+ # Get provider and check status
221
+ provider_registry = get_provider_registry()
222
+ provider = provider_registry.get_provider(payment.provider)
223
+
224
+ if not provider:
225
+ logger.warning(f"Provider {payment.provider} not available for payment {payment.id}")
226
+ self.stats['skipped'] += 1
227
+ return False
228
+
229
+ # Check payment status with provider
230
+ if self.dry_run:
231
+ self.stdout.write(f" [DRY RUN] Would check status for payment {payment.id}")
232
+ self.stats['processed'] += 1
233
+ return True
234
+
235
+ # Get current status from provider
236
+ status_result = provider.get_payment_status(payment.provider_payment_id)
237
+
238
+ if not status_result.success:
239
+ logger.warning(f"Failed to get status for payment {payment.id}: {status_result.error}")
240
+ self.stats['errors'] += 1
241
+ return False
242
+
243
+ # Update payment based on provider status
244
+ old_status = payment.status
245
+ new_status = status_result.status
246
+
247
+ if old_status != new_status:
248
+ with transaction.atomic():
249
+ payment.status = new_status
250
+
251
+ # Update additional fields if provided
252
+ if hasattr(status_result, 'transaction_hash') and status_result.transaction_hash:
253
+ payment.transaction_hash = status_result.transaction_hash
254
+
255
+ if hasattr(status_result, 'confirmations') and status_result.confirmations is not None:
256
+ payment.confirmations_count = status_result.confirmations
257
+
258
+ if new_status == 'completed':
259
+ payment.completed_at = timezone.now()
260
+ self.stats['completed'] += 1
261
+
262
+ # Process balance update using service
263
+ payment_service = PaymentService()
264
+ payment_service.process_completed_payment(payment)
265
+
266
+ elif new_status in ['failed', 'expired', 'cancelled']:
267
+ self.stats['failed'] += 1
268
+
269
+ payment.save()
270
+
271
+ logger.info(f"Payment {payment.id} status updated: {old_status} -> {new_status}")
272
+
273
+ if self.verbose:
274
+ self.stdout.write(f" Updated: {payment.id} ({old_status} -> {new_status})")
275
+
276
+ self.stats['processed'] += 1
277
+ return True
278
+
279
+ except Exception as e:
280
+ logger.error(f"Error processing payment {payment.id}: {e}")
281
+ self.stats['errors'] += 1
282
+ return False
283
+
284
+ def expire_payment(self, payment: UniversalPayment) -> bool:
285
+ """
286
+ Expire a payment that is too old.
287
+
288
+ Args:
289
+ payment: Payment to expire
290
+
291
+ Returns:
292
+ bool: True if payment was expired successfully
293
+ """
294
+ try:
295
+ if self.dry_run:
296
+ self.stdout.write(f" [DRY RUN] Would expire payment {payment.id}")
297
+ return True
298
+
299
+ with transaction.atomic():
300
+ old_status = payment.status
301
+ payment.status = 'expired'
302
+ payment.save()
303
+
304
+ self.stats['expired'] += 1
305
+ logger.info(f"Payment {payment.id} expired (was {old_status})")
306
+
307
+ if self.verbose:
308
+ self.stdout.write(f" Expired: {payment.id} (was {old_status})")
309
+
310
+ return True
311
+
312
+ except Exception as e:
313
+ logger.error(f"Error expiring payment {payment.id}: {e}")
314
+ self.stats['errors'] += 1
315
+ return False
316
+
317
+ def show_summary(self):
318
+ """Display processing summary."""
319
+ self.stdout.write("")
320
+ self.stdout.write(self.style.SUCCESS("📊 PROCESSING SUMMARY"))
321
+ self.stdout.write("-" * 40)
322
+
323
+ summary_items = [
324
+ ("Processed", self.stats['processed'], 'SUCCESS'),
325
+ ("Completed", self.stats['completed'], 'SUCCESS'),
326
+ ("Failed", self.stats['failed'], 'WARNING'),
327
+ ("Expired", self.stats['expired'], 'WARNING'),
328
+ ("Errors", self.stats['errors'], 'ERROR'),
329
+ ("Skipped", self.stats['skipped'], 'WARNING'),
330
+ ]
331
+
332
+ for label, count, style in summary_items:
333
+ style_func = getattr(self.style, style)
334
+ self.stdout.write(f"{label:<12}: {style_func(count)}")
335
+
336
+ # Show completion time
337
+ self.stdout.write("")
338
+ self.stdout.write(f"Completed: {timezone.now().strftime('%Y-%m-%d %H:%M:%S UTC')}")
339
+
340
+ # Show recommendations
341
+ if self.stats['errors'] > 0:
342
+ self.stdout.write("")
343
+ self.stdout.write(
344
+ self.style.WARNING("⚠️ Some payments had errors. Check logs for details.")
345
+ )
346
+
347
+ if self.stats['failed'] > 0:
348
+ self.stdout.write("")
349
+ self.stdout.write(
350
+ self.style.WARNING("⚠️ Some payments failed. Consider investigating failed payments.")
351
+ )
352
+
353
+ if self.dry_run:
354
+ self.stdout.write("")
355
+ self.stdout.write(
356
+ self.style.SUCCESS("✅ Dry run completed. Run without --dry-run to apply changes.")
357
+ )