From f341db4ea7f3e6b539c8946f046eb0833ec534bd Mon Sep 17 00:00:00 2001
From: Ankit Das <89454448+ankitdas13@users.noreply.github.com>
Date: Tue, 7 May 2024 18:32:02 +0530
Subject: [PATCH] feat: Added Dispute entity (#391)
---
documents/disputes.md | 241 ++++++++++++++++++++++++++++++++
lib/razorpay.d.ts | 6 +
lib/razorpay.js | 3 +-
lib/resources/disputes.js | 43 ++++++
lib/types/disputes.d.ts | 185 ++++++++++++++++++++++++
test/resources/disputes.spec.js | 96 +++++++++++++
6 files changed, 573 insertions(+), 1 deletion(-)
create mode 100644 documents/disputes.md
create mode 100644 lib/resources/disputes.js
create mode 100644 lib/types/disputes.d.ts
create mode 100644 test/resources/disputes.spec.js
diff --git a/documents/disputes.md b/documents/disputes.md
new file mode 100644
index 0000000..a5ffff8
--- /dev/null
+++ b/documents/disputes.md
@@ -0,0 +1,241 @@
+## Disputes
+
+### Fetch All Disputes
+
+```js
+var options = {
+ count: 1
+}
+
+instance.disputes.all(options)
+```
+
+**Parameters:**
+
+| Name | Type | Description |
+|---------------|-------------|---------------------------------------------|
+| count | integer | number of dispute to fetch (default: 10) |
+| skip | integer | number of dispute to be skipped (default: 0) |
+
+**Response:**
+```json
+{
+ "entity": "collection",
+ "count": 1,
+ "items": [
+ {
+ "id": "disp_Esz7KAitoYM7PJ",
+ "entity": "dispute",
+ "payment_id": "pay_EsyWjHrfzb59eR",
+ "amount": 10000,
+ "currency": "INR",
+ "amount_deducted": 0,
+ "reason_code": "pre_arbitration",
+ "respond_by": 1590604200,
+ "status": "open",
+ "phase": "pre_arbitration",
+ "created_at": 1590059211,
+ "evidence": {
+ "amount": 10000,
+ "summary": null,
+ "shipping_proof": null,
+ "billing_proof": null,
+ "cancellation_proof": null,
+ "customer_communication": null,
+ "proof_of_service": null,
+ "explanation_letter": null,
+ "refund_confirmation": null,
+ "access_activity_log": null,
+ "refund_cancellation_policy": null,
+ "term_and_conditions": null,
+ "others": null,
+ "submitted_at": null
+ }
+ }
+ ]
+}
+```
+-------------------------------------------------------------------------------------------------------
+
+### Fetch a Dispute
+
+```js
+var disputeId = "disp_0000000000000";
+
+instance.disputes.fetch(disputeId);
+```
+
+**Parameters:**
+
+| Name | Type | Description |
+|-------|-----------|--------------------------------------------------|
+| id* | string | The unique identifier of the dispute. |
+
+**Response:**
+```json
+{
+ "id": "disp_AHfqOvkldwsbqt",
+ "entity": "dispute",
+ "payment_id": "pay_EsyWjHrfzb59eR",
+ "amount": 10000,
+ "currency": "INR",
+ "amount_deducted": 0,
+ "reason_code": "pre_arbitration",
+ "respond_by": 1590604200,
+ "status": "open",
+ "phase": "pre_arbitration",
+ "created_at": 1590059211,
+ "evidence": {
+ "amount": 10000,
+ "summary": "goods delivered",
+ "shipping_proof": null,
+ "billing_proof": null,
+ "cancellation_proof": null,
+ "customer_communication": null,
+ "proof_of_service": null,
+ "explanation_letter": null,
+ "refund_confirmation": null,
+ "access_activity_log": null,
+ "refund_cancellation_policy": null,
+ "term_and_conditions": null,
+ "others": null,
+ "submitted_at": null
+ }
+}
+```
+-------------------------------------------------------------------------------------------------------
+
+### Contest a Dispute
+
+```js
+
+//Use this API sample code for draft
+
+var disputeId = "disp_0000000000000";
+
+instance.disputes.contest(disputeId,{
+ "billing_proof": [
+ "doc_EFtmUsbwpXwBG9",
+ "doc_EFtmUsbwpXwBG8"
+ ],
+ "action": "submit"
+})
+
+
+//Use this API sample code for submit
+
+instance.disputes.contest(disputeId, {
+ "amount": 5000,
+ "summary": "goods delivered",
+ "shipping_proof": [
+ "doc_EFtmUsbwpXwBH9",
+ "doc_EFtmUsbwpXwBH8"
+ ],
+ "others": [
+ {
+ "type": "receipt_signed_by_customer",
+ "document_ids": [
+ "doc_EFtmUsbwpXwBH1",
+ "doc_EFtmUsbwpXwBH7"
+ ]
+ }
+ ],
+ "action": "draft"
+})
+```
+
+**Response:**
+```json
+
+// Draft
+{
+ "id": "disp_AHfqOvkldwsbqt",
+ "entity": "dispute",
+ "payment_id": "pay_EsyWjHrfzb59eR",
+ "amount": 10000,
+ "currency": "INR",
+ "amount_deducted": 0,
+ "reason_code": "chargeback",
+ "respond_by": 1590604200,
+ "status": "open",
+ "phase": "chargeback",
+ "created_at": 1590059211,
+ "evidence": {
+ "amount": 5000,
+ "summary": "goods delivered",
+ "shipping_proof": [
+ "doc_EFtmUsbwpXwBH9",
+ "doc_EFtmUsbwpXwBH8"
+ ],
+ "billing_proof": null,
+ "cancellation_proof": null,
+ "customer_communication": null,
+ "proof_of_service": null,
+ "explanation_letter": null,
+ "refund_confirmation": null,
+ "access_activity_log": null,
+ "refund_cancellation_policy": null,
+ "term_and_conditions": null,
+ "others": [
+ {
+ "type": "receipt_signed_by_customer",
+ "document_ids": [
+ "doc_EFtmUsbwpXwBH1",
+ "doc_EFtmUsbwpXwBH7"
+ ]
+ }
+ ],
+ "submitted_at": null
+ }
+}
+
+//Submit
+{
+ "id": "disp_AHfqOvkldwsbqt",
+ "entity": "dispute",
+ "payment_id": "pay_EsyWjHrfzb59eR",
+ "amount": 10000,
+ "currency": "INR",
+ "amount_deducted": 0,
+ "reason_code": "chargeback",
+ "respond_by": 1590604200,
+ "status": "under_review",
+ "phase": "chargeback",
+ "created_at": 1590059211,
+ "evidence": {
+ "amount": 5000,
+ "summary": "goods delivered",
+ "shipping_proof": [
+ "doc_EFtmUsbwpXwBH9",
+ "doc_EFtmUsbwpXwBH8"
+ ],
+ "billing_proof": [
+ "doc_EFtmUsbwpXwBG9",
+ "doc_EFtmUsbwpXwBG8"
+ ],
+ "cancellation_proof": null,
+ "customer_communication": null,
+ "proof_of_service": null,
+ "explanation_letter": null,
+ "refund_confirmation": null,
+ "access_activity_log": null,
+ "refund_cancellation_policy": null,
+ "term_and_conditions": null,
+ "others": [
+ {
+ "type": "receipt_signed_by_customer",
+ "document_ids": [
+ "doc_EFtmUsbwpXwBH1",
+ "doc_EFtmUsbwpXwBH7"
+ ]
+ }
+ ],
+ "submitted_at": 1590603200
+ }
+}
+```
+-------------------------------------------------------------------------------------------------------
+**PN: * indicates mandatory fields**
+
+
+**For reference click [here](https://razorpay.com/docs/api/documents)**
\ No newline at end of file
diff --git a/lib/razorpay.d.ts b/lib/razorpay.d.ts
index 83d0639..5698d1c 100644
--- a/lib/razorpay.d.ts
+++ b/lib/razorpay.d.ts
@@ -22,6 +22,7 @@ import webhooks from './types/webhooks'
import products from './types/products'
import tokens from './types/tokens'
import iins from './types/iins'
+import disputes from './types/disputes'
interface IRazorpayConfig {
key_id: string;
@@ -144,6 +145,11 @@ declare class Razorpay {
* @see https://razorpay.com/docs/api/payments/cards/iin-api/#iin-entity
*/
iins: ReturnType
+ /**
+ * Dispute Entity
+ * @see https://razorpay.com/docs/api/disputes
+ */
+ disputes: ReturnType
}
export = Razorpay
diff --git a/lib/razorpay.js b/lib/razorpay.js
index dbbd1d8..3738bd1 100644
--- a/lib/razorpay.js
+++ b/lib/razorpay.js
@@ -57,7 +57,8 @@ class Razorpay {
fundAccount : require('./resources/fundAccount')(this.api),
items : require('./resources/items')(this.api),
cards : require('./resources/cards')(this.api),
- webhooks : require('./resources/webhooks')(this.api)
+ webhooks : require('./resources/webhooks')(this.api),
+ disputes : require('./resources/disputes')(this.api)
})
}
}
diff --git a/lib/resources/disputes.js b/lib/resources/disputes.js
new file mode 100644
index 0000000..6c821aa
--- /dev/null
+++ b/lib/resources/disputes.js
@@ -0,0 +1,43 @@
+'use strict';
+
+module.exports = function (api) {
+
+ const BASE_URL = "/disputes";
+
+ return {
+
+ fetch(disputeId, callback) {
+ return api.get({
+ url: `${BASE_URL}/${disputeId}`,
+ }, callback);
+ },
+
+ all(params = {}, callback) {
+ let { count, skip } = params
+
+ count = Number(count) || 10
+ skip = Number(skip) || 0
+
+ return api.get({
+ url: `${BASE_URL}`,
+ data: {
+ count,
+ skip
+ }
+ }, callback)
+ },
+
+ accept(disputeId, callback) {
+ return api.post({
+ url: `${BASE_URL}/${disputeId}/accept`,
+ }, callback);
+ },
+
+ contest(disputeId, param, callback) {
+ return api.patch({
+ url: `${BASE_URL}/${disputeId}/contest`,
+ data: param
+ }, callback);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/types/disputes.d.ts b/lib/types/disputes.d.ts
new file mode 100644
index 0000000..28b92cb
--- /dev/null
+++ b/lib/types/disputes.d.ts
@@ -0,0 +1,185 @@
+import { IMap, INormalizeError, PartialOptional, RazorpayPaginationOptions } from "./api";
+
+export declare namespace Disputes {
+ interface RazorpayDisputesBaseRequestBody {
+
+ }
+
+ interface RazorpayDisputesContestBaseRequestBody {
+ /**
+ * The contested amount in currency subunits
+ */
+ amount: number;
+ /**
+ * The explanation provided by you for contesting the dispute. max length 1000 char
+ */
+ summary: string;
+ /**
+ * List of document ids which serves as proof that the product was shipped to the
+ * customer at their provided address.
+ */
+ shipping_proof: string[];
+ /**
+ * List of document ids which serves as proof of order confirmation, such as a receipt.
+ */
+ billing_proof: string[];
+ /**
+ * List of document ids that serves as proof that this product/service was cancelled.
+ */
+ cancellation_proof: string[];
+ /**
+ * List of document ids listing any written/email communication from the customer
+ * confirming that the customer received the product/service or is satisfied with the
+ * product/service.
+ */
+ customer_communication: string[];
+ /***
+ * List of document ids showing proof of service provided to the customer.
+ */
+ proof_of_service: string[];
+ explanation_letter: string[];
+ /**
+ * List of document ids showing proof that the refund had been provided to the customer.
+ */
+ refund_confirmation: string[];
+ /**
+ * List of document ids of any server or activity logs which prove that the customer accessed
+ * or downloaded the purchased digital product.
+ */
+ access_activity_log: string[];
+ /**
+ * List of document ids listing your refund and/or cancellation policy, as shown to the customer.
+ */
+ refund_cancellation_policy: string[];
+ /**
+ * List of document ids listing your sales terms and conditions, as shown to the customer.
+ */
+ term_and_conditions: string[];
+ /**
+ * Specifies the evidence documents to be uploaded as a part of contesting a dispute.
+ */
+ others: {
+ /**
+ * Describes the custom type of evidence document(s) provided.
+ */
+ type: string
+ /**
+ * List of document ids corresponding to the customer evidence type.
+ */
+ document_ids: string[]
+ }[]
+ /**
+ * The action to be taken for this contest. Possible value is `draft` or `submit`.
+ */
+ action: string;
+ /**
+ * Unix timestamp when the dispute was last submitted by you to Razorpay. The default value is `null`.
+ */
+ submitted_at: any;
+ }
+
+ interface RazorpayDisputesContest {
+
+ }
+
+ interface RazorpayDisputeEvidence extends RazorpayDisputesContestBaseRequestBody{}
+
+ interface RazorpayDispute {
+ /**
+ * The unique identifier of the dispute generated by Razorpay
+ */
+ id: string;
+ /**
+ * Indicates the type of entity.
+ */
+ entity: string;
+ /**
+ * The unique identifier of the payment against which the dispute was created.
+ */
+ payment_id: string;
+ /**
+ * Amount, in currency subunits, for which the dispute was created.
+ */
+ amount: number;
+ /**
+ * 3-letter ISO currency code associated with the amount.
+ */
+ currency: string;
+ /**
+ * The amount, in currency subunits, deducted from your Razorpay current
+ * balance when the dispute is `lost`.
+ */
+ amount_deducted: number;
+ /**
+ * Code associated with the reason for the dispute.
+ */
+ reason_code: string;
+ /**
+ * Unix timestamp by which a response should be sent to the customer.
+ */
+ respond_by: number;
+ /**
+ * The status of the dispute.
+ */
+ status: string;
+ /**
+ * Phase associated with the dispute
+ */
+ phase: string;
+ /**
+ * Unix timestamp when the dispute was created.
+ */
+ created_at: number;
+ /**
+ * Provides details of the evidence submitted/saved for contesting a
+ * dispute.
+ */
+ evidence: RazorpayDisputeEvidence;
+ }
+}
+
+declare function disputes(api: any): {
+ /**
+ * Fetches a dispute given Dispute ID
+ *
+ * @param disputeId - The unique identifier of the dispute.
+ *
+ */
+ fetch(disputeId: string): Promise
+ fetch(disputeId: string, callback: (err: INormalizeError | null, data: Disputes.RazorpayDispute) => void): void;
+ /**
+ * Get all disputes
+ *
+ * @param params - Check [doc](https://razorpay.com/docs/api/disputes/fetch-all) for required params
+ *
+ */
+ all(params?: RazorpayPaginationOptions): Promise<{
+ entity: string,
+ count: number,
+ items: Array
+ }>
+ all(params: RazorpayPaginationOptions, callback: (err: INormalizeError | null, data: {
+ entity: string,
+ count: number,
+ items: Array
+ }) => void): void;
+ /**
+ * Update an account
+ *
+ * @param disputeId - The unique identifier of the dispute.
+ *
+ */
+ accept(disputeId: string): Promise
+ accept(disputeId: string, callback: (err: INormalizeError | null, data: Disputes.RazorpayDispute) => void): void;
+ /**
+ * Contest a dispute
+ *
+ * @param disputeId - The unique identifier of the dispute.
+ * @param params - Check [doc](https://razorpay.com/docs/api/disputes/contest) for required params
+ *
+ */
+ contest(accountId: string, param: Partial): Promise
+ contest(accountId: string, param: Partial, callback: (err: INormalizeError | null, data: Promise) => void): void;
+}
+
+export default disputes
\ No newline at end of file
diff --git a/test/resources/disputes.spec.js b/test/resources/disputes.spec.js
new file mode 100644
index 0000000..614384d
--- /dev/null
+++ b/test/resources/disputes.spec.js
@@ -0,0 +1,96 @@
+'use strict'
+
+const chai = require('chai')
+const { assert } = chai
+const rzpInstance = require('../razorpay')
+const mocker = require('../mocker')
+const equal = require('deep-equal')
+
+let mockRequest = {
+ "amount": 5000,
+ "summary": "goods delivered",
+ "shipping_proof": [
+ "doc_EFtmUsbwpXwBH9",
+ "doc_EFtmUsbwpXwBH8"
+ ],
+ "others": [
+ {
+ "type": "receipt_signed_by_customer",
+ "document_ids": [
+ "doc_EFtmUsbwpXwBH1",
+ "doc_EFtmUsbwpXwBH7"
+ ]
+ }
+ ],
+ "action": "draft"
+}
+
+const BASE_URL = '/disputes',
+ TEST_DISPUTE_ID = 'disp_AHfqOvkldwsbqt';
+
+describe('DISPUTE', () => {
+
+ it('Dispute fetch', (done) => {
+ mocker.mock({
+ url: `/${BASE_URL}/${TEST_DISPUTE_ID}`
+ })
+
+ rzpInstance.disputes.fetch(TEST_DISPUTE_ID).then((response) => {
+ assert.equal(
+ response.__JUST_FOR_TESTS__.url,
+ `/v1/disputes/${TEST_DISPUTE_ID}`,
+ 'Fetch dispute url formed correctly'
+ )
+ done()
+ })
+ })
+
+ it('Fetch all dispute', (done) => {
+
+ mocker.mock({
+ url: `/${BASE_URL}`,
+ })
+
+ rzpInstance.disputes.all({count:10, skip: 0}).then((response) => {
+ console.log(response.__JUST_FOR_TESTS__)
+ assert.equal(
+ response.__JUST_FOR_TESTS__.url,
+ `/v1/disputes?count=10&skip=0`,
+ 'fetch all disputes url formed correctly'
+ )
+ done()
+ })
+ })
+
+ it('Accept a dispute ', (done) => {
+ mocker.mock({
+ url: `/${BASE_URL}/${TEST_DISPUTE_ID}/accept`,
+ method: "POST"
+ })
+
+ rzpInstance.disputes.accept(TEST_DISPUTE_ID).then((response) => {
+ assert.equal(
+ response.__JUST_FOR_TESTS__.url,
+ `/v1/disputes/${TEST_DISPUTE_ID}/accept`,
+ 'accept a dispute url formed correctly'
+ )
+ done()
+ })
+ })
+
+ it('contest a dispute ', (done) => {
+ mocker.mock({
+ url: `/${BASE_URL}/${TEST_DISPUTE_ID}/contest`,
+ method: "PATCH"
+ })
+
+ rzpInstance.disputes.contest(TEST_DISPUTE_ID, mockRequest).then((response) => {
+ assert.equal(
+ response.__JUST_FOR_TESTS__.url,
+ `/v1/disputes/${TEST_DISPUTE_ID}/contest`,
+ 'accept a dispute url formed correctly'
+ )
+ done()
+ })
+ })
+})