diff --git a/documents/oAuthTokenClient.md b/documents/oAuthTokenClient.md new file mode 100644 index 0000000..6a41169 --- /dev/null +++ b/documents/oAuthTokenClient.md @@ -0,0 +1,148 @@ +## OAuth Token Client + +### Generate Authorize Url +```js + +const OAuthTokenClient = require("./dist/oAuthTokenClient"); +const { generateOnboardingSignature } = require("./dist/utils/razorpay-utils") + +// Initialize client +let oAuth = new OAuthTokenClient(); + +let attributes = { + "submerchant_id": "", + "timestamp": Math.floor(Date.now() / 1000) +} + +let onboarding_signature = generateOnboardingSignature(attributes, ""); + +// Not an promise +const authUrl = oAuth.generateAuthUrl({ + "client_id": "", + "response_type": "code", + "redirect_uri": "https://example.com/razorpay_callback", + "scope": ["read_write"], + "state": "NOBYtv8r6c75ex6WZ", + "onboarding_signature": onboarding_signature +}); + +console.log(authUrl) +``` + +**Parameters:** + +| Name | Type | Description | +|----------------------|--|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| client_id* | string | Unique client identifier. | +| redirect_uri* | string | Callback URL used by Razorpay to redirect after the user approves or denies the authorisation request. The client should whitelist the 'redirect_uri'. | +| scopes* | array | Defines what access your application is requesting from the user. You can request one or multiple scopes by adding them to an array as indicated above. | +| state* | string | A random string generated by your service. This parameter helps prevent cross-site request forgery (CSRF) attacks. | +| onboarding_signature | string | A cryptographic string generated by your service using generateOnboardingSignature method in Utils class. Only applicable for accounts created with pre-fill KYC | + +**Response:** +``` +"https://auth.razorpay.com/authorize?response_type=code&client_id=&redirect_uri=https:%2F%2Fexample.com%2Frazorpay_callback&scope[]=read_only&scope[]=rx_read_write&state=NOBYtv8r6c75ex6WZ&onboarding_signature=" +``` + +------------------------------------------------------------------------------------------------------- + +### Get Access token +```js +oAuth.getAccessToken({ + "client_id": "", + "client_secret": "", + "grant_type": "authorization_code", + "redirect_uri": "https://example.com", + "code": "def50200d844dc80cc44dce2c665d07a374d76802", + "mode": "test" +}) +``` + +**Parameters:** + +| Name | Type | Description | +|----------------|--------|------------------------------------------------------------------------------------------------------------------------------| +| client_id* | string | Unique client identifier. | +| client_secret* | string | Client secret string. | +| redirect_uri* | string | Specifies the same redirect_uri used in the authorisation request. | +| grant_type* | string | Defines the grant type for the request. Possible value are:
  • authorization_code
  • client_credentials
| +| code* | string | Decoded authorisation code received in the last step. Note: Pass this parameter only when grant_type is 'authorization_code' | +| mode | string | The type of mode. Possible values:
  • test
  • live (default)
| + +**Response:** +```json +{ + "public_token": "rzp_test_oauth_9xu1rkZqoXlClS", + "token_type": "Bearer", + "expires_in": 7862400, + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IkY1Z0NQYkhhRzRjcUpnIn0.eyJhdWQiOiJGNFNNeEgxanMxbkpPZiIsImp0aSI6IkY1Z0NQYkhhRzRjcUpnIiwiaWF0IjoxNTkyODMxMDExLCJuYmYiOjE1OTI4MzEwMTEsInN1YiI6IiIsImV4cCI6MTYwMDc3OTgxMSwidXNlcl9pZCI6IkYycVBpejJEdzRPRVFwIiwibWVyY2hhbnRfaWQiOiJGMnFQaVZ3N0lNV01GSyIsInNjb3BlcyI6WyJyZWFkX29ubHkiXX0.Wwqt5czhoWpVzP5_aoiymKXoGj-ydo-4A_X2jf_7rrSvk4pXdqzbA5BMrHxPdPbeFQWV6vsnsgbf99Q3g-W4kalHyH67LfAzc3qnJ-mkYDkFY93tkeG-MCco6GJW-Jm8xhaV9EPUak7z9J9jcdluu9rNXYMtd5qxD8auyRYhEgs", + "refresh_token": "def50200f42e07aded65a323f6c53181d802cc797b62cc5e78dd8038d6dff253e5877da9ad32f463a4da0ad895e3de298cbce40e162202170e763754122a6cb97910a1f58e2378ee3492dc295e1525009cccc45635308cce8575bdf373606c453ebb5eb2bec062ca197ac23810cf9d6cf31fbb9fcf5b7d4de9bf524c89a4aa90599b0151c9e4e2fa08acb6d2fe17f30a6cfecdfd671f090787e821f844e5d36f5eacb7dfb33d91e83b18216ad0ebeba2bef7721e10d436c3984daafd8654ed881c581d6be0bdc9ebfaee0dc5f9374d7184d60aae5aa85385690220690e21bc93209fb8a8cc25a6abf1108d8277f7c3d38217b47744d7", + "razorpay_account_id": "acc_Dhk2qDbmu6FwZH" +} +``` +------------------------------------------------------------------------------------------------------- + +### Get Access token using refresh token +```js +oAuth.refreshToken({ + "client_id": "", + "client_secret": "", + "grant_type": "authorization_code", + "refresh_token": "def50200d844dc80cc44dce2c665d07a374d76802" +}) +``` + +**Parameters:** + +| Name | Type | Description | +|----------------|-----------|--------------------------------------------| +| client_id* | string | Unique client identifier. | +| client_secret* | string | Client secret string. | +| grant_type* | string | Defines the grant type for the request. Possible value are:
  • authorization_code
  • client_credentials
| +| refresh_token* | string | The previously-stored refresh token value. | + + +**Response:** +```json +{ + "public_token": "rzp_test_oauth_9xu1rkZqoXlClS", + "token_type": "Bearer", + "expires_in": 7862400, + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijl4dTF", + "refresh_token": "def5020096e1c470c901d34cd60fa53abdaf36620e823ffa53" +} +``` + +------------------------------------------------------------------------------------------------------- + +### Revoke a token +```js +oAuth.revokeToken({ + "client_id": "", + "client_secret": "", + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijl4dTF", + "token_type_hint": "access_token" +}) +``` + +**Parameters:** + +| Name | Type | Description | +|------------------|----------|----------------------------------------------------------------------------------------------------------| +| client_id* | string | Unique client identifier. | +| client_secret* | string | Client secret string. | +| token_type_hint* | string | The type of token for the request. Possible values:
  • access_token
  • refresh_token
| +| token* | string | The token whose access should be revoked. | + +**Response:** +```json +{ + "message": "Token Revoked" +} +``` +------------------------------------------------------------------------------------------------------- + +**PN: * indicates mandatory fields** +
+
+**For reference click [here](https://razorpay.com/docs/partners/platform/onboard-businesses/integrate-oauth/integration-steps)** \ No newline at end of file diff --git a/lib/api.js b/lib/api.js index 8f664a2..dac2d0c 100644 --- a/lib/api.js +++ b/lib/api.js @@ -40,17 +40,32 @@ function normalizeError(err) { class API { constructor(options) { - this.rq = axios.create({ + this.rq = axios.create(this._createConfig(options)) + } + + _createConfig(options) { + const config = { baseURL: options.hostUrl, - auth: { - username: options.key_id, - password: options.key_secret - }, headers: Object.assign( - {'User-Agent': options.ua}, + { 'User-Agent': options.ua }, getValidHeaders(options.headers) - ) - }) + ), + }; + + if (options.key_id && options.key_secret) { + config.auth = { + username: options.key_id, + password: options.key_secret, + }; + } + + if(options.oauthToken){ + config.headers = { + 'Authorization': `Bearer ${options.oauthToken}`, + ...config.headers + } + } + return config; } version = 'v1'; diff --git a/lib/oAuthTokenClient.d.ts b/lib/oAuthTokenClient.d.ts new file mode 100644 index 0000000..b4ee31c --- /dev/null +++ b/lib/oAuthTokenClient.d.ts @@ -0,0 +1,132 @@ +import API, { INormalizeError } from "./types/api" + +declare namespace OAuthTokenClient { + + interface OAuthTokenBaseRequestBody { + /** + * Unique client identifier. + */ + client_id: string; + /** + * Client secret string. + */ + client_secret: string; + } + + interface InitiateAuthorisationRequest extends Pick{ + /** + * Specifies that the application is requesting an + * authorisation code grant. possible value is `code`. + */ + response_type: string; + /** + * Callback URL used by Razorpay to redirect after the user approves or denies the authorisation request. + * The client should whitelist the `redirect_uri`. + */ + redirect_uri: string; + /** + * Defines what access your application is requesting from the user. You can request multiple scopes + * by separating with a space. + * possible values is `read_only` or `read_write`. + */ + scope: string | string[]; + /** + * Check [doc](https://razorpay.com/docs/partners/technology-partners/onboard-businesses/integrate-oauth/integration-steps/#query-parameters) for required params + */ + state: string; + onboarding_signature?: string; + } + + interface OAuthTokenRequest extends OAuthTokenBaseRequestBody { + /** + * Defines the grant type for the request. possible value is `authorization_code` or `refresh_token` + */ + grant_type?: "authorization_code" | "refresh_token"; + /** + * Specifies the same `redirect_uri` used in the authorisation request. + */ + redirect_uri?: string; + /** + * Decoded authorisation code received in the last step. + */ + code?: string; + /** + * The type of mode. possible values is `test` or `live`. + */ + mode?: "test" | "live"; + /** + * Used to refresh the access token when it expires. + */ + refresh_token?: string; + /** + * The type of token for the request. possible value is `access_token` or `refresh_token`. + */ + token_type_hint?: "access_token" | "refresh_token"; + /** + * The token whose access should be revoked. + */ + token?: string; + } + + interface OAuthTokenTokenResponse { + /** + * A public key is used only for public routes such as Checkout or Payments. + */ + public_token: string; + /** + * Defines the type of access token. possible value is `Bearer` + */ + token_type: string; + /** + * Integer representing the TTL of the access token in seconds. + */ + expires_in: number; + /** + * A private key used to access sub-merchant resources on Razorpay. + * used for server-to-server calls only. + */ + access_token: string; + /** + * Used to refresh the access token when it expires. + */ + refresh_token:string; + /** + * Identifies the sub-merchant ID who granted the authorisation. + */ + razorpay_account_id: string; + } +} + +declare class OAuthTokenClient extends API{ + constructor() + + getEntityUrl(): string; + /** + * Initiate Authorisation Using URL + * @param param - Check [doc](https://razorpay.com/docs/partners/technology-partners/onboard-businesses/integrate-oauth/integration-steps/#query-parameters) for required params + */ + generateAuthUrl(param: OAuthTokenClient.InitiateAuthorisationRequest): string; + + /** + * Get access token + * @param param - Check [doc](https://razorpay.com/docs/partners/technology-partners/onboard-businesses/integrate-oauth/integration-steps/#request-parameters) for required params + */ + getAccessToken(param: OAuthTokenClient.OAuthTokenRequest): Promise; + getAccessToken(param: OAuthTokenClient.OAuthTokenRequest, callback: (err: INormalizeError | null, data: OAuthTokenClient.OAuthTokenTokenResponse) => void): void + + /** + * Get refresh token + * @param param - Check [doc](https://razorpay.com/docs/partners/technology-partners/onboard-businesses/integrate-oauth/integration-steps/#refresh-token-api) for required params + */ + refreshToken(param: OAuthTokenClient.OAuthTokenRequest): Promise; + refreshToken(param: OAuthTokenClient.OAuthTokenRequest, callback: (err: INormalizeError | null, data: OAuthTokenClient.OAuthTokenTokenResponse) => void): void + + /** + * Revoke token + * @param param - Check [doc](https://razorpay.com/docs/partners/technology-partners/onboard-businesses/integrate-oauth/integration-steps/#revoke-token-api) for required params + */ + revokeToken(param: OAuthTokenClient.OAuthTokenRequest): Promise<{ message: string;}>; + revokeToken(param: OAuthTokenClient.OAuthTokenRequest, callback: (err: INormalizeError | null, data: { message: string;}) => void): void +} + +export = OAuthTokenClient diff --git a/lib/oAuthTokenClient.js b/lib/oAuthTokenClient.js new file mode 100644 index 0000000..234cca5 --- /dev/null +++ b/lib/oAuthTokenClient.js @@ -0,0 +1,62 @@ +const API = require('./api'); +const pkg = require('../package.json'); +const { validateInput, SCHEMAS } = require('./resources/oAuthTokenValidator'); + +class OAuthTokenClient extends API{ + + constructor() { + super({ + hostUrl : 'https://auth.razorpay.com', + ua: `razorpay-node@${pkg.version}`, + }) + } + + getEntityUrl(params){ + return params.url ; + } + + generateAuthUrl(params) { + const errors = validateInput(params, SCHEMAS.generateAuthUrl); + if (Object.keys(errors).length > 0) return errors; + const baseUrl = `${this.rq.defaults.baseURL}/authorize`; + const queryString = Object.entries(params) + .flatMap(([key, value]) => + Array.isArray(value) + ? value.map(item => `${key}[]=${encodeURIComponent(item)}`) + : key === "redirect_uri" + ? `${key}=${value}` + : `${key}=${encodeURIComponent(value)}` + ) + .join("&"); + return `${baseUrl}?${queryString}`; + } + + getAccessToken(params = {}, callback){ + const errors = validateInput(params, SCHEMAS.getAccessToken); + if (Object.keys(errors).length > 0) return Promise.reject(errors); + return this.post({ + url: '/token', + data: params + }, callback) + } + + refreshToken(params = {}, callback){ + const errors = validateInput(params, SCHEMAS.refreshToken); + if (Object.keys(errors).length > 0) return Promise.reject(errors); + return this.post({ + url: '/token', + data: params + }, callback) + } + + revokeToken(params = {}, callback){ + const errors = validateInput(params, SCHEMAS.revokeToken); + if (Object.keys(errors).length > 0) return Promise.reject(errors); + return this.post({ + url: '/revoke', + data: params + }, callback) + } +} + +module.exports = OAuthTokenClient diff --git a/lib/razorpay.d.ts b/lib/razorpay.d.ts index f0e5ed1..3d65e6c 100644 --- a/lib/razorpay.d.ts +++ b/lib/razorpay.d.ts @@ -26,9 +26,10 @@ import documents from './types/documents' import disputes from './types/disputes' interface IRazorpayConfig { - key_id: string; + key_id?: string; key_secret?: string; headers?: RazorpayHeaders; + oauthToken?: string; } declare class Razorpay { diff --git a/lib/razorpay.js b/lib/razorpay.js index ccfbca0..42ab85c 100644 --- a/lib/razorpay.js +++ b/lib/razorpay.js @@ -15,21 +15,23 @@ class Razorpay { } constructor(options = {}) { - let { key_id, key_secret, headers } = options + let { key_id, key_secret, oauthToken, headers } = options - if (!key_id) { - throw new Error('`key_id` is mandatory') + if (!key_id && !oauthToken) { + throw new Error('`key_id` or `oauthToken` is mandatory') } this.key_id = key_id this.key_secret = key_secret + this.oauthToken = oauthToken this.api = new API({ hostUrl: 'https://api.razorpay.com', ua: `razorpay-node@${Razorpay.VERSION}`, key_id, key_secret, - headers + headers, + oauthToken }) this.addResources() } @@ -59,7 +61,7 @@ class Razorpay { cards : require('./resources/cards')(this.api), webhooks : require('./resources/webhooks')(this.api), documents : require('./resources/documents')(this.api), - disputes : require('./resources/disputes')(this.api) + disputes : require('./resources/disputes')(this.api) }) } } diff --git a/lib/resources/oAuthTokenValidator.js b/lib/resources/oAuthTokenValidator.js new file mode 100644 index 0000000..2040264 --- /dev/null +++ b/lib/resources/oAuthTokenValidator.js @@ -0,0 +1,51 @@ +const { isValidUrl } = require("../utils/razorpay-utils"); + +const SCHEMAS = { + generateAuthUrl: { + client_id: "client_id is empty", + response_type: "response_type is empty", + redirect_uri: "redirect_uri is empty", + scope: "scope is empty", + state: "state is empty", + }, + getAccessToken: { + client_id: "client_id is empty", + client_secret: "client_secret is empty", + grant_type: "grant_type is empty", + redirect_uri: "redirect_uri is empty", + code: "code is empty", + }, + refreshToken: { + client_id: "client_id is empty", + client_secret: "client_secret is empty", + grant_type: "grant_type is empty", + refresh_token: "refresh_token is empty", + }, + revokeToken: { + client_id: "client_id is empty", + client_secret: "client_secret is empty", + token_type_hint: "token_type_hint is empty", + token: "token is empty", + }, + }; + + function validateInput(inputData, schema) { + let errors = {}; + for (let field in schema) { + if (!(field in inputData) || (typeof inputData[field] === "string" && inputData[field].trim() === "")) { + errors[field] = schema[field]; + } else if (field === "redirect_uri" && ! isValidUrl(inputData[field])) { + errors[field] = "redirect_uri is not a valid URL"; + } else if (field === "client_id" && !/^[A-Za-z0-9]{1,14}$/.test(inputData[field])) { + errors[field] = "client_id is not a valid ID"; + } else if(field === "grant_type" && (inputData[field] !== "refresh_token" && inputData[field] !== "authorization_code")){ + errors[field] = "grant_type is not a valid"; + } + } + return errors; + } + +module.exports = { + SCHEMAS, + validateInput +} \ No newline at end of file diff --git a/lib/types/api.d.ts b/lib/types/api.d.ts index 5530f2f..813ebb4 100644 --- a/lib/types/api.d.ts +++ b/lib/types/api.d.ts @@ -1,11 +1,13 @@ import nodeify from '../utils/nodeify' -interface IOption { - hostUrl: string; - key_id: string; - key_secret?: string; - ua: string; - headers?: string; + +interface IOption { + key_id?: string; + key_secret?: string; + headers?: RazorpayHeaders; + oauthToken?: string; + hostUrl: string; + ua: string; } interface IPayload { diff --git a/lib/utils/razorpay-utils.d.ts b/lib/utils/razorpay-utils.d.ts index 5acfcaf..f584875 100644 --- a/lib/utils/razorpay-utils.d.ts +++ b/lib/utils/razorpay-utils.d.ts @@ -85,4 +85,6 @@ export function validatePaymentVerification(payload: RazorpayVerifyPayment | Raz * * @param val */ -export function prettify(val: Object): string \ No newline at end of file +export function prettify(val: Object): string + +export function generateOnboardingSignature(params: any, secret: string): string \ No newline at end of file diff --git a/lib/utils/razorpay-utils.js b/lib/utils/razorpay-utils.js index 2af47d7..6ba6468 100644 --- a/lib/utils/razorpay-utils.js +++ b/lib/utils/razorpay-utils.js @@ -1,3 +1,5 @@ +const crypto = require("crypto"); + function getDateInSecs(date) { return (+new Date(date))/1000 } @@ -142,6 +144,47 @@ function validatePaymentVerification(params={}, signature, secret){ return validateWebhookSignature(payload,signature,secret); }; +function generateOnboardingSignature(params={}, secret){ + let jsonStr = JSON.stringify(params); + return encrypt(jsonStr, secret); +} + +function encrypt(dataToEncrypt, secret) { + try { + // Use first 16 bytes of secret as key + const keyBytes = Buffer.from(secret.slice(0, 16), 'utf8'); + + // Use first 12 bytes of key as IV + const iv = Buffer.alloc(12); + keyBytes.copy(iv, 0, 0, 12); + + // Create cipher with AES-GCM + const cipher = crypto.createCipheriv('aes-128-gcm', keyBytes, iv); + + // Encrypt the data + let encryptedData = cipher.update(dataToEncrypt, 'utf8'); + encryptedData = Buffer.concat([encryptedData, cipher.final()]); + + // Get authentication tag and append to encrypted data + const authTag = cipher.getAuthTag(); + const finalData = Buffer.concat([encryptedData, authTag]); + + // Convert to hex string + return finalData.toString('hex'); + } catch (err) { + throw new Error(`Encryption failed: ${err.message}`); + } +} + +function isValidUrl(url) { + try { + new URL(url); + return true; + } catch (error) { + return false; + } +} + module.exports = { normalizeNotes, normalizeDate, @@ -153,5 +196,7 @@ module.exports = { isNonNullObject, getTestError, validateWebhookSignature, - validatePaymentVerification + validatePaymentVerification, + isValidUrl, + generateOnboardingSignature } diff --git a/package.json b/package.json index aafd44a..53704f5 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "prepublish": "npm test", "clean": "rm -rf dist && mkdir dist", "cp-types": "mkdir dist/types && cp lib/types/* dist/types && cp lib/utils/*d.ts dist/utils", - "cp-ts": "cp lib/razorpay.d.ts dist/ && npm run cp-types", + "cp-ts": "cp lib/razorpay.d.ts dist/ && cp lib/oAuthTokenClient.d.ts dist/ && npm run cp-types", "build:commonjs": "babel lib -d dist", "build": "npm run clean && npm run build:commonjs && npm run cp-ts", "debug": "npm run build && node-debug examples/index.js", diff --git a/test/mocker.js b/test/mocker.js index f0e26a9..e131451 100644 --- a/test/mocker.js +++ b/test/mocker.js @@ -33,12 +33,13 @@ Mocker.prototype.mock = function(params) { if (replyWithError) { return FIXTURES.error } - + let capturedUrl = `${this.req.options.proto}://${this.req.options.hostname}${url}`; return { "success": true, __JUST_FOR_TESTS__: { url, method, + capturedUrl, requestQueryParams, requestBody, headers: this.req.headers diff --git a/test/oAuthTokenClient.spec.js b/test/oAuthTokenClient.spec.js new file mode 100644 index 0000000..80af0a8 --- /dev/null +++ b/test/oAuthTokenClient.spec.js @@ -0,0 +1,138 @@ +"use strict"; + +const mocker = require("./mocker"); +const OAuthTokenClient = require("../lib/oAuthTokenClient"); +const { assert } = require("chai"); + +describe("OAuth", () => { + let rzpHost; + let rzpVersion; + const oAuth = new OAuthTokenClient(); + + beforeEach(() => { + rzpHost = mocker.host; + rzpVersion = mocker.version; + mocker.host = "https://auth.razorpay.com"; + mocker.version = ""; + }); + + afterEach(() => { + mocker.host = rzpHost; + mocker.version = rzpVersion; + }); + + it("get access token", (done) => { + let params = { + client_id: "XXXXXXXXXXkQ5C", + client_secret: "XXXXXXXXXXXXXXXXXXHx7rXX", + grant_type: "authorization_code", + redirect_uri: "http://example.com/razorpay_callback", + code: "def50200d844dc80cc44dce2c665d07a374d76802", + mode: "test", + }; + + mocker.mock({ + url: `/token`, + method: "POST", + requestBody: params, + }); + + oAuth + .getAccessToken(params) + .then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.capturedUrl, + `${mocker.host}/token`, + "Access token url formed correctly" + ); + done(); + }) + }); + + it("get access token with invalid param", (done) => { + let params = { + client_id: "XXXXXXXXXXkQ5C", + client_secret: "XXXXXXXXXXXXXXXXXXHx7rXX", + grant_type: "authorization_code", + }; + + mocker.mock({ + url: `/token`, + method: "POST", + requestBody: params, + }); + + oAuth + .getAccessToken(params) + .catch(err=>{ + assert.hasAllKeys(err, ['redirect_uri', 'code']); + done(); + }) + }); + + it("refresh access token with invalid param", (done) => { + mocker.mock({ + url: `/token`, + method: "POST", + }); + + oAuth + .refreshToken() + .catch((err) => { + assert.hasAnyKeys(err, ['client_id']) + done(); + }) + }); + + it("refresh access token", (done) => { + let params = { + client_id: "XXXXXXXXXXkQ5C", + client_secret: "XXXXXXXXXXXXXXXXXXHx7rXX", + grant_type: "refresh_token", + refresh_token: "def5020096e1c470c901d34cd60fa53abdaf3662sa0", + }; + + mocker.mock({ + url: `/token`, + method: "POST", + requestBody: params, + }); + + oAuth + .refreshToken(params) + .then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.capturedUrl, + `${mocker.host}/token`, + "Refresh access token url formed correctly" + ); + done(); + }) + }); + + it("revoke access token", (done) => { + let params = { + client_id: "XXXXXXXXXXkQ5C", + client_secret: "XXXXXXXXXXXXXXXXXXHx7rXX", + token_type_hint: "access_token", + token: "def5020096e1c470c901d34cd60fa53abdaf3662sa0", + }; + + mocker.mock({ + url: `/revoke`, + method: "POST", + requestBody: params, + }); + + oAuth + .revokeToken(params) + .then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.capturedUrl, + `${mocker.host}/revoke`, + "Revoke access token url formed correctly" + ); + done(); + }) + }); +}); diff --git a/test/razorpay.spec.js b/test/razorpay.spec.js index b909117..4840538 100644 --- a/test/razorpay.spec.js +++ b/test/razorpay.spec.js @@ -5,19 +5,11 @@ const assert = chai.assert const Razorpay = require('../dist/razorpay') describe('Razorpay is initialized properly', () => { - it('Validation for key_id & key_secret', () => { + it('Validation for auth', () => { try { new Razorpay() } catch (e) { - assert.equal(e.message, '`key_id` is mandatory') - } - - try { - new Razorpay({ - key_id: 'XXX' - }) - } catch (e) { - assert.equal(e.message, '`key_secret` is mandatory') + assert.equal(e.message, '`key_id` or `oauthToken` is mandatory') } }) @@ -30,4 +22,12 @@ describe('Razorpay is initialized properly', () => { assert.equal(instance.key_id, 'XXX') assert.equal(instance.key_secret, 'YYY') }) + + it('instance should initialize with oAuth', () => { + let instance = new Razorpay({ + oauthToken: 'XXXXXXXX', + }) + + assert.equal(instance.oauthToken, 'XXXXXXXX') + }) })