Skip to main content

Multiple Kassas

A business often holds several merchant accounts with one provider: a live kassa and a sandbox one, or one per brand. Since 0.5.0 each Django webhook serves all of them from one URL. Single-kassa settings keep working unchanged.

How a call finds its kassa​

List the kassas under KASSAS. The provider's own credentials decide which one a call belongs to:

GatewayA call is matched by
Paymethe key in its Basic auth header (PAYME_KEY)
Clickservice_id in the body, signed with that kassa's SECRET_KEY
PaynetserviceId in the body, else the login (USERNAME)
Uzumthe Basic auth login (USERNAME); the call must then name that kassa's serviceId

A call that matches no kassa is refused as unauthenticated.

Settings​

PAYTECHUZ = {
'PAYME': {
'KASSAS': {
'shop': {'PAYME_ID': '...', 'PAYME_KEY': '...'},
'gifts': {'PAYME_ID': '...', 'PAYME_KEY': '...'},
},
'KASSA_FIELD': 'kassa', # optional, see below
'ACCOUNT_MODEL': 'shop.models.Order',
'ACCOUNT_FIELD': 'id',
'AMOUNT_FIELD': 'amount',
'ONE_TIME_PAYMENT': True,
},
'CLICK': {
'KASSAS': {
'shop': {'SERVICE_ID': '...', 'SECRET_KEY': '...'},
},
# ACCOUNT_MODEL etc. as above
},
'PAYNET': {
'KASSAS': {
'live': {'SERVICE_ID': '1001', 'USERNAME': '...', 'PASSWORD': '...'},
'test': {'SERVICE_ID': '1003', 'USERNAME': '...', 'PASSWORD': '...',
'IS_SANDBOX': True},
},
},
'UZUM': {
'KASSAS': {
'shop': {'SERVICE_ID': '...', 'USERNAME': '...', 'PASSWORD': '...',
'ALLOWED_IPS': ['...']},
},
'IP_HEADER': 'HTTP_X_REAL_IP',
'BEHIND_CLOUDFLARE': True,
},
}

Your view does not change: the same BasePaymeWebhookView subclass on the same URL serves every kassa. Inside a hook, self.kassa is the name of the kassa that called.

Separate books​

Every row in the payments table carries its kassa, and every lookup is scoped to the kassa that called. One kassa never sees, performs, cancels or reports another's transactions, and the same provider transaction id may exist once per kassa. Rows from before 0.5 have kassa = '', which is what a single-kassa setup uses.

Which kassa may take an order​

Set KASSA_FIELD to a field on your account model that holds the kassa the account must be paid through. A call from any other kassa is told the account does not exist. A Paynet kassa may set ACCOUNT_KASSA when the value stored on accounts differs from its key in KASSAS — useful for a sandbox and a live service that share accounts.

Without KASSA_FIELD, any kassa may pay any account.

Sandbox kassas​

Paynet and Uzum kassas take IS_SANDBOX: True. Payments are recorded, but sandbox_payment(params, transaction) is called instead of successfully_payment, so nothing is delivered. The account stays payable for the provider's tester, and sandbox rows stay out of live statements.

Moving an existing setup to KASSAS​

  1. Upgrade and migrate: pip install "paytechuz[django]>=0.5", then python manage.py migrate.
  2. Existing rows have kassa = ''. Before switching to KASSAS, set each row's kassa to the name it will have, otherwise the new kassa cannot see them (for refunds, checks and statements).
  3. Switch the settings and deploy.

Rolling back to 0.4 on Django 5+ needs no reverse migration: the column has a database default and the old code ignores it.