Skip to content

Commit

Permalink
Merge pull request #83 from starkbank/feature/transfer-brcodepayment-…
Browse files Browse the repository at this point in the history
…rules

Add Transfer.rules and BrcodePayment.rules sub-resources
  • Loading branch information
xavier-stark authored Jan 2, 2023
2 parents acb224e + 68d43be commit 5603ef2
Show file tree
Hide file tree
Showing 20 changed files with 252 additions and 36 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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
})
]
}
])

Expand Down Expand Up @@ -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
})
]
},
]);

Expand All @@ -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
Expand Down
11 changes: 8 additions & 3 deletions sdk/brcodePayment/brcodePayment.js
Original file line number Diff line number Diff line change
@@ -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 {
/**
Expand All @@ -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'
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -200,7 +205,7 @@ exports.update = function (id, {status, user} = {}) {
*
*/
let payload = {
status: status,
status: status,
};
return rest.patchId(resource, id, payload, user);
};
2 changes: 2 additions & 0 deletions sdk/brcodePayment/index.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
3 changes: 3 additions & 0 deletions sdk/brcodePayment/rule/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const rule = require('./rule.js');

exports.Rule = rule.Rule;
24 changes: 24 additions & 0 deletions sdk/brcodePayment/rule/rule.js
Original file line number Diff line number Diff line change
@@ -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'};
2 changes: 2 additions & 0 deletions sdk/transfer/index.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
3 changes: 3 additions & 0 deletions sdk/transfer/rule/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const rule = require('./rule.js');

exports.Rule = rule.Rule;
24 changes: 24 additions & 0 deletions sdk/transfer/rule/rule.js
Original file line number Diff line number Diff line change
@@ -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'};
18 changes: 12 additions & 6 deletions sdk/transfer/transfer.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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'
Expand All @@ -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;
Expand All @@ -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);
Expand Down
59 changes: 59 additions & 0 deletions sdk/utils/parse.js
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 1 addition & 1 deletion testTypes/Balance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion testTypes/BrcodePayment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion testTypes/utils/brcodePayment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down
8 changes: 7 additions & 1 deletion testTypes/utils/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand Down
2 changes: 1 addition & 1 deletion tests/testBrcodePayment.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 13 additions & 6 deletions tests/utils/brcodePayment.js
Original file line number Diff line number Diff line change
@@ -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 = [];
Expand Down
8 changes: 7 additions & 1 deletion tests/utils/transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down
Loading

0 comments on commit 5603ef2

Please sign in to comment.