payme-pkg 2.6.5__py3-none-any.whl → 3.0.17__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.

Potentially problematic release.


This version of payme-pkg might be problematic. Click here for more details.

Files changed (51) hide show
  1. payme/__init__.py +1 -0
  2. payme/admin.py +12 -6
  3. payme/apps.py +0 -4
  4. payme/classes/cards.py +203 -0
  5. payme/classes/client.py +30 -0
  6. payme/classes/http.py +107 -0
  7. payme/classes/initializer.py +82 -0
  8. payme/classes/receipts.py +298 -0
  9. payme/const.py +12 -0
  10. payme/exceptions/__init__.py +5 -0
  11. payme/exceptions/general.py +275 -0
  12. payme/exceptions/webhook.py +125 -0
  13. payme/models.py +114 -40
  14. payme/types/response/__init__.py +4 -0
  15. payme/types/response/cards.py +110 -0
  16. payme/types/response/receipts.py +215 -0
  17. payme/types/response/webhook.py +136 -0
  18. payme/urls.py +2 -2
  19. payme/util.py +26 -0
  20. payme/views.py +287 -113
  21. payme_pkg-3.0.17.dist-info/METADATA +193 -0
  22. payme_pkg-3.0.17.dist-info/RECORD +29 -0
  23. {payme_pkg-2.6.5.dist-info → payme_pkg-3.0.17.dist-info}/WHEEL +1 -1
  24. payme/cards/__init__.py +0 -1
  25. payme/cards/subscribe_cards.py +0 -166
  26. payme/decorators/decorators.py +0 -34
  27. payme/errors/exceptions.py +0 -89
  28. payme/methods/cancel_transaction.py +0 -54
  29. payme/methods/check_perform_transaction.py +0 -26
  30. payme/methods/check_transaction.py +0 -43
  31. payme/methods/create_transaction.py +0 -68
  32. payme/methods/generate_link.py +0 -83
  33. payme/methods/get_statement.py +0 -65
  34. payme/methods/perform_transaction.py +0 -47
  35. payme/migrations/0001_initial.py +0 -48
  36. payme/receipts/__init__.py +0 -1
  37. payme/receipts/subscribe_receipts.py +0 -217
  38. payme/serializers.py +0 -86
  39. payme/utils/__init__.py +0 -0
  40. payme/utils/get_params.py +0 -24
  41. payme/utils/logging.py +0 -9
  42. payme/utils/make_aware_datetime.py +0 -21
  43. payme/utils/support.py +0 -8
  44. payme/utils/to_json.py +0 -13
  45. payme_pkg-2.6.5.dist-info/METADATA +0 -13
  46. payme_pkg-2.6.5.dist-info/RECORD +0 -36
  47. /payme/{decorators → classes}/__init__.py +0 -0
  48. /payme/{errors → types}/__init__.py +0 -0
  49. /payme/{methods → types/request}/__init__.py +0 -0
  50. {payme_pkg-2.6.5.dist-info → payme_pkg-3.0.17.dist-info}/LICENSE.txt +0 -0
  51. {payme_pkg-2.6.5.dist-info → payme_pkg-3.0.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,125 @@
1
+ """
2
+ Init Payme base exception.
3
+ """
4
+ import logging
5
+ from rest_framework.exceptions import APIException
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class BasePaymeException(APIException):
11
+ """
12
+ BasePaymeException inherits from APIException.
13
+ """
14
+ status_code = 200
15
+ error_code = None
16
+ message = None
17
+
18
+ # pylint: disable=super-init-not-called
19
+ def __init__(self, message: str = None):
20
+ detail: dict = {
21
+ "error": {
22
+ "code": self.error_code,
23
+ "message": self.message,
24
+ "data": message
25
+ }
26
+ }
27
+ logger.error(f"Payme error detail: {detail}")
28
+ self.detail = detail
29
+
30
+
31
+ class PermissionDenied(BasePaymeException):
32
+ """
33
+ PermissionDenied APIException.
34
+
35
+ Raised when the client is not allowed to access the server.
36
+ """
37
+ status_code = 200
38
+ error_code = -32504
39
+ message = "Permission denied."
40
+
41
+
42
+ class InternalServiceError(BasePaymeException):
43
+ """
44
+ InternalServiceError APIException.
45
+
46
+ Raised when a transaction fails to perform.
47
+ """
48
+ status_code = 200
49
+ error_code = -32400
50
+ message = {
51
+ "uz": "Tizimda xatolik yuzaga keldi.",
52
+ "ru": "Внутренняя ошибка сервиса.",
53
+ "en": "Internal service error."
54
+ }
55
+
56
+
57
+ class MethodNotFound(BasePaymeException):
58
+ """
59
+ MethodNotFound APIException.
60
+
61
+ Raised when the requested method does not exist.
62
+ """
63
+ status_code = 405
64
+ error_code = -32601
65
+ message = "Method not found."
66
+
67
+
68
+ class AccountDoesNotExist(BasePaymeException):
69
+ """
70
+ AccountDoesNotExist APIException.
71
+
72
+ Raised when an account does not exist or has been deleted.
73
+ """
74
+ status_code = 200
75
+ error_code = -31050
76
+ message = {
77
+ "uz": "Hisob topilmadi.",
78
+ "ru": "Счет не найден.",
79
+ "en": "Account does not exist."
80
+ }
81
+
82
+
83
+ class IncorrectAmount(BasePaymeException):
84
+ """
85
+ IncorrectAmount APIException.
86
+
87
+ Raised when the provided amount is incorrect.
88
+ """
89
+ status_code = 200
90
+ error_code = -31001
91
+ message = {
92
+ 'ru': 'Неверная сумма.',
93
+ 'uz': "Noto'g'ri summa.",
94
+ 'en': 'Incorrect amount.'
95
+ }
96
+
97
+
98
+ class TransactionAlreadyExists(BasePaymeException):
99
+ """
100
+ TransactionAlreadyExists APIException.
101
+
102
+ Raised when a transaction already exists in the system,
103
+ preventing the creation of a new transaction with the same identifier.
104
+
105
+ Attributes:
106
+ status_code (int): The HTTP status code for the response.
107
+ error_code (int): The specific error code for this exception.
108
+ message (dict): A dictionary containing localized error messages.
109
+ """
110
+ status_code = 200
111
+ error_code = -31099
112
+ message = {
113
+ "uz": "Tranzaksiya allaqachon mavjud.",
114
+ "ru": "Транзакция уже существует.",
115
+ "en": "Transaction already exists."
116
+ }
117
+
118
+
119
+ exception_whitelist = (
120
+ IncorrectAmount,
121
+ MethodNotFound,
122
+ PermissionDenied,
123
+ AccountDoesNotExist,
124
+ TransactionAlreadyExists
125
+ )
payme/models.py CHANGED
@@ -1,61 +1,135 @@
1
+ """
2
+ This module contains models and functionality for tracking changes in payment transactions.
3
+ It logs any significant modifications to payment transactions such as amount, state, or payment method,
4
+ allowing for a detailed historical record of each transaction's state over time.
5
+ """
1
6
  from django.db import models
2
7
  from django.conf import settings
8
+ from django.utils import timezone
9
+
3
10
  from django.utils.module_loading import import_string
4
- from django.core.exceptions import FieldError
5
11
 
6
- from payme.utils.logging import logger
12
+ AccountModel = import_string(settings.PAYME_ACCOUNT_MODEL)
7
13
 
8
14
 
9
- class MerchantTransactionsModel(models.Model):
15
+ class PaymeTransactions(models.Model):
10
16
  """
11
- MerchantTransactionsModel class \
12
- That's used for managing transactions in database.
17
+ Model to store payment transactions.
13
18
  """
14
- _id = models.CharField(max_length=255, null=True, blank=False)
15
- transaction_id = models.CharField(max_length=255, null=True, blank=False)
16
- order_id = models.BigIntegerField(null=True, blank=True)
17
- amount = models.FloatField(null=True, blank=True)
18
- time = models.BigIntegerField(null=True, blank=True)
19
- perform_time = models.BigIntegerField(null=True, default=0)
20
- cancel_time = models.BigIntegerField(null=True, default=0)
21
- state = models.IntegerField(null=True, default=1)
22
- reason = models.CharField(max_length=255, null=True, blank=True)
23
- created_at_ms = models.CharField(max_length=255, null=True, blank=True)
24
- created_at = models.DateTimeField(auto_now_add=True)
25
- updated_at = models.DateTimeField(auto_now=True)
19
+ CREATED = 0
20
+ INITIATING = 1
21
+ SUCCESSFULLY = 2
22
+ CANCELED = -2
23
+ CANCELED_DURING_INIT = -1
24
+
25
+ STATE = [
26
+ (CREATED, "Created"),
27
+ (INITIATING, "Initiating"),
28
+ (SUCCESSFULLY, "Successfully"),
29
+ (CANCELED, "Canceled after successful performed"),
30
+ (CANCELED_DURING_INIT, "Canceled during initiation"),
31
+ ]
32
+
33
+ transaction_id = models.CharField(max_length=50)
34
+ account = models.ForeignKey(
35
+ AccountModel,
36
+ related_name="payme_transactions",
37
+ on_delete=models.CASCADE
38
+ )
39
+ amount = models.DecimalField(max_digits=10, decimal_places=2)
40
+ state = models.IntegerField(choices=STATE, default=CREATED)
41
+ cancel_reason = models.IntegerField(null=True, blank=True)
42
+ created_at = models.DateTimeField(auto_now_add=True, db_index=True)
43
+ updated_at = models.DateTimeField(auto_now=True, db_index=True)
44
+ performed_at = models.DateTimeField(null=True, blank=True, db_index=True)
45
+ cancelled_at = models.DateTimeField(null=True, blank=True, db_index=True)
46
+
47
+ class Meta:
48
+ """
49
+ Model Meta options.
50
+ """
51
+ verbose_name = "Payme Transaction"
52
+ verbose_name_plural = "Payme Transactions"
53
+ ordering = ["-created_at"]
54
+ db_table = "payme_transactions"
26
55
 
27
56
  def __str__(self):
28
- return str(self._id)
57
+ """
58
+ String representation of the PaymentTransaction model.
59
+ """
60
+ return f"Payme Transaction #{self.transaction_id} Account: {self.account} - {self.state}"
29
61
 
62
+ @classmethod
63
+ def get_by_transaction_id(cls, transaction_id):
64
+ """
65
+ Class method to get a PaymentTransaction instance by its transaction ID.
30
66
 
31
- try:
32
- CUSTOM_ORDER = import_string(settings.ORDER_MODEL)
67
+ :param transaction_id: The unique ID of the transaction.
68
+ :return: The PaymentTransaction instance or None if not found.
69
+ """
70
+ return cls.objects.get(transaction_id=transaction_id)
33
71
 
34
- if not isinstance(CUSTOM_ORDER, models.base.ModelBase):
35
- raise TypeError("The input must be an instance of models.Model class")
72
+ def is_performed(self) -> bool:
73
+ """
74
+ Check if the transaction is completed.
36
75
 
37
- # pylint: disable=protected-access
38
- if 'amount' not in [f.name for f in CUSTOM_ORDER._meta.fields]:
39
- raise FieldError("Missing 'amount' field in your custom order model")
76
+ :return: True if the transaction is completed, False otherwise.
77
+ """
78
+ return self.state == self.SUCCESSFULLY
40
79
 
41
- Order = CUSTOM_ORDER
42
- except (ImportError, AttributeError):
43
- logger.warning("You have no payme custom order model")
80
+ def is_cancelled(self) -> bool:
81
+ """
82
+ Check if the transaction is cancelled.
44
83
 
45
- CUSTOM_ORDER = None
84
+ :return: True if the transaction is cancelled, False otherwise.
85
+ """
86
+ return self.state in [
87
+ self.CANCELED,
88
+ self.CANCELED_DURING_INIT
89
+ ]
46
90
 
47
- class Order(models.Model):
91
+ def is_created(self) -> bool:
48
92
  """
49
- Order class \
50
- That's used for managing order process
93
+ Check if the transaction is created.
94
+
95
+ :return: True if the transaction is created, False otherwise.
96
+ """
97
+ return self.state == self.CREATED
98
+
99
+ def is_created_in_payme(self) -> bool:
100
+ """
101
+ Check if the transaction was created in Payme.
102
+
103
+ :return: True if the transaction was created in Payme, False otherwise.
104
+ """
105
+ return self.state == self.INITIATING
106
+
107
+ def mark_as_cancelled(self, cancel_reason: int, state: int) -> "PaymeTransactions":
51
108
  """
52
- amount = models.IntegerField(null=True, blank=True)
53
- created_at = models.DateTimeField(auto_now_add=True)
54
- updated_at = models.DateTimeField(auto_now=True)
109
+ Mark the transaction as cancelled.
55
110
 
56
- def __str__(self):
57
- return f"ORDER ID: {self.pk} - AMOUNT: {self.amount}"
111
+ :param cancel_reason: The reason for cancelling the transaction.
112
+ :return: True if the transaction was successfully marked as cancelled, False otherwise.
113
+ """
114
+ if self.state == state:
115
+ return self
116
+
117
+ self.state = state
118
+ self.cancel_reason = cancel_reason
119
+ self.cancelled_at = timezone.now()
120
+ self.save()
121
+ return self
122
+
123
+ def mark_as_performed(self) -> bool:
124
+ """
125
+ Mark the transaction as performed.
126
+
127
+ :return: True if the transaction was successfully marked as performed, False otherwise.
128
+ """
129
+ if self.state != self.INITIATING:
130
+ return False
58
131
 
59
- class Meta:
60
- # pylint: disable=missing-class-docstring
61
- managed = False
132
+ self.state = self.SUCCESSFULLY
133
+ self.performed_at = timezone.now()
134
+ self.save()
135
+ return True
@@ -0,0 +1,4 @@
1
+ """
2
+ init all response typing of payme provider
3
+ """
4
+ from .webhook import * # noqa
@@ -0,0 +1,110 @@
1
+ from typing import Dict, Optional
2
+ from dataclasses import dataclass
3
+
4
+
5
+ class Common:
6
+ """
7
+ The common response structure.
8
+ """
9
+
10
+ @classmethod
11
+ def from_dict(cls, data: Dict):
12
+ """
13
+ Prepare fields for nested dataclasses
14
+ """
15
+ field_values = {}
16
+ for field in cls.__dataclass_fields__:
17
+ field_type = cls.__dataclass_fields__[field].type
18
+ field_data = data.get(field)
19
+
20
+ if isinstance(field_data, dict) and issubclass(field_type, Common):
21
+ field_values[field] = field_type.from_dict(field_data)
22
+ else:
23
+ field_values[field] = field_data
24
+
25
+ return cls(**field_values)
26
+
27
+
28
+ @dataclass
29
+ class Card(Common):
30
+ """
31
+ The card object represents a credit card.
32
+ """
33
+ number: str
34
+ expire: str
35
+ token: str
36
+ recurrent: bool
37
+ verify: bool
38
+ type: str
39
+ number_hash: Optional[str] = None
40
+
41
+
42
+ @dataclass
43
+ class Result(Common):
44
+ """
45
+ The result object contains the created card.
46
+ """
47
+ card: Card
48
+
49
+
50
+ @dataclass
51
+ class CardsCreateResponse(Common):
52
+ """
53
+ The cards.create response.
54
+ """
55
+ jsonrpc: str
56
+ result: Result
57
+
58
+
59
+ @dataclass
60
+ class VerifyResult(Common):
61
+ """
62
+ The result object for the verification response.
63
+ """
64
+ sent: bool
65
+ phone: str
66
+ wait: int
67
+
68
+
69
+ @dataclass
70
+ class GetVerifyResponse(Common):
71
+ """
72
+ The verification response structure.
73
+ """
74
+ jsonrpc: str
75
+ result: VerifyResult
76
+
77
+
78
+ @dataclass
79
+ class VerifyResponse(Common):
80
+ """
81
+ The verification response structure.
82
+ """
83
+ jsonrpc: str
84
+ result: Result
85
+
86
+
87
+ @dataclass
88
+ class RemoveCardResult(Common):
89
+ """
90
+ The result object for the removal response.
91
+ """
92
+ success: bool
93
+
94
+
95
+ @dataclass
96
+ class RemoveResponse(Common):
97
+ """
98
+ The remove response structure.
99
+ """
100
+ jsonrpc: str
101
+ result: RemoveCardResult
102
+
103
+
104
+ @dataclass
105
+ class CheckResponse(Common):
106
+ """
107
+ The check response structure.
108
+ """
109
+ jsonrpc: str
110
+ result: Result
@@ -0,0 +1,215 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict, Optional, Union
3
+
4
+
5
+ class Common:
6
+ """
7
+ The common response structure.
8
+ """
9
+ jsonrpc: str
10
+ id: int
11
+
12
+ @classmethod
13
+ def from_dict(cls, data: Dict):
14
+ """
15
+ Prepare fields for nested dataclasses
16
+ """
17
+ field_values = {}
18
+ for field in cls.__dataclass_fields__:
19
+ field_type = cls.__dataclass_fields__[field].type
20
+ field_data = data.get(field)
21
+
22
+ if isinstance(field_data, dict) and issubclass(field_type, Common):
23
+ field_values[field] = field_type.from_dict(field_data)
24
+ else:
25
+ field_values[field] = field_data
26
+
27
+ return cls(**field_values)
28
+
29
+
30
+ @dataclass
31
+ class Account(Common):
32
+ """
33
+ The account object represents a user's banking account.
34
+ """
35
+ _id: str
36
+ account_number: str
37
+ account_name: str
38
+ account_type: str
39
+ bank_name: str
40
+ currency: str
41
+ status: str
42
+
43
+
44
+ @dataclass
45
+ class PaymentMethod(Common):
46
+ """
47
+ The payment method object represents a user's payment method.
48
+ """
49
+ name: str
50
+ title: str
51
+ value: str
52
+ main: Optional[bool] = None
53
+
54
+
55
+ @dataclass
56
+ class Detail(Common):
57
+ """
58
+ The detail object represents additional details for a receipt.
59
+ """
60
+ discount: Optional[str] = None
61
+ shipping: Optional[str] = None
62
+ items: Optional[str] = None
63
+
64
+
65
+ # pylint: disable=C0103
66
+ @dataclass
67
+ class MerchantEpos(Common):
68
+ """
69
+ The merchantEpos object represents a user's ePOS.
70
+ """
71
+ eposId: str
72
+ eposName: str
73
+ eposType: str
74
+ eposTerminalId: str
75
+
76
+
77
+ @dataclass
78
+ class Meta(Common):
79
+ """
80
+ The meta object represents additional metadata for a receipt.
81
+ """
82
+ source: any = None
83
+ owner: any = None
84
+ host: any = None
85
+
86
+
87
+ @dataclass
88
+ class Merchant:
89
+ """
90
+ The merchant object represents a user's merchant.
91
+ """
92
+ _id: str
93
+ name: str
94
+ organization: str
95
+ address: Optional[str] = None
96
+ business_id: Optional[str] = None
97
+ epos: Optional[MerchantEpos] = None
98
+ restrictions: Optional[str] = None
99
+ date: Optional[int] = None
100
+ logo: Optional[str] = None
101
+ type: Optional[str] = None
102
+ terms: Optional[str] = None
103
+
104
+
105
+ @dataclass
106
+ class Payer(Common):
107
+ """
108
+ The payer object represents a user's payer.
109
+ """
110
+ phone: str
111
+
112
+
113
+ @dataclass
114
+ class Receipt(Common):
115
+ """
116
+ The receipt object represents a payment receipt.
117
+ """
118
+ _id: str
119
+ create_time: int
120
+ pay_time: int
121
+ cancel_time: int
122
+ state: int
123
+ type: int
124
+ external: bool
125
+ operation: int
126
+ error: any = None
127
+ description: str = None
128
+ detail: Detail = None
129
+ currency: int = None
130
+ commission: int = None
131
+ card: str = None
132
+ creator: str = None
133
+ payer: Payer = None
134
+ amount: Union[float, int] = None
135
+ account: list[Account] = None
136
+ merchant: Merchant = None
137
+ processing_id: str = None
138
+ meta: Meta = None
139
+
140
+
141
+ @dataclass
142
+ class CreateResult(Common):
143
+ """
144
+ The result object for the create response.
145
+ """
146
+ receipt: Receipt
147
+
148
+
149
+ @dataclass
150
+ class CreateResponse(Common):
151
+ """
152
+ The create response structure.
153
+ """
154
+ result: CreateResult
155
+
156
+
157
+ @dataclass
158
+ class PayResponse(CreateResponse):
159
+ """
160
+ The pay response structure.
161
+ """
162
+
163
+
164
+ @dataclass
165
+ class SendResult(Common):
166
+ """
167
+ The result object for the send response.
168
+ """
169
+ success: bool
170
+
171
+
172
+ @dataclass
173
+ class SendResponse(Common):
174
+ """
175
+ The send response structure.
176
+ """
177
+ result: SendResult
178
+
179
+
180
+ @dataclass
181
+ class CancelResponse(CreateResponse):
182
+ """
183
+ The cancel response structure.
184
+ """
185
+
186
+
187
+ @dataclass
188
+ class CheckResult(Common):
189
+ """
190
+ The result object for the check response.
191
+ """
192
+ state: int
193
+
194
+
195
+ @dataclass
196
+ class CheckResponse(Common):
197
+ """
198
+ The check response structure.
199
+ """
200
+ result: CheckResult
201
+
202
+
203
+ @dataclass
204
+ class GetResponse(CreateResponse):
205
+ """
206
+ The result object for the get response.
207
+ """
208
+
209
+
210
+ @dataclass
211
+ class GetAllResponse(Common):
212
+ """
213
+ The result object for the get all response.
214
+ """
215
+ result: list[Receipt] = None