REST-based payment gateway supporting cards, instant EFT, eBucks, Discovery Miles, and recurring payments in South Africa. Part of the Prosus group.
PayU South Africa is a full-featured payment gateway operated by Prosus, providing local payment methods and global card processing for e-commerce merchants in South Africa. The platform offers both hosted (RPP) and server-to-server (Enterprise API) integration options with support for multiple payment methods including credit/debit cards, instant EFT, eBucks, Discovery Miles, and recurring/subscription payments.
PayU South Africa uses a hash-based authentication model with the following credentials:
For Redirect Payment Page (RPP) integration:
hash = SHA512(merchant_key + security_key + amount + currency + order_id + customer_email)
⚠️ Verify the hash field order with PayU directly before going live. The field concatenation order above is based on available documentation, but PayU SA's exact hash specification varies by integration type (RPP vs Enterprise API) and has not been independently confirmed from live docs. An incorrect field order will cause every transaction to fail with an authentication error. Test your hash generation thoroughly in sandbox and confirm with PayU support if transaction failures occur.
For server-to-server API calls, include:
https://secure.payu.co.za/ (varies by integration method)https://sandbox.payumea.com/ (check PayU documentation for test endpoints)Authorization: Bearer [access_token]
Or include merchant key as first parameter in request body.
Authorize a payment without capturing funds immediately.
Method: POST
Endpoint: /payments/authorize (Redirect) or REST API endpoint
Request Parameters:
{
"merchant_key": "your_merchant_key",
"amount": 100.00,
"currency": "ZAR",
"order_id": "ORDER_12345",
"customer_email": "[email protected]",
"customer_name": "John Doe",
"payment_method": "CREDITCARD",
"payment_method_details": {
"card_number": "4111111111111111",
"expiry_month": "12",
"expiry_year": "2025",
"cvv": "123"
},
"return_url": "https://yourdomain.com/payment-return",
"notify_url": "https://yourdomain.com/payment-ipn",
"hash": "SHA512_COMPUTED_HASH"
}
Authorize and immediately capture funds.
Method: POST
Endpoint: /payments/charge
Request Parameters:
{
"merchant_key": "your_merchant_key",
"amount": 100.00,
"currency": "ZAR",
"order_id": "ORDER_12345",
"customer_email": "[email protected]",
"payment_method": "CREDITCARD",
"provider_specific_data": {
"magellan": {
"additional_details": {
"is3ds": "true",
"merchant_site_url": "https://yourdomain.com"
}
}
},
"hash": "SHA512_COMPUTED_HASH"
}
Capture funds from a previously authorized transaction.
Method: POST
Endpoint: /payments/capture
Request Parameters:
{
"original_transaction_id": "AUTH_TRANS_ID",
"merchant_key": "your_merchant_key",
"amount": 100.00,
"currency": "ZAR"
}
Refund a captured payment (full or partial).
Method: POST
Endpoint: /payments/refund
Request Parameters:
{
"original_transaction_id": "CHARGE_TRANS_ID",
"merchant_key": "your_merchant_key",
"amount": 50.00,
"currency": "ZAR",
"reason": "Customer requested refund"
}
Redirect customer to instant EFT provider selection.
Flow:
Request Parameters:
{
"merchant_key": "your_merchant_key",
"amount": 100.00,
"currency": "ZAR",
"order_id": "ORDER_12345",
"customer_email": "[email protected]",
"payment_method": "EFT",
"return_url": "https://yourdomain.com/payment-return",
"notify_url": "https://yourdomain.com/payment-ipn"
}
Set up recurring charges with customer consent.
Method: POST
Endpoint: /subscription/create
Request Parameters:
{
"merchant_key": "your_merchant_key",
"customer_email": "[email protected]",
"customer_name": "John Doe",
"initial_amount": 100.00,
"recurring_amount": 100.00,
"frequency": "MONTHLY",
"start_date": "2026-03-01",
"end_date": "2026-12-31",
"currency": "ZAR",
"payment_method": "CREDITCARD",
"order_id": "SUB_12345",
"hash": "SHA512_COMPUTED_HASH"
}
Configure your notification URL in the PayU Merchant Dashboard:
PayU sends POST requests to your notification URL with transaction details:
{
"m_payment_id": "ORDER_12345",
"pf_payment_id": "1234567890",
"payment_status": "COMPLETE",
"item_name": "Product Name",
"item_description": "Order description",
"amount_gross": "100.00",
"amount_fee": "2.50",
"amount_net": "97.50",
"custom_str1": "reference_code",
"custom_str2": "customer_id",
"custom_str3": "order_details",
"custom_str4": "metadata",
"custom_str5": "tracking_info",
"status_code": "00001",
"reason_code": "1",
"reason_text": "Successful transaction",
"transaction_id": "1234567890",
"proof_id": "proof_code_12345",
"on01": "bank_name",
"on02": "account_number",
"pf_signature": "SHA512_SIGNATURE_HASH"
}
Your endpoint MUST respond with:
OKpf_signature hash before processingVerify the signature to ensure PayU sent the notification:
expected_signature = SHA512(
passphrase +
merchant_id +
m_payment_id +
amount_gross +
item_name +
item_description +
custom_str1 +
status_code +
reason_code +
transaction_id +
pf_payment_id
)
Compare expected_signature with received pf_signature.
COMPLETE: Payment successful and funds capturedFAILED: Payment failedPENDING: Payment pending (e.g., awaiting fraud review or 3DS completion)CANCELLED: Payment cancelled by customerTIMEOUT: Customer session expiredIf PayU cannot reach your endpoint:
Best for quick integration with minimal PCI compliance requirements.
<form action="https://secure.payu.co.za/rpp.php" method="POST">
<input type="hidden" name="merchant_id" value="10000001">
<input type="hidden" name="merchant_key" value="your_merchant_key">
<input type="hidden" name="return_url" value="https://yourdomain.com/return">
<input type="hidden" name="notify_url" value="https://yourdomain.com/ipn">
<input type="hidden" name="amount" value="100.00">
<input type="hidden" name="item_name" value="Product Name">
<input type="hidden" name="item_description" value="Description">
<input type="hidden" name="custom_str1" value="ORDER_12345">
<input type="hidden" name="email_address" value="[email protected]">
<input type="hidden" name="signature" value="SHA512_COMPUTED_HASH">
<button type="submit">Pay with PayU</button>
</form>
Full checkout control with higher PCI requirements or use tokenization.
// Node.js example
const crypto = require('crypto');
const https = require('https');
function createCharge(orderData) {
const payload = {
merchant_key: process.env.PAYU_MERCHANT_KEY,
amount: orderData.amount,
currency: 'ZAR',
order_id: orderData.orderId,
customer_email: orderData.email,
payment_method: 'CREDITCARD',
payment_method_details: {
card_number: orderData.cardNumber,
expiry_month: orderData.expiryMonth,
expiry_year: orderData.expiryYear,
cvv: orderData.cvv
},
return_url: `https://yourdomain.com/payment/return/${orderData.orderId}`,
notify_url: 'https://yourdomain.com/payment/ipn'
};
// TODO: verify exact hash field order with PayU documentation —
// DO NOT use JSON.stringify(payload) as field order is not guaranteed;
// PayU hashes use specific fields concatenated in a defined order.
// Consult https://corporate.payu.com/developer-documentation-africa/ for the exact formula.
const hash = crypto
.createHash('sha512')
.update(JSON.stringify(payload) + process.env.PAYU_MERCHANT_SALT)
.digest('hex');
payload.hash = hash;
// POST to PayU API endpoint
return postToPayU('/payments/charge', payload);
}
Required for high-value transactions and fraud prevention.
{
"merchant_key": "your_merchant_key",
"amount": 5000.00,
"currency": "ZAR",
"order_id": "HIGH_VALUE_ORDER_123",
"customer_email": "[email protected]",
"payment_method": "CREDITCARD",
"provider_specific_data": {
"magellan": {
"additional_details": {
"is3ds": "true",
"merchant_site_url": "https://yourdomain.com/checkout"
}
}
},
"hash": "SHA512_COMPUTED_HASH"
}
Customer will be redirected to 3DS authentication flow. Transaction remains PENDING for up to 40 minutes awaiting completion. After timeout, status transitions to FAILED.
Leveraging South African bank connections for real-time transfers.
// Redirect customer to EFT selection
const efiParams = {
merchant_key: process.env.PAYU_MERCHANT_KEY,
amount: orderData.amount,
currency: 'ZAR',
order_id: orderData.orderId,
customer_email: orderData.email,
payment_method: 'EFT',
return_url: 'https://yourdomain.com/payment-return',
notify_url: 'https://yourdomain.com/payment-ipn'
};
// Compute hash and redirect to PayU
const redirectUrl = buildPayURedirectURL(efiParams);
res.redirect(redirectUrl);
// Customer selects bank → logs in → approves → instant notification sent to your server
Handling monthly/annual recurring charges.
async function setupRecurringPayment(customerData) {
const subscriptionPayload = {
merchant_key: process.env.PAYU_MERCHANT_KEY,
customer_email: customerData.email,
customer_name: customerData.name,
initial_amount: 99.99, // First charge
recurring_amount: 99.99, // Monthly charge
frequency: 'MONTHLY',
start_date: new Date().toISOString().split('T')[0],
end_date: '2027-12-31',
currency: 'ZAR',
payment_method: 'CREDITCARD',
order_id: `SUB_${customerData.customerId}`,
description: 'Monthly subscription',
hash: computeHash(subscriptionPayload)
};
// Redirect to PayU subscription form
// Customer authorizes and PayU handles recurring charges
}
| Code | Meaning | Action |
|---|---|---|
| 00001 | Successful | No action needed |
| 00002 | Payment Failed | Retry with customer confirmation |
| 00003 | Payment Pending | Wait for final status update via IPN |
| 00004 | Payment Cancelled | Customer cancelled; prompt retry |
| 00005 | Invalid Merchant | Check credentials in PayU dashboard |
| 00006 | Invalid Hash | Verify SHA512 hash computation |
| 00007 | Invalid Amount | Amount format/value incorrect (ZAR cents) |
| 00008 | Invalid Email | Invalid customer email format |
| 00009 | Duplicate Transaction | Order ID already processed; use new ID |
| 00010 | 3DS Required | Customer card requires 3D Secure |
| 00011 | 3DS Failed | Customer failed 3D Secure authentication |
| 00012 | Bank Error | Bank declined transaction; see bank_error_code |
| 00013 | Fraud Alert | Transaction flagged; manual review pending |
// After receiving PENDING status
setTimeout(async () => {
const status = await checkTransactionStatus(transactionId);
if (status === 'COMPLETE') {
// Process order
fulfillOrder();
} else if (status === 'FAILED') {
// Cancel order, notify customer
cancelOrder('3DS authentication failed');
}
}, 40 * 60 * 1000); // Check after 40 minutes max
Hash Computation Precision: SHA512 hashes are extremely sensitive to input order and encoding. Test hash generation extensively in sandbox before production. Include exact field order and UTF-8 encoding.
Amount Format in ZAR: Always use amount in South African Rand with two decimal places (cents). Amount 100.00 = 100 ZAR. Never include currency symbols or thousand separators in the actual field value.
3D Secure Timeout Behavior: Transactions requiring 3DS will remain PENDING for up to 40 minutes while awaiting customer authentication. After 40 minutes without customer action, status automatically transitions to FAILED. Don't treat PENDING as final; implement polling or use webhooks for status updates.
IPN Not Guaranteed Delivery: Although PayU retries 3 times, webhooks can be lost. Implement transaction status reconciliation polls (e.g., daily) against PayU's transaction API to catch missed notifications.
Merchant Site URL Required for 3DS: Omitting merchant_site_url in provider_specific_data will cause 3DS flows to fail silently. This is critical for high-value card transactions where 3DS is mandatory.
Recurring Payment Consent: South African regulations require explicit customer consent before setting up recurring payments. Document consent and store proof. PayU may request evidence of consent during disputes.
Instant EFT Bank Coverage: Smart EFT only works with 4 major banks (Nedbank, Standard Bank, FNB, ABSA). If customer uses another bank, payment fails. Implement fallback to credit card or show supported banks during checkout.
Sandbox vs Production Credentials: Test and production environments use completely different merchant keys and salts. Deploying with test credentials to production will cause all payments to fail. Verify credentials match environment before going live.
Notification URL HTTPS Requirement: PayU will only POST to HTTPS endpoints. HTTP URLs are rejected. Ensure your notification endpoint has valid SSL/TLS certificate.
Response Hash Signature Verification: Always verify the pf_signature from IPN notifications using the exact parameter order and your merchant salt. Skipping verification leaves you vulnerable to forged payment notifications.