From 4cd5602bfb66a1a3dd214c330fe7e3a0ea26c995 Mon Sep 17 00:00:00 2001 From: Ankit Das <89454448+ankitdas13@users.noreply.github.com> Date: Tue, 7 May 2024 18:26:42 +0530 Subject: [PATCH 1/5] feat: Added new api's to customers (#390) --- documents/customer.md | 244 +++++++++++++++++++++++++++++++ lib/resources/customers.js | 26 ++++ lib/types/customers.d.ts | 108 ++++++++++++++ test/resources/customers.spec.js | 100 +++++++++++++ 4 files changed, 478 insertions(+) diff --git a/documents/customer.md b/documents/customer.md index 87f0347e..8b687ed4 100644 --- a/documents/customer.md +++ b/documents/customer.md @@ -146,6 +146,250 @@ instance.customers.fetch(customerId) } ``` +------------------------------------------------------------------------------------------------------- + +### Add Bank Account of Customer + +```js +var customerId = "cust_N5mywh91sXB69O" + +instance.customers.addBankAccount(customerId, { + "ifsc_code" : "UTIB0000194", + "account_number" :"916010082985661", + "beneficiary_name" : "Pratheek", + "beneficiary_address1" : "address 1", + "beneficiary_address2" : "address 2", + "beneficiary_address3" : "address 3", + "beneficiary_address4" : "address 4", + "beneficiary_email" : "random@email.com", + "beneficiary_mobile" : "8762489310", + "beneficiary_city" :"Bangalore", + "beneficiary_state" : "KA", + "beneficiary_country" : "IN" +}); +``` + +**Parameters:** + +| Name | Type | Description | +|---------------|-------------|---------------------------------------------| +| customerId* | string | Customer's bank account number | +| account_number | integer | The id of the customer to be fetched | +| account_number | string | The name of the beneficiary associated with the bank account. | +| beneficiary_name | string | The virtual payment address. | +| beneficiary_address1 | string | The id of the customer to be fetched | +| beneficiary_email | string | Email address of the beneficiary. | +| beneficiary_mobile | integer | Mobile number of the beneficiary. | +| beneficiary_city | string | The name of the city of the beneficiary. | +| beneficiary_state | string | The state of the beneficiary. | +| beneficiary_pin | interger | The pin code of the beneficiary's address. | +| ifsc_code | string | The IFSC code of the bank branch associated with the account. | + +**Response:** +```json +{ + "id" : "cust_1Aa00000000001", + "entity": "customer", + "name" : "Saurav Kumar", + "email" : "Saurav.kumar@example.com", + "contact" : "+919000000000", + "gstin":"29XAbbA4369J1PA", + "notes" : [], + "created_at ": 1234567890 +} +``` + +------------------------------------------------------------------------------------------------------- + +### Delete Bank Account of Customer + +```js +var customerId = "cust_N5mywh91sXB69O" + +var bankAccountId = "ba_N6aM8uo64IzxHu" + +instance.customers.deleteBankAccount(customerId, bankAccountId); +``` + +**Parameters:** + +| Name | Type | Description | +|---------------|-------------|---------------------------------------------| +| customerId* | string | Customer's bank account number | +| bank_id | string | The bank_id that needs to be deleted. | + +**Response:** +```json +{ + "id": "ba_Evg09Ll05SIPSD", + "ifsc": "ICIC0001207", + "bank_name": "ICICI Bank", + "name": "Test R4zorpay", + "account_number": "XXXXXXXXXXXXXXX0434", + "status": "deleted" +} +``` + +------------------------------------------------------------------------------------------------------- + +### Eligibility Check API + +```js +instance.customers.requestEligibilityCheck({ + "inquiry": "affordability", + "amount": 500000, + "currency": "INR", + "customer": { + "id": "cust_KhP5dO1dKmc0Rm", + "contact": "+918220276214", + "ip": "105.106.107.108", + "referrer": "https://merchansite.com/example/paybill", + "user_agent": "Mozilla/5.0" + } +}) +``` + +**Parameters:** + +| Name | Type | Description | +|---------------|-------------|---------------------------------------------| +| inquiry | string | List of methods or instruments on which eligibility check is required. Possible value is `affordability`. | +| amount* | string | The amount for which the order was created, in currency subunits. For example, for an amount of ₹295, enter `29500`. | +| currency* | string | Possible value is `INR`. | +| customer | object | All keys listed [here](https://razorpay.com/docs/payments/payment-gateway/affordability/eligibility-check/#request-parameters) are supported | +| instruments | object | All keys listed [here](https://razorpay.com/docs/payments/payment-gateway/affordability/eligibility-check/#request-parameters) are supported | + +**Response:** +```json +{ + "amount": "500000", + "customer": { + "id": "KkBhM9EC1Y0HTm", + "contact": "+918220722114" + }, + "instruments": [ + { + "method": "emi", + "issuer": "HDFC", + "type": "debit", + "eligibility_req_id": "elig_KkCNLzlNeMYQyZ", + "eligibility": { + "status": "eligible" + } + }, + { + "method": "paylater", + "provider": "getsimpl", + "eligibility_req_id": "elig_KkCNLzlNeMYQyZ", + "eligibility": { + "status": "eligible" + } + }, + { + "method": "paylater", + "provider": "icic", + "eligibility_req_id": "elig_KkCNLzlNeMYQyZ", + "eligibility": { + "status": "eligible" + } + }, + { + "method": "cardless_emi", + "provider": "walnut369", + "eligibility_req_id": "elig_KkCNLzlNeMYQyZ", + "eligibility": { + "status": "ineligible", + "error": { + "code": "GATEWAY_ERROR", + "description": "The customer has not been approved by the partner.", + "source": "business", + "step": "inquiry", + "reason": "user_not_approved" + } + } + }, + { + "method": "cardless_emi", + "provider": "zestmoney", + "eligibility_req_id": "elig_KkCNLzlNeMYQyZ", + "eligibility": { + "status": "ineligible", + "error": { + "code": "GATEWAY_ERROR", + "description": "The customer has exhausted their credit limit.", + "source": "business", + "step": "inquiry", + "reason": "credit_limit_exhausted" + } + } + }, + { + "method": "paylater", + "provider": "lazypay", + "eligibility_req_id": "elig_KkCNLzlNeMYQyZ", + "eligibility": { + "status": "ineligible", + "error": { + "code": "GATEWAY_ERROR", + "description": "The order amount is less than the minimum transaction amount.", + "source": "business", + "step": "inquiry", + "reason": "min_amt_required" + } + } + } + ] +} +``` + +------------------------------------------------------------------------------------------------------- + +### Fetch Eligibility by id + +```js +var eligibilityId = 'elig_F1cxDoHWD4fkQt' +instance.customers.fetchEligibility(eligibilityId); +``` + +**Parameters:** + +| Name | Type | Description | +|---------------|-------------|---------------------------------------------| +| customerId* | string | Customer's bank account number | +| bank_id | string | The bank_id that needs to be deleted. | + +**Response:** +```json +{ + "instruments": [ + { + "method": "paylater", + "provider": "lazypay", + "eligibility_req_id": "elig_LBwGKVvS2X48Lq", + "eligibility": { + "status": "eligible" + } + }, + { + "method": "paylater", + "provider": "getsimpl", + "eligibility_req_id": "elig_LBwGKVvS2X48Lq", + "eligibility": { + "status": "ineligible", + "error": { + "code": "GATEWAY_ERROR", + "description": "The customer has exhausted their credit limit", + "source": "gateway", + "step": "inquiry", + "reason": "credit_limit_exhausted" + } + } + } + ] +} +``` + + ------------------------------------------------------------------------------------------------------- **PN: * indicates mandatory fields** diff --git a/lib/resources/customers.js b/lib/resources/customers.js index 2ea7d24b..4642cfe8 100644 --- a/lib/resources/customers.js +++ b/lib/resources/customers.js @@ -53,6 +53,32 @@ module.exports = function (api) { return api.delete({ url: `/customers/${customerId}/tokens/${tokenId}` }, callback) + }, + + addBankAccount(customerId, params ,callback) { + return api.post({ + url: `/customers/${customerId}/bank_account`, + data: params + }, callback) + }, + + deleteBankAccount(customerId, bankId ,callback) { + return api.delete({ + url: `/customers/${customerId}/bank_account/${bankId}` + }, callback) + }, + + requestEligibilityCheck(params ,callback) { + return api.post({ + url: `/customers/eligibility`, + data: params + }, callback) + }, + + fetchEligibility(eligibilityId, callback) { + return api.get({ + url: `/customers/eligibility/${eligibilityId}`, + }, callback) } } } diff --git a/lib/types/customers.d.ts b/lib/types/customers.d.ts index b69ae97b..c18d5c68 100644 --- a/lib/types/customers.d.ts +++ b/lib/types/customers.d.ts @@ -1,6 +1,7 @@ import { IMap, INormalizeError, RazorpayPaginationOptions } from "./api"; import { Invoices } from "./invoices"; import { Tokens } from "./tokens"; +import { VirtualAccounts } from "./virtualAccounts" export declare namespace Customers { interface RazorpayCustomerBaseRequestBody { @@ -57,6 +58,87 @@ export declare namespace Customers { */ shipping_address?: Invoices.RazorpayInvoiceAddress[]; } + + interface RazorpayCustomerBankAccountRequestBody { + /** + * The IFSC code of the bank branch associated with the account. + */ + ifsc_code: string; + /** + * Customer's bank account number. + */ + account_number: string; + /** + * The name of the beneficiary associated with the bank account. + */ + beneficiary_name?: string; + /** + * The virtual payment address. + */ + beneficiary_address1?: string; + beneficiary_address2?: string; + beneficiary_address3?: string; + beneficiary_address4?: string; + /** + * Email address of the beneficiary. For example, `gaurav.kumar@example.com`. + */ + beneficiary_email?: string; + /** + * Mobile number of the beneficiary. + */ + beneficiary_mobile?: string; + /** + * The name of the city of the beneficiary. + */ + beneficiary_city?: string; + /** + * The state of the beneficiary. + */ + beneficiary_state?: string; + /** + * The country of the beneficiary. + */ + beneficiary_country?: string; + /** + * The pin code of the beneficiary's address. + */ + beneficiary_pin?: string; + } + + interface RazorpayCustomerBankAccount extends Partial{ + success?: string; + } + + interface CustomersEligibility { + id: string; + contact: string; + ip: string; + referrer: string; + user_agent: string; + } + + interface RazorpayCustomerEligibilityRequestBody { + inquiry?: string; + amount: number | string; + currency: string; + customer: Partial; + } + + interface Instruments { + method: string; + issuer: string; + type: string; + provider: string; + eligibility_req_id: string; + eligibility: { + status: string; + error: Omit + } + } + + interface RazorpayCustomerEligibility extends RazorpayCustomerEligibilityRequestBody { + instruments? : Array + } } declare function customers(api: any): { @@ -135,6 +217,32 @@ declare function customers(api: any): { */ deleteToken(customerId: string, tokenId: string): Promise<{ deleted: boolean }> deleteToken(customerId: string, tokenId: string, callback: (err: INormalizeError | null, data: { deleted: boolean }) => void): void; + /** + * Add Bank Account of Customer + * + * @param customerId - The unique identifier of the customer. + * @param param - Check [doc](https://razorpay.com/docs/api/customers/bank-accounts/#1-add-bank-account-of-customer) for required params + */ + addBankAccount(customerId: string, params: Customers.RazorpayCustomerBankAccountRequestBody): Promise + /** + * Delete Bank Account of Customer + * + * @param customerId - The unique identifier of the customer. + * @param bankAccountId - The bank_id that needs to be deleted. + */ + deleteBankAccount(customerId: string, bankAccountId: string): Promise + /** + * Eligibility Check API + * + * @param param - Check [doc](https://razorpay.com/docs/payments/payment-gateway/affordability/eligibility-check/#eligibility-check-api) for required params + */ + requestEligibilityCheck(param: Customers.RazorpayCustomerEligibilityRequestBody): Promise> + /** + * Fetch Eligibility by id + * + * @param eligibilityId - The unique identifier of the eligibility request to be retrieved. + */ + fetchEligibility(eligibilityId: string): Promise> } export default customers \ No newline at end of file diff --git a/test/resources/customers.spec.js b/test/resources/customers.spec.js index c7a9bad3..97cc2c9a 100644 --- a/test/resources/customers.spec.js +++ b/test/resources/customers.spec.js @@ -184,4 +184,104 @@ describe('CUSTOMERS', () => { done() }) }) + + it('Add bank account of customer', (done) => { + const TEST_CUSTOMER_ID = 'cust_6fqBqgrfTSuj5v' + const param = { + "ifsc_code" : "UTIB0000194", + "account_number" :"916010082985661", + "beneficiary_name" : "Pratheek", + "beneficiary_address1" : "address 1", + "beneficiary_address2" : "address 2", + "beneficiary_address3" : "address 3", + "beneficiary_address4" : "address 4", + "beneficiary_email" : "random@email.com", + "beneficiary_mobile" : "8762489310", + "beneficiary_city" :"Bangalore", + "beneficiary_state" : "KA", + "beneficiary_country" : "IN" + } + + mocker.mock({ + url: `/customers/${TEST_CUSTOMER_ID}/bank_account`, + method: 'POST' + }) + + rzpInstance.customers.addBankAccount(TEST_CUSTOMER_ID, param).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/customers/${TEST_CUSTOMER_ID}/bank_account`, + 'Add bank account of customer url formed correctly' + ) + done() + }) + }) + + it('delete bank account of customer', (done) => { + const TEST_CUSTOMER_ID = 'cust_6fqBqgrfTSuj5v' + + const TEST_BANK_ID = 'ba_NEWc5RgwY8AAUC' + + mocker.mock({ + url: `/customers/${TEST_CUSTOMER_ID}/bank_account/${TEST_BANK_ID}`, + method: 'DELETE' + }) + + rzpInstance.customers.deleteBankAccount(TEST_CUSTOMER_ID, TEST_BANK_ID).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/customers/${TEST_CUSTOMER_ID}/bank_account/${TEST_BANK_ID}`, + 'delete bank account of customer url formed correctly' + ) + done() + }) + }) + + it('Fetch eligibility id', (done) => { + + const TEST_ELIGIBILITY_ID = 'elig_F1cxDoHWD4fkQt' + + mocker.mock({ + url: `/customers/eligibility/${TEST_ELIGIBILITY_ID}`, + method: 'GET' + }) + + rzpInstance.customers.fetchEligibility(TEST_ELIGIBILITY_ID).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/customers/eligibility/${TEST_ELIGIBILITY_ID}`, + 'fetch eligibility request url formed correctly' + ) + done() + }) + }) + + it('Request for an eligibility check', (done) => { + const param = { + "inquiry": "affordability", + "amount": 500000, + "currency": "INR", + "customer": { + "id": "cust_KhP5dO1dKmc0Rm", + "contact": "+918220276214", + "ip": "105.106.107.108", + "referrer": "https://merchansite.com/example/paybill", + "user_agent": "Mozilla/5.0" + } + } + + mocker.mock({ + url: `/customers/eligibility`, + method: 'POST' + }) + + rzpInstance.customers.requestEligibilityCheck(param).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/customers/eligibility`, + 'Request for an eligibility check url formed correctly' + ) + done() + }) + }) }) 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 2/5] 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 00000000..a5ffff89 --- /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 83d06394..5698d1c1 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 dbbd1d86..3738bd13 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 00000000..6c821aa9 --- /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 00000000..28b92cb7 --- /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 00000000..614384d0 --- /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() + }) + }) +}) From 5bc90ef3ed5d81f518a8404a75f9e0b1a9a72a7d Mon Sep 17 00:00:00 2001 From: Ankit Das <89454448+ankitdas13@users.noreply.github.com> Date: Tue, 7 May 2024 18:37:30 +0530 Subject: [PATCH 3/5] feat: Added document entity (#392) --- documents/documents.md | 73 ++++++++++++++++++++++++++++++++ lib/razorpay.d.ts | 6 +++ lib/razorpay.js | 1 + lib/resources/documents.js | 21 +++++++++ lib/types/documents.d.ts | 67 +++++++++++++++++++++++++++++ test/resources/documents.spec.js | 56 ++++++++++++++++++++++++ 6 files changed, 224 insertions(+) create mode 100644 documents/documents.md create mode 100644 lib/resources/documents.js create mode 100644 lib/types/documents.d.ts create mode 100644 test/resources/documents.spec.js diff --git a/documents/documents.md b/documents/documents.md new file mode 100644 index 00000000..c223db95 --- /dev/null +++ b/documents/documents.md @@ -0,0 +1,73 @@ +## Document + +### Create a Document + +```js +var formData = { + 'file': { + 'value': fs.createReadStream('/Users/your_name/Downloads/sample_uploaded.pdf'), + 'options': { + 'filename': 'sample_uploaded.pdf', + 'contentType': null + } + }, + 'purpose': 'dispute_evidence' +}; + +instance.documents.create(formData); +``` + +**Parameters:** + +| Name | Type | Description | +|-------|-----------|--------------------------------------------------| +| file* | string | The URL generated once the business proof document is uploaded. | +| purpose | string | Possible value is `dispute_evidence` | + +**Response:** +```json +{ + "id": "doc_EsyWjHrfzb59Re", + "entity": "document", + "purpose": "dispute_evidence", + "name": "doc_19_12_2020.jpg", + "mime_type": "image/png", + "size": 2863, + "created_at": 1590604200 +} +``` +------------------------------------------------------------------------------------------------------- + +### Fetch Document Information + +```js +var documentId = "doc_EsyWjHrfzb59Re"; + +instance.documents.fetch(documentId); +``` + +**Parameters:** + +| Name | Type | Description | +|-------|-----------|--------------------------------------------------| +| Id* | string | The unique identifier of the document. | + +**Response:** +```json +{ + "entity": "document", + "id": "doc_00000000000000", + "purpose": "dispute_evidence", + "created_at": 1701701378, + "mime_type": "application/pdf", + "display_name": "ppm_00000000000000", + "size": 404678, + "url": "" +} +``` +------------------------------------------------------------------------------------------------------- + +**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 5698d1c1..f0e5ed1b 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 documents from './types/documents' import disputes from './types/disputes' interface IRazorpayConfig { @@ -146,6 +147,11 @@ declare class Razorpay { */ iins: ReturnType /** + * Documents Entity + * @see https://razorpay.com/docs/api/documents + */ + documents: ReturnType + /** * Dispute Entity * @see https://razorpay.com/docs/api/disputes */ diff --git a/lib/razorpay.js b/lib/razorpay.js index 3738bd13..ccfbca09 100644 --- a/lib/razorpay.js +++ b/lib/razorpay.js @@ -58,6 +58,7 @@ class Razorpay { items : require('./resources/items')(this.api), cards : require('./resources/cards')(this.api), webhooks : require('./resources/webhooks')(this.api), + documents : require('./resources/documents')(this.api), disputes : require('./resources/disputes')(this.api) }) } diff --git a/lib/resources/documents.js b/lib/resources/documents.js new file mode 100644 index 00000000..efb93e19 --- /dev/null +++ b/lib/resources/documents.js @@ -0,0 +1,21 @@ +'use strict'; + +module.exports = function (api) { + + const BASE_URL = "/documents"; + + return { + create(params, callback) { + return api.postFormData({ + url: `${BASE_URL}`, + formData: params + }, callback); + }, + + fetch(documentId, callback) { + return api.get({ + url: `${BASE_URL}/${documentId}`, + }, callback); + }, + } +} \ No newline at end of file diff --git a/lib/types/documents.d.ts b/lib/types/documents.d.ts new file mode 100644 index 00000000..83d6a50a --- /dev/null +++ b/lib/types/documents.d.ts @@ -0,0 +1,67 @@ +import { INormalizeError } from "./api"; +import * as fs from "fs" + +export declare namespace Documents { + + interface FileCreateParams { + file: { + value: fs.ReadStream; + options?: { + filename?: string; + contentType?: string | null; + }; + }; + purpose: string; + } + + interface RazorpayDocument { + /** + * The unique identifier of the document. + */ + id: string + /** + * Indicates the type of entity. + */ + entity: string + /** + * The reason you are uploading this document. possible value is `dispute_evidence`. + */ + purpose: string + name: string + /** + * Indicates the nature and format in which the document is uploaded. + * possible value is `image/jpg`, `image/jpeg`, `image/png`, `application/pdf` + * + */ + mime_type: string + /** + * Indicates the size of the document in bytes. + */ + size: number + /** + * Unix timestamp at which the document was uploaded. + */ + created_at: number + } + +} + +declare function documents(api: any): { + /** + * Create a Document + * + * @param params - Check [doc](https://razorpay.com/docs/api/documents/create/) for required params + * + */ + create(params: Documents.FileCreateParams): Promise + create(params: Documents.FileCreateParams, callback: (err: INormalizeError | null, data: Documents.RazorpayDocument) => void): void; + /** + * Fetch document by id + * + * @param documentId - The unique identifier of the document + * + */ + fetch(documentId: string): Promise +} + +export default documents \ No newline at end of file diff --git a/test/resources/documents.spec.js b/test/resources/documents.spec.js new file mode 100644 index 00000000..a00804f1 --- /dev/null +++ b/test/resources/documents.spec.js @@ -0,0 +1,56 @@ +'use strict' + +const chai = require('chai') +const { assert } = chai +const rzpInstance = require('../razorpay') +const mocker = require('../mocker') +const equal = require('deep-equal') + +const BASE_URL = '/documents', + TEST_DOCUMENT_ID = 'disp_AHfqOvkldwsbqt'; + +describe('DOCUMENTS', () => { + it('Create an document', (done) => { + + var formData = { + 'file': { + 'value': '/Users/your_name/Downloads/sample_uploaded.pdf', + 'options': { + 'filename': 'README.md', + 'contentType': null + } + }, + 'purpose': 'dispute_evidence' + }; + + mocker.mock({ + url: `${BASE_URL}`, + method: 'POST' + }) + + rzpInstance.documents.create(formData).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/documents`, + 'Create document request url formed' + ) + done() + }) + }) + + it('Fetch document detail', (done) => { + mocker.mock({ + url: `/${BASE_URL}/${TEST_DOCUMENT_ID}`, + method: 'GET' + }) + + rzpInstance.documents.fetch(TEST_DOCUMENT_ID).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/documents/${TEST_DOCUMENT_ID}`, + 'fetch document detail request url formed' + ) + done() + }) + }) +}) From 651f17551c266e99bb56baaef575c819de653113 Mon Sep 17 00:00:00 2001 From: Ankit Das <89454448+ankitdas13@users.noreply.github.com> Date: Tue, 7 May 2024 18:43:03 +0530 Subject: [PATCH 4/5] Feat: Added rto intelligence (#393) --- documents/order.md | 187 +++++++++++++++++++++++++++++ lib/resources/orders.js | 13 ++ lib/types/orders.d.ts | 215 ++++++++++++++++++++++++++++++++++ test/resources/orders.spec.js | 45 +++++++ 4 files changed, 460 insertions(+) diff --git a/documents/order.md b/documents/order.md index 992b8cac..9f3d16dd 100644 --- a/documents/order.md +++ b/documents/order.md @@ -261,6 +261,193 @@ instance.orders.edit(orderId,{ ------------------------------------------------------------------------------------------------------- +### Create an order (Magic checkout) + +```js +instance.orders.create({ + "amount": 50000, + "currency": "INR", + "receipt": "receipt#22", + "notes": { + "key1": "value3", + "key2": "value2" + }, + "rto_review": true, + "line_items": [ + { + "type": "e-commerce", + "sku": "1g234", + "variant_id": "12r34", + "price": "3900", + "offer_price": "3800", + "tax_amount": 0, + "quantity": 1, + "name": "TEST", + "description": "TEST", + "weight": "1700", + "dimensions": { + "length": "1700", + "width": "1700", + "height": "1700" + }, + "image_url": "https://unsplash.com/s/photos/new-wallpaper", + "product_url": "https://unsplash.com/s/photos/new-wallpaper", + "notes": {} + } + ], + "line_items_total": "1200", + "shipping_fee": 100, + "cod_fee": 100, + "customer_details": { + "name": "Gaurav Kumar", + "contact": "+919000090000", + "email": "gaurav.kumar@example.com", + "shipping_address": { + "name": "Gaurav Kumar", + "line1": "84th floor, Millennium Tower", + "line2": "2nd main", + "zipcode": "560000", + "contact": "+919000090000", + "city": "Bangalore", + "state": "Karnataka", + "country": "IND", + "tag": "home", + "landmark": "XYZ Hospital" + }, + "billing_address": { + "name": "Gaurav Kumar", + "line1": "84th floor, Millennium Tower", + "line2": "2nd main", + "zipcode": "560000", + "contact": "+919000090000", + "city": "Bangalore", + "state": "Karnataka", + "country": "IND", + "tag": "home", + "landmark": "XYZ Hospital" + } + }, + "promotions": [{ + "reference_id": "reference", + "code": "code", + "type": "discount", + "value": 20, + "value_type": "flat", + "description": "description" + }], + "device_details": { + "ip": "127.0.0.1", + "user_agent": "abc" + } +}); +``` +**Parameters** + +| Name | Type | Description | +|----------|--------|-------------------------------------| +| amount* | integer | The transaction amount, expressed in the currency subunit, such as paise (in case of INR). | +| currency* | string | The currency in which the transaction should be made. default value is `INR`| +| receipt | string | Your receipt id for this order should be passed here. Maximum length of 40 characters. | +| notes | array | Key-value pair that can be used to store additional information about the entity.| +| rto_review | boolean | Identifier to mark the order eligible for RTO risk prediction. Possible values is `0` or `1` | +| line_items | array | All keys listed [here](https://razorpay.com/docs/payments/magic-checkout/rto-intelligence/#step-1-create-an-order) are supported | +| line_items_total | integer | Sum of offer_price for all line items added in the cart in paise. | +| shipping_fee | integer | Shipping fee charged on the line items in paisa. | +| cod_fee | integer | COD fee charged on the line items in paisa. | +| promotions | array | Used to pass all offer or discount related information including coupon code discount, method discount and so on. | +| customer | array | All keys listed [here](https://razorpay.com/docs/payments/magic-checkout/rto-intelligence/#step-1-create-an-order) are supported | +| device_details | array | All keys listed [here](https://razorpay.com/docs/payments/magic-checkout/rto-intelligence/#step-1-create-an-order) are supported | +| shipping_details | array | All keys listed [here](https://razorpay.com/docs/payments/magic-checkout/rto-intelligence/#step-1-create-an-order) are supported | + +**Response:** +```json +{ + "id": "order_MpyV7eOsTBn24z", + "entity": "order", + "amount": 50000, + "amount_paid": 0, + "amount_due": 50000, + "currency": "INR", + "receipt": "receipt#22", + "status": "created", + "attempts": 0, + "notes": { + "key1": "value3", + "key2": "value2" + }, + "created_at": 1697698714 +} +``` +------------------------------------------------------------------------------------------------------- + +### View RTO/Risk Reasons + +```js +var orderId = "order_DaaS6LOUAASb7Y"; + +instance.orders.viewRtoReview(orderId); +``` +**Parameters** + +| Name | Type | Description | +|----------|--------|-------------------------------------| +| orderId* | string | The id of the order to be retrieve payment info | +| notes* | array | A key-value pair | + +**Response:** +```json +{ + "risk_tier": "high", + "rto_reasons": [ + { + "reason": "short_shipping_address", + "description": "Short shipping address", + "bucket": "address" + }, + { + "reason": "address_pincode_state_mismatch", + "description": "Incorrect pincode state entered", + "bucket": "address" + } + ] +} +``` +------------------------------------------------------------------------------------------------------- + +```js +var orderId = "order_DaaS6LOUAASb7Y"; + +instance.orders.editFulfillment(orderId, { + "payment_method": "upi", + "shipping": { + "waybill": "123456789", + "status": "rto", + "provider": "Bluedart" + } +}); +``` +**Parameters** + +| Name | Type | Description | +|----------|--------|-------------------------------------| +| payment_method | string | The id of the order to be retrieve payment info | +| shipping | array | All keys listed [here](https://razorpay.com/docs/payments/magic-checkout/rto-intelligence/#step-3-update-the-fulfillment-details) are supported | + +**Response:** +```json +{ + "entity": "order.fulfillment", + "order_id": "EKwxwAgItXXXX", + "payment_method": "upi", + "shipping": { + "waybill": "123456789", + "status": "rto", + "provider": "Bluedart" + } +} +``` +------------------------------------------------------------------------------------------------------- + **PN: * indicates mandatory fields**

diff --git a/lib/resources/orders.js b/lib/resources/orders.js index b1bc20c9..49114642 100644 --- a/lib/resources/orders.js +++ b/lib/resources/orders.js @@ -93,6 +93,19 @@ module.exports = function (api) { return api.get({ url: `/orders/${orderId}/?expand[]=transfers&status` }, callback) + }, + + viewRtoReview(orderId, callback) { + return api.post({ + url: `/orders/${orderId}/rto_review` + }, callback) + }, + + editFulfillment(orderId, param = {}, callback) { + return api.post({ + url: `/orders/${orderId}/fulfillment`, + data: param + }) } } } diff --git a/lib/types/orders.d.ts b/lib/types/orders.d.ts index ed46f164..21b6c2ee 100644 --- a/lib/types/orders.d.ts +++ b/lib/types/orders.d.ts @@ -1,3 +1,4 @@ +import { Invoices } from './invoices' import { IMap, RazorpayPaginationOptions, INormalizeError } from './api' import { FundAccounts } from './fundAccount' import { Payments } from './payments' @@ -79,6 +80,33 @@ export declare namespace Orders { * level [auto-capture settings]( https://razorpay.com/docs/payments/payments/capture-settings) configured using the Dashboard. */ payment?: RazorpayCapturePayment; + /** + * Identifier to mark the order eligible for RTO risk prediction. + */ + rto_review?: boolean; + /** + * This will have the details about the specific items added to the cart. + */ + line_items?: LineItems[]; + /** + * Sum of `offer_price` for all line items added in the cart in paise. + */ + line_items_total?: number | string; + shipping_fee?: number; + cod_fee?: number; + /** + * Details of the customer. + */ + customer_details?: CustomerDetails; + /** + * Details of the customer's billing address. + */ + promotions?: Promotion[]; + /** + * Details of the customer. + */ + device_details?: DeviceDetails; + phonepe_switch_context? :string; } interface RazorpayOrderCreateRequestBody extends RazorpayOrderBaseRequestBody { } @@ -222,6 +250,174 @@ export declare namespace Orders { refund_speed: 'optimum' | 'normal'; } } + + interface LineItems { + /** + * Defines the category type. Possible values is `mutual_funds` or `e-commerce` + */ + type: string; + /** + * Unique product id defined by you. + */ + sku: string; + /** + * Unique variant_id defined by you. + */ + variant_id: string; + /** + * Price of the product in paise. + */ + price: string; + /** + * Price charged to the customer in paise. + */ + offer_price: string; + /** + * The tax levied on the product. + */ + tax_amount: number; + /** + * Number of units added in the cart. + */ + quantity: number; + /** + * Name of the product. + */ + name: string; + /** + * Description of the product. + */ + description: string; + /** + * Weight of the product in grams. + */ + weight: string; + /** + * The dimensions of the product. + */ + dimensions: Dimensions; + /** + * URL of the product image. + */ + image_url: string; + /** + * URL of the product's listing page. + */ + product_url: string; + notes?: IMap; + } + + interface Dimensions { + length: string; + width: string; + height: string; + } + + interface Reason { + /** + * Id of the Offer. + */ + reason: string; + /** + * unique identifier for the RTO reason + */ + description: string; + /** + * Categorises the reason into a specific group. + */ + bucket: string; + } + + interface CustomerDetails { + /** + * Customer's name. + */ + name: string; + /** + * The customer's phone number. + */ + contact: string; + /** + * The customer's email address. + */ + email: string; + /** + * Details of the customer's shipping address. + */ + shipping_address: Partial; + /** + * Details of the customer's billing address. + */ + billing_address: Partial; + } + + interface Promotion { + /** + * Id of the Offer. + */ + reference_id: string; + /** + * Coupon code used to avail discount. + */ + code: string; + /** + * Type of Offer. Possible value is `coupon` or `offer` + */ + type: string; + /** + * The offer value that needs to be applied. + */ + value: number; + /** + * The type of value. Possible value is `fixed_amount` or `percentage` + */ + value_type: string; + /** + * Description of the promotion applied. + */ + description?: string; + } + + interface DeviceDetails { + /** + * Public IP Address of the device used to place the order. + */ + ip: string; + /** + * The user-agent header of the customer's browser. + */ + user_agent: string; + } + + interface RazorpayRtoReview { + /** + * Determines how risky the order is. Possible is `high`, `medium` or `low` + */ + risk_tier: string; + rto_reasons: Reason[]; + } + + interface RazorpayShipping { + waybill: string; + status?: string; + provider?: string; + } + + interface RazorpayFulFillmentBaseRequestBody { + /** + * Payment Method opted by the customer to complete the payment. + */ + payment_method?: string; + /** + * Contains the shipping data. + */ + shipping?: RazorpayShipping; + } + + interface RazorpayFulFillment extends RazorpayFulFillmentBaseRequestBody { + entity: string; + order_id: string; + } } declare function orders(api: any): { @@ -290,6 +486,25 @@ declare function orders(api: any): { */ fetchTransferOrder(orderId: string): Promise fetchTransferOrder(orderId: string, callback: (err: INormalizeError | null, data: Orders.RazorpayOrder) => void): void + + /** + * View RTO/Risk Reasons + * + * @param orderId - The unique identifier of the order + * + */ + viewRtoReview(orderId: string): Promise + viewRtoReview(orderId: string, callback: (err: INormalizeError | null, data: Orders.RazorpayRtoReview) => void): void + + /** + * Update the Fulfillment Details + * + * @param orderId - The unique identifier of the order + * @param params - Check [doc](https://razorpay.com/docs/payments/magic-checkout/rto-intelligence/#step-3-update-the-fulfillment-details) for required params + * + */ + editFulfillment(orderId: string, param: Orders.RazorpayFulFillmentBaseRequestBody): Promise + editFulfillment(orderId: string, param: Orders.RazorpayFulFillmentBaseRequestBody, callback: (err: INormalizeError | null, data: any) => void): void } export default orders \ No newline at end of file diff --git a/test/resources/orders.spec.js b/test/resources/orders.spec.js index 1f242de9..fb85fa12 100644 --- a/test/resources/orders.spec.js +++ b/test/resources/orders.spec.js @@ -142,6 +142,33 @@ describe('ORDERS', () => { done() }) }) + + it('Update the fulfillment details', (done) => { + let orderId = 'order_sometestId' + + let param = { + "payment_method": "upi", + "shipping": { + "waybill": "123456789", + "status": "rto", + "provider": "Bluedart" + } + } + + mocker.mock({ + url: `/orders/${orderId}/fulfillment`, + method: 'POST' + }) + + rzpInstance.orders.editFulfillment(orderId, param).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/orders/${orderId}/fulfillment`, + 'Request url formed correctly' + ) + done() + }) + }) }) describe('Fetch order\'s payments', () => { @@ -196,5 +223,23 @@ describe('ORDERS', () => { done() }) }) + + it('view RTO/Risk reasons', (done) => { + let orderId = 'order_sometestId' + + mocker.mock({ + url: `/orders/${orderId}/rto_review`, + method: 'POST' + }) + + rzpInstance.orders.viewRtoReview(orderId).then((response) => { + assert.equal( + response.__JUST_FOR_TESTS__.url, + `/v1/orders/${orderId}/rto_review`, + 'Request url formed correctly' + ) + done() + }) + }) }) }) From c225f94990cb28b031a176bdb122d67532a88236 Mon Sep 17 00:00:00 2001 From: Ankit Das <89454448+ankitdas13@users.noreply.github.com> Date: Tue, 7 May 2024 18:44:11 +0530 Subject: [PATCH 5/5] Added expanded type (#394) --- documents/payment.md | 192 +++++++++++++++++++++++++++++++++++++++- lib/types/payments.d.ts | 113 ++++++++++++++--------- 2 files changed, 260 insertions(+), 45 deletions(-) diff --git a/documents/payment.md b/documents/payment.md index e963e299..25badb1f 100644 --- a/documents/payment.md +++ b/documents/payment.md @@ -877,7 +877,197 @@ instance.iins.fetch(tokenIin); } ``` ------------------------------------------------------------------------------------------------------- + +### Fetch a Payment (With Expanded Card Details) + +```js +var paymentId = "pay_MLzFlOC98cJmHQ"; + +instance.payments.fetch(paymentId, {"expand[]": "card"}); +``` + +| Name | Type | Description | +|-------------|---------|--------------------------------------| +| paymentId* | integer | Unique identifier of the payment | +| expand[] | string | Use to expand the card details when the payment method is `card`. | + +**Response:** +```json +{ + "id": "pay_H9oR0gLCaVlV6m", + "entity": "payment", + "amount": 100, + "currency": "INR", + "status": "failed", + "order_id": "order_H9o58N6qmLYQKC", + "invoice_id": null, + "terminal_id": "term_G5kJnYM9GhhLYT", + "method": "card", + "amount_refunded": 0, + "refund_status": null, + "captured": false, + "description": null, + "card_id": "card_H9oR0ocen1cmZq", + "card": { + "id": "card_H9oR0ocen1cmZq", + "entity": "card", + "name": "Gaurav", + "last4": "1213", + "network": "RuPay", + "type": "credit", + "issuer": "UTIB", + "international": false, + "emi": false, + "sub_type": "business" + }, + "bank": null, + "wallet": null, + "vpa": null, + "email": "gaurav.kumar@example.com", + "contact": "+919000090000", + "notes": { + "email": "gaurav.kumar@example.com", + "phone": "09000090000" + }, + "fee": null, + "tax": null, + "error_code": "BAD_REQUEST_ERROR", + "error_description": "Card issuer is invalid", + "error_source": "customer", + "error_step": "payment_authentication", + "error_reason": "incorrect_card_details", + "acquirer_data": { + "auth_code": null, + "authentication_reference_number": "100222021120200000000742753928" + }, + "created_at": 1620807547 +} +``` +------------------------------------------------------------------------------------------------------- + +### Fetch a Payment (With Expanded Offers Details) + +```js +var paymentId = "pay_MLzFlOC98cJmHQ"; + +instance.payments.fetch(paymentId, {"expand[]": "emi"}); +``` + +**Parameters:** + +| Name | Type | Description | +|-------------|---------|--------------------------------------| +| paymentId* | integer | Unique identifier of the payment | +| expand[] | string | Use to expand the emi details when the payment method is emi. | + +**Response:**
+ +```json +{ + "id": "pay_DG4ZdRK8ZnXC3k", + "entity": "payment", + "amount": 200000, + "currency": "INR", + "status": "authorized", + "order_id": null, + "invoice_id": null, + "international": false, + "method": "emi", + "amount_refunded": 0, + "refund_status": null, + "captured": false, + "description": null, + "card_id": "card_DG4ZdUO3xABb20", + "bank": "ICIC", + "wallet": null, + "vpa": null, + "email": "gaurav@example.com", + "contact": "+919972000005", + "notes": [], + "fee": null, + "tax": null, + "error_code": null, + "error_description": null, + "error_source": null, + "error_step": null, + "error_reason": null, + "emi": { + "issuer": "ICIC", + "rate": 1300, + "duration": 6 + }, + "acquirer_data": { + "auth_code": "828553" + }, + "created_at": 1568026077 +} +``` + +------------------------------------------------------------------------------------------------------- +### Fetch a Payment (With Expanded UPI Details) + +```js +var paymentId = "pay_MLzFlOC98cJmHQ"; + +instance.payments.fetch(paymentId, {"expand[]": "upi"}); +``` + +**Parameters:** + +| Name | Type | Description | +|-------------|---------|--------------------------------------| +| paymentId* | integer | Unique identifier of the payment | +| expand[] | string | Use to expand the UPI details when the payment method is upi. | + +**Response:**
+ +```json + +{ + "id": "pay_DG4ZdRK8ZnXC3k", + "entity": "payment", + "amount": 100, + "currency": "INR", + "status": "captured", + "order_id": "order_GjCr5oKh4AVC51", + "invoice_id": null, + "international": false, + "method": "upi", + "amount_refunded": 0, + "refund_status": null, + "captured": true, + "description": "Payment for Adidas shoes", + "card_id": null, + "bank": null, + "wallet": null, + "vpa": "gaurav.kumar@upi", + "email": "gaurav.kumar@example.com", + "contact": "9000090000", + "customer_id": "cust_K6fNE0WJZWGqtN", + "token_id": "token_KOdY$DBYQOv08n", + "notes": [], + "fee": 1, + "tax": 0, + "error_code": null, + "error_description": null, + "error_source": null, + "error_step": null, + "error_reason": null, + "acquirer_data": { + "rrn": "303107535132" + }, + "created_at": 1605871409, + "upi": { + "payer_account_type": "credit_card", + "vpa": "gaurav.kumar@upi", + "flow": "in_app" + } +} +``` + +------------------------------------------------------------------------------------------------------- + **PN: * indicates mandatory fields**

-**For reference click [here](https://razorpay.com/docs/api/payments/)** \ No newline at end of file +**For reference click [here](https://razorpay.com/docs/api/payments/)** diff --git a/lib/types/payments.d.ts b/lib/types/payments.d.ts index 608893c0..f27e6a0a 100644 --- a/lib/types/payments.d.ts +++ b/lib/types/payments.d.ts @@ -42,6 +42,16 @@ export declare namespace Payments { * The unique identifier of the customer you want to charge. */ customer_id: string; + /** + * The unique identifier of the subscription. + */ + subscription_id?: string; + /** + * The authentication channel for the payment. Possible value is `browser` or `app` + */ + authentication?: { + authentication_channel: string; + }; } interface RazorpayPaymentCreateRequestBody extends RazorpayPaymentBaseRequestBody { } @@ -230,6 +240,10 @@ export declare namespace Payments { * The last 4 digits of the tokenised card. */ last4?: string; + /** + * The name of the aggregator that provided the token. Possible values are `Visa`, `Mastercard`, `Amex` or `HDFC for Diners` + */ + provider_type?: string; } interface RazorpayCardCreateRequest extends RazorpayCardBaseRequestBody { } @@ -422,7 +436,7 @@ export declare namespace Payments { /** * Details of the expiry of the UPI link */ - upi: { + upi?: { /** * Specify the type of the UPI payment flow. * Possible values: `collect` (default), `intent` @@ -435,6 +449,10 @@ export declare namespace Payments { */ expiry_time?: number; } + /** + * Token of the saved VPA. + */ + token?: string; } interface RazorpayPaymentQuery extends RazorpayPaginationOptions { @@ -451,54 +469,51 @@ export declare namespace Payments { */ authentication_channel: 'browser' | 'app' } + } + + interface BrowserInfo { /** - * Information regarding the customer's browser. - * This parameter need not be passed when `authentication_channel=app`. + * Indicates whether the customer's browser supports Java. + * Obtained from the `navigator` HTML DOM object. */ - browser?: { - /** - * Indicates whether the customer's browser supports Java. - * Obtained from the `navigator` HTML DOM object. - */ - java_enabled: boolean; - /** - * ndicates whether the customer's browser is able to execute JavaScript. - * Obtained from the `navigator` HTML DOM object. - */ - javascript_enabled: boolean; - /** - * Time difference between UTC time and the cardholder browser local time. - * Obtained from the `getTimezoneOffset()` method applied to Date object. - */ - timezone_offset: number; - /** - * Total width of the payer's screen in pixels. - * Obtained from the `screen.width` HTML DOM property. - */ - screen_width: number; - /** - * Obtained from the `navigator` HTML DOM object. - */ - screen_height: number; - /** - * Obtained from payer's browser using - * the `screen.colorDepth` HTML DOM property. - */ - color_depth: string; - /** - * Obtained from payer's browser using the navigator. - * language HTML DOM property. Maximum limit of 8 characters. - */ - language: string; - } + java_enabled: boolean; + /** + * Indicates whether the customer's browser is able to execute JavaScript. + * Obtained from the `navigator` HTML DOM object. + */ + javascript_enabled: boolean; + /** + * Time difference between UTC time and the cardholder browser local time. + * Obtained from the `getTimezoneOffset()` method applied to Date object. + */ + timezone_offset: number | string; + /** + * Total width of the payer's screen in pixels. + * Obtained from the `screen.width` HTML DOM property. + */ + screen_width: number | string; + /** + * Obtained from the `navigator` HTML DOM object. + */ + screen_height: number | string; + /** + * Obtained from payer's browser using + * the `screen.colorDepth` HTML DOM property. + */ + color_depth: number | string; + /** + * Obtained from payer's browser using the navigator. + * language HTML DOM property. Maximum limit of 8 characters. + */ + language: string; } interface RazorpayPaymentS2SCreateRequestBody extends RazorpayPaymentBaseRequestBody { - save:boolean | number; + save: boolean | number; /** * Pass the unique token id created when the customer made the first payment. */ - token?:string; + token?: string; /** * Pass the sub-merchant's unique identifier. */ @@ -517,6 +532,12 @@ export declare namespace Payments { * Default value will be passed by Razorpay if not provided by merchant. */ user_agent: string | null; + provider_name?: string; + /** + * Information regarding the customer's browser. + * This parameter need not be passed when `authentication_channel=app`. + */ + browser?: Partial } interface RazorpayPaymentS2SJson { @@ -555,6 +576,10 @@ export declare namespace Payments { entity: string; } + interface ExpandDetails { + 'expand[]': 'card' | 'emi' | 'offers' | 'upi' + } + } declare function payments(api: any): { @@ -581,8 +606,8 @@ declare function payments(api: any): { * @param params - Check [doc](https://razorpay.com/docs/api/payments/#fetch-a-payment) for required params * */ - fetch(paymentId: string, params?: { 'expand[]': 'card' | 'emi' | 'offers' }): Promise - fetch(paymentId: string, params: { 'expand[]': 'card' | 'emi' | 'offers' }, callback: (err: INormalizeError | null, data: Payments.RazorpayPayment) => void): void + fetch(paymentId: string, params?: Payments.ExpandDetails): Promise + fetch(paymentId: string, params: Payments.ExpandDetails, callback: (err: INormalizeError | null, data: Payments.RazorpayPayment) => void): void /** * Capture payment * @@ -820,4 +845,4 @@ declare function payments(api: any): { fetchPaymentDowntimeById(downtimeId: string, callback: (err: INormalizeError | null, data: Payments.RazorpayPaymentDowntime) => void): void } -export default payments \ No newline at end of file +export default payments