django-cfg 1.3.9__py3-none-any.whl → 1.3.11__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 (187) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/payments/admin/networks_admin.py +12 -1
  3. django_cfg/apps/payments/admin/payments_admin.py +13 -0
  4. django_cfg/apps/payments/admin_interface/serializers/payment_serializers.py +62 -14
  5. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_card.html +121 -0
  6. django_cfg/apps/payments/admin_interface/templates/payments/components/payment_qr_code.html +95 -0
  7. django_cfg/apps/payments/admin_interface/templates/payments/components/progress_bar.html +37 -0
  8. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_stats.html +60 -0
  9. django_cfg/apps/payments/admin_interface/templates/payments/components/status_badge.html +41 -0
  10. django_cfg/apps/payments/admin_interface/templates/payments/components/status_overview.html +83 -0
  11. django_cfg/apps/payments/admin_interface/templates/payments/payment_detail.html +363 -0
  12. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +33 -3
  13. django_cfg/apps/payments/admin_interface/views/api/payments.py +102 -0
  14. django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +96 -45
  15. django_cfg/apps/payments/admin_interface/views/forms.py +5 -1
  16. django_cfg/apps/payments/config/__init__.py +14 -15
  17. django_cfg/apps/payments/config/django_cfg_integration.py +59 -1
  18. django_cfg/apps/payments/config/helpers.py +8 -13
  19. django_cfg/apps/payments/migrations/0001_initial.py +33 -46
  20. django_cfg/apps/payments/migrations/0002_rename_payments_un_user_id_7f6e79_idx_payments_un_user_id_8ce187_idx_and_more.py +46 -0
  21. django_cfg/apps/payments/migrations/0003_universalpayment_status_changed_at.py +25 -0
  22. django_cfg/apps/payments/models/managers/payment_managers.py +142 -25
  23. django_cfg/apps/payments/models/payments.py +94 -0
  24. django_cfg/apps/payments/services/core/base.py +4 -4
  25. django_cfg/apps/payments/services/core/payment_service.py +265 -38
  26. django_cfg/apps/payments/services/providers/base.py +209 -3
  27. django_cfg/apps/payments/services/providers/models/__init__.py +2 -0
  28. django_cfg/apps/payments/services/providers/models/base.py +25 -2
  29. django_cfg/apps/payments/services/providers/nowpayments/models.py +2 -2
  30. django_cfg/apps/payments/services/providers/nowpayments/provider.py +57 -9
  31. django_cfg/apps/payments/services/providers/registry.py +5 -5
  32. django_cfg/apps/payments/services/types/requests.py +19 -7
  33. django_cfg/apps/payments/signals/payment_signals.py +31 -2
  34. django_cfg/apps/payments/static/payments/js/api-client.js +6 -1
  35. django_cfg/apps/payments/static/payments/js/payment-detail.js +167 -0
  36. django_cfg/apps/payments/static/payments/js/payment-form.js +35 -26
  37. django_cfg/apps/payments/templatetags/payment_tags.py +8 -0
  38. django_cfg/apps/payments/urls.py +3 -2
  39. django_cfg/apps/payments/views/api/currencies.py +3 -0
  40. django_cfg/apps/payments/views/serializers/currencies.py +18 -5
  41. django_cfg/apps/tasks/admin/tasks_admin.py +2 -2
  42. django_cfg/apps/tasks/static/tasks/css/dashboard.css +68 -217
  43. django_cfg/apps/tasks/static/tasks/js/api.js +40 -84
  44. django_cfg/apps/tasks/static/tasks/js/components/DataManager.js +24 -0
  45. django_cfg/apps/tasks/static/tasks/js/components/TabManager.js +85 -0
  46. django_cfg/apps/tasks/static/tasks/js/components/TaskRenderer.js +216 -0
  47. django_cfg/apps/tasks/static/tasks/js/dashboard/main.mjs +245 -0
  48. django_cfg/apps/tasks/static/tasks/js/dashboard/overview.mjs +123 -0
  49. django_cfg/apps/tasks/static/tasks/js/dashboard/queues.mjs +120 -0
  50. django_cfg/apps/tasks/static/tasks/js/dashboard/tasks.mjs +350 -0
  51. django_cfg/apps/tasks/static/tasks/js/dashboard/workers.mjs +169 -0
  52. django_cfg/apps/tasks/tasks/__init__.py +10 -0
  53. django_cfg/apps/tasks/tasks/demo_tasks.py +133 -0
  54. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +42 -45
  55. django_cfg/apps/tasks/templates/tasks/components/{status_cards.html → overview_content.html} +30 -11
  56. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +19 -0
  57. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +16 -10
  58. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +51 -0
  59. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +30 -0
  60. django_cfg/apps/tasks/templates/tasks/layout/base.html +117 -0
  61. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +82 -0
  62. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +40 -0
  63. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +37 -0
  64. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +41 -0
  65. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +50 -0
  66. django_cfg/apps/tasks/urls.py +2 -2
  67. django_cfg/apps/tasks/urls_admin.py +2 -2
  68. django_cfg/apps/tasks/utils/__init__.py +1 -0
  69. django_cfg/apps/tasks/utils/simulator.py +356 -0
  70. django_cfg/apps/tasks/views/__init__.py +16 -0
  71. django_cfg/apps/tasks/views/api.py +569 -0
  72. django_cfg/apps/tasks/views/dashboard.py +58 -0
  73. django_cfg/core/integration/__init__.py +21 -0
  74. django_cfg/management/commands/rundramatiq_simulator.py +430 -0
  75. django_cfg/models/constance.py +0 -11
  76. django_cfg/models/payments.py +137 -3
  77. django_cfg/modules/django_tasks.py +54 -21
  78. django_cfg/registry/core.py +4 -9
  79. django_cfg/template_archive/django_sample.zip +0 -0
  80. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/METADATA +2 -2
  81. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/RECORD +84 -152
  82. django_cfg/apps/payments/config/constance/__init__.py +0 -22
  83. django_cfg/apps/payments/config/constance/config_service.py +0 -123
  84. django_cfg/apps/payments/config/constance/fields.py +0 -69
  85. django_cfg/apps/payments/config/constance/settings.py +0 -160
  86. django_cfg/apps/payments/migrations/0002_currency_usd_rate_currency_usd_rate_updated_at.py +0 -26
  87. django_cfg/apps/payments/migrations/0003_remove_provider_currency_fields.py +0 -28
  88. django_cfg/apps/payments/migrations/0004_add_reserved_usd_field.py +0 -30
  89. django_cfg/apps/tasks/static/tasks/js/dashboard.js +0 -614
  90. django_cfg/apps/tasks/static/tasks/js/modals.js +0 -452
  91. django_cfg/apps/tasks/static/tasks/js/notifications.js +0 -144
  92. django_cfg/apps/tasks/static/tasks/js/task-monitor.js +0 -454
  93. django_cfg/apps/tasks/static/tasks/js/theme.js +0 -77
  94. django_cfg/apps/tasks/templates/tasks/base.html +0 -96
  95. django_cfg/apps/tasks/templates/tasks/components/info_cards.html +0 -85
  96. django_cfg/apps/tasks/templates/tasks/components/overview_tab.html +0 -22
  97. django_cfg/apps/tasks/templates/tasks/components/queues_tab.html +0 -19
  98. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -103
  99. django_cfg/apps/tasks/templates/tasks/components/tasks_tab.html +0 -32
  100. django_cfg/apps/tasks/templates/tasks/components/workers_tab.html +0 -29
  101. django_cfg/apps/tasks/templates/tasks/dashboard.html +0 -29
  102. django_cfg/apps/tasks/views.py +0 -461
  103. django_cfg/management/commands/app_agent_diagnose.py +0 -470
  104. django_cfg/management/commands/app_agent_generate.py +0 -342
  105. django_cfg/management/commands/app_agent_info.py +0 -308
  106. django_cfg/management/commands/auto_generate.py +0 -486
  107. django_cfg/modules/django_app_agent/__init__.py +0 -87
  108. django_cfg/modules/django_app_agent/agents/__init__.py +0 -40
  109. django_cfg/modules/django_app_agent/agents/base/__init__.py +0 -24
  110. django_cfg/modules/django_app_agent/agents/base/agent.py +0 -354
  111. django_cfg/modules/django_app_agent/agents/base/context.py +0 -236
  112. django_cfg/modules/django_app_agent/agents/base/executor.py +0 -430
  113. django_cfg/modules/django_app_agent/agents/generation/__init__.py +0 -12
  114. django_cfg/modules/django_app_agent/agents/generation/app_generator/__init__.py +0 -15
  115. django_cfg/modules/django_app_agent/agents/generation/app_generator/config_validator.py +0 -147
  116. django_cfg/modules/django_app_agent/agents/generation/app_generator/main.py +0 -99
  117. django_cfg/modules/django_app_agent/agents/generation/app_generator/models.py +0 -32
  118. django_cfg/modules/django_app_agent/agents/generation/app_generator/prompt_manager.py +0 -290
  119. django_cfg/modules/django_app_agent/agents/interfaces.py +0 -376
  120. django_cfg/modules/django_app_agent/core/__init__.py +0 -33
  121. django_cfg/modules/django_app_agent/core/config.py +0 -300
  122. django_cfg/modules/django_app_agent/core/exceptions.py +0 -359
  123. django_cfg/modules/django_app_agent/models/__init__.py +0 -71
  124. django_cfg/modules/django_app_agent/models/base.py +0 -283
  125. django_cfg/modules/django_app_agent/models/context.py +0 -496
  126. django_cfg/modules/django_app_agent/models/enums.py +0 -481
  127. django_cfg/modules/django_app_agent/models/requests.py +0 -500
  128. django_cfg/modules/django_app_agent/models/responses.py +0 -585
  129. django_cfg/modules/django_app_agent/pytest.ini +0 -6
  130. django_cfg/modules/django_app_agent/services/__init__.py +0 -42
  131. django_cfg/modules/django_app_agent/services/app_generator/__init__.py +0 -30
  132. django_cfg/modules/django_app_agent/services/app_generator/ai_integration.py +0 -133
  133. django_cfg/modules/django_app_agent/services/app_generator/context.py +0 -40
  134. django_cfg/modules/django_app_agent/services/app_generator/main.py +0 -202
  135. django_cfg/modules/django_app_agent/services/app_generator/structure.py +0 -316
  136. django_cfg/modules/django_app_agent/services/app_generator/validation.py +0 -125
  137. django_cfg/modules/django_app_agent/services/base.py +0 -437
  138. django_cfg/modules/django_app_agent/services/context_builder/__init__.py +0 -34
  139. django_cfg/modules/django_app_agent/services/context_builder/code_extractor.py +0 -141
  140. django_cfg/modules/django_app_agent/services/context_builder/context_generator.py +0 -276
  141. django_cfg/modules/django_app_agent/services/context_builder/main.py +0 -272
  142. django_cfg/modules/django_app_agent/services/context_builder/models.py +0 -40
  143. django_cfg/modules/django_app_agent/services/context_builder/pattern_analyzer.py +0 -85
  144. django_cfg/modules/django_app_agent/services/project_scanner/__init__.py +0 -31
  145. django_cfg/modules/django_app_agent/services/project_scanner/app_discovery.py +0 -311
  146. django_cfg/modules/django_app_agent/services/project_scanner/main.py +0 -221
  147. django_cfg/modules/django_app_agent/services/project_scanner/models.py +0 -59
  148. django_cfg/modules/django_app_agent/services/project_scanner/pattern_detection.py +0 -94
  149. django_cfg/modules/django_app_agent/services/questioning_service/__init__.py +0 -28
  150. django_cfg/modules/django_app_agent/services/questioning_service/main.py +0 -273
  151. django_cfg/modules/django_app_agent/services/questioning_service/models.py +0 -111
  152. django_cfg/modules/django_app_agent/services/questioning_service/question_generator.py +0 -251
  153. django_cfg/modules/django_app_agent/services/questioning_service/response_processor.py +0 -347
  154. django_cfg/modules/django_app_agent/services/questioning_service/session_manager.py +0 -356
  155. django_cfg/modules/django_app_agent/services/report_service.py +0 -332
  156. django_cfg/modules/django_app_agent/services/template_manager/__init__.py +0 -18
  157. django_cfg/modules/django_app_agent/services/template_manager/jinja_engine.py +0 -236
  158. django_cfg/modules/django_app_agent/services/template_manager/main.py +0 -159
  159. django_cfg/modules/django_app_agent/services/template_manager/models.py +0 -36
  160. django_cfg/modules/django_app_agent/services/template_manager/template_loader.py +0 -100
  161. django_cfg/modules/django_app_agent/services/template_manager/templates/admin.py.j2 +0 -105
  162. django_cfg/modules/django_app_agent/services/template_manager/templates/apps.py.j2 +0 -31
  163. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_config.py.j2 +0 -44
  164. django_cfg/modules/django_app_agent/services/template_manager/templates/cfg_module.py.j2 +0 -81
  165. django_cfg/modules/django_app_agent/services/template_manager/templates/forms.py.j2 +0 -107
  166. django_cfg/modules/django_app_agent/services/template_manager/templates/models.py.j2 +0 -139
  167. django_cfg/modules/django_app_agent/services/template_manager/templates/serializers.py.j2 +0 -91
  168. django_cfg/modules/django_app_agent/services/template_manager/templates/tests.py.j2 +0 -195
  169. django_cfg/modules/django_app_agent/services/template_manager/templates/urls.py.j2 +0 -35
  170. django_cfg/modules/django_app_agent/services/template_manager/templates/views.py.j2 +0 -211
  171. django_cfg/modules/django_app_agent/services/template_manager/variable_processor.py +0 -200
  172. django_cfg/modules/django_app_agent/services/validation_service/__init__.py +0 -25
  173. django_cfg/modules/django_app_agent/services/validation_service/django_validator.py +0 -333
  174. django_cfg/modules/django_app_agent/services/validation_service/main.py +0 -242
  175. django_cfg/modules/django_app_agent/services/validation_service/models.py +0 -66
  176. django_cfg/modules/django_app_agent/services/validation_service/quality_validator.py +0 -352
  177. django_cfg/modules/django_app_agent/services/validation_service/security_validator.py +0 -272
  178. django_cfg/modules/django_app_agent/services/validation_service/syntax_validator.py +0 -203
  179. django_cfg/modules/django_app_agent/ui/__init__.py +0 -25
  180. django_cfg/modules/django_app_agent/ui/cli.py +0 -419
  181. django_cfg/modules/django_app_agent/ui/rich_components.py +0 -622
  182. django_cfg/modules/django_app_agent/utils/__init__.py +0 -38
  183. django_cfg/modules/django_app_agent/utils/logging.py +0 -360
  184. django_cfg/modules/django_app_agent/utils/validation.py +0 -417
  185. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/WHEEL +0 -0
  186. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/entry_points.txt +0 -0
  187. {django_cfg-1.3.9.dist-info → django_cfg-1.3.11.dist-info}/licenses/LICENSE +0 -0
django_cfg/__init__.py CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.3.9"
35
+ __version__ = "1.3.11"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
@@ -143,6 +143,7 @@ class ProviderCurrencyAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
143
143
  # List configuration
144
144
  list_display = [
145
145
  'currency_display',
146
+ 'provider_currency_code_display',
146
147
  'network_display',
147
148
  'provider_display',
148
149
  'fees_display',
@@ -163,7 +164,8 @@ class ProviderCurrencyAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
163
164
  'currency__name',
164
165
  'currency__symbol',
165
166
  'network__name',
166
- 'provider'
167
+ 'provider',
168
+ 'provider_currency_code'
167
169
  ]
168
170
 
169
171
  readonly_fields = [
@@ -181,6 +183,15 @@ class ProviderCurrencyAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
181
183
  icon=Icons.CURRENCY_BITCOIN
182
184
  )
183
185
 
186
+ @display(description="Provider Code")
187
+ def provider_currency_code_display(self, obj: ProviderCurrency):
188
+ """Provider currency code display."""
189
+ return StatusBadge.create(
190
+ text=obj.provider_currency_code,
191
+ variant="warning",
192
+ icon=Icons.CODE
193
+ )
194
+
184
195
  @display(description="Network")
185
196
  def network_display(self, obj: ProviderCurrency):
186
197
  """Network display."""
@@ -56,6 +56,7 @@ class UniversalPaymentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
56
56
  'amount_display',
57
57
  'status_display',
58
58
  'provider_display',
59
+ 'status_changed_display',
59
60
  'created_display'
60
61
  ]
61
62
 
@@ -78,6 +79,7 @@ class UniversalPaymentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
78
79
  'internal_payment_id',
79
80
  'created_at',
80
81
  'updated_at',
82
+ 'status_changed_at',
81
83
  'payment_details_display'
82
84
  ]
83
85
 
@@ -168,6 +170,17 @@ class UniversalPaymentAdmin(OptimizedModelAdmin, DisplayMixin, ModelAdmin):
168
170
  DateTimeDisplayConfig(show_relative=True, show_seconds=False)
169
171
  )
170
172
 
173
+ @display(description="Status Changed")
174
+ def status_changed_display(self, obj):
175
+ """Status changed time display."""
176
+ if not obj.status_changed_at:
177
+ return "-"
178
+ return self.display_datetime_relative(
179
+ obj,
180
+ 'status_changed_at',
181
+ DateTimeDisplayConfig(show_relative=True, show_seconds=False)
182
+ )
183
+
171
184
  # Readonly field displays
172
185
  def payment_details_display(self, obj):
173
186
  """Detailed payment information for detail view."""
@@ -6,6 +6,8 @@ DRF serializers for payment management in admin dashboard.
6
6
 
7
7
  from rest_framework import serializers
8
8
  from django.contrib.auth import get_user_model
9
+ from django.contrib.humanize.templatetags.humanize import naturaltime
10
+
9
11
  from ...models import UniversalPayment
10
12
 
11
13
  User = get_user_model()
@@ -90,6 +92,7 @@ class AdminPaymentDetailSerializer(serializers.Serializer):
90
92
  security_nonce = serializers.CharField(read_only=True)
91
93
  expires_at = serializers.DateTimeField(read_only=True)
92
94
  completed_at = serializers.DateTimeField(read_only=True)
95
+ status_changed_at = serializers.DateTimeField(read_only=True)
93
96
  description = serializers.CharField(read_only=True)
94
97
  callback_url = serializers.URLField(read_only=True)
95
98
  cancel_url = serializers.URLField(read_only=True)
@@ -117,7 +120,6 @@ class AdminPaymentDetailSerializer(serializers.Serializer):
117
120
 
118
121
  def get_age(self, obj):
119
122
  """Get human-readable age of payment."""
120
- from django.contrib.humanize.templatetags.humanize import naturaltime
121
123
  return naturaltime(obj.created_at)
122
124
 
123
125
 
@@ -128,7 +130,7 @@ class AdminPaymentCreateSerializer(serializers.Serializer):
128
130
  """
129
131
  user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
130
132
  amount_usd = serializers.FloatField(min_value=1.0, max_value=100000.0)
131
- currency_code = serializers.CharField(max_length=10, help_text="Currency code (e.g., BTC, ETH)")
133
+ currency_code = serializers.CharField(max_length=20, help_text="Provider currency code (e.g., BTC, ZROERC20)", write_only=True)
132
134
  provider = serializers.CharField(max_length=50)
133
135
  description = serializers.CharField(required=False, allow_blank=True)
134
136
  callback_url = serializers.URLField(required=False, allow_blank=True)
@@ -143,22 +145,67 @@ class AdminPaymentCreateSerializer(serializers.Serializer):
143
145
  return value
144
146
 
145
147
  def create(self, validated_data):
146
- """Create payment with currency lookup."""
147
- from django_cfg.apps.payments.models.currencies import Currency
148
- import uuid
148
+ """Create payment using PaymentService for proper provider integration."""
149
+ from django_cfg.apps.payments.services.core.payment_service import PaymentService
150
+ from django_cfg.apps.payments.services.types.requests import PaymentCreateRequest
151
+ from django_cfg.apps.payments.models import ProviderCurrency
152
+
153
+ # Extract provider_currency_code and find original currency
154
+ provider_currency_code = validated_data.pop('currency_code')
149
155
 
150
- # Extract currency_code and find Currency object
151
- currency_code = validated_data.pop('currency_code')
156
+ # Find the ProviderCurrency to get original currency code
152
157
  try:
153
- currency = Currency.objects.get(code=currency_code)
154
- except Currency.DoesNotExist:
155
- raise serializers.ValidationError(f"Currency {currency_code} not found")
158
+ provider_currency = ProviderCurrency.objects.select_related('currency').get(
159
+ provider_currency_code=provider_currency_code,
160
+ provider=validated_data['provider'],
161
+ is_enabled=True
162
+ )
163
+ original_currency_code = provider_currency.currency.code
164
+ except ProviderCurrency.DoesNotExist:
165
+ raise serializers.ValidationError(f"Provider currency {provider_currency_code} not found for {validated_data['provider']}")
156
166
 
157
- # Generate internal payment ID and create payment
158
- validated_data['currency'] = currency
159
- validated_data['internal_payment_id'] = f"PAY-{uuid.uuid4().hex[:12].upper()}"
167
+ # Create PaymentCreateRequest for the service
168
+ payment_request = PaymentCreateRequest(
169
+ user_id=validated_data['user'].id,
170
+ amount_usd=validated_data['amount_usd'],
171
+ currency_code=original_currency_code, # Use original currency code
172
+ provider=validated_data['provider'],
173
+ description=validated_data.get('description', ''),
174
+ callback_url=validated_data.get('callback_url', ''),
175
+ cancel_url=validated_data.get('cancel_url', ''),
176
+ metadata={'provider_currency_code': provider_currency_code} # Store provider code in metadata
177
+ )
160
178
 
161
- return UniversalPayment.objects.create(**validated_data)
179
+ # Use PaymentService to create payment with provider integration
180
+ payment_service = PaymentService()
181
+ result = payment_service.create_payment(payment_request)
182
+
183
+ if result.success:
184
+ # Get the created payment object from database using payment_id
185
+ from django_cfg.apps.payments.models import UniversalPayment
186
+ payment = UniversalPayment.objects.get(id=result.payment_id)
187
+ return payment
188
+ else:
189
+ raise serializers.ValidationError(f"Payment creation failed: {result.message}")
190
+
191
+ def to_representation(self, instance):
192
+ """Return created payment with ID and currency info."""
193
+ if instance:
194
+ return {
195
+ 'id': str(instance.id),
196
+ 'user': instance.user.id,
197
+ 'amount_usd': instance.amount_usd,
198
+ 'currency_code': instance.currency.code if instance.currency else None,
199
+ 'currency_name': instance.currency.name if instance.currency else None,
200
+ 'provider': instance.provider,
201
+ 'description': instance.description,
202
+ 'callback_url': instance.callback_url,
203
+ 'cancel_url': instance.cancel_url,
204
+ 'internal_payment_id': instance.internal_payment_id,
205
+ 'status': instance.status,
206
+ 'created_at': instance.created_at.isoformat() if instance.created_at else None
207
+ }
208
+ return super().to_representation(instance)
162
209
 
163
210
 
164
211
  class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
@@ -178,6 +225,7 @@ class AdminPaymentUpdateSerializer(serializers.ModelSerializer):
178
225
  if value != UniversalPayment.PaymentStatus.COMPLETED:
179
226
  raise serializers.ValidationError("Cannot change status of completed payment")
180
227
  return value
228
+
181
229
 
182
230
 
183
231
  class AdminPaymentStatsSerializer(serializers.Serializer):
@@ -0,0 +1,121 @@
1
+ {% comment %}
2
+ Payment Card Component
3
+
4
+ Usage:
5
+ {% include 'payments/components/payment_card.html' with payment=payment show_actions=True %}
6
+
7
+ Parameters:
8
+ - payment: UniversalPayment object
9
+ - show_actions: Boolean to show/hide action buttons (default: False)
10
+ {% endcomment %}
11
+
12
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6"
13
+ data-payment-id="{{ payment.id }}">
14
+
15
+ <!-- Header -->
16
+ <div class="flex items-center justify-between mb-4">
17
+ <div class="payment-id">
18
+ <span class="text-sm text-gray-500 dark:text-gray-400">Payment</span>
19
+ <p class="text-lg font-semibold text-gray-900 dark:text-white">
20
+ #{{ payment.internal_payment_id|default:payment.id|truncatechars:8 }}
21
+ </p>
22
+ </div>
23
+ {% include 'payments/components/status_badge.html' with payment=payment %}
24
+ </div>
25
+
26
+ <!-- Amount -->
27
+ <div class="payment-amount mb-4">
28
+ <div class="flex items-baseline">
29
+ <span class="text-3xl font-bold text-gray-900 dark:text-white">
30
+ ${{ payment.amount_usd|floatformat:2 }}
31
+ </span>
32
+ <span class="ml-2 text-lg text-gray-500 dark:text-gray-400">USD</span>
33
+ </div>
34
+ {% if payment.currency.code != 'USD' and payment.pay_amount %}
35
+ <div class="text-sm text-gray-500 dark:text-gray-400">
36
+ {{ payment.pay_amount|floatformat:8 }} {{ payment.currency.code }}
37
+ </div>
38
+ {% endif %}
39
+ </div>
40
+
41
+ <!-- Payment Details -->
42
+ <div class="payment-details space-y-2 mb-4">
43
+ <div class="flex justify-between">
44
+ <span class="text-sm text-gray-500 dark:text-gray-400">Provider</span>
45
+ <div class="flex items-center">
46
+ <div class="w-2 h-2 rounded-full mr-2
47
+ {% if payment.provider == 'cryptapi' %}bg-orange-500
48
+ {% elif payment.provider == 'cryptomus' %}bg-blue-500
49
+ {% elif payment.provider == 'stripe' %}bg-purple-500
50
+ {% elif payment.provider == 'nowpayments' %}bg-green-500
51
+ {% else %}bg-gray-500{% endif %}">
52
+ </div>
53
+ <span class="text-sm font-medium text-gray-900 dark:text-white capitalize">
54
+ {{ payment.provider }}
55
+ </span>
56
+ </div>
57
+ </div>
58
+ <div class="flex justify-between">
59
+ <span class="text-sm text-gray-500 dark:text-gray-400">Created</span>
60
+ <span class="text-sm text-gray-900 dark:text-white">
61
+ {{ payment.created_at|date:"M d, H:i" }}
62
+ </span>
63
+ </div>
64
+ {% if payment.status_changed_at %}
65
+ <div class="flex justify-between">
66
+ <span class="text-sm text-gray-500 dark:text-gray-400">Status Changed</span>
67
+ <span class="text-sm text-gray-900 dark:text-white">
68
+ {{ payment.status_changed_at|date:"M d, H:i" }}
69
+ </span>
70
+ </div>
71
+ {% endif %}
72
+ {% if payment.processed_at %}
73
+ <div class="flex justify-between">
74
+ <span class="text-sm text-gray-500 dark:text-gray-400">Processed</span>
75
+ <span class="text-sm text-gray-900 dark:text-white">
76
+ {{ payment.processed_at|date:"M d, H:i" }}
77
+ </span>
78
+ </div>
79
+ {% endif %}
80
+ {% if payment.pay_address %}
81
+ <div class="flex justify-between">
82
+ <span class="text-sm text-gray-500 dark:text-gray-400">Address</span>
83
+ <span class="text-sm font-mono text-gray-900 dark:text-white">
84
+ {{ payment.pay_address|truncatechars:16 }}
85
+ <button onclick="navigator.clipboard.writeText('{{ payment.pay_address }}')"
86
+ class="ml-1 text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300">
87
+ <span class="material-icons-outlined text-xs">content_copy</span>
88
+ </button>
89
+ </span>
90
+ </div>
91
+ {% endif %}
92
+ </div>
93
+
94
+ <!-- Progress Bar -->
95
+ {% include 'payments/components/progress_bar.html' with payment=payment %}
96
+
97
+ <!-- Actions -->
98
+ {% if show_actions %}
99
+ <div class="payment-actions mt-4 flex space-x-2">
100
+ <a href="{% url 'cfg_payments_admin:payment-detail' payment.id %}"
101
+ class="flex-1 px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors duration-200 text-center">
102
+ <span class="material-icons-outlined text-sm mr-1">visibility</span>
103
+ View Details
104
+ </a>
105
+ {% if payment.status == 'pending' or payment.status == 'confirming' %}
106
+ <button class="px-3 py-1.5 text-xs bg-red-600 text-white hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 rounded-md transition-colors duration-200"
107
+ onclick="cancelPayment('{{ payment.id }}')">
108
+ <span class="material-icons-outlined text-sm mr-1">cancel</span>
109
+ Cancel
110
+ </button>
111
+ {% endif %}
112
+ {% if payment.pay_address %}
113
+ <button class="px-3 py-1.5 text-xs bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 rounded-md transition-colors duration-200"
114
+ onclick="showQRCode('{{ payment.id }}')">
115
+ <span class="material-icons-outlined text-sm mr-1">qr_code</span>
116
+ QR Code
117
+ </button>
118
+ {% endif %}
119
+ </div>
120
+ {% endif %}
121
+ </div>
@@ -0,0 +1,95 @@
1
+ {% load payment_tags %}
2
+
3
+ <!-- Payment QR Code Component -->
4
+ <div class="flex flex-col items-center space-y-4 p-6 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
5
+ {% if payment.pay_address %}
6
+ <!-- QR Code Image -->
7
+ <div class="w-48 h-48 bg-gray-100 dark:bg-gray-700 rounded flex items-center justify-center">
8
+ {% if payment.get_qr_code_url %}
9
+ <img src="{{ payment.get_qr_code_url }}"
10
+ alt="QR Code for {{ payment.pay_address }}"
11
+ class="w-48 h-48 rounded"
12
+ onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
13
+ <div class="hidden w-48 h-48 items-center justify-center">
14
+ <span class="material-icons text-gray-400 text-4xl">qr_code_off</span>
15
+ </div>
16
+ {% else %}
17
+ <span class="material-icons text-gray-400 text-4xl">qr_code_off</span>
18
+ {% endif %}
19
+ </div>
20
+
21
+ <!-- Payment Details -->
22
+ <div class="text-center space-y-2">
23
+ <p class="text-sm font-medium text-gray-900 dark:text-white">
24
+ Payment Address
25
+ </p>
26
+ <p class="text-xs font-mono text-gray-600 dark:text-gray-400 break-all max-w-xs">
27
+ {{ payment.pay_address }}
28
+ </p>
29
+ {% if payment.pay_amount %}
30
+ <p class="text-xs text-gray-500 dark:text-gray-500">
31
+ Amount: {{ payment.formatted_pay_amount }} {{ payment.currency.code }}
32
+ </p>
33
+ {% endif %}
34
+
35
+ <!-- QR Data Preview -->
36
+ {% if payment.qr_data != payment.pay_address %}
37
+ <p class="text-xs text-blue-600 dark:text-blue-400">
38
+ QR contains: {{ payment.qr_data|truncatechars:50 }}
39
+ </p>
40
+ {% endif %}
41
+ </div>
42
+
43
+ <!-- Action Buttons -->
44
+ <div class="flex space-x-2 pt-2">
45
+ <button class="flex-1 px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors duration-200"
46
+ onclick="copyToClipboard('{{ payment.pay_address }}', 'Address copied!')">
47
+ <span class="material-icons text-sm mr-1">content_copy</span>
48
+ Copy Address
49
+ </button>
50
+ {% if payment.pay_amount %}
51
+ <button class="flex-1 px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors duration-200"
52
+ onclick="copyToClipboard('{{ payment.pay_amount }}', 'Amount copied!')">
53
+ <span class="material-icons text-sm mr-1">content_copy</span>
54
+ Copy Amount
55
+ </button>
56
+ {% endif %}
57
+ {% if payment.qr_data %}
58
+ <button class="flex-1 px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors duration-200"
59
+ onclick="copyToClipboard('{{ payment.qr_data }}', 'QR data copied!')">
60
+ <span class="material-icons text-sm mr-1">qr_code</span>
61
+ Copy QR Data
62
+ </button>
63
+ {% endif %}
64
+ </div>
65
+
66
+
67
+ {% else %}
68
+ <div class="text-center py-8">
69
+ <span class="material-icons text-gray-400 text-4xl mb-2">qr_code_off</span>
70
+ <p class="text-sm text-gray-500 dark:text-gray-400">
71
+ QR code not available for this payment
72
+ </p>
73
+ </div>
74
+ {% endif %}
75
+ </div>
76
+
77
+ <!-- Copy to clipboard utility -->
78
+ <script>
79
+ function copyToClipboard(text, message) {
80
+ navigator.clipboard.writeText(text).then(function() {
81
+ // Show success message (you can customize this)
82
+ const toast = document.createElement('div');
83
+ toast.className = 'fixed top-4 right-4 bg-green-500 text-white px-4 py-2 rounded shadow-lg z-50';
84
+ toast.textContent = message || 'Copied to clipboard!';
85
+ document.body.appendChild(toast);
86
+
87
+ setTimeout(() => {
88
+ document.body.removeChild(toast);
89
+ }, 2000);
90
+ }).catch(function(err) {
91
+ console.error('Failed to copy: ', err);
92
+ alert('Failed to copy to clipboard');
93
+ });
94
+ }
95
+ </script>
@@ -0,0 +1,37 @@
1
+ {% comment %}
2
+ Payment Progress Bar Component
3
+
4
+ Usage:
5
+ {% include 'payments/components/progress_bar.html' with payment=payment %}
6
+
7
+ Parameters:
8
+ - payment: UniversalPayment object
9
+ {% endcomment %}
10
+
11
+ <div class="payment-progress">
12
+ <!-- Progress Bar -->
13
+ <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
14
+ <div class="h-2 rounded-full transition-all duration-500 ease-out
15
+ {% if payment.status == 'completed' %}bg-green-600
16
+ {% elif payment.status == 'failed' %}bg-red-600
17
+ {% elif payment.status == 'pending' %}bg-yellow-600
18
+ {% elif payment.status == 'confirming' %}bg-blue-600
19
+ {% elif payment.status == 'cancelled' %}bg-gray-600
20
+ {% else %}bg-blue-600{% endif %}"
21
+ style="width: {% if payment.status == 'completed' %}100{% elif payment.status == 'failed' or payment.status == 'cancelled' %}100{% elif payment.status == 'confirming' %}75{% elif payment.status == 'pending' %}25{% else %}0{% endif %}%">
22
+ </div>
23
+ </div>
24
+
25
+ <!-- Progress Text -->
26
+ <div class="flex justify-between items-center mt-2 text-xs text-gray-500 dark:text-gray-400">
27
+ <span>
28
+ {% if payment.status == 'completed' %}100% Complete
29
+ {% elif payment.status == 'failed' %}Failed
30
+ {% elif payment.status == 'cancelled' %}Cancelled
31
+ {% elif payment.status == 'confirming' %}75% Confirming
32
+ {% elif payment.status == 'pending' %}25% Pending
33
+ {% else %}0% Initiated{% endif %}
34
+ </span>
35
+ <span>{{ payment.get_status_display }}</span>
36
+ </div>
37
+ </div>
@@ -0,0 +1,60 @@
1
+ {% comment %}
2
+ Provider Statistics Component
3
+
4
+ Usage:
5
+ {% include 'payments/components/provider_stats.html' with provider_stats=provider_stats %}
6
+
7
+ Parameters:
8
+ - provider_stats: List of provider statistics objects
9
+ {% endcomment %}
10
+
11
+ <div class="space-y-4">
12
+ {% for stat in provider_stats %}
13
+ <div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600">
14
+ <div class="flex items-center space-x-3">
15
+ <!-- Provider Icon -->
16
+ <div class="w-8 h-8 rounded-full flex items-center justify-center
17
+ {% if stat.provider == 'cryptapi' %}bg-orange-500
18
+ {% elif stat.provider == 'cryptomus' %}bg-blue-500
19
+ {% elif stat.provider == 'stripe' %}bg-purple-500
20
+ {% elif stat.provider == 'nowpayments' %}bg-green-500
21
+ {% else %}bg-gray-500{% endif %}">
22
+ <span class="material-icons-outlined text-white text-sm">payment</span>
23
+ </div>
24
+
25
+ <!-- Provider Info -->
26
+ <div>
27
+ <p class="text-sm font-medium text-gray-900 dark:text-white capitalize">
28
+ {{ stat.provider }}
29
+ </p>
30
+ <p class="text-xs text-gray-500 dark:text-gray-400">
31
+ {{ stat.count }} payment{{ stat.count|pluralize }}
32
+ </p>
33
+ </div>
34
+ </div>
35
+
36
+ <!-- Statistics -->
37
+ <div class="text-right">
38
+ <p class="text-sm font-medium text-gray-900 dark:text-white">
39
+ ${{ stat.volume|floatformat:2|default:"0.00" }}
40
+ </p>
41
+ <div class="flex items-center space-x-2 mt-1">
42
+ <!-- Success Rate Bar -->
43
+ <div class="w-16 bg-gray-200 dark:bg-gray-600 rounded-full h-1.5">
44
+ <div class="bg-green-600 h-1.5 rounded-full transition-all duration-300"
45
+ style="width: {{ stat.success_rate|floatformat:0 }}%"></div>
46
+ </div>
47
+ <span class="text-xs text-gray-500 dark:text-gray-400">
48
+ {{ stat.success_rate|floatformat:0 }}%
49
+ </span>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ {% empty %}
54
+ <div class="text-center py-8">
55
+ <span class="material-icons-outlined text-gray-400 text-4xl mb-2">bar_chart</span>
56
+ <p class="text-sm font-medium text-gray-900 dark:text-white mb-1">No Provider Data</p>
57
+ <p class="text-xs text-gray-500 dark:text-gray-400">Provider statistics will appear here once payments are processed</p>
58
+ </div>
59
+ {% endfor %}
60
+ </div>
@@ -0,0 +1,41 @@
1
+ {% comment %}
2
+ Payment Status Badge Component
3
+
4
+ Usage:
5
+ {% include 'payments/components/status_badge.html' with payment=payment %}
6
+
7
+ Parameters:
8
+ - payment: UniversalPayment object
9
+ {% endcomment %}
10
+
11
+ <span class="payment-status-badge inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
12
+ {% if payment.status == 'pending' %}bg-yellow-100 text-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-400
13
+ {% elif payment.status == 'confirming' %}bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-400
14
+ {% elif payment.status == 'confirmed' or payment.status == 'completed' %}bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400
15
+ {% elif payment.status == 'failed' %}bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400
16
+ {% elif payment.status == 'expired' or payment.status == 'cancelled' %}bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400
17
+ {% elif payment.status == 'refunded' %}bg-purple-100 text-purple-800 dark:bg-purple-900/20 dark:text-purple-400
18
+ {% else %}bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400{% endif %}"
19
+ data-status="{{ payment.status }}">
20
+ {% if payment.status == 'pending' %}
21
+ <span class="material-icons-outlined text-sm mr-1 animate-pulse">pending</span>
22
+ {% elif payment.status == 'confirming' %}
23
+ <span class="material-icons-outlined text-sm mr-1 animate-spin">sync</span>
24
+ {% elif payment.status == 'confirmed' %}
25
+ <span class="material-icons-outlined text-sm mr-1">check_circle</span>
26
+ {% elif payment.status == 'completed' %}
27
+ <span class="material-icons-outlined text-sm mr-1">verified</span>
28
+ {% elif payment.status == 'failed' %}
29
+ <span class="material-icons-outlined text-sm mr-1">error</span>
30
+ {% elif payment.status == 'expired' %}
31
+ <span class="material-icons-outlined text-sm mr-1">schedule</span>
32
+ {% elif payment.status == 'cancelled' %}
33
+ <span class="material-icons-outlined text-sm mr-1">cancel</span>
34
+ {% elif payment.status == 'refunded' %}
35
+ <span class="material-icons-outlined text-sm mr-1">undo</span>
36
+ {% else %}
37
+ <span class="material-icons-outlined text-sm mr-1">help</span>
38
+ {% endif %}
39
+
40
+ <span class="status-text">{{ payment.get_status_display }}</span>
41
+ </span>
@@ -0,0 +1,83 @@
1
+ {% comment %}
2
+ Status Overview Component
3
+
4
+ Usage:
5
+ {% include 'payments/components/status_overview.html' with payment_stats=payment_stats %}
6
+
7
+ Parameters:
8
+ - payment_stats: Payment statistics object with counts and totals
9
+ {% endcomment %}
10
+
11
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
12
+ <!-- Total Payments -->
13
+ {% include 'payments/components/status_card.html' with title="Total Payments" value=payment_stats.total_payments_count|default:0 icon="payments" status="info" %}
14
+
15
+ <!-- Pending Payments -->
16
+ {% include 'payments/components/status_card.html' with title="Pending" value=payment_stats.pending_payments_count|default:0 icon="pending" status="warning" description=payment_stats.confirming_payments_count|add:" confirming"|default:"" %}
17
+
18
+ <!-- Completed Payments -->
19
+ {% include 'payments/components/status_card.html' with title="Completed" value=payment_stats.completed_payments_count|default:0 icon="check_circle" status="success" description=payment_stats.success_rate|floatformat:1|add:"% success rate"|default:"" %}
20
+
21
+ <!-- Total Volume -->
22
+ {% include 'payments/components/status_card.html' with title="Total Volume" value="$"|add:payment_stats.total_volume|floatformat:2|default:"0.00" icon="account_balance" status="info" description=payment_stats.failed_payments_count|add:" failed"|default:"" %}
23
+ </div>
24
+
25
+ <!-- Quick Actions Bar -->
26
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700 p-4 mt-6">
27
+ <div class="flex items-center justify-between">
28
+ <div class="flex items-center space-x-4">
29
+ <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Quick Actions:</span>
30
+ <div class="flex space-x-2">
31
+ <a href="{% url 'cfg_payments_admin:payment-form' %}"
32
+ class="inline-flex items-center px-3 py-1.5 text-xs bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 rounded-md transition-colors">
33
+ <span class="material-icons-outlined text-sm mr-1">add</span>
34
+ New Payment
35
+ </a>
36
+ <button onclick="window.location.reload()"
37
+ class="inline-flex items-center px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors">
38
+ <span class="material-icons-outlined text-sm mr-1">refresh</span>
39
+ Refresh
40
+ </button>
41
+ <button onclick="exportPayments()"
42
+ class="inline-flex items-center px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-500 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 rounded-md transition-colors">
43
+ <span class="material-icons-outlined text-sm mr-1">download</span>
44
+ Export
45
+ </button>
46
+ </div>
47
+ </div>
48
+
49
+ <!-- Live Status Indicator -->
50
+ <div class="flex items-center space-x-2">
51
+ <div class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400">
52
+ <div class="w-2 h-2 bg-green-500 rounded-full mr-2 animate-pulse"></div>
53
+ Live Updates
54
+ </div>
55
+ <span class="text-xs text-gray-500 dark:text-gray-400">
56
+ Last update: <span id="stats-last-update">{% now "H:i:s" %}</span>
57
+ </span>
58
+ </div>
59
+ </div>
60
+ </div>
61
+
62
+ <script>
63
+ // Export payments functionality
64
+ function exportPayments() {
65
+ // TODO: Implement export functionality
66
+ alert('Export functionality not implemented yet');
67
+ }
68
+
69
+ // Update timestamp every minute
70
+ setInterval(function() {
71
+ const now = new Date();
72
+ const timeString = now.toLocaleTimeString('en-US', {
73
+ hour12: false,
74
+ hour: '2-digit',
75
+ minute: '2-digit',
76
+ second: '2-digit'
77
+ });
78
+ const element = document.getElementById('stats-last-update');
79
+ if (element) {
80
+ element.textContent = timeString;
81
+ }
82
+ }, 60000);
83
+ </script>