Steps to Integrate - Mobikwik Link & Pay

PayU's Mobikwik Link & Pay integration is a streamlined one-click payment solution that enhances user experience by eliminating the need for repeated logins and multi-step wallet interactions. This integration guide provides step-by-step instructions for implementing Mobikwik Link & Pay payments on your platform.

Step 1: Check User Balance and Link Status

Before initiating a payment, check if the user's Mobikwik wallet is linked and verify the available balance.

Environment

Environment Endpoint
Test https://test.mobikwik.com/userbalance
Production https://api.mobikwik.com/userbalance
Sample request
Parameter Description
mid
mandatory
Merchant ID assigned by Mobikwik
cell
mandatory
User's mobile number
msgcode
mandatory
Message code for the request
checksum
mandatory
HMAC SHA256 hash for security
aggregatedMerchantId
optional
Aggregated merchant identifier
Sample request
curl --location 'https://test.mobikwik.com/userbalance' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'mid=YOUR_MERCHANT_ID' \
--data-urlencode 'cell=9560012582' \
--data-urlencode 'msgcode=CHECK_BALANCE' \
--data-urlencode 'checksum=GENERATED_CHECKSUM'
Sample response

Success Response:

{
  "status": "success",
  "statusCode": "S001",
  "statusDescription": "Balance Retrieved Successfully",
  "availableBalance": "5000.00",
  "customerLinked": "true",
  "walletStatus": "active"
}

Wallet Not Linked Response:

{
  "status": "failure",
  "statusCode": "ERR002",
  "statusDescription": "Wallet not linked",
  "customerLinked": "false"
}

If customerLinked is false, proceed with the first-time user flow. If true, proceed with the repeat user flow.


Step 2: Payment Initiation API

The Payment Initiation API enables merchants to seamlessly initiate payment requests for transactions using the Mobikwik Link & Pay flow.

Environment

Environment Endpoint
Test https://test.payu.in/v2/payments
Production https://api.payu.in/v2/payments

Required Parameters

Parameter Description
key
mandatory
Merchant key from PayU Dashboard
transactionId
mandatory
Unique transaction identifier
amount
mandatory
Transaction amount (decimal format)
productInfo
mandatory
Product description for the transaction
firstName
mandatory
Customer first name
email
mandatory
Customer email address
phone
mandatory
Customer mobile number
surl
mandatory
Success URL where user will be redirected after successful payment
furl
mandatory
Failure URL where user will be redirected after failed payment
bankcode
mandatory
Must be "MOBIKWIK" for Mobikwik payments
hash
mandatory
SHA512 hash for security and data integrity

Sample Request

S2S Merchant Request
curl --location 'https://test.payu.in/v2/payments' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'key=YOUR_MERCHANT_KEY' \
--data-urlencode 'txnid=TXN123456789' \
--data-urlencode 'amount=1000.00' \
--data-urlencode 'productinfo=Mobikwik Payment' \
--data-urlencode 'firstname=John' \
--data-urlencode '[email protected]' \
--data-urlencode 'phone=9560012582' \
--data-urlencode 'surl=https://yoursite.com/success' \
--data-urlencode 'furl=https://yoursite.com/failure' \
--data-urlencode 'bankcode=MOBIKWIK' \
--data-urlencode 'hash=GENERATED_HASH'
const crypto = require('crypto');
const axios = require('axios');
const querystring = require('querystring');

class MobikwikLinkPayClient {
    constructor(merchantId, merchantKey, secretKey) {
        this.merchantId = merchantId;
        this.merchantKey = merchantKey;
        this.secretKey = secretKey;
        this.testBaseUrl = 'https://test.mobikwik.com';
        this.payuTestUrl = 'https://test.payu.in';
    }

    generateChecksum(data) {
        // Create string from data parameters
        const sortedKeys = Object.keys(data).sort();
        const paramString = sortedKeys.map(key => data[key]).join('|');
        
        return crypto
            .createHmac('sha256', this.secretKey)
            .update(paramString)
            .digest('hex');
    }

    generatePayuHash(data) {
        // PayU hash format: key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5||||||SALT
        const hashString = `${data.key}|${data.txnid}|${data.amount}|${data.productinfo}|${data.firstname}|${data.email}|||||||||||${this.secretKey}`;
        
        return crypto
            .createHash('sha512')
            .update(hashString)
            .digest('hex');
    }
}

// 1. Check User Balance and Link Status
async function checkUserBalance(client, mobileNumber) {
    const url = `${client.testBaseUrl}/userbalance`;
    
    const data = {
        mid: client.merchantId,
        cell: mobileNumber,
        msgcode: 'CHECK_BALANCE'
    };
    data.checksum = client.generateChecksum(data);
    
    const config = {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };
    
    try {
        const response = await axios.post(url, querystring.stringify(data), config);
        return response.data;
    } catch (error) {
        console.error('Error checking user balance:', error.message);
        return null;
    }
}

// 2. Payment Initiation API
async function initiatePayment(client, transactionData) {
    const url = `${client.payuTestUrl}/v2/payments`;
    
    const data = {
        key: client.merchantKey,
        txnid: transactionData.txnid,
        amount: transactionData.amount,
        productinfo: transactionData.productinfo,
        firstname: transactionData.firstname,
        email: transactionData.email,
        phone: transactionData.phone,
        surl: transactionData.surl,
        furl: transactionData.furl,
        bankcode: 'MOBIKWIK'
    };
    data.hash = client.generatePayuHash(data);
    
    const config = {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };
    
    try {
        const response = await axios.post(url, querystring.stringify(data), config);
        return response.data;
    } catch (error) {
        console.error('Error initiating payment:', error.message);
        return null;
    }
}

// 3. Submit OTP and Generate Token
async function generateToken(client, otpData) {
    const url = `${client.testBaseUrl}/tokengenerate`;
    
    const data = {
        mid: client.merchantId,
        cell: otpData.cell,
        msgcode: 'TOKEN_GENERATE',
        otp: otpData.otp,
        amount: otpData.amount,
        tokentype: 'WALLET_LINK'
    };
    data.checksum = client.generateChecksum(data);
    
    const config = {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };
    
    try {
        const response = await axios.post(url, querystring.stringify(data), config);
        return response.data;
    } catch (error) {
        console.error('Error generating token:', error.message);
        return null;
    }
}
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class MobikwikLinkPayClient {
    private final String merchantId;
    private final String merchantKey;
    private final String secretKey;
    private final String testBaseUrl = "https://test.mobikwik.com";
    private final String payuTestUrl = "https://test.payu.in";

    public MobikwikLinkPayClient(String merchantId, String merchantKey, String secretKey) {
        this.merchantId = merchantId;
        this.merchantKey = merchantKey;
        this.secretKey = secretKey;
    }

    /**
     * Generate HMAC SHA256 checksum for Mobikwik API requests
     */
    public String generateChecksum(Map<String, String> data) throws Exception {
        List<String> keys = new ArrayList<>(data.keySet());
        Collections.sort(keys);
        
        StringBuilder paramString = new StringBuilder();
        for (String key : keys) {
            if (paramString.length() > 0) {
                paramString.append("|");
            }
            paramString.append(data.get(key));
        }
        
        Mac sha256Hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        sha256Hmac.init(secretKeySpec);
        
        byte[] hmacBytes = sha256Hmac.doFinal(paramString.toString().getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hmacBytes);
    }
    
    /**
     * Generate SHA512 hash for PayU payment requests
     */
    public String generatePayuHash(Map<String, String> data) throws NoSuchAlgorithmException {
        // PayU hash format: key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5||||||SALT
        String hashString = data.get("key") + "|" + data.get("txnid") + "|" + data.get("amount") + "|" + 
                         data.get("productinfo") + "|" + data.get("firstname") + "|" + data.get("email") + 
                         "|||||||||||" + secretKey;
        
        MessageDigest md = MessageDigest.getInstance("SHA-512");
        byte[] hashBytes = md.digest(hashString.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hashBytes);
    }
    
    /**
     * Convert bytes to hex string
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
    
    /**
     * Send HTTP POST request with form data
     */
    private String sendPostRequest(String urlString, Map<String, String> formData) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        connection.setDoOutput(true);
        
        StringBuilder formDataString = new StringBuilder();
        for (Map.Entry<String, String> entry : formData.entrySet()) {
            if (formDataString.length() > 0) {
                formDataString.append("&");
            }
            formDataString.append(entry.getKey()).append("=").append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
        }
        
        try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
            outputStream.writeBytes(formDataString.toString());
            outputStream.flush();
        }
        
        int responseCode = connection.getResponseCode();
        StringBuilder response = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                responseCode >= 400 ? connection.getErrorStream() : connection.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
        }
        
        connection.disconnect();
        return response.toString();
    }
    
    /**
     * Check user balance and link status
     */
    public String checkUserBalance(String mobileNumber) {
        try {
            String url = testBaseUrl + "/userbalance";
            Map<String, String> data = new HashMap<>();
            data.put("mid", merchantId);
            data.put("cell", mobileNumber);
            data.put("msgcode", "CHECK_BALANCE");
            data.put("checksum", generateChecksum(data));
            
            return sendPostRequest(url, data);
        } catch (Exception e) {
            System.err.println("Error checking user balance: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * Initiate payment through PayU
     */
    public String initiatePayment(Map<String, String> transactionData) {
        try {
            String url = payuTestUrl + "/v2/payments";
            Map<String, String> data = new HashMap<>();
            data.put("key", merchantKey);
            data.put("txnid", transactionData.get("txnid"));
            data.put("amount", transactionData.get("amount"));
            data.put("productinfo", transactionData.get("productinfo"));
            data.put("firstname", transactionData.get("firstname"));
            data.put("email", transactionData.get("email"));
            data.put("phone", transactionData.get("phone"));
            data.put("surl", transactionData.get("surl"));
            data.put("furl", transactionData.get("furl"));
            data.put("bankcode", "MOBIKWIK");
            data.put("hash", generatePayuHash(data));
            
            return sendPostRequest(url, data);
        } catch (Exception e) {
            System.err.println("Error initiating payment: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * Submit OTP and generate token
     */
    public String generateToken(Map<String, String> otpData) {
        try {
            String url = testBaseUrl + "/tokengenerate";
            Map<String, String> data = new HashMap<>();
            data.put("mid", merchantId);
            data.put("cell", otpData.get("cell"));
            data.put("msgcode", "TOKEN_GENERATE");
            data.put("otp", otpData.get("otp"));
            data.put("amount", otpData.get("amount"));
            data.put("tokentype", "WALLET_LINK");
            data.put("checksum", generateChecksum(data));
            
            return sendPostRequest(url, data);
        } catch (Exception e) {
            System.err.println("Error generating token: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
}
import requests
import urllib.parse
import hashlib
import hmac
import json

class MobikwikLinkPayClient:
    def __init__(self, merchant_id, merchant_key, secret_key):
        self.merchant_id = merchant_id
        self.merchant_key = merchant_key
        self.secret_key = secret_key
        self.test_base_url = "https://test.mobikwik.com"
        self.payu_test_url = "https://test.payu.in"
    
    def generate_checksum(self, data):
        """Generate HMAC SHA256 checksum"""
        # Create string from data parameters
        param_string = '|'.join([str(data[key]) for key in sorted(data.keys())])
        return hmac.new(
            self.secret_key.encode('utf-8'), 
            param_string.encode('utf-8'), 
            hashlib.sha256
        ).hexdigest()
    
    def generate_payu_hash(self, data):
        """Generate SHA512 hash for PayU"""
        # PayU hash format: key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5||||||SALT
        hash_string = f"{data['key']}|{data['txnid']}|{data['amount']}|{data['productinfo']}|{data['firstname']}|{data['email']}|||||||||||{self.secret_key}"
        return hashlib.sha512(hash_string.encode('utf-8')).hexdigest()

# 1. Check User Balance and Link Status
def check_user_balance(client, mobile_number):
    url = f"{client.test_base_url}/userbalance"
    
    data = {
        'mid': client.merchant_id,
        'cell': mobile_number,
        'msgcode': 'CHECK_BALANCE'
    }
    data['checksum'] = client.generate_checksum(data)
    
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    try:
        response = requests.post(url, data=data, headers=headers)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error checking user balance: {e}")
        return None

# 2. Payment Initiation API
def initiate_payment(client, transaction_data):
    url = f"{client.payu_test_url}/v2/payments"
    
    data = {
        'key': client.merchant_key,
        'txnid': transaction_data['txnid'],
        'amount': transaction_data['amount'],
        'productinfo': transaction_data['productinfo'],
        'firstname': transaction_data['firstname'],
        'email': transaction_data['email'],
        'phone': transaction_data['phone'],
        'surl': transaction_data['surl'],
        'furl': transaction_data['furl'],
        'bankcode': 'MOBIKWIK'
    }
    data['hash'] = client.generate_payu_hash(data)
    
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    try:
        response = requests.post(url, data=data, headers=headers)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error initiating payment: {e}")
        return None

# 3. Submit OTP and Generate Token
def generate_token(client, otp_data):
    url = f"{client.test_base_url}/tokengenerate"
    
    data = {
        'mid': client.merchant_id,
        'cell': otp_data['cell'],
        'msgcode': 'TOKEN_GENERATE',
        'otp': otp_data['otp'],
        'amount': otp_data['amount'],
        'tokentype': 'WALLET_LINK'
    }
    data['checksum'] = client.generate_checksum(data)
    
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    try:
        response = requests.post(url, data=data, headers=headers)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error generating token: {e}")
        return None
class MobikwikLinkPayClient {
    private $merchantId;
    private $merchantKey;
    private $secretKey;
    private $testBaseUrl = 'https://test.mobikwik.com';
    private $payuTestUrl = 'https://test.payu.in';
    
    public function __construct($merchantId, $merchantKey, $secretKey) {
        $this->merchantId = $merchantId;
        $this->merchantKey = $merchantKey;
        $this->secretKey = $secretKey;
    }
    
    /**
     * Generate HMAC SHA256 checksum for Mobikwik API requests
     */
    public function generateChecksum($data) {
        // Sort keys alphabetically
        ksort($data);
        
        // Create parameter string
        $paramString = implode('|', $data);
        
        // Generate HMAC SHA256 hash
        return hash_hmac('sha256', $paramString, $this->secretKey);
    }
    
    /**
     * Generate SHA512 hash for PayU payment requests
     */
    public function generatePayuHash($data) {
        // PayU hash format: key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5||||||SALT
        $hashString = $data['key'] . '|' . $data['txnid'] . '|' . $data['amount'] . '|' . 
                      $data['productinfo'] . '|' . $data['firstname'] . '|' . $data['email'] . 
                      '|||||||||||' . $this->secretKey;
        
        return hash('sha512', $hashString);
    }
    
    /**
     * Alternative POST method using cURL
     */
    private function curlPostRequest($url, $data) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For testing only, enable in production
        
        $response = curl_exec($ch);
        $error = curl_error($ch);
        
        if ($error) {
            error_log('cURL Error: ' . $error);
            return null;
        }
        
        curl_close($ch);
        return $response;
    }
    
    /**
     * Check user balance and link status
     */
    public function checkUserBalance($mobileNumber) {
        $url = $this->testBaseUrl . '/userbalance';
        
        $data = [
            'mid' => $this->merchantId,
            'cell' => $mobileNumber,
            'msgcode' => 'CHECK_BALANCE'
        ];
        $data['checksum'] = $this->generateChecksum($data);
        
        return $this->curlPostRequest($url, $data);
    }
    
    /**
     * Initiate payment through PayU
     */
    public function initiatePayment($transactionData) {
        $url = $this->payuTestUrl . '/v2/payments';
        
        $data = [
            'key' => $this->merchantKey,
            'txnid' => $transactionData['txnid'],
            'amount' => $transactionData['amount'],
            'productinfo' => $transactionData['productinfo'],
            'firstname' => $transactionData['firstname'],
            'email' => $transactionData['email'],
            'phone' => $transactionData['phone'],
            'surl' => $transactionData['surl'],
            'furl' => $transactionData['furl'],
            'bankcode' => 'MOBIKWIK'
        ];
        $data['hash'] = $this->generatePayuHash($data);
        
        return $this->curlPostRequest($url, $data);
    }
    
    /**
     * Submit OTP and generate token
     */
    public function generateToken($otpData) {
        $url = $this->testBaseUrl . '/tokengenerate';
        
        $data = [
            'mid' => $this->merchantId,
            'cell' => $otpData['cell'],
            'msgcode' => 'TOKEN_GENERATE',
            'otp' => $otpData['otp'],
            'amount' => $otpData['amount'],
            'tokentype' => 'WALLET_LINK'
        ];
        $data['checksum'] = $this->generateChecksum($data);
        
        return $this->curlPostRequest($url, $data);
    }
}

Sample Response

There will be different scenarios and the response according to different scenarios:

  • Success scenario

    {
      "status": "success",
      "data": {
        "paymentId": "PAY_12345",
        "transactionId": "TXN123456789",
        "amount": "1000.00",
        "status": "pending",
        "redirectUrl": "https://mobikwik.payment.url",
        "message": "Payment initiated successfully"
      }
    }
  • Failure scenario

    {
      "status": "error",
      "errorCode": "INVALID_AMOUNT",
      "message": "Invalid amount provided",
      "data": null
    }

Payment Flow Types

The Payment Initiation API automatically determines the appropriate flow based on user status:

For Linked Users (Auto-debit Flow)

  • API directly processes payment using stored token
  • Faster checkout experience
  • Immediate payment confirmation

For Unlinked Users (Redirect Flow)

  • User redirected to Mobikwik authentication
  • OTP verification for wallet linking
  • Token generation for future transactions
Important Notes

• The Payment Initiation API handles flow determination automatically
• Ensure bankcode=MOBIKWIK is included in all requests
• User identification via mobile number is mandatory
• Hash/Checksum generation is required for security
• Test thoroughly in sandbox before production deployment


Step 3: Process Payment Based on User Status

After initiating the payment via the Payment Initiation API, the system will process the payment based on whether the user's wallet is linked or not.

Step 4: Submit OTP and Generate Token

After the user enters the OTP, submit it to generate a wallet token for future transactions.

Environment

Environment Endpoint
Test https://test.mobikwik.com/tokengenerate
Production https://api.mobikwik.com/tokengenerate
Request parameters
Parameter Description
mid
mandatory
Merchant ID
cell
mandatory
User's mobile number
msgcode
mandatory
Message code
otp
mandatory
OTP entered by user
amount
mandatory
Transaction amount
tokentype
mandatory
Token type identifier
checksum
mandatory
HMAC SHA256 hash
Sample request
curl --location 'https://test.mobikwik.com/tokengenerate' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'mid=YOUR_MERCHANT_ID' \
--data-urlencode 'cell=9560012582' \
--data-urlencode 'msgcode=TOKEN_GENERATE' \
--data-urlencode 'otp=123456' \
--data-urlencode 'amount=1000.00' \
--data-urlencode 'tokentype=WALLET_LINK' \
--data-urlencode 'checksum=GENERATED_CHECKSUM'
Sample response

Success Response:

{
  "status": "success",
  "statusCode": "S003",
  "statusDescription": "Token Generated Successfully",
  "token": "TKN_ABC123XYZ789",
  "tokenExpiry": "365",
  "orderid": "ORD123456"
}

Invalid OTP Response:

{
  "status": "failure",
  "statusCode": "ERR003",
  "statusDescription": "Invalid OTP"
}
Note

Store the generated token securely for future transactions. Tokens are valid for 365 days by default.


📘

References:

  • When the wallet balance is insufficient, use the Add Money & Debit API to allow users to load money and complete the transaction in a single flow. For more information, refer to Add Money to Wallet And Debit API
  • Verify the transaction status using the Check Status API. For more information, refer to Check Status API.
  • Process refunds using the standard PayU refund mechanism. Refunds are processed in the T+1 settlement cycle. For more information, refer to Refund Transaction API.
  • If a token expires or becomes invalid, regenerate it using the Token Regenerate API. For more information, refer to Regenerate Token API.

Error handling

Error Code Description Recommended Action
ERR001 Insufficient Balance Redirect to Add Money flow
ERR002 Wallet not linked Initiate first-time user flow
ERR003 Invalid OTP Allow OTP retry (max 3 attempts)
ERR004 Transaction Failed Show error message, offer retry
ERR005 Invalid Token Regenerate token

Ask AI Beta

Hi! I am an AI Assistant. Ask me about PayU and get help with your integration.
Responses are generated by AI, may contain some mistakes.

EXAMPLE QUESTIONS