diff --git a/README.md b/README.md index 02de414..8f4d24e 100644 --- a/README.md +++ b/README.md @@ -173,3 +173,9 @@ from sberbank.tasks import check_payments def task_check_payments(): check_payments() ``` + + +### Настройки +```python +ORDER_NUMBER_PREFIX - Префикс номера заказа. По умолчанию 'SC-'. +``` \ No newline at end of file diff --git a/sberbank/admin.py b/sberbank/admin.py index 4a40d23..4bf3d34 100644 --- a/sberbank/admin.py +++ b/sberbank/admin.py @@ -7,16 +7,18 @@ @admin.register(Payment) class PaymentAdmin(admin.ModelAdmin): list_display = ( - 'uid', 'bank_id', 'amount', 'status', 'created', 'updated', + 'uid', 'bank_id', 'order_number', + 'amount', 'status', 'created', 'updated', ) list_filter = ('status',) search_fields = ( - 'uid', 'bank_id', 'amount' + 'uid', 'bank_id', 'amount', 'order_number' ) readonly_fields = ( 'created', 'updated', 'uid', 'bank_id', 'client_id', 'amount', - 'status', 'method', 'details', 'error_code', 'error_message' + 'status', 'method', 'details', 'error_code', 'error_message', + 'order_number' ) fieldsets = ( @@ -25,7 +27,7 @@ class PaymentAdmin(admin.ModelAdmin): { 'fields': [ ('uid', 'bank_id', 'client_id'), - 'status', 'method', + 'order_number', 'status', 'method', ('amount',), ] } diff --git a/sberbank/migrations/0007_auto_20191204_1542.py b/sberbank/migrations/0007_auto_20191204_1542.py new file mode 100644 index 0000000..cc06d6d --- /dev/null +++ b/sberbank/migrations/0007_auto_20191204_1542.py @@ -0,0 +1,95 @@ +# Generated by Django 2.2.8 on 2019-12-04 05:42 + +from django.db import migrations, models +import jsonfield.encoder +import jsonfield.fields +import sberbank.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sberbank', '0006_payment_method'), + ] + + operations = [ + migrations.AlterModelOptions( + name='payment', + options={'ordering': ['-updated'], 'verbose_name': 'payment', 'verbose_name_plural': 'payments'}, + ), + migrations.AlterField( + model_name='logentry', + name='action', + field=models.CharField(db_index=True, max_length=100, verbose_name='action'), + ), + migrations.AlterField( + model_name='logentry', + name='bank_id', + field=models.UUIDField(blank=True, db_index=True, null=True, verbose_name='bank payment ID'), + ), + migrations.AlterField( + model_name='logentry', + name='created', + field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='created'), + ), + migrations.AlterField( + model_name='logentry', + name='payment_id', + field=models.UUIDField(blank=True, db_index=True, null=True, verbose_name='payment ID'), + ), + migrations.AlterField( + model_name='logentry', + name='request_text', + field=models.TextField(blank=True, null=True, verbose_name='request text'), + ), + migrations.AlterField( + model_name='logentry', + name='response_text', + field=models.TextField(blank=True, null=True, verbose_name='response text'), + ), + migrations.AlterField( + model_name='payment', + name='amount', + field=models.DecimalField(decimal_places=2, max_digits=128, verbose_name='amount'), + ), + migrations.AlterField( + model_name='payment', + name='bank_id', + field=models.UUIDField(blank=True, db_index=True, null=True, verbose_name='bank ID'), + ), + migrations.AlterField( + model_name='payment', + name='client_id', + field=models.TextField(blank=True, null=True, verbose_name='client ID'), + ), + migrations.AlterField( + model_name='payment', + name='created', + field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='created'), + ), + migrations.AlterField( + model_name='payment', + name='details', + field=jsonfield.fields.JSONField(blank=True, dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}, null=True, verbose_name='details'), + ), + migrations.AlterField( + model_name='payment', + name='error_code', + field=models.PositiveIntegerField(blank=True, null=True, verbose_name='error code'), + ), + migrations.AlterField( + model_name='payment', + name='error_message', + field=models.TextField(blank=True, null=True, verbose_name='error message'), + ), + migrations.AlterField( + model_name='payment', + name='status', + field=models.PositiveSmallIntegerField(choices=[(0, 'CREATED'), (1, 'PENDING'), (2, 'SUCCEEDED'), (3, 'FAILED'), (4, 'REFUNDED')], db_index=True, default=sberbank.models.Status(0), verbose_name='status'), + ), + migrations.AlterField( + model_name='payment', + name='updated', + field=models.DateTimeField(auto_now=True, db_index=True, verbose_name='modified'), + ), + ] diff --git a/sberbank/migrations/0008_payment_order_number.py b/sberbank/migrations/0008_payment_order_number.py new file mode 100644 index 0000000..219c136 --- /dev/null +++ b/sberbank/migrations/0008_payment_order_number.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.9 on 2020-01-13 05:56 + +from django.db import migrations, models +import sberbank.sberbank_settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('sberbank', '0007_auto_20191204_1542'), + ] + + operations = [ + migrations.AddField( + model_name='payment', + name='order_number', + field=models.CharField(default=sberbank.sberbank_settings.order_number, editable=False, max_length=64, verbose_name='Номер заказа'), + ), + ] diff --git a/sberbank/migrations/0009_auto_20201118_1804.py b/sberbank/migrations/0009_auto_20201118_1804.py new file mode 100644 index 0000000..3a9c5fc --- /dev/null +++ b/sberbank/migrations/0009_auto_20201118_1804.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.13 on 2020-11-18 08:04 + +from django.db import migrations +import jsonfield.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('sberbank', '0008_payment_order_number'), + ] + + operations = [ + migrations.AlterField( + model_name='payment', + name='details', + field=jsonfield.fields.JSONField(blank=True, null=True, verbose_name='details'), + ), + ] diff --git a/sberbank/models.py b/sberbank/models.py index b61ee28..735fdfe 100644 --- a/sberbank/models.py +++ b/sberbank/models.py @@ -5,6 +5,8 @@ from django.db import models from django.utils.translation import ugettext as _ +from sberbank import sberbank_settings + class Choice(IntEnum): @classmethod @@ -48,6 +50,10 @@ class Payment(models.Model): uid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) bank_id = models.UUIDField(_("bank ID"), null=True, blank=True, db_index=True) amount = models.DecimalField(_("amount"), max_digits=128, decimal_places=2) + order_number = models.CharField( + 'Номер заказа', max_length=64, editable=False, + default=sberbank_settings.generate_order_number + ) error_code = models.PositiveIntegerField(_("error code"), null=True, blank=True) error_message = models.TextField(_("error message"), null=True, blank=True) status = models.PositiveSmallIntegerField(_("status"), choices=Status.choices(), diff --git a/sberbank/sberbank_settings.py b/sberbank/sberbank_settings.py new file mode 100644 index 0000000..8bd0a33 --- /dev/null +++ b/sberbank/sberbank_settings.py @@ -0,0 +1,21 @@ +import secrets + +from django.conf import settings + +ORDER_NUMBER_PREFIX = getattr(settings, 'ORDER_NUMBER_PREFIX', 'SC-') + + +def order_number(): + """Генерация уникального номера заказа для отправки в сбербанк. + + Размер номера заказа в байтах не должен превышать 32 байт. + Функция вычисляет размер префикса заказа и генерирует строку + в зависимости от размера префикса. + """ + prefix_size = len(ORDER_NUMBER_PREFIX.encode('ascii')) + if prefix_size > 16: + raise ValueError('Размер префикса не может превышать 16 байт.') + random_string = secrets.token_hex(8) + return ORDER_NUMBER_PREFIX + random_string + +generate_order_number = getattr(settings, 'generate_order_number', order_number) diff --git a/sberbank/service.py b/sberbank/service.py index edda517..efd0642 100644 --- a/sberbank/service.py +++ b/sberbank/service.py @@ -6,6 +6,7 @@ from django.conf import settings from django.utils.translation import ugettext as _ +from sberbank import sberbank_settings from sberbank.exceptions import NetworkException, ProcessingException, \ PaymentNotFoundException from sberbank.models import Payment, LogEntry, Status, Method @@ -68,10 +69,16 @@ def mobile_pay(self, amount, token, ip, **kwargs): raise TypeError( "Wrong amount type, passed {} ({}) instead of decimal".format(amount, type(amount))) - payment = Payment(amount=amount, client_id=client_id, method=db_method, details={ - 'username': self.merchant.get("username"), - 'currency': currency - }) + payment = Payment( + amount=amount, + client_id=client_id, + method=db_method, + order_number=sberbank_settings.generate_order_number(), + details={ + 'username': self.merchant.get("username"), + 'currency': currency + } + ) if kwargs.get('params'): payment.details.update(kwargs.get('params')) @@ -82,7 +89,7 @@ def mobile_pay(self, amount, token, ip, **kwargs): data = { 'merchant': self.merchant_id, - 'orderNumber': payment.uid.hex, + 'orderNumber': payment.order_number, 'paymentToken': token, 'ip': ip } @@ -131,20 +138,26 @@ def pay(self, amount, preauth=False, **kwargs): raise TypeError( "Wrong amount type, passed {} ({}) instead of decimal".format(amount, type(amount))) - payment = Payment(amount=amount, client_id=client_id, method=Method.WEB, details={ - 'username': self.merchant.get("username"), - 'currency': currency, - 'success_url': success_url, - 'fail_url': fail_url, - 'session_timeout': session_timeout, - 'client_id': client_id - }) + payment = Payment( + amount=amount, + client_id=client_id, + method=Method.WEB, + order_number=sberbank_settings.generate_order_number(), + details={ + 'username': self.merchant.get("username"), + 'currency': currency, + 'success_url': success_url, + 'fail_url': fail_url, + 'session_timeout': session_timeout, + 'client_id': client_id + } + ) payment.details.update(details) payment.save() data = { - 'orderNumber': payment.uid.hex, + 'orderNumber': payment.order_number, 'amount': int(amount * 100), 'returnUrl': success_url, 'failUrl': fail_url, diff --git a/setup.py b/setup.py index c0b70fd..ca3302f 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,8 @@ import setuptools setuptools.setup( - name='django-sberbank', - version='0.2.31', + name='fs-django-sberbank', + version='0.2.36', description='Django app for Sberbank payments', url='http://github.com/madprogrammer/django-sberbank', author='Sergey Anufrienko',