Skip to main content

Production-oriented Django integration for the Click.uz payment gateway

Project description

django-click-uz

CI PyPI Python Django Click.uz

Django 5+ integration for the Click.uz Shop API: payment URLs, prepare/complete webhooks, MD5 signatures, optional audit log, replay protection, HTTPS and optional IP checks on callbacks.

O‘zbekcha (batafsil) README_UZ.md
Changelog CHANGELOG.md
Repository github.com/Matnazar-Matnazarov/django-click-uz
Extra docs (Markdown) docs/

What’s in the package

Part Role
click_uz Django app: add to INSTALLED_APPS, run migrations, include urls. Endpoints: prepare/, complete/, callback/, webhook/. Includes webhook_guard, ModelOrderHandler, signals, Uzbek locale/uz.
click_up Compatibility imports only: ClickUp, ClickWebhook, unique_transaction_param. Do not add to INSTALLED_APPS.
Config CLICK dict or flat CLICK_*; optional WEBHOOK_ALLOWED_CIDRS, WEBHOOK_REQUIRE_HTTPS, WEBHOOK_STRICT_IN_DEBUG.

Requirements

  • Python 3.12+
  • Django 5.0+

Installation

pip install django-click-uz
# settings.py
INSTALLED_APPS = [
    # ...
    "django.contrib.contenttypes",
    "django.contrib.auth",
    "click_uz",
    "orders",  # your app with Order model
]

Configuration

Use either the CLICK dict or flat CLICK_* variables. If both are set, CLICK wins.

Option A — CLICK dict (recommended)

import os

CLICK = {
    "SERVICE_ID": int(os.environ["CLICK_SERVICE_ID"]),
    "MERCHANT_ID": int(os.environ["CLICK_MERCHANT_ID"]),
    "SECRET_KEY": os.environ["CLICK_SECRET_KEY"],
    # Optional Click user id for Merchant API; defaults to MERCHANT_ID if omitted
    # "USER_ID": 12345,
    "ACCOUNT_MODEL": "orders.Order",
    "AMOUNT_FIELD": "amount",
    "STATUS_FIELD": "status",
    "STATUS_PENDING": "pending",
    "STATUS_WAITING": "waiting_payment",
    "STATUS_PAID": "paid",
    "STATUS_CANCELLED": "cancelled",
    "MERCHANT_TRANS_FIELD": "transaction_param",
    "COMMISSION_PERCENT": 0,
    "DISABLE_ADMIN": False,
    "ENABLE_AUDIT": True,
    # Production hardening (optional):
    # "WEBHOOK_ALLOWED_CIDRS": ["203.0.113.0/24"],
    # "WEBHOOK_STRICT_IN_DEBUG": True,
}

Option B — flat settings (legacy / click-pkg style)

CLICK_SERVICE_ID = 12345
CLICK_MERCHANT_ID = 67890
CLICK_SECRET_KEY = "your-secret-key"
CLICK_ACCOUNT_MODEL = "orders.Order"
CLICK_AMOUNT_FIELD = "amount"
CLICK_STATUS_FIELD = "status"
CLICK_MERCHANT_TRANS_FIELD = "transaction_param"
CLICK_COMMISSION_PERCENT = 0
CLICK_DISABLE_ADMIN = False

Webhooks require HANDLER_CLASS or ACCOUNT_MODEL + AMOUNT_FIELD + STATUS_FIELD.


Example: Order model

After prepare, the default handler sets status to waiting_payment; on success completepaid; on rejection → cancelled. Align STATUS_* in CLICK with your choices.

# orders/models.py
from django.conf import settings
from django.db import models


class Order(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    amount = models.DecimalField(max_digits=12, decimal_places=2)
    status = models.CharField(
        max_length=32,
        choices=[
            ("pending", "Pending"),
            ("waiting_payment", "Waiting payment"),
            ("paid", "Paid"),
            ("cancelled", "Cancelled"),
        ],
        default="pending",
    )
    transaction_param = models.CharField(max_length=255, blank=True, default="")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self) -> str:
        return f"Order {self.pk}{self.amount}"
python manage.py makemigrations
python manage.py migrate

URLs

A) Include package routes (single webhook URL for the Click cabinet):

from django.urls import include, path

urlpatterns = [
    path("payment/click/", include("click_uz.urls")),
]

Webhook example: https://your-domain.example/payment/click/webhook/
(same prefix: prepare/, complete/, callback/).

B) Custom path with your ClickWebhook subclass:

from django.urls import path

from orders.views import ClickWebhookAPIView

urlpatterns = [
    path("payment/click/update/", ClickWebhookAPIView.as_view(), name="click-webhook"),
]

Register the full HTTPS URL in the Click merchant cabinet.


Views: webhook + pay link

Webhook subclass

params contains snapshot fields (id, merchant_trans_id, amount, state, …) and a nested payload with Click fields.

# orders/views.py
from click_up.views import ClickWebhook

from .models import Order


class ClickWebhookAPIView(ClickWebhook):
    def successfully_payment(self, params):
        order_id = params.get("id")
        try:
            order = Order.objects.get(pk=order_id)
            # Model handler already set status to paid; add email, analytics, etc.
        except Order.DoesNotExist:
            pass

    def cancelled_payment(self, params):
        pass

    def prepare_accepted(self, params):
        """Optional: runs after prepare succeeds."""
        pass

Create order + payment link (simple — order PK as transaction_param)

import json

from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt

from click_up import ClickUp

from .models import Order


@method_decorator(csrf_exempt, name="dispatch")
class CreateOrderView(View):
    def post(self, request):
        body = json.loads(request.body or "{}")
        amount = body.get("amount")
        order = Order.objects.create(
            user=request.user if getattr(request, "user", None) and request.user.is_authenticated else None,
            amount=amount,
        )
        paylink = ClickUp().initializer.generate_pay_link(
            id=order.pk,
            amount=order.amount,
            return_url="https://example.com/orders/done/",
            unique_transaction_id=False,
        )
        return JsonResponse({"order_id": order.pk, "payment_link": paylink})

Recommended: unique transaction_param per checkout

from click_up import ClickUp, unique_transaction_param

from .models import Order

tid = unique_transaction_param(order.pk)
order.transaction_param = tid
order.save(update_fields=["transaction_param"])

paylink = ClickUp().initializer.generate_pay_link(
    id=order.pk,
    amount=order.amount,
    return_url="https://example.com/orders/done/",
    transaction_param=tid,
)

Requires MERCHANT_TRANS_FIELD / CLICK_MERCHANT_TRANS_FIELD pointing at transaction_param (see configuration above).

Django REST Framework: same logic inside APIView.post, using request.data instead of json.loads.


Security & operations

  • Callback JSON to Click stays in English; Django admin / config messages can use click_uz translations (locale/uz/).
  • MD5 sign_string verification is the main authenticity check.
  • click_uz.webhook_guard: enforce HTTPS in production; optional WEBHOOK_ALLOWED_CIDRS. Behind a TLS-terminating proxy, set SECURE_PROXY_SSL_HEADER.

Documentation (local)

No hosted doc site is provided. To build the MkDocs site locally:

pip install -e ".[dev]"
# or: uv sync --all-extras
mkdocs serve

Open the URL printed in the terminal (usually http://127.0.0.1:8000).


Development

pip install -e ".[dev]"
pytest

Releases: bump version in pyproject.toml, update CHANGELOG.md, commit, then tag and push vX.Y.Z (e.g. v0.1.2) to trigger publish.yml. Each PyPI upload needs a new version.


vs older click-pkg tutorials

Old tutorial This package
pip install click-pkg pip install django-click-uz
INSTALLED_APPS: click_up Only click_uz
is_test_mode Use Click test credentials / PAY_URL from Click docs if applicable

License

MIT — see LICENSE.

Click API reference: docs.click.uz.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_click_uz-0.1.2.tar.gz (27.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_click_uz-0.1.2-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

Details for the file django_click_uz-0.1.2.tar.gz.

File metadata

  • Download URL: django_click_uz-0.1.2.tar.gz
  • Upload date:
  • Size: 27.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_click_uz-0.1.2.tar.gz
Algorithm Hash digest
SHA256 088b8506f4e82184128acf638fc630ac88cd4c61e65ed39037e2511b29e52b4a
MD5 48f6ad0c920e22d72ee6113bddb45831
BLAKE2b-256 d0be79a934edfadd4069cacbb299c5263d560c4f882d4e1ea414f865a4354142

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_click_uz-0.1.2.tar.gz:

Publisher: publish.yml on Matnazar-Matnazarov/django-click-uz

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_click_uz-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_click_uz-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0fccc6ce14f4d4bb7ba5b4d587398459b7b0f3fb721266a63a8b8c882d07bb2d
MD5 3ae5add72c91d5d1767f03e0048f9114
BLAKE2b-256 cbdf56c61e3fa81c930daac6e898e51e32a12e03442328a74e08e4f8388d777d

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_click_uz-0.1.2-py3-none-any.whl:

Publisher: publish.yml on Matnazar-Matnazarov/django-click-uz

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page