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