PayU India Python Integration
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.
Warnings
- breaking 'payu' on PyPI is an HPC workflow scheduling tool — completely unrelated to PayU India payments. 'pip install payu' installs the wrong thing.
- 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.
- 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.
- breaking Hash must be lowercase. SHA512 output must be converted with .lower(). Uppercase hash causes payment failure.
- 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.
- gotcha Test environment: test.payu.in. Production: secure.payu.in. Using production URL with test keys or vice versa causes connection failure.
- gotcha txnid must be unique for every transaction — max 25 chars, alphanumeric. Duplicate txnid causes 'duplicate transaction' error.
Install
-
pip install git+https://github.com/payu-india/web-sdk-python.git
Imports
- Hash generation (manual — recommended approach)
import 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
import 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
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)