PayU India Python Integration
raw JSON → payu_websdk (GitHub only) verified Tue May 12 auth: no python install: stale quickstart: stale
PayU India payment gateway. PACKAGE NAME CONFUSION: 'payu' on PyPI (1.2.1) is an unrelated HPC workflow tool — NOT PayU payments. Official PayU India Python SDK is 'payu_websdk' (from payu-india/web-sdk-python on GitHub, not on PyPI). 'payu-sdk' (1.2.0 on PyPI) is unofficial/community. Most Python integrations use manual hash generation without any SDK. Core requirement: SHA512 hash must be generated server-side for every transaction. Amount as string in rupees. Test environment: test.payu.in, Live: secure.payu.in.
pip install git+https://github.com/payu-india/web-sdk-python.git Warnings
breaking 'payu' on PyPI is an HPC workflow scheduling tool — completely unrelated to PayU India payments. 'pip install payu' installs the wrong thing. ↓
fix Use payu_websdk (install from GitHub) or implement hash generation manually with requests.
breaking Hash formula requires exactly 6 pipe characters between udf5 and salt: key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5||||||salt. Wrong pipe count causes PayU to reject the transaction with 'hash mismatch' error. ↓
fix Count pipes: after udf5 there are 6 consecutive pipes before salt. Use f-string: f'...{udf5}||||||{salt}'
breaking Amount must be a string with 2 decimal places — not an integer or float. '500.00' not 500 or 500.0. Wrong type causes hash mismatch. ↓
fix amount = f'{float_amount:.2f}' — always format to 2 decimal places as string.
breaking Hash must be lowercase. SHA512 output must be converted with .lower(). Uppercase hash causes payment failure. ↓
fix hashlib.sha512(hash_str.encode()).hexdigest().lower()
gotcha Reverse hash verification mandatory after payment. PayU returns hash in reverse order: sha512(SALT|status||||||udf5|udf4|udf3|udf2|udf1|email|firstname|productinfo|amount|txnid|key). Must verify this on your server. ↓
fix Implement reverse hash: salt|status||||||udf5|udf4|...|key and compare with hash returned in PayU callback.
gotcha Test environment: test.payu.in. Production: secure.payu.in. Using production URL with test keys or vice versa causes connection failure. ↓
fix TEST: POST to https://test.payu.in/_payment. LIVE: POST to https://secure.payu.in/_payment.
gotcha txnid must be unique for every transaction — max 25 chars, alphanumeric. Duplicate txnid causes 'duplicate transaction' error. ↓
fix Use UUID: import uuid; txnid = str(uuid.uuid4()).replace('-', '')[:25]
breaking Installing Python packages directly from GitHub (e.g., 'pip install git+...' as suggested for 'payu_websdk') requires the 'git' command-line tool to be installed on your system. The error 'No such file or directory: git' indicates that git is missing or not in your system's PATH. ↓
fix Ensure 'git' is installed in your environment. For 'python:3.9-slim' or similar Debian-based images, add `apt-get update && apt-get install -y git` to your Dockerfile. For other environments, install 'git' via your system's package manager or from git-scm.com and ensure it's in your PATH.
breaking The error "Cannot find command 'git'" indicates that 'git' is not installed or not in the system's PATH. This is a common requirement for `pip install` when a dependency is fetched directly from a Git repository (e.g., using `git+https://`). ↓
fix Ensure 'git' is installed on the system and available in the PATH. For Alpine-based images, you typically install it using `apk add git`.
Install compatibility stale last tested: 2026-05-12
python os / libc status wheel install import disk
3.10 alpine (musl) - - - -
3.10 slim (glibc) - - - -
3.11 alpine (musl) - - - -
3.11 slim (glibc) - - - -
3.12 alpine (musl) - - - -
3.12 slim (glibc) - - - -
3.13 alpine (musl) - - - -
3.13 slim (glibc) - - - -
3.9 alpine (musl) - - - -
3.9 slim (glibc) - - - -
Imports
- Hash generation (manual — recommended approach) wrong
# Wrong: amount as int generate_payu_hash(..., amount=500, ...) # must be string # Wrong: uppercase hash return hashlib.sha512(...).hexdigest().upper() # must be lowercase # Wrong: wrong pipe count between udf5 and salt hash_str = f'{key}|{txnid}|{amount}|{productinfo}|{firstname}|{email}|{salt}' # missing udf fieldscorrectimport hashlib def generate_payu_hash(key, txnid, amount, productinfo, firstname, email, salt, udf1='', udf2='', udf3='', udf4='', udf5=''): """ PayU hash formula: sha512(key|txnid|amount|productinfo|firstname|email| udf1|udf2|udf3|udf4|udf5||||||salt) Amount must be a string. Hash must be lowercase. """ hash_str = ( f'{key}|{txnid}|{amount}|{productinfo}|{firstname}|{email}|' f'{udf1}|{udf2}|{udf3}|{udf4}|{udf5}||||||{salt}' ) return hashlib.sha512(hash_str.encode('utf-8')).hexdigest().lower() # Usage hash_val = generate_payu_hash( key='your_key', txnid='unique_txn_001', amount='500.00', # string, in rupees productinfo='Widget', firstname='John', email='john@example.com', salt='your_salt' ) print(hash_val) - payu_websdk Client wrong
# Wrong: 'payu' package from PyPI is a HPC workflow tool import payu client = payu.something() # completely unrelated packagecorrectimport payu_websdk # ENV: 'TEST' or 'LIVE' client = payu_websdk.Client('YOUR_KEY', 'YOUR_SALT', 'TEST') # Verify payment status result = client.verify_payment('txnid_001') print(result) # Refund refund = client.refund_transaction( mihpayid='payu_transaction_id', token='unique_refund_token', amount='100.00' ) print(refund)
Quickstart stale last tested: 2026-04-23
import hashlib
import requests
import uuid
# PayU credentials from dashboard.payu.in
KEY = 'your_merchant_key'
SALT = 'your_merchant_salt'
ENV = 'test' # 'test' or 'prod'
BASE_URL = 'https://test.payu.in' if ENV == 'test' else 'https://secure.payu.in'
def generate_hash(key, txnid, amount, productinfo, firstname, email, salt, **udf):
udf1 = udf.get('udf1', '')
udf2 = udf.get('udf2', '')
udf3 = udf.get('udf3', '')
udf4 = udf.get('udf4', '')
udf5 = udf.get('udf5', '')
hash_str = f'{key}|{txnid}|{amount}|{productinfo}|{firstname}|{email}|{udf1}|{udf2}|{udf3}|{udf4}|{udf5}||||||{salt}'
return hashlib.sha512(hash_str.encode()).hexdigest().lower()
# Create payment params
txnid = str(uuid.uuid4())[:20]
amount = '500.00' # string, rupees
hash_val = generate_hash(
KEY, txnid, amount,
'Widget', 'John', 'john@example.com',
SALT
)
payment_data = {
'key': KEY,
'txnid': txnid,
'amount': amount,
'productinfo': 'Widget',
'firstname': 'John',
'email': 'john@example.com',
'phone': '9999999999',
'surl': 'https://yourapp.com/payment/success', # success URL
'furl': 'https://yourapp.com/payment/failure', # failure URL
'hash': hash_val,
}
# POST this form data to BASE_URL/_payment
print('POST to:', f'{BASE_URL}/_payment')
print('Params:', payment_data)