paytechuz 0.1.2__py3-none-any.whl → 0.1.4__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 paytechuz might be problematic. Click here for more details.

core/base.py CHANGED
@@ -4,6 +4,7 @@ Base classes for payment gateways.
4
4
  from abc import ABC, abstractmethod
5
5
  from typing import Dict, Any, Optional, Union
6
6
 
7
+
7
8
  class BasePaymentGateway(ABC):
8
9
  """
9
10
  Base class for all payment gateways.
@@ -23,7 +24,12 @@ class BasePaymentGateway(ABC):
23
24
  self.is_test_mode = is_test_mode
24
25
 
25
26
  @abstractmethod
26
- def create_payment(self, amount: Union[int, float, str], account_id: Union[int, str], **kwargs) -> Dict[str, Any]:
27
+ def create_payment(
28
+ self,
29
+ amount: Union[int, float, str],
30
+ account_id: Union[int, str],
31
+ **kwargs
32
+ ) -> Dict[str, Any]:
27
33
  """
28
34
  Create a payment.
29
35
 
@@ -51,7 +57,11 @@ class BasePaymentGateway(ABC):
51
57
  raise NotImplementedError
52
58
 
53
59
  @abstractmethod
54
- def cancel_payment(self, transaction_id: str, reason: Optional[str] = None) -> Dict[str, Any]:
60
+ def cancel_payment(
61
+ self,
62
+ transaction_id: str,
63
+ reason: Optional[str] = None
64
+ ) -> Dict[str, Any]:
55
65
  """
56
66
  Cancel payment.
57
67
 
@@ -65,7 +75,6 @@ class BasePaymentGateway(ABC):
65
75
  raise NotImplementedError
66
76
 
67
77
 
68
-
69
78
  class BaseWebhookHandler(ABC):
70
79
  """
71
80
  Base class for payment gateway webhook handlers.
gateways/payme/client.py CHANGED
@@ -3,6 +3,7 @@ Payme payment gateway client.
3
3
  """
4
4
  import logging
5
5
  from typing import Dict, Any, Optional, Union
6
+ import base64
6
7
 
7
8
  from paytechuz.core.base import BasePaymentGateway
8
9
  from paytechuz.core.http import HttpClient
@@ -13,6 +14,7 @@ from paytechuz.gateways.payme.receipts import PaymeReceipts
13
14
 
14
15
  logger = logging.getLogger(__name__)
15
16
 
17
+
16
18
  class PaymeGateway(BasePaymentGateway):
17
19
  """
18
20
  Payme payment gateway implementation.
@@ -56,116 +58,138 @@ class PaymeGateway(BasePaymentGateway):
56
58
  payme_key=payme_key
57
59
  )
58
60
 
61
+ def generate_pay_link(
62
+ self,
63
+ id: Union[int, str],
64
+ amount: Union[int, float, str],
65
+ return_url: str,
66
+ account_field_name: str = "order_id"
67
+ ) -> str:
68
+ """
69
+ Generate a payment link for a specific order.
70
+
71
+ Parameters
72
+ ----------
73
+ id : Union[int, str]
74
+ Unique identifier for the account/order.
75
+ amount : Union[int, float, str]
76
+ Payment amount in som.
77
+ return_url : str
78
+ URL to redirect after payment completion.
79
+ account_field_name : str, optional
80
+ Field name for account identifier (default: "order_id").
81
+
82
+ Returns
83
+ -------
84
+ str
85
+ Payme checkout URL with encoded parameters.
86
+
87
+ References
88
+ ----------
89
+ https://developer.help.paycom.uz/initsializatsiya-platezhey/
90
+ """
91
+ # Convert amount to tiyin (1 som = 100 tiyin)
92
+ amount_tiyin = int(float(amount) * 100)
93
+
94
+ # Build parameters
95
+ params = (
96
+ f'm={self.payme_id};'
97
+ f'ac.{account_field_name}={id};'
98
+ f'a={amount_tiyin};'
99
+ f'c={return_url}'
100
+ )
101
+ encoded_params = base64.b64encode(params.encode("utf-8")).decode("utf-8")
102
+
103
+ # Return URL based on environment
104
+ base_url = "https://test.paycom.uz" if self.is_test_mode else "https://checkout.paycom.uz"
105
+ return f"{base_url}/{encoded_params}"
106
+
107
+ async def generate_pay_link_async(
108
+ self,
109
+ id: Union[int, str],
110
+ amount: Union[int, float, str],
111
+ return_url: str,
112
+ account_field_name: str = "order_id"
113
+ ) -> str:
114
+ """
115
+ Async version of generate_pay_link.
116
+
117
+ Parameters
118
+ ----------
119
+ id : Union[int, str]
120
+ Unique identifier for the account/order.
121
+ amount : Union[int, float, str]
122
+ Payment amount in som.
123
+ return_url : str
124
+ URL to redirect after payment completion.
125
+ account_field_name : str, optional
126
+ Field name for account identifier (default: "order_id").
127
+
128
+ Returns
129
+ -------
130
+ str
131
+ Payme checkout URL with encoded parameters.
132
+ """
133
+ return self.generate_pay_link(
134
+ id=id,
135
+ amount=amount,
136
+ return_url=return_url,
137
+ account_field_name=account_field_name
138
+ )
139
+
59
140
  @handle_exceptions
60
141
  def create_payment(
61
142
  self,
143
+ id: Union[int, str],
62
144
  amount: Union[int, float, str],
63
- account_id: Union[int, str],
64
- **kwargs
65
- ) -> Dict[str, Any]:
145
+ return_url: str = "",
146
+ account_field_name: str = "order_id"
147
+ ) -> str:
66
148
  """
67
- Create a payment using Payme receipts.
149
+ Create a payment using Payme.
68
150
 
69
151
  Args:
70
- amount: The payment amount in som
71
- account_id: The account ID or order ID
72
- **kwargs: Additional parameters for the payment
73
- - description: Payment description
74
- - detail: Payment details
75
- - callback_url: URL to redirect after payment
76
- - return_url: URL to return after payment
77
- - phone: Customer phone number
78
- - email: Customer email
79
- - language: Language code (uz, ru, en)
80
- - expire_minutes: Payment expiration time in minutes
152
+ amount: Payment amount in som
153
+ account_id: Account or order ID
154
+ return_url: Return URL after payment (default: "")
155
+ account_field_name: Field name for account ID (default: "order_id")
81
156
 
82
157
  Returns:
83
- Dict containing payment details including transaction ID and payment URL
158
+ str: Payme payment URL
159
+ """
160
+ return self.generate_pay_link(
161
+ id=id,
162
+ amount=amount,
163
+ return_url=return_url,
164
+ account_field_name=account_field_name
165
+ )
166
+
167
+ @handle_exceptions
168
+ async def create_payment_async(
169
+ self,
170
+ id: Union[int, str],
171
+ amount: Union[int, float, str],
172
+ return_url: str = "",
173
+ account_field_name: str = "order_id"
174
+ ) -> str:
84
175
  """
85
- # Format amount to tiyin (1 som = 100 tiyin)
86
- amount_tiyin = format_amount(amount)
87
-
88
- # Extract additional parameters
89
- description = kwargs.get('description', f'Payment for account {account_id}')
90
- detail = kwargs.get('detail', {})
91
- callback_url = kwargs.get('callback_url')
92
- return_url = kwargs.get('return_url')
93
- phone = kwargs.get('phone')
94
- email = kwargs.get('email')
95
- language = kwargs.get('language', 'uz')
96
- expire_minutes = kwargs.get('expire_minutes', 60) # Default 1 hour
97
-
98
- # Check if we have a merchant key
99
- if self.payme_key:
100
- # Create receipt using the API
101
- receipt_data = self.receipts.create(
102
- amount=amount_tiyin,
103
- account={"account_id": str(account_id)},
104
- description=description,
105
- detail=detail,
106
- callback_url=callback_url,
107
- return_url=return_url,
108
- phone=phone,
109
- email=email,
110
- language=language,
111
- expire_minutes=expire_minutes
112
- )
113
-
114
- # Extract receipt ID and payment URL
115
- receipt_id = receipt_data.get('receipt', {}).get('_id')
116
- payment_url = receipt_data.get('receipt', {}).get('pay_url')
117
-
118
- return {
119
- 'transaction_id': receipt_id,
120
- 'payment_url': payment_url,
121
- 'amount': amount,
122
- 'account_id': account_id,
123
- 'status': 'created',
124
- 'raw_response': receipt_data
125
- }
126
- else:
127
- # Generate a payment URL using payme-pkg style
128
- # This is a fallback method that doesn't require authentication
129
- import base64
130
- from paytechuz.core.utils import generate_id
131
-
132
- # Generate a unique transaction ID
133
- transaction_id = generate_id("payme")
134
-
135
- # Format amount to the smallest currency unit (tiyin)
136
- # amount_tiyin is already in tiyin format
137
-
138
- # Build the payment parameters string
139
- # Format: m=merchant_id;ac.field=value;a=amount;c=callback_url
140
- params_str = f"m={self.payme_id};ac.id={account_id};a={amount_tiyin}"
141
-
142
- # Add callback URL if provided (this is used for return URL in payme-pkg)
143
- if return_url:
144
- params_str += f";c={return_url}"
145
-
146
- # Encode the parameters string to base64
147
- encoded_params = base64.b64encode(params_str.encode("utf-8")).decode("utf-8")
148
-
149
- # Build the payment URL
150
- if self.is_test_mode:
151
- payment_url = f"https://test.paycom.uz/{encoded_params}"
152
- else:
153
- payment_url = f"https://checkout.paycom.uz/{encoded_params}"
154
-
155
- # Print the parameters for debugging
156
- print("Payme payment parameters:")
157
- print(f"Parameters string: {params_str}")
158
- print(f"Encoded parameters: {encoded_params}")
159
- print(f"Payment URL: {payment_url}")
160
-
161
- return {
162
- 'transaction_id': transaction_id,
163
- 'payment_url': payment_url,
164
- 'amount': amount,
165
- 'account_id': account_id,
166
- 'status': 'created',
167
- 'raw_response': {}
168
- }
176
+ Async version of create_payment.
177
+
178
+ Args:
179
+ amount: Payment amount in som
180
+ account_id: Account or order ID
181
+ return_url: Return URL after payment (default: "")
182
+ account_field_name: Field name for account ID (default: "order_id")
183
+
184
+ Returns:
185
+ str: Payme payment URL
186
+ """
187
+ return await self.generate_pay_link_async(
188
+ id=id,
189
+ amount=amount,
190
+ return_url=return_url,
191
+ account_field_name=account_field_name
192
+ )
169
193
 
170
194
  @handle_exceptions
171
195
  def check_payment(self, transaction_id: str) -> Dict[str, Any]:
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: paytechuz
3
+ Version: 0.1.4
4
+ Summary: Unified Python package for Uzbekistan payment gateways
5
+ Home-page: https://github.com/Muhammadali-Akbarov/paytechuz
6
+ Author: Muhammadali Akbarov
7
+ Author-email: muhammadali17abc@gmail.com
8
+ License: MIT
9
+ Requires-Python: >=3.6
10
+ Description-Content-Type: text/markdown
11
+ Dynamic: author
12
+ Dynamic: author-email
13
+ Dynamic: home-page
14
+ Dynamic: requires-python
15
+
16
+ # PayTechUZ
17
+
18
+ [![PyPI version](https://badge.fury.io/py/paytechuz.svg)](https://badge.fury.io/py/paytechuz)
19
+ [![Python Versions](https://img.shields.io/pypi/pyversions/paytechuz.svg)](https://pypi.org/project/paytechuz/)
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
21
+ ## Installation
22
+
23
+ ### Basic Installation
24
+
25
+ ```bash
26
+ pip install paytechuz
27
+ ```
28
+
29
+ ### Framework-Specific Installation
30
+
31
+ ```bash
32
+ # For Django
33
+ pip install paytechuz[django]
34
+
35
+ # For FastAPI
36
+ pip install paytechuz[fastapi]
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### Generate Payment Links
42
+
43
+ ```python
44
+ from paytechuz.gateways.payme import PaymeGateway
45
+ from paytechuz.gateways.click import ClickGateway
46
+
47
+ # Initialize gateways
48
+ payme = PaymeGateway()
49
+ click = ClickGateway()
50
+
51
+ # Generate payment links
52
+ payme_link = payme.generate_payment_link(
53
+ id="order_123",
54
+ amount=150000, # amount in UZS
55
+ return_url="https://example.com/return"
56
+ )
57
+
58
+ click_link = click.generate_payment_link(
59
+ id="order_123",
60
+ amount=150000, # amount in UZS
61
+ return_url="https://example.com/return"
62
+ )
63
+ ```
64
+
65
+ ### Django Integration
66
+
67
+ 1. Add to `INSTALLED_APPS`:
68
+
69
+ ```python
70
+ # settings.py
71
+ INSTALLED_APPS = [
72
+ # ...
73
+ 'paytechuz.integrations.django',
74
+ ]
75
+
76
+ PAYME_ID = 'your_payme_merchant_id'
77
+ PAYME_KEY = 'your_payme_merchant_key'
78
+ PAYME_ACCOUNT_MODEL = 'your_app.models.YourModel' # For example: 'orders.models.Order'
79
+ PAYME_ACCOUNT_FIELD = 'id'
80
+ PAYME_AMOUNT_FIELD = 'amount' # Field for storing payment amount
81
+ PAYME_ONE_TIME_PAYMENT = True # Allow only one payment per account
82
+
83
+ CLICK_SERVICE_ID = 'your_click_service_id'
84
+ CLICK_MERCHANT_ID = 'your_click_merchant_id'
85
+ CLICK_SECRET_KEY = 'your_click_secret_key'
86
+ CLICK_ACCOUNT_MODEL = 'your_app.models.YourModel'
87
+ CLICK_COMMISSION_PERCENT = 0.0
88
+ ```
89
+
90
+ 2. Create webhook handlers:
91
+
92
+ ```python
93
+ # views.py
94
+ from paytechuz.integrations.django.views import PaymeWebhookView
95
+ from .models import Order
96
+
97
+ class PaymeWebhookView(PaymeWebhookView):
98
+ def successfully_payment(self, params, transaction):
99
+ order = Order.objects.get(id=transaction.account_id)
100
+ order.status = 'paid'
101
+ order.save()
102
+
103
+ def cancelled_payment(self, params, transaction):
104
+ order = Order.objects.get(id=transaction.account_id)
105
+ order.status = 'cancelled'
106
+ order.save()
107
+ ```
108
+
109
+ ### FastAPI Integration
110
+
111
+ 1. Create webhook handler:
112
+
113
+ ```python
114
+ from fastapi import FastAPI, Request
115
+ from paytechuz.integrations.fastapi import PaymeWebhookHandler
116
+
117
+ app = FastAPI()
118
+
119
+ class CustomPaymeWebhookHandler(PaymeWebhookHandler):
120
+ def successfully_payment(self, params, transaction):
121
+ # Handle successful payment
122
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
123
+ order.status = "paid"
124
+ self.db.commit()
125
+
126
+ def cancelled_payment(self, params, transaction):
127
+ # Handle cancelled payment
128
+ order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
129
+ order.status = "cancelled"
130
+ self.db.commit()
131
+
132
+ @app.post("/payments/payme/webhook")
133
+ async def payme_webhook(request: Request):
134
+ handler = CustomPaymeWebhookHandler(
135
+ payme_id="your_merchant_id",
136
+ payme_key="your_merchant_key"
137
+ )
138
+ return await handler.handle_webhook(request)
139
+ ```
140
+
141
+ ## Documentation
142
+
143
+ Detailed documentation is available in multiple languages:
144
+
145
+ - 📖 [English Documentation](docs/en/index.md)
146
+ - 📖 [O'zbek tilidagi hujjatlar](docs/uz/index.md)
@@ -1,5 +1,5 @@
1
1
  core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- core/base.py,sha256=bHmRMYZ97Kj76a4kL7UMRcecnZF-rKnBYZddQvRtRmQ,2497
2
+ core/base.py,sha256=pW_0QrlyX0F8XVToeeDEI78ygAF2IgvvvyUGYMHH9bs,2565
3
3
  core/constants.py,sha256=P2zeZ_cfZIttdC1vqkpIngkfRFh6loWzJYEgzQb5cKA,1660
4
4
  core/exceptions.py,sha256=XMJkqiponTkvhjoh3S2iFNuU3UbBdFW4130kd0hpudg,5489
5
5
  core/http.py,sha256=qmLR6ujxmIPjwoTS4vvKRIvDnNcpl84sS1HmVB890b0,7686
@@ -12,7 +12,7 @@ gateways/click/merchant.py,sha256=vJ_DivA1KfRT5p3sfA5yZGMYXoUmVbAM7QHvaXr6VCU,72
12
12
  gateways/click/webhook.py,sha256=rph-NmjjnBKMW4rcxQTXrHHdK-uMrU39kXnbqK56leo,7936
13
13
  gateways/payme/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  gateways/payme/cards.py,sha256=aL6su_ZTCBPU8qmrz2Jcw_Wn7Zf9RwIu8k10o4AWFTs,5420
15
- gateways/payme/client.py,sha256=C1OQWLo7LIAF3xbFOLyCaJ3xFwQaFVpaxWCYajCltTY,8201
15
+ gateways/payme/client.py,sha256=B4jxjX_7J_uR-NP8-B6wO_VrnwO_RRh1ATiKsO4ZinM,7855
16
16
  gateways/payme/receipts.py,sha256=DdrZMPeDvQmGyqAEOTmtUorfcIVVb3t2tg31l7TXqHo,8904
17
17
  gateways/payme/webhook.py,sha256=-0O8vzMtiu4U8FWFKDA6EfyoX4NEGqcEq-T0yNtVhM4,12374
18
18
  integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,8 +29,7 @@ integrations/fastapi/__init__.py,sha256=DLnhAZQZf2ghu8BuFFfE7FzbNKWQQ2SLG8qxldRu
29
29
  integrations/fastapi/models.py,sha256=eWGUpiKufj47AK8Hld4A91jRDj0ZKQzAf95CyUozmvo,4638
30
30
  integrations/fastapi/routes.py,sha256=D17QeyY4-aX6tCNmk5h3UiavukvVrE5e6JOFCy4t_n8,36629
31
31
  integrations/fastapi/schemas.py,sha256=CkNohj22mQQje8Pu_IkTQwUPAoYHNOKXlGjqaRX_SGQ,3784
32
- paytechuz/__init__.py,sha256=U1xqjVNUCnMOu11K-u1qkpMkQHa8WtUu10lUgjg49RA,1935
33
- paytechuz-0.1.2.dist-info/METADATA,sha256=S1UyhyOmeMX79I5hFqIdO4JHnM-82ddHppEcDapSdGg,6058
34
- paytechuz-0.1.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
35
- paytechuz-0.1.2.dist-info/top_level.txt,sha256=v03IobsNVIGSgCareObqCsEwLgXOpwBvBTWy3y8-G_M,37
36
- paytechuz-0.1.2.dist-info/RECORD,,
32
+ paytechuz-0.1.4.dist-info/METADATA,sha256=4QSapmW-hD_4AJm0rE-OvnXIhRRgsePVnsX6UKczfh4,3861
33
+ paytechuz-0.1.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
34
+ paytechuz-0.1.4.dist-info/top_level.txt,sha256=tfgxeqY7QDfOFb07FwoMkscdgKV9VIbJLnJnh2FNjRM,27
35
+ paytechuz-0.1.4.dist-info/RECORD,,
@@ -1,4 +1,3 @@
1
1
  core
2
2
  gateways
3
3
  integrations
4
- paytechuz
paytechuz/__init__.py DELETED
@@ -1,74 +0,0 @@
1
- """PayTechUZ - Unified payment library for Uzbekistan payment systems.
2
-
3
- This library provides a unified interface for working with Payme and Click
4
- payment systems in Uzbekistan. It supports Django, Flask, and FastAPI.
5
- """
6
- from typing import Any
7
-
8
- __version__ = '0.1.1'
9
-
10
- # Define dummy classes to avoid import errors
11
- class PaymeGateway:
12
- """Dummy PaymeGateway class to avoid import errors."""
13
- def __init__(self, **kwargs):
14
- pass
15
-
16
-
17
- class ClickGateway:
18
- """Dummy ClickGateway class to avoid import errors."""
19
- def __init__(self, **kwargs):
20
- pass
21
-
22
-
23
- class PaymentGateway:
24
- """Dummy PaymentGateway enum to avoid import errors."""
25
- class PAYME:
26
- value = 'payme'
27
-
28
- class CLICK:
29
- value = 'click'
30
-
31
-
32
- # Import framework integrations - these imports are used to check availability
33
- # of frameworks, not for direct usage
34
- try:
35
- import django # noqa: F401 - Used for availability check
36
- HAS_DJANGO = True
37
- except ImportError:
38
- HAS_DJANGO = False
39
-
40
- try:
41
- import fastapi # noqa: F401 - Used for availability check
42
- HAS_FASTAPI = True
43
- except ImportError:
44
- HAS_FASTAPI = False
45
-
46
- try:
47
- import flask # noqa: F401 - Used for availability check
48
- HAS_FLASK = True
49
- except ImportError:
50
- HAS_FLASK = False
51
-
52
-
53
- def create_gateway(gateway_type: str, **kwargs) -> Any:
54
- """
55
- Create a payment gateway instance.
56
-
57
- Args:
58
- gateway_type: Type of gateway ('payme' or 'click')
59
- **kwargs: Gateway-specific configuration
60
-
61
- Returns:
62
- Payment gateway instance
63
-
64
- Raises:
65
- ValueError: If the gateway type is not supported
66
- ImportError: If the required gateway module is not available
67
- """
68
- # Just use the dummy classes for now
69
- if gateway_type.lower() == 'payme':
70
- return PaymeGateway(**kwargs)
71
- if gateway_type.lower() == 'click':
72
- return ClickGateway(**kwargs)
73
-
74
- raise ValueError(f"Unsupported gateway type: {gateway_type}")
@@ -1,198 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: paytechuz
3
- Version: 0.1.2
4
- Summary: Unified Python package for Uzbekistan payment gateways
5
- Home-page: https://github.com/Muhammadali-Akbarov/paytechuz
6
- Author: Muhammadali Akbarov
7
- Author-email: muhammadali17abc@gmail.com
8
- License: MIT
9
- Keywords: paytechuz,payme,click,uzbekistan,payment,gateway,payment-gateway,payment-processing,django,flask,fastapi
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.6
14
- Classifier: Programming Language :: Python :: 3.7
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- Classifier: License :: OSI Approved :: MIT License
21
- Classifier: Operating System :: OS Independent
22
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Requires-Python: >=3.6
24
- Description-Content-Type: text/markdown
25
- Requires-Dist: requests<3.0,>=2.0
26
- Requires-Dist: dataclasses<1.0,>=0.6; python_version < "3.7"
27
- Provides-Extra: django
28
- Requires-Dist: django<5.0,>=3.0; extra == "django"
29
- Requires-Dist: djangorestframework<4.0,>=3.0; extra == "django"
30
- Provides-Extra: fastapi
31
- Requires-Dist: fastapi<1.0.0,>=0.68.0; extra == "fastapi"
32
- Requires-Dist: sqlalchemy<3.0,>=1.4; extra == "fastapi"
33
- Requires-Dist: httpx<1.0,>=0.20; extra == "fastapi"
34
- Requires-Dist: python-multipart==0.0.20; extra == "fastapi"
35
- Requires-Dist: pydantic<2.0,>=1.8; extra == "fastapi"
36
- Provides-Extra: flask
37
- Requires-Dist: flask<3.0,>=2.0; extra == "flask"
38
- Requires-Dist: flask-sqlalchemy<3.0,>=2.5; extra == "flask"
39
- Dynamic: author
40
- Dynamic: author-email
41
- Dynamic: classifier
42
- Dynamic: description
43
- Dynamic: description-content-type
44
- Dynamic: home-page
45
- Dynamic: keywords
46
- Dynamic: license
47
- Dynamic: provides-extra
48
- Dynamic: requires-dist
49
- Dynamic: requires-python
50
- Dynamic: summary
51
-
52
- # paytechuz
53
-
54
- paytechuz is a unified payment library for integration with popular payment systems in Uzbekistan (Payme and Click).
55
-
56
- [![PyPI version](https://badge.fury.io/py/paytechuz.svg)](https://badge.fury.io/py/paytechuz)
57
- [![Python Versions](https://img.shields.io/pypi/pyversions/paytechuz.svg)](https://pypi.org/project/paytechuz/)
58
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
59
-
60
- ## Installation
61
-
62
- To install paytechuz with all dependencies:
63
-
64
- ```bash
65
- pip install paytechuz
66
- ```
67
-
68
- For specific framework support:
69
-
70
- ```bash
71
- # For Django
72
- pip install paytechuz[django]
73
-
74
- # For FastAPI
75
- pip install paytechuz[fastapi]
76
- ```
77
-
78
- ## Quick Start
79
-
80
- ### Django Integration
81
-
82
- 1. Add the app to your `INSTALLED_APPS`:
83
-
84
- ```python
85
- # settings.py
86
- INSTALLED_APPS = [
87
- # ...
88
- 'paytechuz.integrations.django',
89
- ]
90
-
91
- # Payme settings
92
- PAYME_ID = 'your_payme_merchant_id'
93
- PAYME_KEY = 'your_payme_merchant_key'
94
- PAYME_ACCOUNT_MODEL = 'your_app.YourModel' # For example: 'orders.Order'
95
- PAYME_ACCOUNT_FIELD = 'id' # Field for account identifier
96
- PAYME_AMOUNT_FIELD = 'amount' # Field for storing payment amount
97
- PAYME_ONE_TIME_PAYMENT = True # Allow only one payment per account
98
- ```
99
-
100
- 2. Set up the webhook URLs:
101
-
102
- ```python
103
- # urls.py
104
- from django.urls import path
105
- from django.views.decorators.csrf import csrf_exempt
106
-
107
- from your_app.views import PaymeWebhookView, ClickWebhookView
108
-
109
-
110
- urlpatterns = [
111
- # ...
112
- path('payments/payme/', csrf_exempt(PaymeWebhookView.as_view()), name='payme_webhook'),
113
- path('payments/click/', csrf_exempt(ClickWebhookView.as_view()), name='click_webhook'),
114
- ]
115
- ```
116
-
117
- 3. Create custom webhook handlers:
118
-
119
- ```python
120
- # views.py
121
- from paytechuz.integrations.django.views import PaymeWebhookView as BasePaymeWebhookView
122
- from .models import Order
123
-
124
- class PaymeWebhookView(BasePaymeWebhookView):
125
- def successfully_payment(self, params, transaction):
126
- """Called when payment is successful"""
127
- order_id = transaction.account_id
128
- order = Order.objects.get(id=order_id)
129
- order.status = 'paid'
130
- order.save()
131
-
132
- def cancelled_payment(self, params, transaction):
133
- """Called when payment is cancelled"""
134
- order_id = transaction.account_id
135
- order = Order.objects.get(id=order_id)
136
- order.status = 'cancelled'
137
- order.save()
138
- ```
139
-
140
- ### FastAPI Integration
141
-
142
- 1. Create a custom webhook handler:
143
-
144
- ```python
145
- from fastapi import APIRouter, Depends, Request
146
- from sqlalchemy.orm import Session
147
-
148
- from app.database.db import get_db
149
- from app.models.models import Order
150
- from paytechuz.integrations.fastapi import PaymeWebhookHandler
151
-
152
- # Payme configuration
153
- PAYME_ID = 'your_payme_id'
154
- PAYME_KEY = 'your_payme_key'
155
-
156
- class CustomPaymeWebhookHandler(PaymeWebhookHandler):
157
- def successfully_payment(self, params, transaction) -> None:
158
- """Called when payment is successful"""
159
- order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
160
- if order:
161
- order.status = "paid"
162
- self.db.commit()
163
-
164
- def cancelled_payment(self, params, transaction) -> None:
165
- """Called when payment is cancelled"""
166
- order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
167
- if order:
168
- order.status = "cancelled"
169
- self.db.commit()
170
- ```
171
-
172
- 2. Create a webhook endpoint:
173
-
174
- ```python
175
- router = APIRouter()
176
-
177
- @router.post("/payments/payme/webhook")
178
- async def payme_webhook(request: Request, db: Session = Depends(get_db)):
179
- """Handle Payme webhook requests"""
180
- handler = CustomPaymeWebhookHandler(
181
- db=db,
182
- payme_id=PAYME_ID,
183
- payme_key=PAYME_KEY,
184
- account_model=Order,
185
- account_field="id",
186
- amount_field="amount",
187
- one_time_payment=False
188
- )
189
- result = await handler.handle_webhook(request)
190
- return result
191
- ```
192
-
193
- ## Documentation
194
-
195
- For detailed documentation, see:
196
-
197
- - [English Documentation](paytechuz/docs/en/index.md)
198
- - [O'zbek tilidagi hujjatlar](paytechuz/docs/index.md)