Skip to main content

paylinker-django

PyPI version Python versions Django versions License: MIT Typed

Django integration for the paylinker payment SDK.

Provides webhook views, provider transaction tracking, and abstract handler bases for Click and Payme payment providers in Uzbekistan.

Installation

pip install paylinker-django

Quick Start

1. Add to INSTALLED_APPS

INSTALLED_APPS = [
    ...
    "paylinker_django",
]

2. Configure settings

PAYLINKER = {
    "PAYME": {
        "merchant_id": env("PAYME_MERCHANT_ID"),
        "merchant_key": env("PAYME_SECRET_KEY"),
        "test_key": env("PAYME_TEST_KEY", default=""),
        "is_test": env.bool("PAYME_IS_TEST", default=False),
    },
    "CLICK": {
        "merchant_id": int(env("CLICK_MERCHANT_ID")),
        "service_id": int(env("CLICK_SERVICE_ID")),
        "secret_key": env("CLICK_SECRET_KEY"),
    },
    "PAYME_HANDLER": "apps.payments.handlers.MyPaymeHandler",
    "CLICK_HANDLER": "apps.payments.handlers.MyClickHandler",
    "ACCOUNT_FIELD": "order_id",
}

3. Implement handlers

# apps/payments/handlers.py
from paylinker.providers.click.enums import ClickErrorCode
from paylinker.providers.click.webhook import OrderCheckResult
from paylinker.providers.payme.rpc_handler import PaymeError
from paylinker_django.handlers import DjangoClickOrderValidator, DjangoPaymeTransactionHandler

from .models import Order


class MyPaymeHandler(DjangoPaymeTransactionHandler):
    def validate_order(self, order_id, amount_tiyin):
        try:
            order = Order.objects.get(pk=order_id)
        except Order.DoesNotExist:
            raise PaymeError(-31050, "Не найден", "Topilmadi", "Not found", "order_id")
        if order.is_paid:
            raise PaymeError(-31099, "Оплачен", "To'langan", "Already paid")
        if int(order.amount * 100) != amount_tiyin:
            raise PaymeError(-31001, "Неверная сумма", "Noto'g'ri summa", "Wrong amount")

    def on_payment_performed(self, provider_tx):
        order = Order.objects.get(pk=provider_tx.order_id)
        order.mark_paid()

    def on_payment_cancelled(self, provider_tx, reason, was_performed):
        if was_performed:
            order = Order.objects.get(pk=provider_tx.order_id)
            order.refund()


class MyClickValidator(DjangoClickOrderValidator):
    def validate_order(self, merchant_trans_id, amount):
        try:
            order = Order.objects.get(pk=merchant_trans_id)
        except Order.DoesNotExist:
            return OrderCheckResult.fail(ClickErrorCode.ORDER_NOT_FOUND, "Not found")
        if float(order.amount) != amount:
            return OrderCheckResult.fail(ClickErrorCode.INCORRECT_AMOUNT, "Bad amount")
        return OrderCheckResult.ok()

    def on_payment_completed(self, provider_tx):
        order = Order.objects.get(pk=provider_tx.order_id)
        order.mark_paid()

4. Include URLs

# urls.py
from django.urls import include, path

urlpatterns = [
    path("payments/", include("paylinker_django.urls")),
]

This exposes:

  • POST /payments/webhooks/payme/ — Payme JSON-RPC endpoint
  • POST /payments/webhooks/click/ — Click SHOP-API endpoint

5. Run migrations

python manage.py migrate paylinker_django

Architecture

paylinker          — Core SDK (framework-agnostic)
  ├── configs, providers, signing, schemas, HTTP clients
  └── handler protocols: TransactionHandler (Payme), OrderValidator (Click)

paylinker-django   — Django adapter (this package)
  ├── ProviderTransaction model   — tracks provider-specific state
  ├── DjangoPaymeTransactionHandler  — manages Payme lifecycle + model CRUD
  ├── DjangoClickOrderValidator      — manages Click lifecycle + model CRUD
  ├── Webhook views                  — parse HTTP → delegate to handlers
  └── Settings integration           — PAYLINKER dict in Django settings

Your project only implements business-logic hooks:

  • validate_order() — order existence and amount verification
  • on_payment_performed() / on_payment_completed() — post-payment actions
  • on_payment_cancelled() — cancellation handling

Generating payment URLs

from paylinker_django.conf import get_payme_config, get_click_config
from paylinker import PaymeProvider, ClickProvider

# Payme
payme = PaymeProvider(get_payme_config())
url = payme.generate_payment_url(
    amount=100000,  # tiyin
    transaction_param=str(order.pk),
    account_field="order_id",
)

# Click
click = ClickProvider(get_click_config())
url = click.generate_payment_url(
    amount=1000.0,  # UZS
    transaction_param=str(order.pk),
)

Building & Publishing

This package uses Hatchling with a single source of truth for the version (src/paylinker_django/__init__.py).

Local build

pip install -e ".[dev]"
python -m build              # produces dist/*.whl and dist/*.tar.gz
twine check --strict dist/*  # validate PyPI metadata

Release workflow (recommended)

Releases are automated via GitHub Actions using PyPI Trusted Publishing (no long-lived API tokens). One-time setup on PyPI / TestPyPI: add this repo as a trusted publisher for the paylinker-django project, with workflow publish.yml and environments pypi / testpypi.

Then for each release:

# 1. Bump version in src/paylinker_django/__init__.py
# 2. Update CHANGELOG.md
git commit -am "release: v0.2.0"

# 3. Tag & push  → triggers TestPyPI publish
git tag v0.2.0
git push origin main --tags

# 4. Create a GitHub Release for the tag → triggers real PyPI publish
gh release create v0.2.0 --notes-from-tag

Manual publish (fallback)

python -m build
twine upload --repository testpypi dist/*    # TestPyPI
twine upload dist/*                          # PyPI

Versioning

Follows SemVer: MAJOR.MINOR.PATCH.

License

MIT

Metadata

Release files for paylinker-django 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for paylinker-django 0.1.6
File Size Uploaded Provenance
paylinker_django-0.1.6.tar.gz 17.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for paylinker-django 0.1.6
File Interpreter ABI Platform
paylinker_django-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 34.7 kB

Release files / paylinker_django-0.1.6.tar.gz

Download URL paylinker_django-0.1.6.tar.gz
Size 17.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a8ff7bab2451fc2924507e3f058d616d878d4242176012a65d0b7810beaef883
BLAKE2b-256 checksum
How to use checksums
cebc59c53f082d73db729a171931c9d6b87ab6e8e1d2dc22169d6ce3be46457d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / paylinker_django-0.1.6-py3-none-any.whl

Download URL paylinker_django-0.1.6-py3-none-any.whl
Size 17.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
98bceeac4a6a031a4e26bb303bdb04ebafa57c35df612a31045c860bbbe8bf11
BLAKE2b-256 checksum
How to use checksums
cd69f3aff2cb27fe1a6bbde9d20a9c91e8baeccc6a4a067fb065a50800ff3c20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Security for paylinker-django 0.1.6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page