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.
Parameter | HDFC | ICICI | Axis | PayU | Citrus | PayTM | EBS | RazorPay | CCAvenue |
---|---|---|---|---|---|---|---|---|---|
order_id | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
amount | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
customer_id | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
customer_email | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
customer_phone | Optional | Optional | Optional | Yes | Yes | Yes | Yes | Yes | Yes |
description | Optional | Optional | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
product_id | No | No | No | No | No | No | No | No | No |
billing_address_first_name | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_last_name | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_line1 | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_line2 | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_line3 | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_city | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_state | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_country | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_postal_code | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_phone | No | No | No | Optional | Optional | No | Yes | No | Yes |
billing_address_country_code_iso | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_first_name | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_last_name | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_line1 | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_line2 | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_line3 | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_city | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_state | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_country | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_postal_code | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_phone | No | No | No | Optional | Optional | No | Yes | No | Yes |
shipping_address_country_code_iso | No | No | No | Optional | Optional | No | Yes | No | Yes |
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 have to redirect the customer for entering payment information.
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.
Variant | Description |
---|---|
web | Renders a desktop-optimized version of the checkout page |
mobile | Renders a mobile-optimized version of the checkout page |
iframe | Provides 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.
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;">
</iframe>
Expiry
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
Status | ID | Meaning |
---|---|---|
NEW | 10 | Newly created order |
PENDING_VBV | 23 | Authentication is in progress |
CHARGED | 21 | Successful transaction |
AUTHENTICATION_FAILED | 26 | User did not complete authentication |
AUTHORIZATION_FAILED | 27 | User completed authentication, but the bank refused the transaction |
JUSPAY_DECLINED | 22 | User input is not accepted by the underlying PG |
AUTHORIZING | 28 | Transaction status is pending from bank |
STARTED | 20 | Transaction is pending. Juspay system isn't able to find a gateway to process a transaction |
AUTO_REFUNDED | 36 | Transaction is automatically refunded |
CAPTURE_INITIATED | 33 | Capture pending for the pre-authorized transaction |
CAPTURE_FAILED | 34 | Capture failed for the pre-authorized transaction |
VOID_INITIATED | 32 | Void pending for the pre-authorized transaction |
VOIDED | 31 | Void is successful for the pre-authorized transaction |
VOID_FAILED | 35 | Void failed for the pre-authorized transaction |
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
python
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) + '=' + \
urllib.quote_plus(params.get(i)))
encoded_string = urllib.quote_plus('&'.join(encoded_sorted))
dig = hmac.new(key, \
msg=encoded_string, \
digestmod=hashlib.sha256).digest()
assert urllib.quote_plus(base64.b64encode(dig).decode()) == \
signature
#Ruby example for HMAC signature verification:**
python
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). \
split("signature=")[1].split("&")[0]
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) {
Ccw8ZE5fkr0%3D&signature_algorithm=HMAC-SHA256
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
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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);
System.out.println(""+sortedParams);
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
$encoded_params;
foreach ($params as $key => $value) {
if($key!='signature' && $key != 'signature_algorithm') {
$encoded_params[urlencode($key)] = urlencode($value);
}
}
ksort($encoded_params);
$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;
}
You can also read the status from the server using /order/status API explained here. Status along with many other data will be returned as part of the /order/status 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 fulfillment of the order.
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.
Updated over 1 year ago