From 78b662001d99a4a32dfc7fdaf6db0770c4410783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:19:12 +0200 Subject: [PATCH] docs(issuing): document card control response shapes and fix return docs --- src/api/issuing/controls.js | 85 ++++++++++++++++++++++++-- test/issuing/controls/controls-unit.js | 82 +++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 5 deletions(-) diff --git a/src/api/issuing/controls.js b/src/api/issuing/controls.js index 3d5c246a..9679d0ed 100644 --- a/src/api/issuing/controls.js +++ b/src/api/issuing/controls.js @@ -6,6 +6,81 @@ import { buildQueryParams } from '../../services/utils.js'; const CONTROLS_PATH = 'controls'; const ISSUING_PATH = 'issuing'; +/** + * The period of time over which a velocity limit's amount can be spent. + * + * @typedef {Object} VelocityWindow + * @property {('daily'|'weekly'|'monthly'|'all_time')} type The velocity window's unit of time. + */ + +/** + * The velocity limit returned by the get, list and update control operations, which determines how much a + * target card can spend over a given timeframe. + * + * @typedef {Object} VelocityLimitWithRemainingAmount + * @property {number} amount_remaining The remaining amount that can be spent, in minor units. Minimum 0. + * @property {number} amount_limit The amount that can be spent, in minor units. Minimum 0. + * @property {VelocityWindow} velocity_window The period of time over which the specified amount_limit can be spent. + * @property {string[]} [mcc_list] The list of merchant category codes (MCCs) that the velocity limit applies to, + * as four-digit ISO 18245 codes. + * @property {string[]} [mid_list] The list of merchant identification (MID) codes to allow or block transactions + * from. You can provide either mcc_list or mid_list, but not both. + */ + +/** + * The merchant category code (MCC) rule, which determines the types of businesses transactions can be processed from. + * + * @typedef {Object} MccLimit + * @property {('allow'|'block')} type Sets whether to allow or block the list of MCCs supplied. + * @property {string[]} mcc_list The list of MCCs to allow or block transactions from, as 4-digit ISO 18245 codes. + */ + +/** + * The merchant identification (MID) code rule, which determines the merchants from whom transactions can be processed. + * + * @typedef {Object} MidLimit + * @property {('allow'|'block')} type Sets whether to allow or block the list of MIDs supplied. + * @property {string[]} mid_list The list of merchant identification (MID) codes to allow or block transactions + * from. Each code is 1 to 15 characters. + */ + +/** + * A card control. The shape depends on control_type: a velocity_limit control carries velocity_limit, an + * mcc_limit control carries mcc_limit and a mid_limit control carries mid_limit. + * + * @typedef {Object} CardControlResponse + * @property {string} id The control's unique identifier (ctr_ followed by 26 characters). + * @property {string} [description] The description of the control. Maximum 256 characters. + * @property {('velocity_limit'|'mcc_limit'|'mid_limit')} control_type The control's type. A velocity_limit + * determines how much can be spent over a given period of time. An mcc_limit determines the types of + * businesses from which transactions can be processed. A mid_limit specifies the merchants from whom + * transactions can be processed. + * @property {string} target_id The ID of the card (crd_) or control profile (cpr_) the control is applied to. + * @property {boolean} is_editable Indicates whether you can change this control. false: an immutable control + * applied by Checkout.com. true: you applied this control and can change it. + * @property {string} created_date The UTC date and time the control was created, in ISO 8601 format. + * @property {string} last_modified_date The UTC date and time the control was last modified, in ISO 8601 format. + * @property {VelocityLimitWithRemainingAmount} [velocity_limit] Present when control_type is velocity_limit. The + * create operation returns it without amount_remaining. + * @property {MccLimit} [mcc_limit] Present when control_type is mcc_limit. + * @property {MidLimit} [mid_limit] Present when control_type is mid_limit. + * @property {Object} [_links] The links related to the control. Returned by the create operation. + */ + +/** + * The list of controls applied to a target. + * + * @typedef {Object} CardControlsListResponse + * @property {CardControlResponse[]} controls The list of controls applied to the specified target. + */ + +/** + * The details of a removed control. + * + * @typedef {Object} RemoveCardControlResponse + * @property {string} id The removed control's unique identifier. + */ + /** * Controls class for managing card control operations * @@ -22,7 +97,7 @@ export default class Controls { * * @memberof Controls * @param {Object} body Card control params. - * @return {Promise} A promise to the card response. + * @return {Promise} A promise to the created control. */ async createCardControl(body) { try { @@ -44,7 +119,7 @@ export default class Controls { * * @memberof Controls * @param {Object} params Card control params. - * @return {Promise} A promise to the card response. + * @return {Promise} A promise to the list of controls applied to the target. */ async getCardControls(params) { try { @@ -62,7 +137,7 @@ export default class Controls { * * @memberof Controls * @param {string} id Card control id. - * @return {Promise} A promise to the card response. + * @return {Promise} A promise to the control details. */ async getCardControlDetails(id) { try { @@ -84,7 +159,7 @@ export default class Controls { * @memberof Controls * @param {string} id Card control id. * @param {Object} body Card control params. - * @return {Promise} A promise to the card response. + * @return {Promise} A promise to the updated control. */ async updateCardControl(id, body) { try { @@ -106,7 +181,7 @@ export default class Controls { * * @memberof Controls * @param {string} id Card control id. - * @return {Promise} A promise to the card response. + * @return {Promise} A promise to the removed control id. */ async deleteCardControl(id) { try { diff --git a/test/issuing/controls/controls-unit.js b/test/issuing/controls/controls-unit.js index ba4caad8..8c2d730d 100644 --- a/test/issuing/controls/controls-unit.js +++ b/test/issuing/controls/controls-unit.js @@ -315,6 +315,88 @@ describe('Unit::Issuing::Controls', () => { expect(controlsResponse.control_type).to.equal("velocity_limit") }); + const updateControlResponses = { + velocity_limit: { + id: "ctr_gp7vkmxayztufjz6top5bjcdra", + description: "Maximum spend of 500€ per week for restaurants", + control_type: "velocity_limit", + target_id: "crd_fa6psq42dcdd6fdn5gifcq1491", + is_editable: true, + created_date: "2021-09-09T19:41:39Z", + last_modified_date: "2021-09-09T19:41:39Z", + velocity_limit: { + amount_remaining: 45000, + amount_limit: 50000, + velocity_window: { type: "weekly" }, + mcc_list: ["4121", "4582"] + } + }, + mcc_limit: { + id: "ctr_gp7vkmxayztufjz6top5bjcdra", + description: "Allow the card to be used only in restaurants and supermarkets", + control_type: "mcc_limit", + target_id: "crd_fa6psq42dcdd6fdn5gifcq1491", + is_editable: true, + created_date: "2021-09-09T19:41:39Z", + last_modified_date: "2021-09-09T19:41:39Z", + mcc_limit: { type: "allow", mcc_list: ["5932", "5411"] } + }, + mid_limit: { + id: "ctr_gp7vkmxayztufjz6top5bjcdra", + description: "Allow the card to be used only in AZ Pizza", + control_type: "mid_limit", + target_id: "crd_fa6psq42dcdd6fdn5gifcq1491", + is_editable: true, + created_date: "2021-09-09T19:41:39Z", + last_modified_date: "2021-09-09T19:41:39Z", + mid_limit: { type: "allow", mid_list: ["593278", "541114"] } + } + }; + + Object.entries(updateControlResponses).forEach(([controlType, body]) => { + it(`should return every field of the ${controlType} update control response`, async () => { + nock('https://123456789.access.sandbox.checkout.com') + .post('/connect/token') + .reply(200, { + access_token: 'test_access_token', + expires_in: 3600, + token_type: 'Bearer', + scope: 'issuing:controls-write issuing:controls-read' + }); + + nock('https://123456789.api.sandbox.checkout.com') + .put('/issuing/controls/ctr_gp7vkmxayztufjz6top5bjcdra') + .reply(200, body); + + const cko = new Checkout('test_client_secret', { + client: 'ack_testclie123456', + scope: ['issuing:controls-write', 'issuing:controls-read'], + environment: 'sandbox', + subdomain: '123456789' + }); + + const response = await cko.issuing.updateCardControl("ctr_gp7vkmxayztufjz6top5bjcdra", { + description: body.description + }); + + expect(response).to.deep.equal(body); + expect(response.id).to.equal(body.id); + expect(response.description).to.equal(body.description); + expect(response.control_type).to.equal(controlType); + expect(response.target_id).to.equal(body.target_id); + expect(response.is_editable).to.equal(true); + expect(response.created_date).to.equal(body.created_date); + expect(response.last_modified_date).to.equal(body.last_modified_date); + expect(response[controlType]).to.deep.equal(body[controlType]); + if (controlType === 'velocity_limit') { + expect(response.velocity_limit.amount_remaining).to.equal(45000); + expect(response.velocity_limit.amount_limit).to.equal(50000); + expect(response.velocity_limit.velocity_window.type).to.equal("weekly"); + expect(response.velocity_limit.mcc_list).to.deep.equal(["4121", "4582"]); + } + }); + }); + it('should throw when updating a card`s controls', async () => { nock('https://123456789.access.sandbox.checkout.com') .post('/connect/token')