diff --git a/CHANGELOG.md b/CHANGELOG.md index c47f9391..aa860908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ Given a version number MAJOR.MINOR.PATCH, increment: ## [Unreleased] +### Added +- rules attribute to Transfer resource +- Transfer.Rule sub-resource +- rules attribute to BrcodePayment resource +- BrcodePayment.Rule sub-resource ## [2.13.2] - 2022-12-15 ### Fixed diff --git a/README.md b/README.md index c13077b6..aa4b764e 100644 --- a/README.md +++ b/README.md @@ -407,7 +407,7 @@ const starkbank = require('starkbank'); ## Create transfers -You can also create transfers in the SDK (TED/Pix). +You can also create transfers in the SDK (TED/Pix) and configure transfer behavior according to its rules. ```javascript const starkbank = require('starkbank'); @@ -433,7 +433,13 @@ const starkbank = require('starkbank'); taxId: '372.864.795-04', name: 'Jon Snow', scheduled: '2021-09-08', - tags: [] + tags: [], + rules: [ + new starkbank.transfer.Rule({ + key: "resendingLimit", // Set maximum number of retries if Transfer fails due to systemic issues at the receiver bank + value: 5 // Our resending limit is 10 by default + }) + ] } ]) @@ -1097,7 +1103,13 @@ const starkbank = require('starkbank'); description: "Tony Stark's Suit", amount: 7654321, scheduled: '2020-02-29', - tags: ['Stark', 'Suit'] + tags: ['Stark', 'Suit'], + rules: [ + new starkbank.brcodePayment.Rule({ + key: "resendingLimit", // Set maximum number of retries if Payment fails due to systemic issues at the receiver bank + value: 5 // Our resending limit is 10 by default + }) + ] }, ]); @@ -1107,6 +1119,7 @@ const starkbank = require('starkbank'); })(); ``` +**Note**: You can also configure payment behavior according to its rules **Note**: Instead of using dictionary objects, you can also pass each invoice element in the native BrcodePayment object format ## Get a BR Code payment diff --git a/sdk/brcodePayment/brcodePayment.js b/sdk/brcodePayment/brcodePayment.js index 2bdffecd..e33691e3 100644 --- a/sdk/brcodePayment/brcodePayment.js +++ b/sdk/brcodePayment/brcodePayment.js @@ -1,6 +1,9 @@ const rest = require('../utils/rest.js'); const check = require('../utils/check.js'); -const Resource = require('../utils/resource.js').Resource; +const parseObjects = require('../utils/parse.js').parseObjects; +const Resource = require('../utils/resource.js').Resource +const { Rule } = require('./rule/rule.js'); +const ruleResource = require('./rule/rule.js').subResource; class BrcodePayment extends Resource { /** @@ -20,6 +23,7 @@ class BrcodePayment extends Resource { * @param amount [int, default null]: amount automatically calculated from line or barCode. ex: 23456 (= R$ 234.56) * @param scheduled [string, default now]: payment scheduled date or datetime. ex: '2020-11-25T17:59:26.249976+00:00' * @param tags [list of strings, default null]: list of strings for tagging + * @param rule [list of BrcodePayment.Rules, default []]: list of BrcodePayment.Rule objects for modifying transfer behavior. ex: [BrcodePayment.Rule(key="resendingLimit", value=5)] * * ## Attributes (return-only): * @param id [string, default null]: unique id returned when payment is created. ex: '5656565656565656' @@ -32,7 +36,7 @@ class BrcodePayment extends Resource { * */ constructor({ - brcode, taxId, description, amount = null, scheduled = null, tags = null, + brcode, taxId, description, amount = null, scheduled = null, tags = null, rules = null, id = null, name = null, status = null, type = null, fee = null, updated = null, created = null }) { super(id); @@ -43,6 +47,7 @@ class BrcodePayment extends Resource { this.amount = amount; this.scheduled = check.datetime(scheduled); this.tags = tags; + this.rules = parseObjects(rules, ruleResource, Rule); this.id = id; this.status = status; this.type = type; @@ -200,7 +205,7 @@ exports.update = function (id, {status, user} = {}) { * */ let payload = { - status: status, + status: status, }; return rest.patchId(resource, id, payload, user); }; diff --git a/sdk/brcodePayment/index.js b/sdk/brcodePayment/index.js index 52d72e11..1dab9efe 100644 --- a/sdk/brcodePayment/index.js +++ b/sdk/brcodePayment/index.js @@ -1,6 +1,8 @@ const brcodePayment = require('./brcodePayment.js'); exports.log = require('./log'); +exports.Rule = require('./rule/rule.js').Rule; +exports.rule = require('./rule'); exports.create = brcodePayment.create; exports.get = brcodePayment.get; exports.query = brcodePayment.query; diff --git a/sdk/brcodePayment/rule/index.js b/sdk/brcodePayment/rule/index.js new file mode 100644 index 00000000..71b49e7f --- /dev/null +++ b/sdk/brcodePayment/rule/index.js @@ -0,0 +1,3 @@ +const rule = require('./rule.js'); + +exports.Rule = rule.Rule; diff --git a/sdk/brcodePayment/rule/rule.js b/sdk/brcodePayment/rule/rule.js new file mode 100644 index 00000000..591ac4e3 --- /dev/null +++ b/sdk/brcodePayment/rule/rule.js @@ -0,0 +1,24 @@ +const SubResource = require('../../utils/subResource').SubResource + + +class Rule extends SubResource { + /** + * + * BrcodePayment.Rule object + * + * @description The BrcodePayment.Rule object modifies the behavior of BrcodePayment objects when passed as an argument upon their creation. + * + * Parameters (required): + * @param key [string]: Rule to be customized, describes what BrcodePayment behavior will be altered. ex: "resendingLimit" + * @param value [integer]: Value of the rule. ex: 5 + * + */ + constructor({key, value}) { + super(); + this.key = key; + this.value = value; + } +} + +exports.Rule = Rule; +exports.subResource = {'class': exports.Rule, 'name': 'Rule'}; diff --git a/sdk/transfer/index.js b/sdk/transfer/index.js index f8398571..580bebab 100644 --- a/sdk/transfer/index.js +++ b/sdk/transfer/index.js @@ -1,6 +1,8 @@ const transfer = require('./transfer.js'); exports.log = require('./log'); +exports.Rule = require('./rule/rule.js').Rule; +exports.rule = require('./rule'); exports.create = transfer.create; exports.delete = transfer.delete; exports.query = transfer.query; diff --git a/sdk/transfer/rule/index.js b/sdk/transfer/rule/index.js new file mode 100644 index 00000000..71b49e7f --- /dev/null +++ b/sdk/transfer/rule/index.js @@ -0,0 +1,3 @@ +const rule = require('./rule.js'); + +exports.Rule = rule.Rule; diff --git a/sdk/transfer/rule/rule.js b/sdk/transfer/rule/rule.js new file mode 100644 index 00000000..a7a20615 --- /dev/null +++ b/sdk/transfer/rule/rule.js @@ -0,0 +1,24 @@ +const SubResource = require('../../utils/subResource').SubResource + + +class Rule extends SubResource { + /** + * + * Transfer.Rule object + * + * @description The Transfer.Rule object modifies the behavior of Transfer objects when passed as an argument upon their creation. + * + * Parameters (required): + * @param key [string]: Rule to be customized, describes what Transfer behavior will be altered. ex: "resendingLimit" + * @param value [integer]: Value of the rule. ex: 5 + * + */ + constructor({key, value}) { + super(); + this.key = key; + this.value = value; + } +} + +exports.Rule = Rule; +exports.subResource = {'class': exports.Rule, 'name': 'Rule'}; diff --git a/sdk/transfer/transfer.js b/sdk/transfer/transfer.js index fd58c072..a26d3e45 100644 --- a/sdk/transfer/transfer.js +++ b/sdk/transfer/transfer.js @@ -1,6 +1,9 @@ const rest = require('../utils/rest.js'); const check = require('../utils/check.js'); +const parseObjects = require('../utils/parse.js').parseObjects; const Resource = require('../utils/resource.js').Resource +const { Rule } = require('./rule/rule.js'); +const ruleResource = require('./rule/rule.js').subResource; class Transfer extends Resource { @@ -19,13 +22,14 @@ class Transfer extends Resource { * @param bankCode [string]: code of the receiver bank institution in Brazil. If an ISPB (8 digits) is informed, a PIX transfer will be created, else a TED will be issued. ex: '20018183' or '341' * @param branchCode [string]: receiver bank account branch. Use '-' in case there is a verifier digit. ex: '1357-9' * @param accountNumber [string]: Receiver Bank Account number. Use '-' before the verifier digit. ex: '876543-2' - * @param accountType [string, default 'checking']: Receiver bank account type. This parameter only has effect on Pix Transfers. ex: 'checking', 'savings', 'salary' or 'payment' - * @param externalId [string, default null]: url safe string that must be unique among all your transfers. Duplicated external_ids will cause failures. By default, this parameter will block any transfer that repeats amount and receiver information on the same date. ex: 'my-internal-id-123456' - * @param description [string, default null]: optional description to override default description to be shown in the bank statement. ex: 'Payment for service #1234' * * Parameters (optional): - * @param tags [list of strings]: list of strings for reference when searching for transfers. ex: ['employees', 'monthly'] + * @param accountType [string, default 'checking']: Receiver bank account type. This parameter only has effect on Pix Transfers. ex: 'checking', 'savings', 'salary' or 'payment' + * @param externalId [string, default null]: url safe string that must be unique among all your transfers. Duplicated external_ids will cause failures. By default, this parameter will block any transfer that repeats amount and receiver information on the same date. ex: 'my-internal-id-123456' * @param scheduled [string, default now]: date or datetime when the transfer will be processed. May be pushed to next business day if necessary. ex: '2020-11-12T00:14:22.806+00:00' or '2020-11-30' + * @param description [string, default null]: optional description to override default description to be shown in the bank statement. ex: 'Payment for service #1234' + * @param tags [list of strings, default []]: list of strings for reference when searching for transfers. ex: ['employees', 'monthly'] + * @param rules [list of Transfer.Rules, default []]: list of Transfer.Rule objects for modifying transfer behaviour. ex: [Transfer.Rule(key="resendingLimit", value=5)] * * Attributes (return-only): * @param id [string, default null]: unique id returned when Transfer is created. ex: '5656565656565656' @@ -37,8 +41,9 @@ class Transfer extends Resource { * */ constructor({ - amount, name, taxId, bankCode, branchCode, accountNumber, accountType, externalId, scheduled, - description, tags, fee, status, created, updated, transactionIds, id + amount, name, taxId, bankCode, branchCode, accountNumber, accountType, + externalId, scheduled, description, tags, rules, id, fee, status, + transactionIds, created, updated }) { super(id); this.amount = amount; @@ -52,6 +57,7 @@ class Transfer extends Resource { this.scheduled = scheduled; this.description = description; this.tags = tags; + this.rules = parseObjects(rules, ruleResource, Rule); this.fee = fee; this.status = status; this.created = check.datetime(created); diff --git a/sdk/utils/parse.js b/sdk/utils/parse.js new file mode 100644 index 00000000..d4c1c468 --- /dev/null +++ b/sdk/utils/parse.js @@ -0,0 +1,59 @@ +const starkbank = require('../../index.js'); +const Ellipticcurve = require('starkbank-ecdsa'); +const rest = require('../utils/rest.js'); +const error = require('../error.js'); +const api = require('./api'); + + +exports.parseObjects = function (objects, resource, resourceClass) { + if (objects == null) + return null; + + let parsedObjects = []; + for (let object of objects) { + if (object instanceof resourceClass) { + parsedObjects.push(object); + continue; + } + object = Object.assign(new resource['class'](object), object); + parsedObjects.push(object); + } + return parsedObjects; +} + +exports.parseAndVerify = async function (resource, content, signature, user = null) { + content = await this.verify(content, signature, user); + + let object = Object.assign(new resource['class'](api.lastName(resource['name'])), JSON.parse(content)) + if (resource['name'] === 'Event'){ + object = Object.assign(new resource['class'](), JSON.parse(content)['event']); + } + + return object; +} + +exports.verify = async function (content, signature, user = null) { + try { + signature = Ellipticcurve.Signature.fromBase64(signature); + } catch (e) { + throw new error.InvalidSignatureError('The provided signature is not valid'); + } + + if (await verifySignature(content, signature, user)) { + return content; + } + if (await verifySignature(content, signature, user, true)) { + return content; + } + throw new error.InvalidSignatureError('Provided signature and content do not match Stark public key'); +} + +async function verifySignature(content, signature, user = null, refresh = false) { + let publicKey = starkbank.cache['stark-public-key']; + if (!publicKey || refresh) { + let pem = await rest.getPublicKey(user); + publicKey = Ellipticcurve.PublicKey.fromPem(pem); + starkbank.cache['stark-public-key'] = publicKey; + } + return Ellipticcurve.Ecdsa.verify(content, signature, publicKey); +} diff --git a/testTypes/Balance.test.ts b/testTypes/Balance.test.ts index 9f22735c..4c1c86e9 100644 --- a/testTypes/Balance.test.ts +++ b/testTypes/Balance.test.ts @@ -4,7 +4,7 @@ import starkbank from "starkbank"; import assert from 'assert'; starkbank.user = require('./utils/user').exampleProject; -starkbank.Balance + describe('TestBalanceGet', function(){ it('test_success', async () => { let balance = await starkbank.balance.get(); diff --git a/testTypes/BrcodePayment.test.ts b/testTypes/BrcodePayment.test.ts index 5230ae9d..5dbd4bef 100644 --- a/testTypes/BrcodePayment.test.ts +++ b/testTypes/BrcodePayment.test.ts @@ -6,7 +6,7 @@ import { generateExampleBrcodePaymentsJson } from './utils/brcodePayment'; starkbank.user = require('./utils/user').exampleProject; describe('TestBrcodePaymentPost', function () { - jest.setTimeout(150000); + jest.setTimeout(1500000); it('test_success', async () => { let payments = await generateExampleBrcodePaymentsJson(5); payments = await starkbank.brcodePayment.create(payments); diff --git a/testTypes/utils/brcodePayment.ts b/testTypes/utils/brcodePayment.ts index 8e7d0326..383cb218 100644 --- a/testTypes/utils/brcodePayment.ts +++ b/testTypes/utils/brcodePayment.ts @@ -8,7 +8,13 @@ export async function generateExampleBrcodePaymentsJson(n: number, nextDay = fal brcode: "00020101021226890014br.gov.bcb.pix2567invoice-h.sandbox.starkbank.com/v2/db86835d61274c7799a1f637b2b6f8b652040000530398654040.005802BR5915Stark Bank S.A.6009Sao Paulo62070503***6304FCCE", taxId: '22.653.392/0001-20', description: "Tony Stark's Suit", - amount: 7654321 + amount: 7654321, + rules: [ + new starkbank.brcodePayment.Rule({ + key: "resendingLimit", + value: 2 + }) + ] }; let payments = []; diff --git a/testTypes/utils/transfer.ts b/testTypes/utils/transfer.ts index 478020c6..5296ba14 100644 --- a/testTypes/utils/transfer.ts +++ b/testTypes/utils/transfer.ts @@ -23,7 +23,13 @@ exports.generateExampleTransfersJson = function (n: number, amount = null, tomor accountNumber: '10000-0', accountType: 'checking', description: choice(null, 'Test description') as string, - externalId: "" + externalId: "", + rules: [ + new starkbank.transfer.Rule({ + key: 'resendingLimit', + value: 5 + }) + ] }; let transfers: starkbank.Transfer[] = []; diff --git a/tests/testBrcodePayment.js b/tests/testBrcodePayment.js index 9de39af9..5889c665 100644 --- a/tests/testBrcodePayment.js +++ b/tests/testBrcodePayment.js @@ -5,7 +5,7 @@ const generateExampleBrcodePaymentsJson = require('./utils/brcodePayment.js').ge starkbank.user = require('./utils/user').exampleProject; describe('TestBrcodePaymentPost', function(){ - this.timeout(10000); + this.timeout(100000); it('test_success', async () => { let payments = await generateExampleBrcodePaymentsJson(5); payments = await starkbank.brcodePayment.create(payments); diff --git a/tests/utils/brcodePayment.js b/tests/utils/brcodePayment.js index b10eef61..55cf1d70 100644 --- a/tests/utils/brcodePayment.js +++ b/tests/utils/brcodePayment.js @@ -1,16 +1,23 @@ const starkbank = require('../../index.js'); +const { Rule } = require('../../sdk/brcodePayment/index.js'); const generateExampleInvoicesJson = require('./invoice.js').generateExampleInvoicesJson; const random = require('./random.js'); exports.generateExampleBrcodePaymentsJson = async function (n, nextDay = false) { let exampleBrcodePayment = { - brcode: "00020126580014br.gov.bcb.pix0136a629532e-7693-4846-852d-1bbff817b5a8520400005303986540510.005802BR5908T'Challa6009Sao Paulo62090505123456304B14A", - taxId: '20.018.183/0001-80', - description: "Tony Stark's Suit", - amount: 7654321, - scheduled: '2020-02-29', - tags: ['Stark', 'Suit'] + brcode: "00020126580014br.gov.bcb.pix0136a629532e-7693-4846-852d-1bbff817b5a8520400005303986540510.005802BR5908T'Challa6009Sao Paulo62090505123456304B14A", + taxId: '20.018.183/0001-80', + description: "Tony Stark's Suit", + amount: 7654321, + scheduled: '2020-02-29', + tags: ['Stark', 'Suit'], + rules: [ + new Rule({ + key: "resendingLimit", + value: 2 + }) + ] }; let payments = []; diff --git a/tests/utils/transfer.js b/tests/utils/transfer.js index 6c902e04..36be15b1 100644 --- a/tests/utils/transfer.js +++ b/tests/utils/transfer.js @@ -22,7 +22,13 @@ exports.generateExampleTransfersJson = function (n, amount = null, tomorrow = fa branchCode: '0001', accountNumber: '10000-0', accountType: 'checking', - description: choice(null, 'Test description') + description: choice(null, 'Test description'), + rules: [ + new starkbank.transfer.Rule({ + key: 'resendingLimit', + value: 5 + }), + ] }; let transfers = []; diff --git a/types/brcodePayment/brcodePayment.d.ts b/types/brcodePayment/brcodePayment.d.ts index 713529b5..2a59b0a2 100644 --- a/types/brcodePayment/brcodePayment.d.ts +++ b/types/brcodePayment/brcodePayment.d.ts @@ -19,6 +19,7 @@ declare module 'starkbank' { * @param amount [int, default null]: amount automatically calculated from line or barCode. ex: 23456 (= R$ 234.56) * @param scheduled [string, default now]: payment scheduled date or datetime. ex: '2020-11-25T17:59:26.249976+00:00' * @param tags [list of strings, default null]: list of strings for tagging + * @param rule [list of BrcodePayment.Rules, default []]: list of BrcodePayment.Rule objects for modifying transfer behavior. ex: [BrcodePayment.Rule(key="resendingLimit", value=5)] * * ## Attributes (return-only): * @param id [string, default null]: unique id returned when payment is created. ex: '5656565656565656' @@ -38,6 +39,7 @@ declare module 'starkbank' { amount : number | null scheduled : string | null tags : string[] | null + rule : Rule[] | null readonly id : string readonly name : string @@ -49,8 +51,8 @@ declare module 'starkbank' { constructor(params: { brcode: string, taxId: string, description: string, amount?: number | null, scheduled?: string | null, - tags?: string[] | null, name?: string | null, status?: string | null, type?: string | null, fee?: number | null, updated?: string | null, - created?: string | null, + tags?: string[] | null, rules?: Rule[] | null, name?: string | null, status?: string | null, type?: string | null, + fee?: number | null, updated?: string | null, created?: string | null, }) } @@ -188,6 +190,27 @@ declare module 'starkbank' { */ function update(id: string, params?: {status: string, user?: Project | Organization | null}): Promise; + export class Rule { + /** + * + * BrcodePayment.Rule object + * + * @description The BrcodePayment.Rule object modifies the behavior of BrcodePayment objects when passed as an argument upon their creation. + * + * Parameters (required): + * @param key [string]: Rule to be customized, describes what BrcodePayment behavior will be altered. ex: "resendingLimit" + * @param value [integer]: Value of the rule. ex: 5 + * + */ + key: string + value: number + + constructor(params: { + key: string, + value: number + }) + } + export class Log { /** * diff --git a/types/transfer/transfer.d.ts b/types/transfer/transfer.d.ts index 21accdc2..96992514 100644 --- a/types/transfer/transfer.d.ts +++ b/types/transfer/transfer.d.ts @@ -17,13 +17,14 @@ declare module 'starkbank' { * @param bankCode [string]: code of the receiver bank institution in Brazil. If an ISPB (8 digits) is informed, a PIX transfer will be created, else a TED will be issued. ex: '20018183' or '341' * @param branchCode [string]: receiver bank account branch. Use '-' in case there is a verifier digit. ex: '1357-9' * @param accountNumber [string]: Receiver Bank Account number. Use '-' before the verifier digit. ex: '876543-2' - * @param accountType [string, default 'checking']: Receiver bank account type. This parameter only has effect on Pix Transfers. ex: 'checking', 'savings', 'salary' or 'payment' - * @param externalId [string, default null]: url safe string that must be unique among all your transfers. Duplicated external_ids will cause failures. By default, this parameter will block any transfer that repeats amount and receiver information on the same date. ex: 'my-internal-id-123456' * * Parameters (optional): - * @param tags [list of strings]: list of strings for reference when searching for transfers. ex: ['employees', 'monthly'] + * @param accountType [string, default 'checking']: Receiver bank account type. This parameter only has effect on Pix Transfers. ex: 'checking', 'savings', 'salary' or 'payment' + * @param externalId [string, default null]: url safe string that must be unique among all your transfers. Duplicated external_ids will cause failures. By default, this parameter will block any transfer that repeats amount and receiver information on the same date. ex: 'my-internal-id-123456' * @param scheduled [string, default now]: date or datetime when the transfer will be processed. May be pushed to next business day if necessary. ex: '2020-11-12T00:14:22.806+00:00' or '2020-11-30' * @param description [string, default null]: optional description to override default description to be shown in the bank statement. ex: 'Payment for service #1234' + * @param tags [list of strings, default []]: list of strings for reference when searching for transfers. ex: ['employees', 'monthly'] + * @param rules [list of Transfer.Rules, default []]: list of Transfer.Rule objects for modifying transfer behavior. ex: [Transfer.Rule(key="resendingLimit", value=5)] * * Attributes (return-only): * @param id [string, default null]: unique id returned when Transfer is created. ex: '5656565656565656' @@ -41,12 +42,13 @@ declare module 'starkbank' { bankCode: string branchCode: string accountNumber: string - accountType: string - externalId: string - tags: string[] - scheduled: string + accountType: string | null + externalId: string | null + scheduled: string | null description: string | null + tags: string | null + rules: transfer.Rule[] | null readonly id : string readonly fee : number @@ -58,9 +60,9 @@ declare module 'starkbank' { constructor(params: { amount: number, name: string, taxId: string, bankCode: string, branchCode: string, accountNumber: string, - accountType?: string, externalId?: string, tags?: string[], scheduled?: string, description?: string | null, - id?: string | null, fee?: number | null, status?: string | null, transactionIds?: string[] | null, - created?: string | null, updated?: string | null, + accountType?: string, externalId?: string, tags?: string[], rules?: transfer.Rule[], scheduled?: string, + description?: string | null, id?: string | null, fee?: number | null, status?: string | null, + transactionIds?: string[] | null, created?: string | null, updated?: string | null, }) } @@ -213,6 +215,26 @@ declare module 'starkbank' { function _delete(id: string, user?: Project | Organization): Promise; export { _delete as delete } + export class Rule { + /** + * + * Transfer.Rule object + * + * @description The Transfer.Rule object modifies the behavior of Transfer objects when passed as an argument upon their creation. + * + * Parameters (required): + * @param key [string]: Rule to be customized, describes what Transfer behavior will be altered. ex: "resendingLimit" + * @param value [integer]: Value of the rule. ex: 5 + * + */ + key: string + value: number + + constructor(params: { + key: string, + value: number + }) + } export class Log { /**