Hey! These docs are for version 3.2, which is no longer officially supported. Click here for the latest version, 1.0!

Creating Orders

Creating your first Order

Order is the cornerstone for conducting payments with Juspay. Order to Juspay is akin to what a shopping cart to a merchant is. Order encapsulates all the information that is required for payment. All the payments, refunds, etc. are associated with an Order and it becomes the root reference.

Keeping in mind the fundamentals of distributed systems, we have let you choose your order_id when you create the order. This way, you can check the status of your order through our API whenever you want.

To know how to create the order, please refer to our API documentation.

Nonetheless, please find the below snippet to quickly create an order. Note that you have to substitute the correct API Key.

curl -k https://api.juspay.in/orders \
    -u your_api_key: \
    -d "amount=10.00" \
    -d "order_id=ord_007" \
    -d "customer_id=guest_user_101" \
    -d "[email protected]" \
    -d "customer_phone=919988665522" \
    -d "product_id=:pid" \
    -d "description=Order Info"

Mandatory Parameters

Different payment gateways & aggregators have a varying set of parameters that are mandatory. Depending on your backend gateway please ensure that the mandatory parameters are sent to avoid any failures.


To understand what each of these parameters is, please refer to our API documentation.

Payment redirection

Redirection to Payment Gateway

Once the order has been created, the response will contain the payment link to which you can redirect the customer for entering payment information. If you are using the custom checkout page then the transaction can be called through API, SDK or PayV3 based on the platform being integrated.

Example Response

  "order_id": "1478851764",
  "id": "ord_e294a26e66ad4336a992ceab81ad704c",
  "status": "CREATED",
  "status_id": 1,
  "payment_links": {
    "web": "https://api.juspay.in/merchant/pay/ord_e294a26e66ad4336a992ceab81ad704c",
    "mobile": "https://api.juspay.in/merchant/pay/ord_e294a26e66ad4336a992ceab81ad704c?mobile=true",
    "iframe": "https://api.juspay.in/merchant/ipay/ord_e294a26e66ad4336a992ceab81ad704c"

payment_links attribute will be present in both /order/create API and /order/status API. We have provided three variants to make it easy for your customers. As the name implies, these are best suited for the respective channels.

webRenders a desktop-optimized version of the checkout page
mobileRenders a mobile-optimized version of the checkout page
iframeProvides an iFrame that you can embed as part of your checkout page

Payment methods

All the payment methods enabled in your account will be displayed to the user. This is to ensure maximum acceptance which improves your success rate significantly. To enable only required payment methods pass an extra parameter to the payment URL.

To enable only Netbanking, pass the following parameter: payment_options=nb. To enable only Wallets, pass the following parameter: payment_options=wallet. To enable Netbanking and Wallets together, pass the following parameter: payment_options=nb|wallet.

<iframe src="https://api.juspay.in/merchant/ipay/ord_e294a26e66ad4336a992ceab81ad704c?payment_options=nb"
width="630" height="400"
style="border: 1px solid #CCC;padding: 20px;height: auto;min-height: 300px;">

Sample code for creating an iFrame with EMI support:

<iframe src="https://api.juspay.in/merchant/ipay/ord_e294a26e66ad4336a992ceab81ad704c?payment_options=nb"
width="630" height="400"
style="border: 1px solid #CCC;padding: 20px;height: auto;min-height: 300px;">


Links become invalid as soon as the order expires. The default expiry is 15 minutes from the time of creation. This value is customizable via our dashboard. Please follow this link to customize it to your need.

The maximum expiry time is 24 hours. This limit is set due to security restrictions. If you wish to extend the expiry period, then please reach out to us. Your chances of convincing us will improve significantly if you use a good random generator for Order ID attribute.

Payment Response

Payment Response
Once the payment is complete the user is redirected to the return_url configured by you. Following is the typical destination where the user is taken to:

HTTP GET https://merchant.shop.com/paymentresponse/handler?order_id=order_id_007&status=CHARGED&status_id=21&signature=euKzwwiUztPPg3MCEYpgKZfcyTr1uQq1hzKkhP8G1vQ%253D&signature_algorithm=HMAC-SHA256

Please note that the parameters are sent using HTTP GET by default. To enable HTTP POST for your MID, please drop an email to [email protected]

Transaction Status codes and their meaning

NEW10Newly created order
PENDING_VBV23Authentication is in progress
VBV_SUCCESSFUL24Authorization is in progress
CHARGED21Successful transaction
AUTHENTICATION_FAILED26User did not complete authentication
AUTHORIZATION_FAILED27User completed authentication, but the bank refused the transaction
JUSPAY_DECLINED22User input is not accepted by the underlying PG
AUTHORIZING28Transaction status is pending from bank
COD_INITIATED29User has Cash on Delivery as payment mode
STARTED20Transaction is pending. Juspay system isn't able to find a gateway to process a transaction
AUTO_REFUNDED36Transaction is automatically refunded
CAPTURE_INITIATED33Capture pending for the pre-authorized transaction
CAPTURE_FAILED34Capture failed for the pre-authorized transaction
VOID_INITIATED32Void pending for the pre-authorized transaction
VOIDED31Void is successful for the pre-authorized transaction
VOID_FAILED35Void failed for the pre-authorized transaction
NOT_FOUND40Provided order id is not created at Juspay

Transaction is successful only if you receive CHARGED as the value in status. For all other cases, you must assume that the payment has failed or the finite status is not known from the upstream gateway at the moment.

Status Verification

After the redirect, the authenticity should be verified using the signature in the response. The signature parameter in the return_url gives the HMAC signature computed using the algorithm specified by the signature_algorithm parameter. The HMAC is calculated using the following algorithm:

  • Get all the parameters (key=value pairs) from the return_url.
  • It is assumed that the parameters in the return_url are converted into key/value pairs.
  • All parameters except signature and signature_algorithm are used in the following steps.
  • Percentage encode each key and value pairs.
  • Sort the list of parameters alphabetically (ASCII based sort) by encoded key.
  • For each key/value pair:
    • Append the encoded key to the output string.
    • Append the '=' character to the output string.
    • Append the encoded value to the output string.
    • If there are more than one key/value pairs, append a '&' character to the output string.
  • Percentage encode the generated string.
  • The HMAC of the string can be calculated using the Response Key configured in merchant settings.
  • Percentage encode the generated hash, validate against the signature in response (the signature should percentage decoded once before comparing with the generated hash).

To enable the signature generation at JusPay end for the payment response, you must first create a response key under Settings of Juspay dashboard [https://dashboard.expresscheckout.juspay.in/]. Once you have created a key successfully, navigate to Settings-->General (https://dashboard.expresscheckout.juspay.in/) section and select "Yes" for the option "Use signed response".

Once you have completed the above two steps, all the redirection to your website from JusPay will have the signature and the algorithm.

The signature algorithm used by JusPay is HMAC-SHA256. The algorithm is explicitly passed as an argument so that verification is accurate. Newer or more secure algorithms might be introduced in the future.

It is also possible to check the status using the /order/status API. Based on the response object, a success confirmation page or failure message can be shown to the customer. Since this is an authenticated call, done from the server side, signature verification is not required.

#Python example for HMAC signature verification

import urllib
import hmac
import hashlib
import base64

key = 'your_secret_key'
# params := key/value dictionary except `signature`
#           and `signature_algorithm`
# signature := "5ctBJ0vURSTS9awUhbTBXCpUeDEJG8X%252B6c%253D"
# signature_algorithm := "HMAC-SHA256"

encoded_sorted = []
for i in sorted(params.keys()):
    encoded_sorted.append(urllib.quote_plus(i) + '=' + \

encoded_string = urllib.quote_plus('&'.join(encoded_sorted))
dig = hmac.new(key, \
              msg=encoded_string, \

assert urllib.quote_plus(base64.b64encode(dig).decode()) == \
#Ruby example for HMAC signature verification:**
require 'uri'
require 'cgi'
require 'openssl'
require 'Base64'

key = "your_secret_key"
# params := key/value hash except `signature`
#           and `signature_algorithm`
# signature := "5ctBJ0vURSTS9awUhbTBXCpUeDEJG8X%252B6c%253D"
# signature_algorithm := "HMAC-SHA256"

encoded_sorted = []
params.keys.sort.each { |k| encoded_list << URI.encode(k) + \
                        "=" + URI.encode(params[k]) }

encoded_string = CGI.escape(encoded_sorted.join("&"))

hash_string = CGI.escape(Base64.encode64(OpenSSL::HMAC. \
                        digest(OpenSSL::Digest.new('sha256'), \
                        key, data)).strip())
hash_string == URI.decode(return_url). \
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class Util {

     * @param args
    public static void main(String[] args) {
        LinkedHashMap<String, String> param = new LinkedHashMap<String, String>();
        param.put("order_id", ":order_id");
        param.put("status", ":status");
        param.put("status_id", ":status_id");
        System.out.println("param :: "+param);
        String expectedHashparam="OHEZ3sYJa%2F9ZyNZ79u3r4p4F2p9O8%2FjSCcw8ZE5fkr0%3D";
         String scretkey=":Response key";
        try {
            System.out.println(" return value :: "+validateHMAC_SHA256(param,expectedHashparam,scretkey));
        } catch (InvalidKeyException e) {
            // TODO Auto-generated catch block
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
     public static boolean validateHMAC_SHA256(Map<String, String> params, String expectedHash, String key)
                throws java.io.UnsupportedEncodingException, java.security.NoSuchAlgorithmException,
                java.security.InvalidKeyException, java.io.UnsupportedEncodingException {
            if (key == null) return false;

            Map<String, String> sortedParams = new TreeMap<String, String>(params);

            StringBuilder queryStr = new StringBuilder("");
            for (String curkey : sortedParams.keySet())
                queryStr.append(curkey + "=" + sortedParams.get(curkey) + "&");
            queryStr.deleteCharAt(queryStr.length() - 1);
            System.out.println("queryStr ::"+queryStr);
            String message = URLEncoder.encode(queryStr.toString(), "UTF-8");
            Mac hasher = Mac.getInstance("HmacSHA256");
            hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
            byte[] hash = hasher.doFinal(message.getBytes());
            String generatedSign = (URLEncoder.encode(DatatypeConverter.printBase64Binary(hash), "UTF-8"));
            System.out.println("generatedSign ::"+generatedSign);
            return (generatedSign.equals(expectedHash));

function verify_hmac($params, $secret) {
           $receivedHmac = $params['signature'];
           // UrlEncode key/value pairs
           foreach ($params as $key => $value) {
               if($key!='signature' && $key != 'signature_algorithm') {
                   $encoded_params[urlencode($key)] = urlencode($value);
           $serialized_params = "";
           foreach ($encoded_params as $key => $value) {
               $serialized_params = $serialized_params . $key . "=" . $value . "&";
           $serialized_params = urlencode(substr($serialized_params, 0, -1));
           $computedHmac = base64_encode(hash_hmac('sha256', $serialized_params, $secret, true));
           $receivedHmac = urldecode($receivedHmac);
           return urldecode($computedHmac) == $receivedHmac;
const crypto = require('crypto');

function verify_hmac(params, secret) {

  var paramsList = [];
  for (var key in params) {
    if (key != 'signature' && key != 'signature_algorithm') {
      paramsList[key] = params[key];

  paramsList = sortObjectByKeys(paramsList);

  var paramsString = '';
  for (var key in paramsList) {
    paramsString = paramsString + key + '=' + paramsList[key] + '&';

  let encodedParams = encodeURIComponent(paramsString.substring(0, paramsString.length - 1));
  let computedHmac = crypto.createHmac('sha256', secret).update(encodedParams).digest('base64');
  let receivedHmac = decodeURIComponent(params.signature);

  console.log("computedHmac :", computedHmac)
  console.log("receivedHmac :", receivedHmac)

  return decodeURIComponent(computedHmac) == receivedHmac;

function sortObjectByKeys(o) {
  return Object.keys(o)
    .reduce((r, k) => ((r[k] = o[k]), r), {});

console.log(verify_hmac({"status_id":"21","status":"CHARGED","order_id":"**6**3**","signature":"******crdU/AW8BkpqnMHK2********TE=","signature_algorithm":"HMAC-SHA256"},{{Response Key}}))

Order Status

The order should always be fulfilled by checking the status from the server using /order/:order_id API explained here. Status along with many other data will be returned as part of the /order/:order_id API. This is an alternative to the GET params in return_url.

Failing to do status verification will result in hackers gaming your system. So, please ensure that status verification is in place before you go LIVE with us.

Example request

curl -X GET \
  https://api.juspay.in/orders/wv_test_ord_110011 \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'version: 2018-10-25' \
  "merchant_id": "wv_test",
  "order_id": "wv_test_ord_110011",
  "customer_id": "[email protected]",
  "product_id": "",
  "status": "CHARGED",
  "status_id": 21,
  "amount": 400,
  "currency": "INR",
  "refunded": false,
  "amount_refunded": 0,
  "return_url": "http://skyview./order/cc-confirm",

The above response is truncated for brevity. However, as you can see above, the status was received as CHARGED. This indicates that the payment was successful and so, you can proceed with the fulfilment of the order.Also ensure to verify the parameters like amount, payment_method, payment_method_type in the response are as per the details present in your system.

Status Conflicts

In a typical payment using ExpressCheckout, the control goes from your Checkout Page to Juspay's servers, then to Payment Gateway(PG) or Aggregator, and then to the Bank pages for the second-factor authentication. You will then be redirected to the PG page after the payment is authenticated and authorized, and then back to your inventory confirmation page. In all the above cases there can be timeouts due to bad network or bank/PG downtime. This leads to temporary status conflict between Juspay and/or the PG/bank, as we missed the updated response after the payment.

These conflicts are reconciled periodically when we get the updated status from the PG. The updated status for an order will be notified to the merchant via emails or webhooks. You can also poll the current status of the order using /order/status API to check for an update. This can also be triggered individually for each order, from the order details page in the merchant dashboard.

To enable email alerts, please contact [email protected].

The notification will be sent when the status changes from PENDING_VBV/AUTHORIZATION_FAILED to CHARGED.