django-cfg 1.2.21__py3-none-any.whl → 1.2.23__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 (92) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/newsletter/signals.py +9 -8
  3. django_cfg/apps/payments/__init__.py +8 -0
  4. django_cfg/apps/payments/admin/__init__.py +23 -0
  5. django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
  6. django_cfg/apps/payments/admin/balance_admin.py +434 -0
  7. django_cfg/apps/payments/admin/currencies_admin.py +186 -0
  8. django_cfg/apps/payments/admin/filters.py +259 -0
  9. django_cfg/apps/payments/admin/payments_admin.py +142 -0
  10. django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
  11. django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
  12. django_cfg/apps/payments/apps.py +22 -0
  13. django_cfg/apps/payments/config/__init__.py +87 -0
  14. django_cfg/apps/payments/config/module.py +162 -0
  15. django_cfg/apps/payments/config/providers.py +93 -0
  16. django_cfg/apps/payments/config/settings.py +136 -0
  17. django_cfg/apps/payments/config/utils.py +198 -0
  18. django_cfg/apps/payments/decorators.py +291 -0
  19. django_cfg/apps/payments/managers/__init__.py +22 -0
  20. django_cfg/apps/payments/managers/api_key_manager.py +35 -0
  21. django_cfg/apps/payments/managers/balance_manager.py +361 -0
  22. django_cfg/apps/payments/managers/currency_manager.py +32 -0
  23. django_cfg/apps/payments/managers/payment_manager.py +44 -0
  24. django_cfg/apps/payments/managers/subscription_manager.py +37 -0
  25. django_cfg/apps/payments/managers/tariff_manager.py +29 -0
  26. django_cfg/apps/payments/middleware/__init__.py +13 -0
  27. django_cfg/apps/payments/middleware/api_access.py +261 -0
  28. django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
  29. django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
  30. django_cfg/apps/payments/migrations/0001_initial.py +1003 -0
  31. django_cfg/apps/payments/migrations/__init__.py +1 -0
  32. django_cfg/apps/payments/models/__init__.py +67 -0
  33. django_cfg/apps/payments/models/api_keys.py +96 -0
  34. django_cfg/apps/payments/models/balance.py +209 -0
  35. django_cfg/apps/payments/models/base.py +30 -0
  36. django_cfg/apps/payments/models/currencies.py +138 -0
  37. django_cfg/apps/payments/models/events.py +73 -0
  38. django_cfg/apps/payments/models/payments.py +301 -0
  39. django_cfg/apps/payments/models/subscriptions.py +270 -0
  40. django_cfg/apps/payments/models/tariffs.py +102 -0
  41. django_cfg/apps/payments/serializers/__init__.py +56 -0
  42. django_cfg/apps/payments/serializers/api_keys.py +51 -0
  43. django_cfg/apps/payments/serializers/balance.py +59 -0
  44. django_cfg/apps/payments/serializers/currencies.py +55 -0
  45. django_cfg/apps/payments/serializers/payments.py +62 -0
  46. django_cfg/apps/payments/serializers/subscriptions.py +71 -0
  47. django_cfg/apps/payments/serializers/tariffs.py +56 -0
  48. django_cfg/apps/payments/services/__init__.py +65 -0
  49. django_cfg/apps/payments/services/billing/__init__.py +8 -0
  50. django_cfg/apps/payments/services/cache/__init__.py +15 -0
  51. django_cfg/apps/payments/services/cache/base.py +30 -0
  52. django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
  53. django_cfg/apps/payments/services/core/__init__.py +17 -0
  54. django_cfg/apps/payments/services/core/balance_service.py +449 -0
  55. django_cfg/apps/payments/services/core/payment_service.py +393 -0
  56. django_cfg/apps/payments/services/core/subscription_service.py +616 -0
  57. django_cfg/apps/payments/services/internal_types.py +266 -0
  58. django_cfg/apps/payments/services/middleware/__init__.py +8 -0
  59. django_cfg/apps/payments/services/providers/__init__.py +19 -0
  60. django_cfg/apps/payments/services/providers/base.py +137 -0
  61. django_cfg/apps/payments/services/providers/cryptapi.py +262 -0
  62. django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
  63. django_cfg/apps/payments/services/providers/registry.py +99 -0
  64. django_cfg/apps/payments/services/validators/__init__.py +8 -0
  65. django_cfg/apps/payments/signals/__init__.py +13 -0
  66. django_cfg/apps/payments/signals/api_key_signals.py +150 -0
  67. django_cfg/apps/payments/signals/payment_signals.py +127 -0
  68. django_cfg/apps/payments/signals/subscription_signals.py +196 -0
  69. django_cfg/apps/payments/urls.py +78 -0
  70. django_cfg/apps/payments/utils/__init__.py +42 -0
  71. django_cfg/apps/payments/utils/config_utils.py +243 -0
  72. django_cfg/apps/payments/utils/middleware_utils.py +228 -0
  73. django_cfg/apps/payments/utils/validation_utils.py +94 -0
  74. django_cfg/apps/payments/views/__init__.py +62 -0
  75. django_cfg/apps/payments/views/api_key_views.py +164 -0
  76. django_cfg/apps/payments/views/balance_views.py +75 -0
  77. django_cfg/apps/payments/views/currency_views.py +111 -0
  78. django_cfg/apps/payments/views/payment_views.py +111 -0
  79. django_cfg/apps/payments/views/subscription_views.py +135 -0
  80. django_cfg/apps/payments/views/tariff_views.py +131 -0
  81. django_cfg/apps/support/signals.py +16 -4
  82. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  83. django_cfg/core/config.py +6 -0
  84. django_cfg/models/revolution.py +14 -0
  85. django_cfg/modules/base.py +9 -0
  86. django_cfg/modules/django_email.py +42 -4
  87. django_cfg/modules/django_unfold/dashboard.py +20 -0
  88. {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/METADATA +2 -1
  89. {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/RECORD +92 -14
  90. {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/WHEEL +0 -0
  91. {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/entry_points.txt +0 -0
  92. {django_cfg-1.2.21.dist-info → django_cfg-1.2.23.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1003 @@
1
+ # Generated by Django 5.2.6 on 2025-09-23 14:27
2
+
3
+ import django.core.validators
4
+ import django.db.models.deletion
5
+ import uuid
6
+ from django.conf import settings
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+ initial = True
12
+
13
+ dependencies = [
14
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.CreateModel(
19
+ name="Currency",
20
+ fields=[
21
+ (
22
+ "id",
23
+ models.BigAutoField(
24
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
25
+ ),
26
+ ),
27
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
28
+ ("updated_at", models.DateTimeField(auto_now=True)),
29
+ (
30
+ "code",
31
+ models.CharField(
32
+ help_text="Currency code (e.g., USD, BTC, ETH)", max_length=10, unique=True
33
+ ),
34
+ ),
35
+ ("name", models.CharField(help_text="Full currency name", max_length=100)),
36
+ (
37
+ "symbol",
38
+ models.CharField(help_text="Currency symbol (e.g., $, ₿, Ξ)", max_length=10),
39
+ ),
40
+ (
41
+ "currency_type",
42
+ models.CharField(
43
+ choices=[("fiat", "Fiat Currency"), ("crypto", "Cryptocurrency")],
44
+ help_text="Type of currency",
45
+ max_length=10,
46
+ ),
47
+ ),
48
+ (
49
+ "decimal_places",
50
+ models.PositiveSmallIntegerField(
51
+ default=2, help_text="Number of decimal places for this currency"
52
+ ),
53
+ ),
54
+ (
55
+ "is_active",
56
+ models.BooleanField(
57
+ default=True, help_text="Whether this currency is active for payments"
58
+ ),
59
+ ),
60
+ (
61
+ "min_payment_amount",
62
+ models.FloatField(
63
+ default=1.0, help_text="Minimum payment amount for this currency"
64
+ ),
65
+ ),
66
+ (
67
+ "usd_rate",
68
+ models.FloatField(
69
+ default=1.0,
70
+ help_text="Exchange rate to USD (1 unit of this currency = X USD)",
71
+ ),
72
+ ),
73
+ (
74
+ "rate_updated_at",
75
+ models.DateTimeField(
76
+ blank=True, help_text="When the exchange rate was last updated", null=True
77
+ ),
78
+ ),
79
+ ],
80
+ options={
81
+ "verbose_name": "Currency",
82
+ "verbose_name_plural": "Currencies",
83
+ "db_table": "payment_currencies",
84
+ "ordering": ["code"],
85
+ "indexes": [
86
+ models.Index(fields=["code"], name="payment_cur_code_e2a506_idx"),
87
+ models.Index(fields=["currency_type"], name="payment_cur_currenc_6057a9_idx"),
88
+ models.Index(fields=["is_active"], name="payment_cur_is_acti_8d558f_idx"),
89
+ ],
90
+ },
91
+ ),
92
+ migrations.CreateModel(
93
+ name="EndpointGroup",
94
+ fields=[
95
+ (
96
+ "id",
97
+ models.BigAutoField(
98
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
99
+ ),
100
+ ),
101
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
102
+ ("updated_at", models.DateTimeField(auto_now=True)),
103
+ (
104
+ "name",
105
+ models.CharField(help_text="Endpoint group name", max_length=100, unique=True),
106
+ ),
107
+ ("display_name", models.CharField(help_text="Human-readable name", max_length=200)),
108
+ ("description", models.TextField(blank=True, help_text="Group description")),
109
+ (
110
+ "basic_price",
111
+ models.FloatField(
112
+ default=0.0,
113
+ help_text="Basic tier monthly price",
114
+ validators=[django.core.validators.MinValueValidator(0.0)],
115
+ ),
116
+ ),
117
+ (
118
+ "premium_price",
119
+ models.FloatField(
120
+ default=0.0,
121
+ help_text="Premium tier monthly price",
122
+ validators=[django.core.validators.MinValueValidator(0.0)],
123
+ ),
124
+ ),
125
+ (
126
+ "enterprise_price",
127
+ models.FloatField(
128
+ default=0.0,
129
+ help_text="Enterprise tier monthly price",
130
+ validators=[django.core.validators.MinValueValidator(0.0)],
131
+ ),
132
+ ),
133
+ (
134
+ "basic_limit",
135
+ models.PositiveIntegerField(
136
+ default=1000, help_text="Basic tier monthly usage limit"
137
+ ),
138
+ ),
139
+ (
140
+ "premium_limit",
141
+ models.PositiveIntegerField(
142
+ default=10000, help_text="Premium tier monthly usage limit"
143
+ ),
144
+ ),
145
+ (
146
+ "enterprise_limit",
147
+ models.PositiveIntegerField(
148
+ default=0, help_text="Enterprise tier monthly usage limit (0 = unlimited)"
149
+ ),
150
+ ),
151
+ (
152
+ "is_active",
153
+ models.BooleanField(default=True, help_text="Is this endpoint group active"),
154
+ ),
155
+ (
156
+ "require_api_key",
157
+ models.BooleanField(default=True, help_text="Require API key for access"),
158
+ ),
159
+ ],
160
+ options={
161
+ "verbose_name": "Endpoint Group",
162
+ "verbose_name_plural": "Endpoint Groups",
163
+ "db_table": "endpoint_groups",
164
+ "ordering": ["name"],
165
+ "indexes": [
166
+ models.Index(fields=["name"], name="endpoint_gr_name_970d2f_idx"),
167
+ models.Index(fields=["is_active"], name="endpoint_gr_is_acti_a9b5f6_idx"),
168
+ ],
169
+ },
170
+ ),
171
+ migrations.CreateModel(
172
+ name="PaymentEvent",
173
+ fields=[
174
+ (
175
+ "id",
176
+ models.UUIDField(
177
+ default=uuid.uuid4,
178
+ editable=False,
179
+ help_text="Unique identifier",
180
+ primary_key=True,
181
+ serialize=False,
182
+ ),
183
+ ),
184
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
185
+ ("updated_at", models.DateTimeField(auto_now=True)),
186
+ (
187
+ "payment_id",
188
+ models.CharField(db_index=True, help_text="Payment identifier", max_length=255),
189
+ ),
190
+ (
191
+ "event_type",
192
+ models.CharField(
193
+ choices=[
194
+ ("payment_created", "Payment Created"),
195
+ ("webhook_received", "Webhook Received"),
196
+ ("webhook_processed", "Webhook Processed"),
197
+ ("balance_updated", "Balance Updated"),
198
+ ("refund_processed", "Refund Processed"),
199
+ ("status_changed", "Status Changed"),
200
+ ("error_occurred", "Error Occurred"),
201
+ ],
202
+ db_index=True,
203
+ help_text="Type of event",
204
+ max_length=50,
205
+ ),
206
+ ),
207
+ (
208
+ "sequence_number",
209
+ models.PositiveBigIntegerField(help_text="Sequential number per payment"),
210
+ ),
211
+ ("event_data", models.JSONField(help_text="Event data payload")),
212
+ (
213
+ "processed_by",
214
+ models.CharField(
215
+ help_text="Worker/server that processed this event", max_length=100
216
+ ),
217
+ ),
218
+ (
219
+ "correlation_id",
220
+ models.CharField(
221
+ blank=True,
222
+ help_text="Correlation ID for tracing",
223
+ max_length=255,
224
+ null=True,
225
+ ),
226
+ ),
227
+ (
228
+ "idempotency_key",
229
+ models.CharField(
230
+ help_text="Idempotency key to prevent duplicates",
231
+ max_length=255,
232
+ unique=True,
233
+ ),
234
+ ),
235
+ ],
236
+ options={
237
+ "verbose_name": "Payment Event",
238
+ "verbose_name_plural": "Payment Events",
239
+ "db_table": "payment_events",
240
+ "ordering": ["sequence_number"],
241
+ "indexes": [
242
+ models.Index(
243
+ fields=["payment_id", "sequence_number"],
244
+ name="payment_eve_payment_aaf2d1_idx",
245
+ ),
246
+ models.Index(
247
+ fields=["event_type", "created_at"], name="payment_eve_event_t_e821f3_idx"
248
+ ),
249
+ models.Index(fields=["idempotency_key"], name="payment_eve_idempot_c47628_idx"),
250
+ models.Index(fields=["correlation_id"], name="payment_eve_correla_fa1dc3_idx"),
251
+ models.Index(fields=["created_at"], name="payment_eve_created_0c1f4a_idx"),
252
+ ],
253
+ },
254
+ ),
255
+ migrations.CreateModel(
256
+ name="Subscription",
257
+ fields=[
258
+ (
259
+ "id",
260
+ models.UUIDField(
261
+ default=uuid.uuid4,
262
+ editable=False,
263
+ help_text="Unique identifier",
264
+ primary_key=True,
265
+ serialize=False,
266
+ ),
267
+ ),
268
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
269
+ ("updated_at", models.DateTimeField(auto_now=True)),
270
+ (
271
+ "tier",
272
+ models.CharField(
273
+ choices=[
274
+ ("basic", "Basic"),
275
+ ("premium", "Premium"),
276
+ ("enterprise", "Enterprise"),
277
+ ],
278
+ default="basic",
279
+ help_text="Subscription tier",
280
+ max_length=20,
281
+ ),
282
+ ),
283
+ (
284
+ "status",
285
+ models.CharField(
286
+ choices=[
287
+ ("active", "Active"),
288
+ ("inactive", "Inactive"),
289
+ ("expired", "Expired"),
290
+ ("cancelled", "Cancelled"),
291
+ ("suspended", "Suspended"),
292
+ ],
293
+ default="active",
294
+ help_text="Subscription status",
295
+ max_length=20,
296
+ ),
297
+ ),
298
+ (
299
+ "monthly_price",
300
+ models.FloatField(
301
+ help_text="Monthly subscription price",
302
+ validators=[django.core.validators.MinValueValidator(0.0)],
303
+ ),
304
+ ),
305
+ (
306
+ "usage_limit",
307
+ models.PositiveIntegerField(
308
+ default=1000, help_text="Monthly usage limit (0 = unlimited)"
309
+ ),
310
+ ),
311
+ (
312
+ "usage_current",
313
+ models.PositiveIntegerField(default=0, help_text="Current month usage"),
314
+ ),
315
+ (
316
+ "last_billed",
317
+ models.DateTimeField(blank=True, help_text="Last billing date", null=True),
318
+ ),
319
+ (
320
+ "next_billing",
321
+ models.DateTimeField(blank=True, help_text="Next billing date", null=True),
322
+ ),
323
+ (
324
+ "expires_at",
325
+ models.DateTimeField(
326
+ blank=True, help_text="Subscription expiration", null=True
327
+ ),
328
+ ),
329
+ (
330
+ "cancelled_at",
331
+ models.DateTimeField(blank=True, help_text="Cancellation date", null=True),
332
+ ),
333
+ (
334
+ "metadata",
335
+ models.JSONField(default=dict, help_text="Additional subscription metadata"),
336
+ ),
337
+ (
338
+ "endpoint_group",
339
+ models.ForeignKey(
340
+ help_text="Endpoint group",
341
+ on_delete=django.db.models.deletion.CASCADE,
342
+ related_name="subscriptions",
343
+ to="django_cfg_payments.endpointgroup",
344
+ ),
345
+ ),
346
+ (
347
+ "user",
348
+ models.ForeignKey(
349
+ help_text="Subscriber",
350
+ on_delete=django.db.models.deletion.CASCADE,
351
+ related_name="subscriptions",
352
+ to=settings.AUTH_USER_MODEL,
353
+ ),
354
+ ),
355
+ ],
356
+ options={
357
+ "verbose_name": "Subscription",
358
+ "verbose_name_plural": "Subscriptions",
359
+ "db_table": "user_subscriptions",
360
+ "ordering": ["-created_at"],
361
+ },
362
+ ),
363
+ migrations.CreateModel(
364
+ name="Tariff",
365
+ fields=[
366
+ (
367
+ "id",
368
+ models.BigAutoField(
369
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
370
+ ),
371
+ ),
372
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
373
+ ("updated_at", models.DateTimeField(auto_now=True)),
374
+ ("name", models.CharField(help_text="Tariff name", max_length=100, unique=True)),
375
+ (
376
+ "display_name",
377
+ models.CharField(help_text="Human-readable tariff name", max_length=200),
378
+ ),
379
+ ("description", models.TextField(blank=True, help_text="Tariff description")),
380
+ (
381
+ "monthly_price",
382
+ models.FloatField(
383
+ default=0.0,
384
+ help_text="Monthly price in USD",
385
+ validators=[django.core.validators.MinValueValidator(0.0)],
386
+ ),
387
+ ),
388
+ (
389
+ "request_limit",
390
+ models.PositiveIntegerField(
391
+ default=1000, help_text="Monthly request limit (0 = unlimited)"
392
+ ),
393
+ ),
394
+ ("is_active", models.BooleanField(default=True, help_text="Is this tariff active")),
395
+ ],
396
+ options={
397
+ "verbose_name": "Tariff",
398
+ "verbose_name_plural": "Tariffs",
399
+ "db_table": "tariffs",
400
+ "ordering": ["monthly_price"],
401
+ "indexes": [
402
+ models.Index(fields=["is_active"], name="tariffs_is_acti_e0eeb6_idx"),
403
+ models.Index(fields=["monthly_price"], name="tariffs_monthly_fc7022_idx"),
404
+ ],
405
+ },
406
+ ),
407
+ migrations.CreateModel(
408
+ name="TariffEndpointGroup",
409
+ fields=[
410
+ (
411
+ "id",
412
+ models.BigAutoField(
413
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
414
+ ),
415
+ ),
416
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
417
+ ("updated_at", models.DateTimeField(auto_now=True)),
418
+ (
419
+ "is_enabled",
420
+ models.BooleanField(
421
+ default=True, help_text="Is this endpoint group enabled for this tariff"
422
+ ),
423
+ ),
424
+ (
425
+ "endpoint_group",
426
+ models.ForeignKey(
427
+ help_text="Endpoint group",
428
+ on_delete=django.db.models.deletion.CASCADE,
429
+ related_name="tariffs",
430
+ to="django_cfg_payments.endpointgroup",
431
+ ),
432
+ ),
433
+ (
434
+ "tariff",
435
+ models.ForeignKey(
436
+ help_text="Tariff plan",
437
+ on_delete=django.db.models.deletion.CASCADE,
438
+ related_name="endpoint_groups",
439
+ to="django_cfg_payments.tariff",
440
+ ),
441
+ ),
442
+ ],
443
+ options={
444
+ "verbose_name": "Tariff Endpoint Group",
445
+ "verbose_name_plural": "Tariff Endpoint Groups",
446
+ "db_table": "tariff_endpoint_groups",
447
+ },
448
+ ),
449
+ migrations.CreateModel(
450
+ name="UniversalPayment",
451
+ fields=[
452
+ (
453
+ "id",
454
+ models.UUIDField(
455
+ default=uuid.uuid4,
456
+ editable=False,
457
+ help_text="Unique identifier",
458
+ primary_key=True,
459
+ serialize=False,
460
+ ),
461
+ ),
462
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
463
+ ("updated_at", models.DateTimeField(auto_now=True)),
464
+ (
465
+ "amount_usd",
466
+ models.FloatField(
467
+ help_text="Payment amount in USD",
468
+ validators=[django.core.validators.MinValueValidator(1.0)],
469
+ ),
470
+ ),
471
+ (
472
+ "currency_code",
473
+ models.CharField(help_text="Currency used for payment", max_length=10),
474
+ ),
475
+ (
476
+ "actual_amount_usd",
477
+ models.FloatField(
478
+ blank=True, help_text="Actual received amount in USD", null=True
479
+ ),
480
+ ),
481
+ (
482
+ "actual_currency_code",
483
+ models.CharField(
484
+ blank=True, help_text="Actual received currency", max_length=10, null=True
485
+ ),
486
+ ),
487
+ (
488
+ "fee_amount_usd",
489
+ models.FloatField(
490
+ blank=True,
491
+ help_text="Fee amount in USD",
492
+ null=True,
493
+ validators=[django.core.validators.MinValueValidator(0.0)],
494
+ ),
495
+ ),
496
+ (
497
+ "provider",
498
+ models.CharField(
499
+ choices=[
500
+ ("nowpayments", "NowPayments"),
501
+ ("stripe", "Stripe"),
502
+ ("internal", "Internal"),
503
+ ],
504
+ help_text="Payment provider",
505
+ max_length=50,
506
+ ),
507
+ ),
508
+ (
509
+ "status",
510
+ models.CharField(
511
+ choices=[
512
+ ("pending", "Pending"),
513
+ ("confirming", "Confirming"),
514
+ ("confirmed", "Confirmed"),
515
+ ("completed", "Completed"),
516
+ ("failed", "Failed"),
517
+ ("expired", "Expired"),
518
+ ("cancelled", "Cancelled"),
519
+ ("refunded", "Refunded"),
520
+ ],
521
+ default="pending",
522
+ help_text="Payment status",
523
+ max_length=20,
524
+ ),
525
+ ),
526
+ (
527
+ "provider_payment_id",
528
+ models.CharField(
529
+ blank=True,
530
+ help_text="Provider's payment ID",
531
+ max_length=255,
532
+ null=True,
533
+ unique=True,
534
+ ),
535
+ ),
536
+ (
537
+ "internal_payment_id",
538
+ models.CharField(
539
+ help_text="Internal payment identifier", max_length=100, unique=True
540
+ ),
541
+ ),
542
+ (
543
+ "pay_address",
544
+ models.CharField(
545
+ blank=True,
546
+ help_text="Cryptocurrency payment address",
547
+ max_length=200,
548
+ null=True,
549
+ ),
550
+ ),
551
+ (
552
+ "pay_amount",
553
+ models.FloatField(
554
+ blank=True, help_text="Amount to pay in cryptocurrency", null=True
555
+ ),
556
+ ),
557
+ (
558
+ "network",
559
+ models.CharField(
560
+ blank=True,
561
+ help_text="Blockchain network (mainnet, testnet, etc.)",
562
+ max_length=50,
563
+ null=True,
564
+ ),
565
+ ),
566
+ ("description", models.TextField(blank=True, help_text="Payment description")),
567
+ (
568
+ "order_id",
569
+ models.CharField(
570
+ blank=True, help_text="Order reference ID", max_length=255, null=True
571
+ ),
572
+ ),
573
+ ("metadata", models.JSONField(default=dict, help_text="Additional metadata")),
574
+ (
575
+ "webhook_data",
576
+ models.JSONField(
577
+ blank=True, help_text="Raw webhook data from provider", null=True
578
+ ),
579
+ ),
580
+ (
581
+ "expires_at",
582
+ models.DateTimeField(
583
+ blank=True, help_text="Payment expiration time", null=True
584
+ ),
585
+ ),
586
+ (
587
+ "completed_at",
588
+ models.DateTimeField(
589
+ blank=True, help_text="Payment completion time", null=True
590
+ ),
591
+ ),
592
+ (
593
+ "processed_at",
594
+ models.DateTimeField(
595
+ blank=True,
596
+ help_text="When the payment was processed and funds added to balance",
597
+ null=True,
598
+ ),
599
+ ),
600
+ (
601
+ "user",
602
+ models.ForeignKey(
603
+ help_text="User who initiated this payment",
604
+ on_delete=django.db.models.deletion.CASCADE,
605
+ related_name="universal_payments",
606
+ to=settings.AUTH_USER_MODEL,
607
+ ),
608
+ ),
609
+ ],
610
+ options={
611
+ "verbose_name": "Universal Payment",
612
+ "verbose_name_plural": "Universal Payments",
613
+ "db_table": "universal_payments",
614
+ "ordering": ["-created_at"],
615
+ },
616
+ ),
617
+ migrations.CreateModel(
618
+ name="Transaction",
619
+ fields=[
620
+ (
621
+ "id",
622
+ models.UUIDField(
623
+ default=uuid.uuid4,
624
+ editable=False,
625
+ help_text="Unique identifier",
626
+ primary_key=True,
627
+ serialize=False,
628
+ ),
629
+ ),
630
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
631
+ ("updated_at", models.DateTimeField(auto_now=True)),
632
+ (
633
+ "amount_usd",
634
+ models.FloatField(
635
+ help_text="Transaction amount in USD (positive for credits, negative for debits)"
636
+ ),
637
+ ),
638
+ (
639
+ "transaction_type",
640
+ models.CharField(
641
+ choices=[
642
+ ("payment", "Payment"),
643
+ ("subscription", "Subscription"),
644
+ ("refund", "Refund"),
645
+ ("credit", "Credit"),
646
+ ("debit", "Debit"),
647
+ ("hold", "Hold"),
648
+ ("release", "Release"),
649
+ ("fee", "Fee"),
650
+ ("adjustment", "Adjustment"),
651
+ ],
652
+ help_text="Type of transaction",
653
+ max_length=20,
654
+ ),
655
+ ),
656
+ (
657
+ "description",
658
+ models.TextField(help_text="Human-readable description of the transaction"),
659
+ ),
660
+ (
661
+ "balance_before",
662
+ models.FloatField(help_text="User balance before this transaction"),
663
+ ),
664
+ (
665
+ "balance_after",
666
+ models.FloatField(help_text="User balance after this transaction"),
667
+ ),
668
+ (
669
+ "reference_id",
670
+ models.CharField(
671
+ blank=True, help_text="External reference ID", max_length=255, null=True
672
+ ),
673
+ ),
674
+ (
675
+ "metadata",
676
+ models.JSONField(default=dict, help_text="Additional transaction metadata"),
677
+ ),
678
+ (
679
+ "subscription",
680
+ models.ForeignKey(
681
+ blank=True,
682
+ help_text="Related subscription (if applicable)",
683
+ null=True,
684
+ on_delete=django.db.models.deletion.SET_NULL,
685
+ related_name="transactions",
686
+ to="django_cfg_payments.subscription",
687
+ ),
688
+ ),
689
+ (
690
+ "user",
691
+ models.ForeignKey(
692
+ help_text="User who made this transaction",
693
+ on_delete=django.db.models.deletion.CASCADE,
694
+ related_name="transactions",
695
+ to=settings.AUTH_USER_MODEL,
696
+ ),
697
+ ),
698
+ (
699
+ "payment",
700
+ models.ForeignKey(
701
+ blank=True,
702
+ help_text="Related payment (if applicable)",
703
+ null=True,
704
+ on_delete=django.db.models.deletion.SET_NULL,
705
+ related_name="transactions",
706
+ to="django_cfg_payments.universalpayment",
707
+ ),
708
+ ),
709
+ ],
710
+ options={
711
+ "verbose_name": "Transaction",
712
+ "verbose_name_plural": "Transactions",
713
+ "db_table": "user_transactions",
714
+ "ordering": ["-created_at"],
715
+ },
716
+ ),
717
+ migrations.CreateModel(
718
+ name="UserBalance",
719
+ fields=[
720
+ (
721
+ "id",
722
+ models.BigAutoField(
723
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
724
+ ),
725
+ ),
726
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
727
+ ("updated_at", models.DateTimeField(auto_now=True)),
728
+ (
729
+ "amount_usd",
730
+ models.FloatField(
731
+ default=0.0,
732
+ help_text="Current balance in USD",
733
+ validators=[django.core.validators.MinValueValidator(0.0)],
734
+ ),
735
+ ),
736
+ (
737
+ "reserved_usd",
738
+ models.FloatField(
739
+ default=0.0,
740
+ help_text="Reserved balance in USD (for pending transactions)",
741
+ validators=[django.core.validators.MinValueValidator(0.0)],
742
+ ),
743
+ ),
744
+ (
745
+ "total_earned",
746
+ models.FloatField(
747
+ default=0.0,
748
+ help_text="Total amount earned (lifetime)",
749
+ validators=[django.core.validators.MinValueValidator(0.0)],
750
+ ),
751
+ ),
752
+ (
753
+ "total_spent",
754
+ models.FloatField(
755
+ default=0.0,
756
+ help_text="Total amount spent (lifetime)",
757
+ validators=[django.core.validators.MinValueValidator(0.0)],
758
+ ),
759
+ ),
760
+ (
761
+ "last_transaction_at",
762
+ models.DateTimeField(
763
+ blank=True, help_text="When the last transaction occurred", null=True
764
+ ),
765
+ ),
766
+ (
767
+ "user",
768
+ models.OneToOneField(
769
+ help_text="User who owns this balance",
770
+ on_delete=django.db.models.deletion.CASCADE,
771
+ related_name="balance",
772
+ to=settings.AUTH_USER_MODEL,
773
+ ),
774
+ ),
775
+ ],
776
+ options={
777
+ "verbose_name": "User Balance",
778
+ "verbose_name_plural": "User Balances",
779
+ "db_table": "user_balances",
780
+ },
781
+ ),
782
+ migrations.CreateModel(
783
+ name="APIKey",
784
+ fields=[
785
+ (
786
+ "id",
787
+ models.UUIDField(
788
+ default=uuid.uuid4,
789
+ editable=False,
790
+ help_text="Unique identifier",
791
+ primary_key=True,
792
+ serialize=False,
793
+ ),
794
+ ),
795
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
796
+ ("updated_at", models.DateTimeField(auto_now=True)),
797
+ ("name", models.CharField(help_text="Human-readable key name", max_length=100)),
798
+ (
799
+ "key_value",
800
+ models.CharField(
801
+ help_text="API key value (plain text)", max_length=255, unique=True
802
+ ),
803
+ ),
804
+ (
805
+ "key_prefix",
806
+ models.CharField(help_text="Key prefix for identification", max_length=20),
807
+ ),
808
+ ("is_active", models.BooleanField(default=True, help_text="Is key active")),
809
+ (
810
+ "last_used",
811
+ models.DateTimeField(blank=True, help_text="Last usage timestamp", null=True),
812
+ ),
813
+ (
814
+ "usage_count",
815
+ models.PositiveBigIntegerField(default=0, help_text="Total usage count"),
816
+ ),
817
+ (
818
+ "expires_at",
819
+ models.DateTimeField(blank=True, help_text="Key expiration", null=True),
820
+ ),
821
+ (
822
+ "user",
823
+ models.ForeignKey(
824
+ help_text="API key owner",
825
+ on_delete=django.db.models.deletion.CASCADE,
826
+ related_name="api_keys",
827
+ to=settings.AUTH_USER_MODEL,
828
+ ),
829
+ ),
830
+ ],
831
+ options={
832
+ "verbose_name": "API Key",
833
+ "verbose_name_plural": "API Keys",
834
+ "db_table": "api_keys",
835
+ "ordering": ["-created_at"],
836
+ "indexes": [
837
+ models.Index(fields=["user", "is_active"], name="api_keys_user_id_6e7352_idx"),
838
+ models.Index(fields=["key_value"], name="api_keys_key_val_a809c4_idx"),
839
+ models.Index(fields=["key_prefix"], name="api_keys_key_pre_9c2634_idx"),
840
+ models.Index(fields=["last_used"], name="api_keys_last_us_f1be76_idx"),
841
+ models.Index(fields=["expires_at"], name="api_keys_expires_f68b1a_idx"),
842
+ ],
843
+ },
844
+ ),
845
+ migrations.CreateModel(
846
+ name="CurrencyNetwork",
847
+ fields=[
848
+ (
849
+ "id",
850
+ models.BigAutoField(
851
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
852
+ ),
853
+ ),
854
+ ("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
855
+ ("updated_at", models.DateTimeField(auto_now=True)),
856
+ (
857
+ "network_name",
858
+ models.CharField(
859
+ help_text="Network name (e.g., mainnet, polygon, bsc)", max_length=50
860
+ ),
861
+ ),
862
+ (
863
+ "network_code",
864
+ models.CharField(help_text="Network code for API integration", max_length=20),
865
+ ),
866
+ (
867
+ "is_active",
868
+ models.BooleanField(default=True, help_text="Whether this network is active"),
869
+ ),
870
+ (
871
+ "confirmation_blocks",
872
+ models.PositiveIntegerField(
873
+ default=1, help_text="Number of confirmations required"
874
+ ),
875
+ ),
876
+ (
877
+ "currency",
878
+ models.ForeignKey(
879
+ help_text="Currency this network supports",
880
+ on_delete=django.db.models.deletion.CASCADE,
881
+ related_name="networks",
882
+ to="django_cfg_payments.currency",
883
+ ),
884
+ ),
885
+ ],
886
+ options={
887
+ "verbose_name": "Currency Network",
888
+ "verbose_name_plural": "Currency Networks",
889
+ "db_table": "payment_currency_networks",
890
+ "indexes": [
891
+ models.Index(
892
+ fields=["currency", "is_active"], name="payment_cur_currenc_8709aa_idx"
893
+ ),
894
+ models.Index(fields=["network_code"], name="payment_cur_network_597115_idx"),
895
+ ],
896
+ "unique_together": {("currency", "network_code")},
897
+ },
898
+ ),
899
+ migrations.AddIndex(
900
+ model_name="subscription",
901
+ index=models.Index(fields=["user", "status"], name="user_subscr_user_id_c57286_idx"),
902
+ ),
903
+ migrations.AddIndex(
904
+ model_name="subscription",
905
+ index=models.Index(
906
+ fields=["endpoint_group", "status"], name="user_subscr_endpoin_12e673_idx"
907
+ ),
908
+ ),
909
+ migrations.AddIndex(
910
+ model_name="subscription",
911
+ index=models.Index(
912
+ fields=["status", "expires_at"], name="user_subscr_status_e7170d_idx"
913
+ ),
914
+ ),
915
+ migrations.AddIndex(
916
+ model_name="subscription",
917
+ index=models.Index(fields=["next_billing"], name="user_subscr_next_bi_d8c922_idx"),
918
+ ),
919
+ migrations.AddIndex(
920
+ model_name="subscription",
921
+ index=models.Index(fields=["created_at"], name="user_subscr_created_fe5591_idx"),
922
+ ),
923
+ migrations.AlterUniqueTogether(
924
+ name="subscription",
925
+ unique_together={("user", "endpoint_group")},
926
+ ),
927
+ migrations.AlterUniqueTogether(
928
+ name="tariffendpointgroup",
929
+ unique_together={("tariff", "endpoint_group")},
930
+ ),
931
+ migrations.AddIndex(
932
+ model_name="universalpayment",
933
+ index=models.Index(fields=["user", "status"], name="universal_p_user_id_7bdba2_idx"),
934
+ ),
935
+ migrations.AddIndex(
936
+ model_name="universalpayment",
937
+ index=models.Index(
938
+ fields=["provider_payment_id"], name="universal_p_provide_bdcb09_idx"
939
+ ),
940
+ ),
941
+ migrations.AddIndex(
942
+ model_name="universalpayment",
943
+ index=models.Index(
944
+ fields=["internal_payment_id"], name="universal_p_interna_371bac_idx"
945
+ ),
946
+ ),
947
+ migrations.AddIndex(
948
+ model_name="universalpayment",
949
+ index=models.Index(fields=["status"], name="universal_p_status_e531bf_idx"),
950
+ ),
951
+ migrations.AddIndex(
952
+ model_name="universalpayment",
953
+ index=models.Index(fields=["provider"], name="universal_p_provide_9820af_idx"),
954
+ ),
955
+ migrations.AddIndex(
956
+ model_name="universalpayment",
957
+ index=models.Index(fields=["currency_code"], name="universal_p_currenc_56f1fc_idx"),
958
+ ),
959
+ migrations.AddIndex(
960
+ model_name="universalpayment",
961
+ index=models.Index(fields=["created_at"], name="universal_p_created_978509_idx"),
962
+ ),
963
+ migrations.AddIndex(
964
+ model_name="universalpayment",
965
+ index=models.Index(fields=["processed_at"], name="universal_p_process_1c8a1f_idx"),
966
+ ),
967
+ migrations.AddIndex(
968
+ model_name="transaction",
969
+ index=models.Index(
970
+ fields=["user", "created_at"], name="user_transa_user_id_63659d_idx"
971
+ ),
972
+ ),
973
+ migrations.AddIndex(
974
+ model_name="transaction",
975
+ index=models.Index(fields=["transaction_type"], name="user_transa_transac_dc0ae2_idx"),
976
+ ),
977
+ migrations.AddIndex(
978
+ model_name="transaction",
979
+ index=models.Index(fields=["amount_usd"], name="user_transa_amount__2eeba2_idx"),
980
+ ),
981
+ migrations.AddIndex(
982
+ model_name="transaction",
983
+ index=models.Index(fields=["created_at"], name="user_transa_created_1cb93d_idx"),
984
+ ),
985
+ migrations.AddIndex(
986
+ model_name="transaction",
987
+ index=models.Index(fields=["reference_id"], name="user_transa_referen_c8e19f_idx"),
988
+ ),
989
+ migrations.AddIndex(
990
+ model_name="userbalance",
991
+ index=models.Index(fields=["user"], name="user_balanc_user_id_9eea44_idx"),
992
+ ),
993
+ migrations.AddIndex(
994
+ model_name="userbalance",
995
+ index=models.Index(fields=["amount_usd"], name="user_balanc_amount__6ff57b_idx"),
996
+ ),
997
+ migrations.AddIndex(
998
+ model_name="userbalance",
999
+ index=models.Index(
1000
+ fields=["last_transaction_at"], name="user_balanc_last_tr_e5d8ae_idx"
1001
+ ),
1002
+ ),
1003
+ ]