npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

expresscheckout-nodejs

v1.0.0

Published

Juspay's official expresscheckout-nodejs sdk

Downloads

3,325

Readme

Juspay's Expresscheckout NodeJs SDK

Please go through the documentation for Juspay API here

Contents

Installation

Installation using npm

npm i expresscheckout-nodejs

Getting Started

To access the apis you will need merchantId and authentication methods, juspay supports Basic authentication with apikey for most of the routes. This SDK also supports JWT authentication for routes that currently support it. It'll be prioritized over Basic authentication in case of multiple authentication options.

Identify Endpoints

Rest endpoints for Juspay

| Environment | Endpoint | | ------------------| ---------------------------------| | Sandbox | https://sandbox.juspay.in | | Production | https://api.juspay.in |

Rest endpoints for HDFC tenant

| Environment | Endpoint | | ------------------| -----------------------------------------| | Sandbox | https://smartgatewayuat.hdfcbank.com | | Production | https://smartgateway.hdfcbank.com |

Default endpoint is https://sandbox.juspay.in

Authentication and Access

Current version of expresscheckout-nodejs supports JWE+JWS and Basic authentication method using apiKey. Basic info on each methods

Make sure to pass merchantId in Juspay initialization

Basic Authentication method

To use this method use apiKey generated from your dashboard under sidebar Payments > Settings > Security > API Keys

const juspay = new Juspay({
  apiKey: "your apiKey",
  merchantId: "your merchantId"
})

JWE+JWS encryption

This method is more secure authentication method that encrypts the signed payload using AES-256-GCM algorithm, payload is signed using RSA-SHA256 algorithm of actual data. Hence, we'll use 2 pairs for keys, one for JWE and one for JWS.

JWT and JWE+JWS will be used interchangeably here.

const juspay = new Juspay.Juspay({
  merchantId: "your merchantId",
  jweAuth: {
    keyId: "your keyId",
    privateKey: "your privateKey",
    publicKey: "your publicKey",
  },
})

To get the keys go to sidebar then Payments > Settings > Security > JWT Keys > Upload New JWT > (I don't have the JWT Keys, I want to auto generate the keys I have the JWT Keys already, I want to manually upload the Public Key). Keys will be downloaded as per your selection.

Keys should not be hard coded and should be saved securely either on server file system, env or KMS services.

JWE components (as json)

| Component | Function | | ------------------| ------------------------------------------------------------------------------------------------------------------| | encryptedKey | A random key created during runtime and shared after encrypting (alg RSA-OAEP-256) it using publicKey | | encryptedPayload | Actual cipher text is encrypted using AES-GCM-256 data, for JWE+JWS use case it will be JWS see below in JWS components section | | iv | It adds randomness to encryption, ensuring varied ciphertext for the same plaintext | | tag | It ensures the integrity and authenticity of the ciphertext, detecting any unauthorized modifications | | header | metadata information containing keyId(for rotation mainly), alg, enc, cty |

JWS components (as stringified json)

| Component | Function | | ------------------| ------------------------------------------------------------------| | payload | request data | | header | Contains algorithm to sign, key-id (for rotation mainly) | | signature | The generated signature which will be verified on Juspay’s end |

You don't have to worry about encrypting and decrypting the data as it'll be handled by sdk itself. If JWE configuration is present and api supports it, it'll prioritize that over apiKey. For cases where api only supports basic auth and apikey is not given, it'll throw appropriate errors.

Importing SDKs

CommonJS

const { Juspay, APIError } = require('expresscheckout-nodejs')

OR

const expresscheckout = require('expresscheckout-nodejs')
// usage expresscheckout.Juspay, expresscheckout.APIError

Using Typescript/ES Module

import Juspay, { APIError } from 'expresscheckout-nodejs'

OR

import * as expresscheckout from 'expresscheckout-nodejs'
// usage expresscheckout. Juspay, expresscheckout.APIError, expresscheckout.CreateCustomerRequest

SDK Resources

Now that we have setup authentication and have access to call juspay apis we'll see an example of orderSession api using promise and try/catch blocks whichever suits your programming style.

Using try/catch blocks

try {
  const juspay = new Juspay.Juspay({
    merchantId: 'merchantId',
    apiKey: 'apiKey',
  })
  const orderSessionResponse = await juspay.orderSession.create({
    amount: 1,
    order_id: 'order_' + Date.now(),
    payment_page_client_id: 'your payment page client id',
  })
  console.log(orderSessionResponse)
} catch (error) {
  console.log(error)
}

Using Promises

const juspay = new Juspay({
  merchantId: 'merchantId',
  apiKey: 'apiKey',
})
const orderId = 'ORD_' + Date.now()
juspay.order
  .create({
    amount: 100,
    order_id: orderId,
    // optional fields below
    currency: 'INR',
    customer_id: 'juspay_test_1',
    customer_email: '[email protected]',
    customer_phone: '9988776655',
    product_id: '123456',
    return_url: 'https://abc.xyz.com/123456',
    description: 'Sample Description',
    billing_address_first_name: 'Juspay',
    billing_address_last_name: 'Technologies',
    billing_address_line1: 'Girija Building',
    billing_address_line2: 'Ganapati Temple Road',
    billing_address_line3: '8th Block, Koramangala',
    billing_address_city: 'Bengaluru',
    billing_address_state: 'Karnataka',
    billing_address_country: 'India',
    billing_address_postal_code: '560095',
    billing_address_phone: '9988776655',
    billing_address_country_code_iso: 'IND',
    shipping_address_first_name: 'Juspay',
    shipping_address_last_name: 'Technologies',
    shipping_address_line1: 'Girija Building',
    shipping_address_line2: 'Ganapathi Temple Road',
    shipping_address_line3: '8th Block, Koramangala',
    shipping_address_city: 'Bengaluru',
    shipping_address_state: 'Karnataka',
    shipping_address_country: 'India',
    shipping_address_postal_code: '560095',
    shipping_address_phone: '9988776655',
    shipping_address_country_code_iso: 'IND',
    'options.get_client_auth_token': true,
    basket:
      '[{"id":"PID1","quantity":1,"unitPrice":25123.25}, {"id":"PID2","quantity":1,"unitPrice":25123.25}]',
  })
  .then((res) => console.log(res))
  .catch((err) => console.error(res))

Override default configs for single resource

Let's say you want to increase a timeout for payments api other than default 80,000ms. Do Not modify juspay instance directly, as it's shared and can cause troubles with other apis. Use optional juspayConfig params inside resource function calls to override configs.

try {
  const juspay = new Juspay({
    jweAuth: {
      keyId: process.env.KEY_ID,
      privateKey: prepareKey(process.env.PRIVATE_KEY),
      publicKey: prepareKey(process.env.PUBLIC_KEY),
    },
    merchantId: 'merchantId',
  })
  // uses default timeout
  const order = await juspay.order.create({
    amount: 100,
    order_id: 'ORD_' + Date.now(),
  })
  const paymentResponse = await juspay.payments.create(
    {
      order_id: order.order_id,
      payment_method_type: 'CARD',
      redirect_after_payment: true,
      payment_method: 'MASTERCARD',
      card_exp_month: '04',
      card_exp_year: '24',
      card_security_code: '424',
      save_to_locker: false,
      card_number: '4242424242424242',
    },
    // resource specific timeout
    {
      timeout: 100000,
    }
  )
  console.log(paymentResponse)
} catch (error) {
  throw error
}

you can override all the juspay environment specific config using resource function parameter as shown below

PCI compliant merchants can use order+txns call inside payments resource itself, read more here.

In some cases you will need to pass customer id to use juspay's active-active features. Here's how you can add resources specific headers.

const orderStatus = await juspay.order.status(
  order.order_id,
  {
    'options.add_full_gateway_response': true,
  },
  {
    timeout: 10000,
    version: '2024-01-03'
    headers: {
      'x-customerid': customerId,
    },
  }
)

Response http object

SDK attaches http key in response object for the user if it's of any use.

Error Handling And Examples

SDK errors are categorized in three parts APIError, JuspayError and Error. APIError is api errors coming from servers like AuthenticationError, InvalidRequestError, InternalServerError. Here JuspayError is thrown by sdk for cases like IllegalPrivateKey, IllegalPublicKey, DecryptionFailed, SignatureValidationFailed etc. Error is usually user error for setting up authentication configurations or some unkown cases. Also APIError extends JuspayError.

APIError to refund unknown order id

// unknown order id
const orderId = 'order_' + Date.now()
juspay.order
  .refund(orderId, {
    unique_request_id: 'refund_test_' + Date.now(),
    order_id: orderId,
    amount: 1,
  })
  .catch((res) => {
    // prints true
    console.log(err instanceof APIError)
    // prints true
    console.log(err instanceof JuspayError)
    console.error(res)
  })

JuspayError in case of failed signature verification

const order_id = 'ORD_' + Date.now()
juspay.order
  .status('order_id', {
    order_id,
    'options.add_full_gateway_response': true,
  })
  .then((res) => {
    console.log(res)
  })
  .catch((err) => {
    // prints true
    console.log(err instanceof JuspayError)
    console.error(err)
  })

APIError will be false in this case as decryption and verification is done by sdk and error is raised by sdk. If you want to raise flags in your system for such tampering cases please use error names such as SignatureValidationFailed or DecryptionFailed.

Logging

Logging sdk events like request, response and resource url becomes important for debugging in large and complicated systems. Hence, sdk comes with winston logger and has minimal logging configuration with it. It is enabled by default.

If you want to customize logging in accordance with your project, please go through this section. Because nodejs does not have standardized logging framework support like Java SDK has exposed a basic logging interface to customize it for your own system.

Juspay.customLogger of type (resource: string) => IJuspayLogger takes a function with one string parameter i.e. resource (which is the name of the class from which it's printed, used for debugging purposes) and returns the instance of the class which implements IJuspayLogger interface. interface definition below

Disable Logs

import Juspay from 'expresscheckout-nodejs'

Juspay.customLogger = (resource) => Juspay.silentLogger

Custom winston logger

import Juspay from 'expresscheckout-nodejs'
import winston from 'winston'

Juspay.customLogger = (resource) => winston.createLogger({
    transports: new winston.transports.Console(),
})

Custom pino or buyan logger

import Juspay from 'expresscheckout-nodejs'
import pino from 'pino'
import bunyan from 'bunyan'

// pino
Juspay.customLogger = (resource) => new pino()

// buyan
Juspay.customLogger = (resource) => bunyan.createLogger({name: 'expresscheckout-nodejs-sampleProject'})

Custom logging framework

If you have your custom logging framework, the instance should look like IJuspayLogger interface, it's a basic interface as shown below.

interface IJuspayLogger {
  info: (message: any) => IJuspayLogger | unknown
  error: (message: any) => IJuspayLogger | unknown
}
// making a custom logger, it has to implement IJuspayLogger interface
class CustomLogger {
  constructor(defaultJuspayLogs) {}

  info(message) {
    console.log(message)
    return this
  }

  error(message) {
    console.log(message)
    return this
  }
}
Juspay.customLogger = (resource) => new CustomLogger(resource)

because of a common interface of logger and SDK's logging usage is not chained we can do something like

import Juspay from 'expresscheckout-nodejs'

Juspay.customLogger = (resource) => console

to print it directly on the console for quick tests. But as it's printing to console it will not print nested deep objects.

Resources List

Here's the list of supported apis in this sdk under current version

| Resource | Endpoint | Authentication Methods | Documentation | | --------------------------------| -----------------------------------------------------------|--------------------------------------| ----------------------------------------------------------------------------------------------------------------------------| | customers.create | POST: /customers | Basic | here | | customers.get | GET: /customers/:customer_id | Basic | here | | customers.update | POST: /customers/:customer_id | Basic | here | | payments.create | POST: /txns | Basic, JWE+JWS | here | | order.create | POST: /orders | Basic | here | | order.status | GET: /orders/:order_id | Basic, JWE+JWS | here | | order.update | POST: /orders/:order_id | Basic | here | | order.refund | POST: /orders/:order_id/refunds | Basic, JWE+JWS | here | | orderSession.create | POST: /session | Basic, JWE+JWS | here |

Please note that JWE+JWS or JWT if supported has different route than normal, general nomenclature has /v4/ prefixed, with few exceptions with order.status has /v4/order-status and refund has /v4/orders/${order_id}/refunds

Questions?

Still have any questions? Feel free to mail us here - [email protected].