---
title: Django Integration
description: Integrate Payme, Click, Uzum, Paynet and Octo into a Django project with PayTechUZ — installation, settings, models, webhooks and callback handling.
source: https://pay-tech.uz/en/docs/integrations/django
---

# Django Integration

Complete guide for integrating PayTechUZ with Django applications.

:::tip Complete Example Projects

Looking for ready-to-use Django projects? We have comprehensive examples for different payment scenarios:

- **🛒 [Shop Example](https://github.com/PayTechUz/Shop)** - Full e-commerce integration with **order-based payments** using Payme and Click. Perfect for online stores and marketplaces.

- **🚕 [Taxi Example](https://github.com/PayTechUz/Taxi)** - Complete implementation of **wallet balance top-up** using Payme, Click, Uzum, and Paynet. Ideal for service apps, taxi services, and balance-based systems.

Both examples include complete Django setup, webhook handlers, and production-ready code!
:::

## Installation

Install PayTechUZ with Django support:

```bash
pip install paytechuz[django]
```

## Django Settings

Add PayTechUZ configuration to your Django settings:

:::note
You can remove the configuration for payment providers you don't need. Only configure the providers you plan to use in your project.
:::

```python
# settings.py

INSTALLED_APPS = [
    # ...
    'paytechuz.integrations.django',
]

PAYTECHUZ = {
    'PAYME': {
        'PAYME_ID': 'your_payme_id',
        'PAYME_KEY': 'your_payme_key',
        'ACCOUNT_MODEL': 'payment.models.Invoice',  #  Your invoice model
        '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': 'payment.models.Invoice',
        'ACCOUNT_FIELD': 'id',
        'COMMISSION_PERCENT': 0.0,
        'ONE_TIME_PAYMENT': True,
        'IS_TEST_MODE': True,  # Set to False in production
    },
    'UZUM': {
        'SERVICE_ID': 'your_service_id',  # Uzum Service ID for Biller URL
        'USERNAME': 'your_uzum_username',  # For webhook Basic Auth
        'PASSWORD': 'your_uzum_password',  # For webhook Basic Auth
        'ACCOUNT_MODEL': 'payment.models.Invoice',
        'ACCOUNT_FIELD': 'id',  # or 'id'
        'AMOUNT_FIELD': 'amount',
        'ONE_TIME_PAYMENT': True, # Set to False if you want to allow multiple payments for the same invoice
        'IS_TEST_MODE': True,  # Set to False in production
    },
    'PAYNET': {
        'MERCHANT_ID': 'your_merchant_id', # Paynet Merchant ID
        'SERVICE_ID': 'your_paynet_service_id',
        'USERNAME': 'your_paynet_username',
        'PASSWORD': 'your_paynet_password',
        'ACCOUNT_MODEL': 'payment.models.Invoice',
        'ACCOUNT_FIELD': 'id',
        'AMOUNT_FIELD': 'amount',
        'ONE_TIME_PAYMENT': True,
        'IS_TEST_MODE': True,
    },
    'OCTO_BANK': {
        'OCTO_SHOP_ID': 42125,  # Octo Shop ID
        'OCTO_SECRET': 'your_octo_secret',  # Octo Secret Key
        'OCTO_UNIQUE_KEY': 'your_octo_unique_key',  # Required in production
        'NOTIFY_URL': 'https://example.com/webhooks/octo/',  # Callback URL
        'ACCOUNT_MODEL': 'payment.models.Invoice',
        'ACCOUNT_FIELD': 'id',
        'AMOUNT_FIELD': 'amount',
        'ONE_TIME_PAYMENT': True,
        'TEST_MODE': True,  # False in production — enables signature verification
    }
}
```

:::warning Octo signature verification
`TEST_MODE: True` disables callback signature checks. Before going live set it
to `False` and provide `OCTO_UNIQUE_KEY`, which the Octo team issues to you —
the webhook refuses to start without it.
:::

Create models for handling orders and payments:

```python
# shop/models.py
from django.db import models
from django.contrib.auth.models import User

class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    product_name = models.CharField(max_length=255)
    total_amount = models.DecimalField(max_digits=12, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Order {self.id} - {self.product_name}"

# payment/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from shop.models import Order  # Import Order from shop app

class Invoice(models.Model):
    STATUS_CHOICES = (
        ('pending', 'Pending'),
        ('paid', 'Paid'),
        ('cancelled', 'Cancelled'),
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    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"Invoice {self.id} for Order {self.order.id}"
```

## Views

Create views for handling payments:

:::note
You only need to create webhook views for the payment providers you're using. Remove the imports and classes for providers you don't need.
:::

```python
# payment/views.py
from paytechuz.integrations.django.views import (
    BasePaymeWebhookView,
    BaseClickWebhookView,
    BaseUzumWebhookView,
    BasePaynetWebhookView,
    BaseOctoWebhookView,
)
from .models import Invoice

class PaymeWebhookView(BasePaymeWebhookView):
    def successfully_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'paid'
        invoice.save()

    def cancelled_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'cancelled'
        invoice.save()

    def get_check_data(self, params, account): # optional
        """
        Return additional data for CheckPerformTransaction (fiscal receipt)

        You can override this method to return additional data for fiscal receipt
        """
        return {
            "additional": {"first_name": account.user.first_name}, # optional
            "detail": { # optional
                "receipt_type": 0,
                "shipping": {"title": "Yetkazib berish", "price": 10000},
                "items": [
                    {
                        "discount": 0,
                        "title": account.product_name,
                        "price": account.amount * 100,
                        "count": 1,
                        "code": "00001",
                        "units": 1,
                        "vat_percent": 0,
                        "package_code": "123456"
                    }
                ]
            }
        }

class ClickWebhookView(BaseClickWebhookView):
    def successfully_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'paid'
        invoice.save()

    def cancelled_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'cancelled'
        invoice.save()

class PaynetWebhookView(BasePaynetWebhookView):
    def successfully_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'paid'
        invoice.save()

    def cancelled_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'cancelled'
        invoice.save()

    def get_check_data(self, params, account): # optional
        # Return additional data for GetInformation
        # You can use any key value pairs
        return {
            "fields": {
                "first_name": account.user.first_name,
                "balance": 0 # account.user.balance
            }
        }

class UzumWebhookView(BaseUzumWebhookView):
    def successfully_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'paid'
        invoice.save()

    def cancelled_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'cancelled'
        invoice.save()

class OctoWebhookView(BaseOctoWebhookView):
    def successfully_payment(self, params, transaction):
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'paid'
        invoice.save()

    def cancelled_payment(self, params, transaction):
        # Also fires on a refund, which moves a paid transaction to cancelled
        invoice = Invoice.objects.get(id=transaction.account_id)
        invoice.status = 'cancelled'
        invoice.save()
```

## URLs

Configure URL patterns:

:::note
Only add URL patterns for the payment providers you're using. Remove the imports and paths for providers you don't need.
:::

```python
# urls.py
from django.urls import path
from payment.views import (
    PaymeWebhookView,
    ClickWebhookView,
    UzumWebhookView,
    PaynetWebhookView,
    OctoWebhookView,
)
from shop.views import OrderCreateView

urlpatterns = [
    # ...
    path('webhooks/payme/', PaymeWebhookView.as_view(), name='payme_webhook'),
    path('webhooks/click/', ClickWebhookView.as_view(), name='click_webhook'),
    # Uzum sends the operation as a URL segment, so the pattern must capture it
    path('webhooks/uzum/<str:action>/', UzumWebhookView.as_view(), name='uzum_webhook'),
    path('webhooks/paynet/', PaynetWebhookView.as_view(), name='paynet_webhook'),
    path('webhooks/octo/', OctoWebhookView.as_view(), name='octo_webhook'),

    # Order creation endpoint
    path('order/create/', OrderCreateView.as_view(), name='order_create'),
]
```

:::note Octo callback URL
The Octo webhook path must match `NOTIFY_URL` in `PAYTECHUZ['OCTO_BANK']`,
since that is the address Octo posts to.
:::


## Creating Payment Links

Here's an example **Django Rest Framework (DRF)** view that handles order creation and generates a payment link based on the selected provider (without using serializers):

:::note
Customize the logic to include only the payment gateways you wish to support. Remove the imports and `elif` blocks for unused gateways.
:::

```python
# shop/views.py
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from paytechuz.gateways.payme import PaymeGateway
from paytechuz.gateways.click import ClickGateway
from paytechuz.gateways.uzum import UzumGateway
from paytechuz.gateways.paynet import PaynetGateway
from paytechuz.gateways.octo import OctoGateway
from payment.models import Invoice

class OrderCreateView(APIView):
    def post(self, request):
        data = request.data
        product_name = data.get('product_name')
        amount = data.get('amount')
        payment_type = data.get('payment_type')  # 'payme', 'click', 'uzum', 'paynet', 'octo'

        if not all([product_name, amount, payment_type]):
            return Response(
                {'error': 'product_name, amount, and payment_type are required.'},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 1. Create Order
        order = Order.objects.create(
            user=request.user,
            product_name=product_name,
            total_amount=amount
        )

        # 2. Create Invoice linked to the Order
        invoice = Invoice.objects.create(
            user=request.user,
            order=order,
            amount=amount,
            status='pending'
        )

        payment_url = None

        # 3. Generate Payment Link based on payment_type
        if payment_type == 'payme':
            gateway = PaymeGateway(
                payme_id=settings.PAYTECHUZ['PAYME']['PAYME_ID'],
                payme_key=settings.PAYTECHUZ['PAYME']['PAYME_KEY'],
                is_test_mode=settings.PAYTECHUZ['PAYME']['IS_TEST_MODE']
            )
            payment_url = gateway.create_payment(
                id=invoice.id,
                amount=invoice.amount,
                return_url="https://example.com/success",
                account_field_name=settings.PAYTECHUZ['PAYME']['ACCOUNT_FIELD']
            )

        elif payment_type == 'click':
            gateway = ClickGateway(
                service_id=settings.PAYTECHUZ['CLICK']['SERVICE_ID'],
                merchant_id=settings.PAYTECHUZ['CLICK']['MERCHANT_ID'],
                merchant_user_id=settings.PAYTECHUZ['CLICK']['MERCHANT_USER_ID'],
                secret_key=settings.PAYTECHUZ['CLICK']['SECRET_KEY'],
                is_test_mode=settings.PAYTECHUZ['CLICK']['IS_TEST_MODE']
            )
            payment_url = gateway.create_payment(
                id=invoice.id,
                amount=invoice.amount,
                return_url="https://example.com/success"
            )

        elif payment_type == 'uzum':
            gateway = UzumGateway(
                service_id=settings.PAYTECHUZ['UZUM']['SERVICE_ID'],
                is_test_mode=settings.PAYTECHUZ['UZUM']['IS_TEST_MODE']
            )
            payment_url = gateway.create_payment(
                id=invoice.id,
                amount=invoice.amount,  # in som, converted to tiyin in the URL
                return_url="https://example.com/success"
            )

        elif payment_type == 'paynet':
            gateway = PaynetGateway(
                merchant_id=settings.PAYTECHUZ['PAYNET']['MERCHANT_ID'],
                is_test_mode=settings.PAYTECHUZ['PAYNET']['IS_TEST_MODE']
            )
            # Generate Paynet payment URL
            # URL format: https://app.paynet.uz/?m={merchant_id}&c={payment_id}&a={amount}
            payment_url = gateway.create_payment(
                id=invoice.id,  # Payment ID (c parameter)
                amount=invoice.amount  # amount in tiyin (optional, a parameter)
            )
            # Or without amount (amount will be configured on Paynet's side)
            # payment_url = gateway.create_payment(id=invoice.id)

        elif payment_type == 'octo':
            gateway = OctoGateway(
                octo_shop_id=settings.PAYTECHUZ['OCTO_BANK']['OCTO_SHOP_ID'],
                octo_secret=settings.PAYTECHUZ['OCTO_BANK']['OCTO_SECRET'],
                notify_url=settings.PAYTECHUZ['OCTO_BANK']['NOTIFY_URL'],
                is_test_mode=settings.PAYTECHUZ['OCTO_BANK']['TEST_MODE']
            )
            # One-stage payment (auto_capture); returns the Octo checkout URL
            payment_url = gateway.create_payment(
                id=invoice.id,
                amount=invoice.amount,  # in som
                return_url="https://example.com/success",
                description=f"Invoice #{invoice.id}"
            )

        return Response({
            'order_id': order.id,
            'invoice_id': invoice.id,
            'payment_url': payment_url
        }, status=status.HTTP_201_CREATED)
```
