Unified Python package for Uzbekistan payment gateways (Payme, Click, Atmos)
Project description
paytechuz
PayTechUZ is a unified payment library for integrating with popular payment systems in Uzbekistan. It provides a simple and consistent interface for working with Payme, Click, and Atmos payment gateways.
📖 Complete Documentation | 🚀 Quick Start Guide
Features
- API: Consistent interface for multiple payment providers
- Secure: Built-in security features for payment processing
- Framework Integration: Native support for Django and FastAPI
- Webhook Handling: Easy-to-use webhook handlers for payment notifications
- Transaction Management: Automatic transaction tracking and management
- Extensible: Easy to add new payment providers
Installation
Basic Installation
pip install paytechuz
Framework-Specific Installation
# For Django
pip install paytechuz[django]
# For FastAPI
pip install paytechuz[fastapi]
# For Flask
pip install paytechuz[flask]
API Key Configuration
Important: PayTechUZ requires a valid license API key for license validation.
# Set your license API key as an environment variable
export PAYTECH_LICENSE_API_KEY="your-license-api-key-here"
To obtain a production license API key, please visit https://pay-tech.uz/console or contact @muhammadali_me on Telegram.
Quick Start
💡 Need help? Check out our complete documentation for detailed guides and examples.
Generate Payment Links
from paytechuz.gateways.payme import PaymeGateway
from paytechuz.gateways.click import ClickGateway
from paytechuz.gateways.atmos import AtmosGateway
# Initialize Payme gateway
payme = PaymeGateway(
payme_id="your_payme_id",
payme_key="your_payme_key",
is_test_mode=True # Set to False in production environment
)
# Initialize Click gateway
click = ClickGateway(
service_id="your_service_id",
merchant_id="your_merchant_id",
merchant_user_id="your_merchant_user_id",
secret_key="your_secret_key",
is_test_mode=True # Set to False in production environment
)
# Initialize Atmos gateway
atmos = AtmosGateway(
consumer_key="your_consumer_key",
consumer_secret="your_consumer_secret",
store_id="your_store_id",
terminal_id="your_terminal_id", # optional
is_test_mode=True # Set to False in production environment
)
# Generate payment links
payme_link = payme.create_payment(
id="order_123",
amount=150000, # amount in UZS
return_url="https://example.com/return"
)
click_link = click.create_payment(
id="order_123",
amount=150000, # amount in UZS
description="Test payment",
return_url="https://example.com/return"
)
atmos_payment = atmos.create_payment(
account_id="order_123",
amount=150000 # amount in UZS
)
atmos_link = atmos_payment['payment_url']
Card Management (Payme)
You can manage saved cards using the cards property of the PaymeGateway instance.
# Initialize Payme gateway
payme = PaymeGateway(
payme_id="your_payme_id",
payme_key="your_payme_key",
is_test_mode=True
)
# 1. Create a new card
# This will return a token that needs to be verified
create_response = payme.cards.create(
card_number="8600000000000000",
expire_date="12/25",
save=True # Set to True to save the card for future use
)
token = create_response['result']['card']['token']
# 2. Get verification code (if not received automatically)
payme.cards.get_verify_code(token=token)
# 3. Verify the card with the code sent to the user via SMS
verify_response = payme.cards.verify(
token=token,
code="123456"
)
# 4. Check card status
check_response = payme.cards.check(token=token)
is_active = check_response['result']['card']['active']
# 5. Remove a saved card
payme.cards.remove(token=token)
Receipts Management (Payme)
You can manage receipts using the receipts property of the PaymeGateway instance.
# Initialize Payme gateway
payme = PaymeGateway(
payme_id="your_payme_id",
payme_key="your_payme_key",
is_test_mode=True
)
# 1. Create a receipt
create_response = payme.receipts.create(
amount=100000, # 1000 sum (in tiyin)
account={"order_id": "12345"},
description="Payment for order #12345"
)
receipt_id = create_response['result']['receipt']['_id']
# 2. Pay a receipt with a saved card
pay_response = payme.receipts.pay(
receipt_id=receipt_id,
token="card_token_here"
)
# 3. Check receipt status
check_response = payme.receipts.check(receipt_id=receipt_id)
is_paid = check_response['result']['state'] == 2 # 2 means paid
# 4. Send receipt to phone number (SMS)
payme.receipts.send(
receipt_id=receipt_id,
phone="998901234567"
)
# 5. Cancel a receipt
payme.receipts.cancel(
receipt_id=receipt_id,
reason="Refund requested"
)
Card Management (Click)
Click supports card tokenization via the standardized cards property.
# Initialize Click gateway
click = ClickGateway(
service_id="your_service_id",
merchant_id="your_merchant_id",
merchant_user_id="your_merchant_user_id",
secret_key="your_secret_key",
is_test_mode=True
)
# 1. Request a card token
# This returns a temporary token and sends SMS to the user
token_response = click.cards.create(
card_number="8600000000000000",
expire_date="0330", # MMYY format
save=True # Same as Payme! (maps to temporary=0)
)
card_token = token_response['card_token']
# 2. Verify the card token
click.cards.verify(
card_token=card_token,
sms_code="12345"
)
# 3. Make a payment with the verified token
payment_response = click.cards.pay(
card_token=card_token,
amount=50000,
transaction_parameter="order_123"
)
payment_id = payment_response['payment_id']
Invoice Management (Click)
You can manage invoices using the invoices property (or receipts alias) of the ClickGateway instance.
# 1. Create an invoice
invoice_response = click.invoices.create(
id="order_123",
amount=150000,
phone="998901234567",
description="Payment for order #123"
)
invoice_id = invoice_response['invoice_id']
# 2. Check invoice status
status_response = click.invoices.check(invoice_id=invoice_id)
is_paid = status_response['status'] == 10 # 10 means paid (Click status codes vary)
# 3. Cancel an invoice
click.invoices.cancel(
invoice_id=invoice_id,
reason="Order cancelled"
)
Universal Wrapper (Recommended)
For the seamless integration, use UniversalPayment. It provides a completely standardized interface where method names, arguments, and return types are normalized across all providers.
from paytechuz import UniversalPayment
# 1. Initialize for Payme
client = UniversalPayment('payme', {
'payme_id': "your_id",
'payme_key': "your_key",
'is_test_mode': True
})
# OR Initialize for Click (code below works identical for both!)
# client = UniversalPayment('click', {
# 'service_id': "...",
# 'merchant_id': "...",
# 'merchant_user_id': "...",
# 'secret_key': "...",
# 'is_test_mode': True
# })
# 2. Create standard payment link
url = client.generate_payment_url(
amount=100000, # Standard: Always in UZS (UniversalPayment handles conversion)
order_id="order_123"
)
# 3. Manage Cards (Standardized)
# Returns standard dict: {'token': '...', 'phone': '...', 'is_active': bool}
card_data = client.cards.create(
number="8600000000000000",
expire="0330",
save=True
)
token = card_data['token']
# Verify
client.cards.verify(token=token, code="12345")
# 4. Manage Invoices (Standardized)
invoice = client.invoices.create(amount=50000, order_id="order_124")
invoice_id = invoice['invoice_id']
# Pay invoice with token (if supported)
# client.invoices.pay(invoice_id=invoice_id, token=token)
Django Integration
- Create Order model:
# models.py
from django.db import models
from django.utils import timezone
class Order(models.Model):
STATUS_CHOICES = (
('pending', 'Pending'),
('paid', 'Paid'),
('cancelled', 'Cancelled'),
('delivered', 'Delivered'),
)
product_name = models.CharField(max_length=255)
amount = models.DecimalField(max_digits=12, decimal_places=2)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
created_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.id} - {self.product_name} ({self.amount})"
- Add to
INSTALLED_APPSand configure settings:
# settings.py
INSTALLED_APPS = [
# ...
'paytechuz.integrations.django',
]
PAYTECHUZ = {
'PAYME': {
'PAYME_ID': 'your_payme_id',
'PAYME_KEY': 'your_payme_key',
'ACCOUNT_MODEL': 'your_app.models.Order', # For example: 'orders.models.Order'
'ACCOUNT_FIELD': 'id',
'AMOUNT_FIELD': 'amount',
'ONE_TIME_PAYMENT': True,
'IS_TEST_MODE': True, # Set to False in production
},
'CLICK': {
'SERVICE_ID': 'your_service_id',
'MERCHANT_ID': 'your_merchant_id',
'MERCHANT_USER_ID': 'your_merchant_user_id',
'SECRET_KEY': 'your_secret_key',
'ACCOUNT_MODEL': 'your_app.models.Order',
'COMMISSION_PERCENT': 0.0,
'IS_TEST_MODE': True, # Set to False in production
},
'ATMOS': {
'CONSUMER_KEY': 'your_atmos_consumer_key',
'CONSUMER_SECRET': 'your_atmos_consumer_secret',
'STORE_ID': 'your_atmos_store_id',
'TERMINAL_ID': 'your_atmos_terminal_id', # Optional
'API_KEY': 'your_atmos_api_key'
'ACCOUNT_MODEL': 'your_app.models.Order',
'ACCOUNT_FIELD': 'id',
'IS_TEST_MODE': True, # Set to False in production
}
}
- Create webhook handlers:
# views.py
from paytechuz.integrations.django.views import (
BasePaymeWebhookView,
BaseClickWebhookView,
BaseAtmosWebhookView
)
from .models import Order
class PaymeWebhookView(BasePaymeWebhookView):
def successfully_payment(self, params, transaction):
order = Order.objects.get(id=transaction.account_id)
order.status = 'paid'
order.save()
def cancelled_payment(self, params, transaction):
order = Order.objects.get(id=transaction.account_id)
order.status = 'cancelled'
order.save()
class ClickWebhookView(BaseClickWebhookView):
def successfully_payment(self, params, transaction):
order = Order.objects.get(id=transaction.account_id)
order.status = 'paid'
order.save()
def cancelled_payment(self, params, transaction):
order = Order.objects.get(id=transaction.account_id)
order.status = 'cancelled'
order.save()
class AtmosWebhookView(BaseAtmosWebhookView):
def successfully_payment(self, params, transaction):
order = Order.objects.get(id=transaction.account_id)
order.status = 'paid'
order.save()
def cancelled_payment(self, params, transaction):
order = Order.objects.get(id=transaction.account_id)
order.status = 'cancelled'
order.save()
- Add webhook URLs to
urls.py:
# urls.py
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from .views import PaymeWebhookView, ClickWebhookView, AtmosWebhookView
urlpatterns = [
# ...
path('payments/webhook/payme/', csrf_exempt(PaymeWebhookView.as_view()), name='payme_webhook'),
path('payments/webhook/click/', csrf_exempt(ClickWebhookView.as_view()), name='click_webhook'),
path('payments/webhook/atmos/', csrf_exempt(AtmosWebhookView.as_view()), name='atmos_webhook'),
]
FastAPI Integration
- Set up database models:
from datetime import datetime, timezone
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime
from paytechuz.integrations.fastapi import Base as PaymentsBase
from paytechuz.integrations.fastapi.models import run_migrations
# Create database engine
SQLALCHEMY_DATABASE_URL = "sqlite:///./payments.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
# Create base declarative class
Base = declarative_base()
# Create Order model
class Order(Base):
__tablename__ = "orders"
id = Column(Integer, primary_key=True, index=True)
product_name = Column(String, index=True)
amount = Column(Float)
status = Column(String, default="pending")
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
# Create payment tables using run_migrations
run_migrations(engine)
# Create Order table
Base.metadata.create_all(bind=engine)
# Create session
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
- Create webhook handlers:
from fastapi import FastAPI, Request, Depends
from sqlalchemy.orm import Session
from paytechuz.integrations.fastapi import PaymeWebhookHandler, ClickWebhookHandler
from paytechuz.gateways.atmos.webhook import AtmosWebhookHandler
app = FastAPI()
# Dependency to get the database session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
class CustomPaymeWebhookHandler(PaymeWebhookHandler):
def successfully_payment(self, params, transaction):
# Handle successful payment
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
order.status = "paid"
self.db.commit()
def cancelled_payment(self, params, transaction):
# Handle cancelled payment
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
order.status = "cancelled"
self.db.commit()
class CustomClickWebhookHandler(ClickWebhookHandler):
def successfully_payment(self, params, transaction):
# Handle successful payment
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
order.status = "paid"
self.db.commit()
def cancelled_payment(self, params, transaction):
# Handle cancelled payment
order = self.db.query(Order).filter(Order.id == transaction.account_id).first()
order.status = "cancelled"
self.db.commit()
@app.post("/payments/payme/webhook")
async def payme_webhook(request: Request, db: Session = Depends(get_db)):
handler = CustomPaymeWebhookHandler(
db=db,
payme_id="your_merchant_id",
payme_key="your_merchant_key",
account_model=Order,
account_field='id',
amount_field='amount'
)
return await handler.handle_webhook(request)
@app.post("/payments/click/webhook")
async def click_webhook(request: Request, db: Session = Depends(get_db)):
handler = CustomClickWebhookHandler(
db=db,
service_id="your_service_id",
secret_key="your_secret_key",
account_model=Order
)
return await handler.handle_webhook(request)
@app.post("/payments/atmos/webhook")
async def atmos_webhook(request: Request, db: Session = Depends(get_db)):
import json
# Atmos webhook handler
atmos_handler = AtmosWebhookHandler(license_api_key="your_atmos_api_key")
try:
# Get request body
body = await request.body()
webhook_data = json.loads(body.decode('utf-8'))
# Process webhook
response = atmos_handler.handle_webhook(webhook_data)
if response['status'] == 1:
# Payment successful
invoice = webhook_data.get('invoice')
# Update order status
order = db.query(Order).filter(Order.id == invoice).first()
if order:
order.status = "paid"
db.commit()
return response
except Exception as e:
return {
'status': 0,
'message': f'Error: {str(e)}'
}
📖 Documentation: pay-tech.uz
💬 Support: Telegram
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file paytechuz-0.3.29.tar.gz.
File metadata
- Download URL: paytechuz-0.3.29.tar.gz
- Upload date:
- Size: 57.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62dcfedb1763049765f9cee0fbdadaacef512513fdb87aa02808bc1f77c1483c
|
|
| MD5 |
e98e4ade115e4f970855fc3e2b963f7e
|
|
| BLAKE2b-256 |
a54094d45187caf6d22ba0f3e77336edb6a73b43ffa003419a97f8ea2c966e03
|
File details
Details for the file paytechuz-0.3.29-cp312-cp312-macosx_10_13_universal2.whl.
File metadata
- Download URL: paytechuz-0.3.29-cp312-cp312-macosx_10_13_universal2.whl
- Upload date:
- Size: 1.9 MB
- Tags: CPython 3.12, macOS 10.13+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe495cb2c000a70a7641e102561628ad1ed48449e8dcbeca8ee6e705c27546a0
|
|
| MD5 |
790092fbbd52588ccadec66148909b84
|
|
| BLAKE2b-256 |
8055155b4d125c28ebace4e26a5906429ab79c3d6db6bca78ab4badc307b2b64
|