Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 80 additions & 5 deletions src/api/issuing/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -22,7 +97,7 @@ export default class Controls {
*
* @memberof Controls
* @param {Object} body Card control params.
* @return {Promise<Object>} A promise to the card response.
* @return {Promise<CardControlResponse>} A promise to the created control.
*/
async createCardControl(body) {
try {
Expand All @@ -44,7 +119,7 @@ export default class Controls {
*
* @memberof Controls
* @param {Object} params Card control params.
* @return {Promise<Object>} A promise to the card response.
* @return {Promise<CardControlsListResponse>} A promise to the list of controls applied to the target.
*/
async getCardControls(params) {
try {
Expand All @@ -62,7 +137,7 @@ export default class Controls {
*
* @memberof Controls
* @param {string} id Card control id.
* @return {Promise<Object>} A promise to the card response.
* @return {Promise<CardControlResponse>} A promise to the control details.
*/
async getCardControlDetails(id) {
try {
Expand All @@ -84,7 +159,7 @@ export default class Controls {
* @memberof Controls
* @param {string} id Card control id.
* @param {Object} body Card control params.
* @return {Promise<Object>} A promise to the card response.
* @return {Promise<CardControlResponse>} A promise to the updated control.
*/
async updateCardControl(id, body) {
try {
Expand All @@ -106,7 +181,7 @@ export default class Controls {
*
* @memberof Controls
* @param {string} id Card control id.
* @return {Promise<Object>} A promise to the card response.
* @return {Promise<RemoveCardControlResponse>} A promise to the removed control id.
*/
async deleteCardControl(id) {
try {
Expand Down
82 changes: 82 additions & 0 deletions test/issuing/controls/controls-unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Loading