From e0b972bee9cce7300afad3ade9a2b0fd21514623 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Mon, 27 Oct 2025 14:01:58 +0100 Subject: [PATCH 01/18] add getposdetails endpoint --- lib/barion.js | 18 ++++- lib/constants.js | 8 ++- lib/domain/GetPosDetails.js | 18 +++++ lib/domain/_constraints.js | 17 +++++ lib/domain/index.js | 4 +- lib/domain/pos/BusinessContact.js | 17 +++++ lib/domain/pos/CustomerServiceContact.js | 17 +++++ lib/domain/pos/TechnicalContact.js | 17 +++++ lib/services.js | 10 +++ test/barion.test.js | 84 ++++++++++++++++++++++-- 10 files changed, 203 insertions(+), 7 deletions(-) create mode 100644 lib/domain/GetPosDetails.js create mode 100644 lib/domain/pos/BusinessContact.js create mode 100644 lib/domain/pos/CustomerServiceContact.js create mode 100644 lib/domain/pos/TechnicalContact.js diff --git a/lib/barion.js b/lib/barion.js index 0d9aae9..228ebca 100644 --- a/lib/barion.js +++ b/lib/barion.js @@ -13,7 +13,8 @@ const { CompletePayment, StartPaymentWithGoogleToken, StartPaymentWithAppleToken, - ValidateApplePaySession + ValidateApplePaySession, + GetPosDetails } = require('./domain'); const { buildRequest, buildRequestWithoutValidation } = require('./build'); const { sanitizeThenValidate } = require('./validate'); @@ -300,4 +301,19 @@ Barion.prototype.validateApplePaySession = function (options, callback) { }, callback); }; +/** + * Gets the details of a specific POS. + * @param {Object} options - Options that are required to get POS details. + * @param {Function} [callback] - Callback that handles the response. If not passed, the function returns a *Promise*. + * @see {@link https://docs.barion.com/Pos-Get-v1|Barion API Documentation} + */ +Barion.prototype.getPosDetails = function (options, callback) { + return doRequestAndReturn({ + defaults: this.defaults, + customs: options, + schema: GetPosDetails, + service: services.getPosDetails + }, callback); +}; + module.exports = Barion; diff --git a/lib/constants.js b/lib/constants.js index 3309599..9d55048 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -12,7 +12,8 @@ const { CompletePayment, StartPaymentWithAppleToken, ValidateApplePaySession, - StartPaymentWithGoogleToken + StartPaymentWithGoogleToken, + GetPosDetails } = require('./domain'); /** @@ -95,6 +96,11 @@ module.exports = { method: 'POST', path: '/v2/ApplePay/ValidateSession', model: ValidateApplePaySession + }, + getPosDetails: { + method: 'GET', + path: '/v1/Pos', + model: GetPosDetails } } }; diff --git a/lib/domain/GetPosDetails.js b/lib/domain/GetPosDetails.js new file mode 100644 index 0000000..0dad96a --- /dev/null +++ b/lib/domain/GetPosDetails.js @@ -0,0 +1,18 @@ +const Joi = require('joi'); +const { CaseInsensitiveSchema } = require('../schema'); + +/** + * Used to retrieve all details and the current state of a point of sale (POS). + * + * Note: Uses API key authentication (wallet endpoint). + * + * @see {@link https://docs.barion.com/Pos-Get-v1|Barion API Documentation} + */ +const schema = Joi.object({ + ApiKey: Joi.string().required() + .pattern(/^[0-9a-fA-F]{32}$/), + PublicKey: Joi.string().required() + .guid() +}); + +module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/domain/_constraints.js b/lib/domain/_constraints.js index 19456e2..c39bcdf 100644 --- a/lib/domain/_constraints.js +++ b/lib/domain/_constraints.js @@ -108,5 +108,22 @@ module.exports = { 'NoPreference', 'ChallengeRequired', 'NoChallengeNeeded' + ], + ShopStatus: [ + 'Unspecified', + 'NewUnderApproval', + 'NewForbidden', + 'Open', + 'OpenWaitForModificationApproval', + 'OpenModificationForbidden', + 'TemporaryClosed', + 'TemporaryClosedWaitForModificationApproval', + 'TemporaryClosedModificationForbidden', + 'PermanentlyClosed', + 'PermanentlyClosedByUser', + 'PermanentlyClosedByOfficer', + 'ClosedByOfficer', + 'ReOpen_WaitingForApproval', + 'Draft' ] }; diff --git a/lib/domain/index.js b/lib/domain/index.js index ee37c49..1744b53 100644 --- a/lib/domain/index.js +++ b/lib/domain/index.js @@ -13,6 +13,7 @@ const StatementDownload = require('./StatementDownload'); const StartPaymentWithGoogleToken = require('./StartPaymentWithGoogleToken'); const StartPaymentWithAppleToken = require('./StartPaymentWithAppleToken'); const ValidateApplePaySession = require('./ValidateApplePaySession'); +const GetPosDetails = require('./GetPosDetails'); module.exports = { InitialOptions, @@ -29,5 +30,6 @@ module.exports = { CompletePayment, StartPaymentWithGoogleToken, StartPaymentWithAppleToken, - ValidateApplePaySession + ValidateApplePaySession, + GetPosDetails }; diff --git a/lib/domain/pos/BusinessContact.js b/lib/domain/pos/BusinessContact.js new file mode 100644 index 0000000..debb18e --- /dev/null +++ b/lib/domain/pos/BusinessContact.js @@ -0,0 +1,17 @@ +const Joi = require('joi'); +const { CaseInsensitiveSchema } = require('../../schema'); + +/** + * This structure represents the business contact details of a shop in Barion. + * + * @see {@link https://docs.barion.com/BusinessContact|Barion API Documentation} + */ +const schema = Joi.object({ + Name: Joi.string().optional(), + PhoneNumber: Joi.string().optional() + .max(30), + Email: Joi.string().optional() + .email() +}); + +module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/domain/pos/CustomerServiceContact.js b/lib/domain/pos/CustomerServiceContact.js new file mode 100644 index 0000000..99e1518 --- /dev/null +++ b/lib/domain/pos/CustomerServiceContact.js @@ -0,0 +1,17 @@ +const Joi = require('joi'); +const { CaseInsensitiveSchema } = require('../../schema'); + +/** + * This structure represents the customer service contact details of a shop in Barion. + * + * @see {@link https://docs.barion.com/CustomerServiceContact|Barion API Documentation} + */ +const schema = Joi.object({ + Name: Joi.string().optional(), + PhoneNumber: Joi.string().optional() + .max(30), + Email: Joi.string().optional() + .email() +}); + +module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/domain/pos/TechnicalContact.js b/lib/domain/pos/TechnicalContact.js new file mode 100644 index 0000000..84672d1 --- /dev/null +++ b/lib/domain/pos/TechnicalContact.js @@ -0,0 +1,17 @@ +const Joi = require('joi'); +const { CaseInsensitiveSchema } = require('../../schema'); + +/** + * This structure represents the technical contact details of a shop in Barion. + * + * @see {@link https://docs.barion.com/TechnicalContact|Barion API Documentation} + */ +const schema = Joi.object({ + Name: Joi.string().optional(), + PhoneNumber: Joi.string().optional() + .max(30), + Email: Joi.string().optional() + .email() +}); + +module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/services.js b/lib/services.js index acd058e..5610cbb 100644 --- a/lib/services.js +++ b/lib/services.js @@ -166,6 +166,15 @@ function validateApplePaySession(environment, options) { return doRequest(environment, endPoints.validateApplePaySession, options); } +/** + * Gets the details of a specific POS. + * @param {String} environment - The environment to use ('test' or 'prod'). + * @param {Object} options - The final request object to send to the Barion API. + */ +function getPosDetails(environment, options) { + return doRequest(environment, endPoints.getPosDetails, options); +} + module.exports = { startPayment, getPaymentState, @@ -181,6 +190,7 @@ module.exports = { startPaymentWithGoogleToken, startPaymentWithAppleToken, validateApplePaySession, + getPosDetails, _private: { getBaseUrl, doRequest diff --git a/test/barion.test.js b/test/barion.test.js index 2820ef8..0908863 100644 --- a/test/barion.test.js +++ b/test/barion.test.js @@ -55,7 +55,8 @@ const Barions = { downloadStatement: returnSuccess, startPaymentWithGoogleToken: returnSuccess, startPaymentWithAppleToken: returnSuccess, - validateApplePaySession: returnSuccess + validateApplePaySession: returnSuccess, + getPosDetails: returnSuccess } }), ServiceErrorBarion: proxyquire('../lib/barion', { @@ -73,7 +74,8 @@ const Barions = { downloadStatement: returnError, startPaymentWithGoogleToken: returnError, startPaymentWithAppleToken: returnError, - validateApplePaySession: returnError + validateApplePaySession: returnError, + getPosDetails: returnError } }), ValidationErrorBarion: proxyquire('../lib/barion', { @@ -91,7 +93,8 @@ const Barions = { downloadStatement: returnSuccess, startPaymentWithGoogleToken: returnSuccess, startPaymentWithAppleToken: returnSuccess, - validateApplePaySession: returnSuccess + validateApplePaySession: returnSuccess, + getPosDetails: returnSuccess }, './build': { buildRequest: throwValidationError @@ -112,7 +115,8 @@ const Barions = { downloadStatement: returnSuccess, startPaymentWithGoogleToken: returnSuccess, startPaymentWithAppleToken: returnSuccess, - validateApplePaySession: returnSuccess + validateApplePaySession: returnSuccess, + getPosDetails: returnSuccess }, './build': { buildRequestWithoutValidation: throwSanitizationError @@ -1278,4 +1282,76 @@ describe('lib/barion.js', function () { expect(promise).to.eventually.rejectedWith(sanitizationErrorObject).notify(done); }); }); + + describe('#getPosDetails(options, [callback])', function () { + const request = { + ApiKey: '277a6ae112b041928e6cbc7d0612afa1', + PublicKey: '277a6ae1-12b0-4192-8e6c-bc7d0612afa1' + }; + + it('should answer with callback on success', function (done) { + okBarion.getPosDetails(request, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.equal(successObject); + done(); + }); + }); + + it('should answer with callback on success when validation is turned off', function (done) { + okBarionWithoutValidation.getPosDetails(request, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.equal(successObject); + done(); + }); + }); + + it('should answer with callback on error', function (done) { + serviceErrorBarion.getPosDetails(request, (err, res) => { + expect(err).to.deep.equal(errorObject); + expect(res).to.be.null; + done(); + }); + }); + + it('should answer with callback on validation error', function (done) { + validationErrorBarion.getPosDetails(request, (err, res) => { + expect(err).to.deep.equal(validationErrorObject); + expect(res).to.be.null; + done(); + }); + }); + + it('should answer with callback on sanitization error when validation is turned off', function (done) { + sanitizationErrorBarion.getPosDetails(request, (err, res) => { + expect(err).to.deep.equal(sanitizationErrorObject); + expect(res).to.be.null; + done(); + }); + }); + + it('should answer with Promise on success', function (done) { + const promise = okBarion.getPosDetails(request); + expect(promise).to.eventually.deep.equal(successObject).notify(done); + }); + + it('should answer with Promise on success when validation is turned off', function (done) { + const promise = okBarionWithoutValidation.getPosDetails(request); + expect(promise).to.eventually.deep.equal(successObject).notify(done); + }); + + it('should answer with Promise on error', function (done) { + const promise = serviceErrorBarion.getPosDetails(request); + expect(promise).to.eventually.rejectedWith(errorObject).notify(done); + }); + + it('should answer with Promise on validation error', function (done) { + const promise = validationErrorBarion.getPosDetails(request); + expect(promise).to.eventually.rejectedWith(validationErrorObject).notify(done); + }); + + it('should answer with Promise on sanitization error when validation is turned off', function (done) { + const promise = sanitizationErrorBarion.getPosDetails(request); + expect(promise).to.eventually.rejectedWith(sanitizationErrorObject).notify(done); + }); + }); }); From 32c3aa7dbb9cd81758b327855ada2d5f31961aa0 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Mon, 27 Oct 2025 14:49:48 +0100 Subject: [PATCH 02/18] add pos create endpoint --- lib/barion.js | 23 +++++- lib/constants.js | 8 ++- lib/domain/CreatePos.js | 72 +++++++++++++++++++ lib/domain/_constraints.js | 27 +++++++ lib/domain/index.js | 4 +- lib/domain/pos/ExpectedTurnover.js | 18 +++++ lib/services.js | 18 +++-- test/barion.test.js | 110 +++++++++++++++++++++++++++-- 8 files changed, 266 insertions(+), 14 deletions(-) create mode 100644 lib/domain/CreatePos.js create mode 100644 lib/domain/pos/ExpectedTurnover.js diff --git a/lib/barion.js b/lib/barion.js index 228ebca..17ab210 100644 --- a/lib/barion.js +++ b/lib/barion.js @@ -14,7 +14,8 @@ const { StartPaymentWithGoogleToken, StartPaymentWithAppleToken, ValidateApplePaySession, - GetPosDetails + GetPosDetails, + CreatePos } = require('./domain'); const { buildRequest, buildRequestWithoutValidation } = require('./build'); const { sanitizeThenValidate } = require('./validate'); @@ -259,8 +260,9 @@ Barion.prototype.downloadStatement = function (options, callback) { /** * Starts a new payment in Barion using Google Pay token. * @param {Object} options - Options that are required to start payment with Google Pay. - * @param {Function} [callback] - Callback that handles the response. If not passed, the function returns a *Promise*. - * @see {@link https://docs.barion.com/Payment-StartPaymentWithGoogleToken-v3|Barion API: Start payment with Google Pay?} + * @param {Function} [callback] - Callback that handles the response. + * If not passed, the function returns a *Promise*. + * @see {@link https://docs.barion.com/Payment-StartPaymentWithGoogleToken-v3} */ Barion.prototype.startPaymentWithGoogleToken = function (options, callback) { return doRequestAndReturn({ @@ -316,4 +318,19 @@ Barion.prototype.getPosDetails = function (options, callback) { }, callback); }; +/** + * Creates a new POS (shop) in Barion. + * @param {Object} options - Options that are required to create a POS. + * @param {Function} [callback] - Callback that handles the response. If not passed, the function returns a *Promise*. + * @see {@link https://docs.barion.com/Pos-Create-v1|Barion API Documentation} + */ +Barion.prototype.createPos = function (options, callback) { + return doRequestAndReturn({ + defaults: this.defaults, + customs: options, + schema: CreatePos, + service: services.createPos + }, callback); +}; + module.exports = Barion; diff --git a/lib/constants.js b/lib/constants.js index 9d55048..4c8f9dc 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -13,7 +13,8 @@ const { StartPaymentWithAppleToken, ValidateApplePaySession, StartPaymentWithGoogleToken, - GetPosDetails + GetPosDetails, + CreatePos } = require('./domain'); /** @@ -101,6 +102,11 @@ module.exports = { method: 'GET', path: '/v1/Pos', model: GetPosDetails + }, + createPos: { + method: 'POST', + path: '/v1/Pos', + model: CreatePos } } }; diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js new file mode 100644 index 0000000..a961fbe --- /dev/null +++ b/lib/domain/CreatePos.js @@ -0,0 +1,72 @@ +const Joi = require('joi'); +const { CaseInsensitiveSchema } = require('../schema'); +const { ShopCategory } = require('./_constraints'); +const ExpectedTurnover = require('./pos/ExpectedTurnover'); + +/** + * Used to create a shop (POS) in Barion. + * + * Note: Uses API key authentication (wallet endpoint). + * + * @see {@link https://docs.barion.com/Pos-Create-v1|Barion API Documentation} + */ +const schema = Joi.object({ + ApiKey: Joi.string().required() + .pattern(/^[0-9a-fA-F]{32}$/), + Name: Joi.string().required() + .max(200), + Url: Joi.string().required() + .max(2000) + .uri({ scheme: [ 'https' ] }), + Description: Joi.string().required() + .min(20) + .max(200), + Logo: Joi.string().required(), + Category: Joi.array().required() + .items(Joi.string().valid(...ShopCategory)) + .min(1), + BusinessContact: Joi.object({ + Name: Joi.string().required(), + PhoneNumber: Joi.string().required() + .max(30), + Email: Joi.string().required() + .email() + }).required(), + TechnicalContact: Joi.object({ + Name: Joi.string().required(), + PhoneNumber: Joi.string().required() + .max(30), + Email: Joi.string().required() + .email() + }).required(), + CustomerServiceContact: Joi.object({ + Name: Joi.string().required(), + PhoneNumber: Joi.string().required() + .max(30), + Email: Joi.string().required() + .email() + }).required(), + PrimaryCurrency: Joi.string().required() + .valid('HUF', 'CZK', 'EUR', 'USD'), + ExpectedTurnover: ExpectedTurnover.required(), + AverageBasketValue: Joi.number().optional() + .integer() + .greater(0), + PercentageOfB2BCustomers: Joi.number().optional() + .min(0) + .max(100), + PercentageOfNonEuCards: Joi.number().optional() + .min(0) + .max(100), + FullPixelImplemented: Joi.boolean().required(), + UseForEInvoicing: Joi.boolean().required(), + CallBackUrl: Joi.string().optional() + .max(2000) + .uri(), + ReferenceId: Joi.string().optional(), + NoteForApproval: Joi.string().optional(), + CustomTemplate: Joi.string().optional(), + CustomCss: Joi.string().optional() +}); + +module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/domain/_constraints.js b/lib/domain/_constraints.js index c39bcdf..7197d57 100644 --- a/lib/domain/_constraints.js +++ b/lib/domain/_constraints.js @@ -125,5 +125,32 @@ module.exports = { 'ClosedByOfficer', 'ReOpen_WaitingForApproval', 'Draft' + ], + ShopCategory: [ + 'Agriculture', + 'BookNewsPaper', + 'Ad', + 'BonusCoupon', + 'Dating', + 'Electronics', + 'FashionClothes', + 'FoodDrink', + 'FurnitureAntiquity', + 'GiftToyFlower', + 'BeautyHealth', + 'HomeDesignGarden', + 'JobEducation', + 'BuildingMaterialMachine', + 'Baby', + 'FilmMusic', + 'Other', + 'Pet', + 'Property', + 'Service', + 'SportLeisureTravel', + 'BettingGambling', + 'Tobacco', + 'Vehicle', + 'WatchJewelry' ] }; diff --git a/lib/domain/index.js b/lib/domain/index.js index 1744b53..d80d4ed 100644 --- a/lib/domain/index.js +++ b/lib/domain/index.js @@ -14,6 +14,7 @@ const StartPaymentWithGoogleToken = require('./StartPaymentWithGoogleToken'); const StartPaymentWithAppleToken = require('./StartPaymentWithAppleToken'); const ValidateApplePaySession = require('./ValidateApplePaySession'); const GetPosDetails = require('./GetPosDetails'); +const CreatePos = require('./CreatePos'); module.exports = { InitialOptions, @@ -31,5 +32,6 @@ module.exports = { StartPaymentWithGoogleToken, StartPaymentWithAppleToken, ValidateApplePaySession, - GetPosDetails + GetPosDetails, + CreatePos }; diff --git a/lib/domain/pos/ExpectedTurnover.js b/lib/domain/pos/ExpectedTurnover.js new file mode 100644 index 0000000..49f8c23 --- /dev/null +++ b/lib/domain/pos/ExpectedTurnover.js @@ -0,0 +1,18 @@ +const Joi = require('joi'); +const { CaseInsensitiveSchema } = require('../../schema'); + +/** + * This structure represents the expected turnover for a shop in Barion. + * + * @see {@link https://docs.barion.com/ExpectedTurnover|Barion API Documentation} + */ +const schema = Joi.object({ + MinAmount: Joi.number().optional() + .min(0), + MaxAmount: Joi.number().optional() + .min(0), + Amount: Joi.number().optional() + .min(0) +}); + +module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/services.js b/lib/services.js index 5610cbb..22fdc67 100644 --- a/lib/services.js +++ b/lib/services.js @@ -144,7 +144,7 @@ function downloadStatement (environment, options) { * @param {String} environment - The environment to use ('test' or 'prod'). * @param {Object} options - The final request body to send to the Barion API. */ -function startPaymentWithGoogleToken(environment, options) { +function startPaymentWithGoogleToken (environment, options) { return doRequest(environment, endPoints.startPaymentWithGoogleToken, options); } @@ -153,7 +153,7 @@ function startPaymentWithGoogleToken(environment, options) { * @param {String} environment - The environment to use ('test' or 'prod'). * @param {Object} options - The final request body to send to the Barion API. */ -function startPaymentWithAppleToken(environment, options) { +function startPaymentWithAppleToken (environment, options) { return doRequest(environment, endPoints.startPaymentWithAppleToken, options); } @@ -162,7 +162,7 @@ function startPaymentWithAppleToken(environment, options) { * @param {String} environment - The environment to use ('test' or 'prod'). * @param {Object} options - The final request body to send to the Barion API. */ -function validateApplePaySession(environment, options) { +function validateApplePaySession (environment, options) { return doRequest(environment, endPoints.validateApplePaySession, options); } @@ -171,10 +171,19 @@ function validateApplePaySession(environment, options) { * @param {String} environment - The environment to use ('test' or 'prod'). * @param {Object} options - The final request object to send to the Barion API. */ -function getPosDetails(environment, options) { +function getPosDetails (environment, options) { return doRequest(environment, endPoints.getPosDetails, options); } +/** + * Creates a new POS (shop) in Barion. + * @param {String} environment - The environment to use ('test' or 'prod'). + * @param {Object} options - The final request object to send to the Barion API. + */ +function createPos (environment, options) { + return doRequest(environment, endPoints.createPos, options); +} + module.exports = { startPayment, getPaymentState, @@ -191,6 +200,7 @@ module.exports = { startPaymentWithAppleToken, validateApplePaySession, getPosDetails, + createPos, _private: { getBaseUrl, doRequest diff --git a/test/barion.test.js b/test/barion.test.js index 0908863..1633e89 100644 --- a/test/barion.test.js +++ b/test/barion.test.js @@ -56,7 +56,8 @@ const Barions = { startPaymentWithGoogleToken: returnSuccess, startPaymentWithAppleToken: returnSuccess, validateApplePaySession: returnSuccess, - getPosDetails: returnSuccess + getPosDetails: returnSuccess, + createPos: returnSuccess } }), ServiceErrorBarion: proxyquire('../lib/barion', { @@ -75,7 +76,8 @@ const Barions = { startPaymentWithGoogleToken: returnError, startPaymentWithAppleToken: returnError, validateApplePaySession: returnError, - getPosDetails: returnError + getPosDetails: returnError, + createPos: returnError } }), ValidationErrorBarion: proxyquire('../lib/barion', { @@ -94,7 +96,8 @@ const Barions = { startPaymentWithGoogleToken: returnSuccess, startPaymentWithAppleToken: returnSuccess, validateApplePaySession: returnSuccess, - getPosDetails: returnSuccess + getPosDetails: returnSuccess, + createPos: returnSuccess }, './build': { buildRequest: throwValidationError @@ -116,7 +119,8 @@ const Barions = { startPaymentWithGoogleToken: returnSuccess, startPaymentWithAppleToken: returnSuccess, validateApplePaySession: returnSuccess, - getPosDetails: returnSuccess + getPosDetails: returnSuccess, + createPos: returnSuccess }, './build': { buildRequestWithoutValidation: throwSanitizationError @@ -1021,7 +1025,8 @@ describe('lib/barion.js', function () { ], Currency: 'HUF', PayerEmailAddress: 'payer@example.com', - GooglePayToken: 'eyJwcm90b2NvbFZlcnNpb24iOiJFQ192MiIsInNpZ25lZE1lc3NhZ2UiOiJ7XCJlbmNyeXB0ZWRNZXNzYWdlXCI6XCJleGFtcGxlLXRva2VuXCJ9In0=' + GooglePayToken: 'eyJwcm90b2NvbFZlcnNpb24iOiJFQ192MiIsInNpZ25lZE1lc3NhZ2UiOiJ7XCJl' + + 'bmNyeXB0ZWRNZXNzYWdlXCI6XCJleGFtcGxlLXRva2VuXCJ9In0=' }; it('should answer with callback on success', function (done) { @@ -1354,4 +1359,99 @@ describe('lib/barion.js', function () { expect(promise).to.eventually.rejectedWith(sanitizationErrorObject).notify(done); }); }); + + describe('#createPos(options, [callback])', function () { + const request = { + ApiKey: '277a6ae112b041928e6cbc7d0612afa1', + Name: 'Test Shop', + Url: 'https://example.com', + Description: 'This is a test shop for unit testing purposes.', + Logo: 'base64encodedlogo', + Category: [ 'Electronics', 'Other' ], + BusinessContact: { + Name: 'John Doe', + PhoneNumber: '36701234567', + Email: 'business@example.com' + }, + TechnicalContact: { + Name: 'Jane Smith', + PhoneNumber: '36701234568', + Email: 'tech@example.com' + }, + CustomerServiceContact: { + Name: 'Support Team', + PhoneNumber: '36701234569', + Email: 'support@example.com' + }, + PrimaryCurrency: 'EUR', + ExpectedTurnover: { Amount: 10000 }, + FullPixelImplemented: true, + UseForEInvoicing: false + }; + + it('should answer with callback on success', function (done) { + okBarion.createPos(request, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.equal(successObject); + done(); + }); + }); + + it('should answer with callback on success when validation is turned off', function (done) { + okBarionWithoutValidation.createPos(request, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.equal(successObject); + done(); + }); + }); + + it('should answer with callback on error', function (done) { + serviceErrorBarion.createPos(request, (err, res) => { + expect(err).to.deep.equal(errorObject); + expect(res).to.be.null; + done(); + }); + }); + + it('should answer with callback on validation error', function (done) { + validationErrorBarion.createPos(request, (err, res) => { + expect(err).to.deep.equal(validationErrorObject); + expect(res).to.be.null; + done(); + }); + }); + + it('should answer with callback on sanitization error when validation is turned off', function (done) { + sanitizationErrorBarion.createPos(request, (err, res) => { + expect(err).to.deep.equal(sanitizationErrorObject); + expect(res).to.be.null; + done(); + }); + }); + + it('should answer with Promise on success', function (done) { + const promise = okBarion.createPos(request); + expect(promise).to.eventually.deep.equal(successObject).notify(done); + }); + + it('should answer with Promise on success when validation is turned off', function (done) { + const promise = okBarionWithoutValidation.createPos(request); + expect(promise).to.eventually.deep.equal(successObject).notify(done); + }); + + it('should answer with Promise on error', function (done) { + const promise = serviceErrorBarion.createPos(request); + expect(promise).to.eventually.rejectedWith(errorObject).notify(done); + }); + + it('should answer with Promise on validation error', function (done) { + const promise = validationErrorBarion.createPos(request); + expect(promise).to.eventually.rejectedWith(validationErrorObject).notify(done); + }); + + it('should answer with Promise on sanitization error when validation is turned off', function (done) { + const promise = sanitizationErrorBarion.createPos(request); + expect(promise).to.eventually.rejectedWith(sanitizationErrorObject).notify(done); + }); + }); }); From 7572a3e91dce31f6926a2550e7a10374c0637428 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Mon, 27 Oct 2025 15:11:13 +0100 Subject: [PATCH 03/18] add integration testss --- test/integration/integration.test.js | 256 +++++++++++++++++++++++++++ test/integration/test-data.js | 54 ++++++ 2 files changed, 310 insertions(+) diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index bb51000..0a7912b 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -580,4 +580,260 @@ describe('Integration tests', function () { } ); }); + + describe('Create POS (callback)', function () { + it('should create a POS when validation is turned on', function (done) { + const requestBody = { + ...testData.createPos.successRequestBody, + Name: `Test Shop ${Date.now()}` + }; + validatedBarion.createPos(requestBody, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.include(testData.createPos.successResponseBody); + expect(res.PublicKey).to.be.a('string'); + expect(res.SecretKey).to.be.a('string'); + expect(res.Name).to.equal(requestBody.Name); + done(); + }); + }); + + it('should answer with BarionModelError when request object is not proper and validation is turned on', + function (done) { + validatedBarion.createPos(testData.createPos.errorRequestBody, (err, res) => { + expect(res).to.be.null; + expect(err.name).to.equal('BarionModelError'); + expect(err.errors).to.be.an('array').and.have.length.greaterThan(0); + done(); + }); + } + ); + + it('should create a POS when validation is turned off', function (done) { + const requestBody = { + ...testData.createPos.successRequestBody, + Name: `Test Shop ${Date.now()}` + }; + notValidatedBarion.createPos(requestBody, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.include(testData.createPos.successResponseBody); + expect(res.PublicKey).to.be.a('string'); + expect(res.SecretKey).to.be.a('string'); + expect(res.Name).to.equal(requestBody.Name); + done(); + }); + }); + + it('should answer with BarionError when request object is not proper and validation is turned off', + function (done) { + notValidatedBarion.createPos(testData.createPos.errorRequestBody, (err, res) => { + expect(res).to.be.null; + expect(err.name).to.equal('BarionError'); + expect(err.errors).to.be.an('array'); + expect(err.errors[0]).to.deep.include(testData.createPos.expectedError); + done(); + }); + } + ); + }); + + describe('Create POS (Promise)', function () { + it('should create a POS when validation is turned on', function (done) { + const requestBody = { + ...testData.createPos.successRequestBody, + Name: `Test Shop ${Date.now()}` + }; + validatedBarion.createPos(requestBody) + .then(res => { + expect(res).to.deep.include(testData.createPos.successResponseBody); + expect(res.PublicKey).to.be.a('string'); + expect(res.SecretKey).to.be.a('string'); + expect(res.Name).to.equal(requestBody.Name); + done(); + }); + }); + + it('should answer with BarionModelError when request object is not proper and validation is turned on', + function (done) { + validatedBarion.createPos(testData.createPos.errorRequestBody) + .catch(err => { + expect(err.name).to.equal('BarionModelError'); + expect(err.errors).to.be.an('array').and.have.length.greaterThan(0); + done(); + }); + } + ); + + it('should create a POS when validation is turned off', function (done) { + const requestBody = { + ...testData.createPos.successRequestBody, + Name: `Test Shop ${Date.now()}` + }; + notValidatedBarion.createPos(requestBody) + .then(res => { + expect(res).to.deep.include(testData.createPos.successResponseBody); + expect(res.PublicKey).to.be.a('string'); + expect(res.SecretKey).to.be.a('string'); + expect(res.Name).to.equal(requestBody.Name); + done(); + }); + }); + + it('should answer with BarionError when request object is not proper and validation is turned off', + function (done) { + notValidatedBarion.createPos(testData.createPos.errorRequestBody) + .catch(err => { + expect(err.name).to.equal('BarionError'); + expect(err.errors).to.be.an('array'); + expect(err.errors[0]).to.deep.include(testData.createPos.expectedError); + done(); + }); + } + ); + }); + + describe('Get POS details (callback)', function () { + + let successRequestBody = {}; + + beforeEach(function (done) { + const createPosRequest = { + ...testData.createPos.successRequestBody, + Name: `Test Shop ${Date.now()}` + }; + notValidatedBarion.createPos(createPosRequest, function (err, data) { + if (err) { + return done(err); + } + + successRequestBody = { + ApiKey: testData.createPos.successRequestBody.ApiKey, + PublicKey: data.PublicKey + }; + done(); + }); + }); + + afterEach(function () { + successRequestBody = null; + }); + + it('should get POS details when validation is turned on', function (done) { + validatedBarion.getPosDetails(successRequestBody, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.include(testData.getPosDetails.successResponseBody); + expect(res.PublicKey).to.equal(successRequestBody.PublicKey); + expect(res.Name).to.be.a('string'); + expect(res.Status).to.be.a('string'); + done(); + }); + }); + + it('should answer with BarionModelError when request object is not proper and validation is turned on', + function (done) { + validatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody, (err, res) => { + expect(res).to.be.null; + expect(err.name).to.equal('BarionError'); + expect(err.errors).to.be.an('array'); + expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + done(); + }); + } + ); + + it('should get POS details when validation is turned off', function (done) { + notValidatedBarion.getPosDetails(successRequestBody, (err, res) => { + expect(err).to.be.null; + expect(res).to.deep.include(testData.getPosDetails.successResponseBody); + expect(res.PublicKey).to.equal(successRequestBody.PublicKey); + expect(res.Name).to.be.a('string'); + expect(res.Status).to.be.a('string'); + done(); + }); + }); + + it('should answer with BarionError when request object is not proper and validation is turned off', + function (done) { + notValidatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody, (err, res) => { + expect(res).to.be.null; + expect(err.name).to.equal('BarionError'); + expect(err.errors).to.be.an('array'); + expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + done(); + }); + } + ); + }); + + describe('Get POS details (Promise)', function () { + + let successRequestBody = {}; + + beforeEach(function (done) { + const createPosRequest = { + ...testData.createPos.successRequestBody, + Name: `Test Shop ${Date.now()}` + }; + notValidatedBarion.createPos(createPosRequest, function (err, data) { + if (err) { + return done(err); + } + + successRequestBody = { + ApiKey: testData.createPos.successRequestBody.ApiKey, + PublicKey: data.PublicKey + }; + done(); + }); + }); + + afterEach(function () { + successRequestBody = null; + }); + + it('should get POS details when validation is turned on', function (done) { + validatedBarion.getPosDetails(successRequestBody) + .then(res => { + expect(res).to.deep.include(testData.getPosDetails.successResponseBody); + expect(res.PublicKey).to.equal(successRequestBody.PublicKey); + expect(res.Name).to.be.a('string'); + expect(res.Status).to.be.a('string'); + done(); + }); + }); + + it('should answer with BarionError when request object is not proper and validation is turned on', + function (done) { + validatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody) + .catch(err => { + expect(err.name).to.equal('BarionError'); + expect(err.errors).to.be.an('array'); + expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + done(); + }); + } + ); + + it('should get POS details when validation is turned off', function (done) { + notValidatedBarion.getPosDetails(successRequestBody) + .then(res => { + expect(res).to.deep.include(testData.getPosDetails.successResponseBody); + expect(res.PublicKey).to.equal(successRequestBody.PublicKey); + expect(res.Name).to.be.a('string'); + expect(res.Status).to.be.a('string'); + done(); + }); + }); + + it('should answer with BarionError when request object is not proper and validation is turned off', + function (done) { + notValidatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody) + .catch(err => { + expect(err.name).to.equal('BarionError'); + expect(err.errors).to.be.an('array'); + expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + done(); + }); + } + ); + }); }); diff --git a/test/integration/test-data.js b/test/integration/test-data.js index 973f452..29d21c4 100644 --- a/test/integration/test-data.js +++ b/test/integration/test-data.js @@ -191,5 +191,59 @@ module.exports = { expectedError: { ErrorCode: 'StatementNotFound' } + }, + createPos: { + successRequestBody: { + ApiKey, + Name: `Test Shop ${Date.now()}`, + Url: 'https://example.com', + Description: 'Integration test shop for automated testing purposes.', + Logo: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', + Category: [ 'Other' ], + BusinessContact: { + Name: 'Test Business Contact', + PhoneNumber: '36301234567', + Email: 'business@example.com' + }, + TechnicalContact: { + Name: 'Test Technical Contact', + PhoneNumber: '36301234568', + Email: 'tech@example.com' + }, + CustomerServiceContact: { + Name: 'Test Support Team', + PhoneNumber: '36301234569', + Email: 'support@example.com' + }, + PrimaryCurrency: 'HUF', + ExpectedTurnover: { Amount: 100000 }, + FullPixelImplemented: false, + UseForEInvoicing: false + }, + successResponseBody: { + Errors: [] + }, + errorRequestBody: { + ApiKey, + Name: 'Test Shop', + Url: 'https://example.com', + Description: 'Too short' + }, + expectedError: { + ErrorCode: 'ModelValidationError' + } + }, + getPosDetails: { + // successRequestBody: to be defined runtime, after creating a POS + successResponseBody: { + Errors: [] + }, + errorRequestBody: { + ApiKey, + PublicKey: '00000000-0000-0000-0000-000000000000' + }, + expectedError: { + ErrorCode: 'ShopNotFound' + } } }; From 4f1c6242814b503dd1133d47a0e1b841058b896d Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 05:38:52 +0100 Subject: [PATCH 04/18] refactor contacts --- lib/domain/pos/BaseContact.js | 25 ++++++++++++++++++++++++ lib/domain/pos/BusinessContact.js | 12 ++---------- lib/domain/pos/CustomerServiceContact.js | 12 ++---------- lib/domain/pos/TechnicalContact.js | 12 ++---------- 4 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 lib/domain/pos/BaseContact.js diff --git a/lib/domain/pos/BaseContact.js b/lib/domain/pos/BaseContact.js new file mode 100644 index 0000000..4f41ccf --- /dev/null +++ b/lib/domain/pos/BaseContact.js @@ -0,0 +1,25 @@ +const Joi = require('joi'); + +/** + * Base schema for contact details with common fields. + * This serves as the foundation for all contact types in Barion. + */ +const baseContactSchema = Joi.object({ + PhoneNumber: Joi.string().optional() + .max(30), + Email: Joi.string().optional() + .email() +}); + +/** + * Creates a contact schema with an optional Name field. + * Used for contacts that require a name (Business and Technical contacts). + */ +const namedContactSchema = baseContactSchema.keys({ + Name: Joi.string().optional() +}); + +module.exports = { + baseContactSchema, + namedContactSchema +}; diff --git a/lib/domain/pos/BusinessContact.js b/lib/domain/pos/BusinessContact.js index debb18e..440cf4e 100644 --- a/lib/domain/pos/BusinessContact.js +++ b/lib/domain/pos/BusinessContact.js @@ -1,17 +1,9 @@ -const Joi = require('joi'); const { CaseInsensitiveSchema } = require('../../schema'); +const { namedContactSchema } = require('./BaseContact'); /** * This structure represents the business contact details of a shop in Barion. * * @see {@link https://docs.barion.com/BusinessContact|Barion API Documentation} */ -const schema = Joi.object({ - Name: Joi.string().optional(), - PhoneNumber: Joi.string().optional() - .max(30), - Email: Joi.string().optional() - .email() -}); - -module.exports = new CaseInsensitiveSchema(schema); +module.exports = new CaseInsensitiveSchema(namedContactSchema); diff --git a/lib/domain/pos/CustomerServiceContact.js b/lib/domain/pos/CustomerServiceContact.js index 99e1518..a96667e 100644 --- a/lib/domain/pos/CustomerServiceContact.js +++ b/lib/domain/pos/CustomerServiceContact.js @@ -1,17 +1,9 @@ -const Joi = require('joi'); const { CaseInsensitiveSchema } = require('../../schema'); +const { baseContactSchema } = require('./BaseContact'); /** * This structure represents the customer service contact details of a shop in Barion. * * @see {@link https://docs.barion.com/CustomerServiceContact|Barion API Documentation} */ -const schema = Joi.object({ - Name: Joi.string().optional(), - PhoneNumber: Joi.string().optional() - .max(30), - Email: Joi.string().optional() - .email() -}); - -module.exports = new CaseInsensitiveSchema(schema); +module.exports = new CaseInsensitiveSchema(baseContactSchema); diff --git a/lib/domain/pos/TechnicalContact.js b/lib/domain/pos/TechnicalContact.js index 84672d1..36b8579 100644 --- a/lib/domain/pos/TechnicalContact.js +++ b/lib/domain/pos/TechnicalContact.js @@ -1,17 +1,9 @@ -const Joi = require('joi'); const { CaseInsensitiveSchema } = require('../../schema'); +const { namedContactSchema } = require('./BaseContact'); /** * This structure represents the technical contact details of a shop in Barion. * * @see {@link https://docs.barion.com/TechnicalContact|Barion API Documentation} */ -const schema = Joi.object({ - Name: Joi.string().optional(), - PhoneNumber: Joi.string().optional() - .max(30), - Email: Joi.string().optional() - .email() -}); - -module.exports = new CaseInsensitiveSchema(schema); +module.exports = new CaseInsensitiveSchema(namedContactSchema); From d1d13e032bfd811748f421b3f6188adeeabb1406 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 05:46:02 +0100 Subject: [PATCH 05/18] extend test coverage --- test/services.test.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/services.test.js b/test/services.test.js index 7ec9099..cc4f8d4 100644 --- a/test/services.test.js +++ b/test/services.test.js @@ -250,6 +250,34 @@ describe('lib/services.js', function () { }); }); + describe('#getPosDetails(environment, options)', function () { + it('should return a Promise on success', function (done) { + const services = serviceMocks.okService; + services.getPosDetails('test', {}) + .then(() => done()); + }); + + it('should return a Promise on failure', function (done) { + const services = serviceMocks.errorService; + services.getPosDetails('test', {}) + .catch(() => done()); + }); + }); + + describe('#createPos(environment, options)', function () { + it('should return a Promise on success', function (done) { + const services = serviceMocks.okService; + services.createPos('test', {}) + .then(() => done()); + }); + + it('should return a Promise on failure', function (done) { + const services = serviceMocks.errorService; + services.createPos('test', {}) + .catch(() => done()); + }); + }); + describe('#getBaseUrl', function () { it('should return test environment\'s URL to pointless input', function () { const services = serviceMocks.okService; From bca20df06f096a30457231eef057cbbd841ec513 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 05:52:00 +0100 Subject: [PATCH 06/18] update readme.md --- README.md | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aff0ac5..3b2d7be 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Helps manage e-payment transactions through the [Barion Smart Gateway](https://w - [Get existing accounts of the user](#get-existing-accounts-of-the-user---bariongetaccountsoptions-callback) - [Send money to a Barion user or email address](#send-e-money-to-an-email-address---barionemailtransferoptions-callback) - [Download statement files](#download-statement-files---bariondownloadstatementoptions-callback) + - [Get POS details](#get-pos-details---bariongetposdetailsoptions-callback) + - [Create a new POS](#create-a-new-pos---barioncreateposoptions-callback) - [Handling errors](#handling-errors) - [Secure vs. insecure mode](#secure-vs-insecure-mode) @@ -83,7 +85,9 @@ barion.getPaymentState({ - send money out of Barion via international bank transfer - get existing accounts of the user - send money to existing Barion account or email address -- download monthly or daily statements of your account. +- download monthly or daily statements of your account +- get details of a specific POS (shop) +- create a new POS (shop) in Barion. > **IMPORTANT**: ``node-barion`` is completely consistent with [Barion Docs](https://docs.barion.com/Main_Page), so you can use exactly the same field names, that are specified in it. **Reading the official Barion documentation is highly recommended** before starting to use ``node-barion`` module.
> **IMPORTANT**: Barion uses *PascalCased* field naming, but **node-barion is case insensitive** (this means that if Barion Docs mention a field name *PaymentId*, you can either use *PaymentId*, *paymentId*, *paymentid* or *paymentID* notation in your application, as ``node-barion`` converts these to the standard *PaymentId* name). @@ -670,6 +674,187 @@ barion.downloadStatement({ }); ``` +### Get POS details - barion.getPosDetails(options, \[callback\]) +To retrieve details and the current state of a specific POS (shop), call the ``getPosDetails`` function. [[Barion Docs](https://docs.barion.com/Pos-Get-v1)] + +**Authentication**: This endpoint requires Api Key authentication. + +**Parameters**: + - ``ApiKey``: Api Key for authentication (string). (required) + + - ``PublicKey``: The public key (identifier) of the POS to retrieve details for (string, GUID format). (required) + +**Output**: [Read at Barion Docs](https://docs.barion.com/Pos-Get-v1#Output_properties) + +#### Usage example +##### With callback +```js +barion.getPosDetails({ + ApiKey: 'your-api-key-here', + PublicKey: '12345678-1234-1234-1234-123456789012' +}, function (err, data) { + //handle error / process data +}); +``` +##### With promise +```js +barion.getPosDetails({ + ApiKey: 'your-api-key-here', + PublicKey: '12345678-1234-1234-1234-123456789012' +}).then(data => { + //process data +}).catch(err => { + //handle error +}); +``` + +### Create a new POS - barion.createPos(options, \[callback\]) +To create a new POS (shop) in the Barion system, call the ``createPos`` function. [[Barion Docs](https://docs.barion.com/Pos-Create-v1)] + +**Authentication**: This endpoint requires Api Key authentication. + +**Parameters**: + - ``ApiKey``: Api Key for authentication (string). (required) + + - ``Name``: The name of the shop (string, max 200 characters). (required) + + - ``Url``: The URL of the shop (string, max 2000 characters, must be HTTPS). (required) + + - ``Description``: Description of the shop (string, min 20, max 200 characters). (required) + + - ``Logo``: Base64 encoded logo image of the shop (string). (required) + + - ``Category``: Array of shop categories (string[]). (required)
+ Allowed values include: + - ``'FashionAndAccessories'`` + - ``'ChildProducts'`` + - ``'HomeAndGarden'`` + - ``'HealthAndBeauty'`` + - ``'Books'`` + - ``'ToysAndGames'`` + - ``'ElectronicsAndComputers'`` + - ``'FoodAndDrinks'`` + - ``'SportsAndLeisure'`` + - ``'Services'`` + - ``'TravelAndEntertainment'`` + - ``'DigitalContent'`` + - ``'Other'`` + + - ``BusinessContact``: Contact information for business matters (object). (required) + - ``Name``: Contact person name (string). (required) + - ``PhoneNumber``: Contact phone number (string, max 30 characters). (required) + - ``Email``: Contact email address (string, valid email format). (required) + + - ``TechnicalContact``: Contact information for technical matters (object). (required) + - ``Name``: Contact person name (string). (required) + - ``PhoneNumber``: Contact phone number (string, max 30 characters). (required) + - ``Email``: Contact email address (string, valid email format). (required) + + - ``CustomerServiceContact``: Contact information for customer service (object). (required) + - ``Name``: Contact person name (string). (required) + - ``PhoneNumber``: Contact phone number (string, max 30 characters). (required) + - ``Email``: Contact email address (string, valid email format). (required) + + - ``PrimaryCurrency``: The primary currency of the shop (string). (required)
+ Allowed values are: + - ``'HUF'`` (Hungarian forint) + - ``'CZK'`` (Czech crown) + - ``'EUR'`` (Euro) + - ``'USD'`` (U.S. dollar) + + - ``ExpectedTurnover``: The expected turnover for the shop (object). (required) + - ``Amount``: Expected turnover amount (number, integer, greater than 0). (required) + + - ``FullPixelImplemented``: Indicates if full pixel tracking is implemented (boolean). (required) + + - ``UseForEInvoicing``: Indicates if the shop will be used for e-invoicing (boolean). (required) + + - ``AverageBasketValue``: Average basket value in the shop (number, integer, greater than 0). (optional) + + - ``PercentageOfB2BCustomers``: Percentage of B2B customers (number, 0-100). (optional) + + - ``PercentageOfNonEuCards``: Percentage of non-EU cards (number, 0-100). (optional) + + - ``CallBackUrl``: Callback URL for the shop (string, max 2000 characters, valid URI). (optional) + + - ``ReferenceId``: Reference identifier for the shop (string). (optional) + + - ``NoteForApproval``: Note for the approval process (string). (optional) + + - ``CustomTemplate``: Custom template for the shop (string). (optional) + + - ``CustomCss``: Custom CSS for the shop (string). (optional) + +**Output**: [Read at Barion Docs](https://docs.barion.com/Pos-Create-v1#Output_properties) + +#### Usage example +##### With callback +```js +barion.createPos({ + ApiKey: 'your-api-key-here', + Name: 'My Awesome Shop', + Url: 'https://example.com', + Description: 'This is a shop that sells awesome products and provides great service.', + Logo: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', + Category: [ 'ElectronicsAndComputers' ], + BusinessContact: { + Name: 'John Doe', + PhoneNumber: '36301234567', + Email: 'business@example.com' + }, + TechnicalContact: { + Name: 'Jane Smith', + PhoneNumber: '36301234568', + Email: 'tech@example.com' + }, + CustomerServiceContact: { + Name: 'Support Team', + PhoneNumber: '36301234569', + Email: 'support@example.com' + }, + PrimaryCurrency: 'HUF', + ExpectedTurnover: { Amount: 100000 }, + FullPixelImplemented: false, + UseForEInvoicing: false +}, function (err, data) { + //handle error / process data +}); +``` +##### With promise +```js +barion.createPos({ + ApiKey: 'your-api-key-here', + Name: 'My Awesome Shop', + Url: 'https://example.com', + Description: 'This is a shop that sells awesome products and provides great service.', + Logo: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', + Category: [ 'ElectronicsAndComputers' ], + BusinessContact: { + Name: 'John Doe', + PhoneNumber: '36301234567', + Email: 'business@example.com' + }, + TechnicalContact: { + Name: 'Jane Smith', + PhoneNumber: '36301234568', + Email: 'tech@example.com' + }, + CustomerServiceContact: { + Name: 'Support Team', + PhoneNumber: '36301234569', + Email: 'support@example.com' + }, + PrimaryCurrency: 'HUF', + ExpectedTurnover: { Amount: 100000 }, + FullPixelImplemented: false, + UseForEInvoicing: false +}).then(data => { + //process data +}).catch(err => { + //handle error +}); +``` + ### Handling errors There are 3 main types of errors that can be thrown when you use the ``node-barion`` module: - ``BarionError``: Thrown when the Barion system responds with errors. From 0123ee9bf932c8b3b771b42334e6814201b6e0f9 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 06:10:20 +0100 Subject: [PATCH 07/18] more contact refactor --- lib/domain/CreatePos.js | 25 ++++-------------------- lib/domain/pos/BaseContact.js | 8 ++++---- lib/domain/pos/BusinessContact.js | 9 --------- lib/domain/pos/CustomerServiceContact.js | 9 --------- lib/domain/pos/TechnicalContact.js | 9 --------- 5 files changed, 8 insertions(+), 52 deletions(-) delete mode 100644 lib/domain/pos/BusinessContact.js delete mode 100644 lib/domain/pos/CustomerServiceContact.js delete mode 100644 lib/domain/pos/TechnicalContact.js diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index a961fbe..a545036 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -2,6 +2,7 @@ const Joi = require('joi'); const { CaseInsensitiveSchema } = require('../schema'); const { ShopCategory } = require('./_constraints'); const ExpectedTurnover = require('./pos/ExpectedTurnover'); +const { baseContactSchema, namedContactSchema } = require('./pos/BaseContact'); /** * Used to create a shop (POS) in Barion. @@ -25,27 +26,9 @@ const schema = Joi.object({ Category: Joi.array().required() .items(Joi.string().valid(...ShopCategory)) .min(1), - BusinessContact: Joi.object({ - Name: Joi.string().required(), - PhoneNumber: Joi.string().required() - .max(30), - Email: Joi.string().required() - .email() - }).required(), - TechnicalContact: Joi.object({ - Name: Joi.string().required(), - PhoneNumber: Joi.string().required() - .max(30), - Email: Joi.string().required() - .email() - }).required(), - CustomerServiceContact: Joi.object({ - Name: Joi.string().required(), - PhoneNumber: Joi.string().required() - .max(30), - Email: Joi.string().required() - .email() - }).required(), + BusinessContact: namedContactSchema.required(), + TechnicalContact: namedContactSchema.required(), + CustomerServiceContact: baseContactSchema.required(), PrimaryCurrency: Joi.string().required() .valid('HUF', 'CZK', 'EUR', 'USD'), ExpectedTurnover: ExpectedTurnover.required(), diff --git a/lib/domain/pos/BaseContact.js b/lib/domain/pos/BaseContact.js index 4f41ccf..30e2f6b 100644 --- a/lib/domain/pos/BaseContact.js +++ b/lib/domain/pos/BaseContact.js @@ -5,18 +5,18 @@ const Joi = require('joi'); * This serves as the foundation for all contact types in Barion. */ const baseContactSchema = Joi.object({ - PhoneNumber: Joi.string().optional() + PhoneNumber: Joi.string().required() .max(30), - Email: Joi.string().optional() + Email: Joi.string().required() .email() }); /** - * Creates a contact schema with an optional Name field. + * Creates a contact schema with a required Name field. * Used for contacts that require a name (Business and Technical contacts). */ const namedContactSchema = baseContactSchema.keys({ - Name: Joi.string().optional() + Name: Joi.string().required() }); module.exports = { diff --git a/lib/domain/pos/BusinessContact.js b/lib/domain/pos/BusinessContact.js deleted file mode 100644 index 440cf4e..0000000 --- a/lib/domain/pos/BusinessContact.js +++ /dev/null @@ -1,9 +0,0 @@ -const { CaseInsensitiveSchema } = require('../../schema'); -const { namedContactSchema } = require('./BaseContact'); - -/** - * This structure represents the business contact details of a shop in Barion. - * - * @see {@link https://docs.barion.com/BusinessContact|Barion API Documentation} - */ -module.exports = new CaseInsensitiveSchema(namedContactSchema); diff --git a/lib/domain/pos/CustomerServiceContact.js b/lib/domain/pos/CustomerServiceContact.js deleted file mode 100644 index a96667e..0000000 --- a/lib/domain/pos/CustomerServiceContact.js +++ /dev/null @@ -1,9 +0,0 @@ -const { CaseInsensitiveSchema } = require('../../schema'); -const { baseContactSchema } = require('./BaseContact'); - -/** - * This structure represents the customer service contact details of a shop in Barion. - * - * @see {@link https://docs.barion.com/CustomerServiceContact|Barion API Documentation} - */ -module.exports = new CaseInsensitiveSchema(baseContactSchema); diff --git a/lib/domain/pos/TechnicalContact.js b/lib/domain/pos/TechnicalContact.js deleted file mode 100644 index 36b8579..0000000 --- a/lib/domain/pos/TechnicalContact.js +++ /dev/null @@ -1,9 +0,0 @@ -const { CaseInsensitiveSchema } = require('../../schema'); -const { namedContactSchema } = require('./BaseContact'); - -/** - * This structure represents the technical contact details of a shop in Barion. - * - * @see {@link https://docs.barion.com/TechnicalContact|Barion API Documentation} - */ -module.exports = new CaseInsensitiveSchema(namedContactSchema); From e041f63e6af25019d29cda3ecfa7768a784da14e Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 06:32:35 +0100 Subject: [PATCH 08/18] fix ExptectedTurnover --- README.md | 13 +++++++++---- lib/domain/CreatePos.js | 2 +- lib/domain/pos/ExpectedTurnover.js | 16 ++++++++++------ test/barion.test.js | 2 +- test/integration/test-data.js | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3b2d7be..80c8ef4 100644 --- a/README.md +++ b/README.md @@ -762,8 +762,13 @@ To create a new POS (shop) in the Barion system, call the ``createPos`` function - ``'EUR'`` (Euro) - ``'USD'`` (U.S. dollar) - - ``ExpectedTurnover``: The expected turnover for the shop (object). (required) - - ``Amount``: Expected turnover amount (number, integer, greater than 0). (required) + - ``ExpectedTurnover``: The expected turnover tier for the shop (object). (required) + - ``ExpectedTurnover``: Turnover tier value (number, integer, 1-6). (required)
+ The value represents predefined turnover ranges based on PrimaryCurrency: + - For HUF: 1 (1-100K), 2 (100K-1M), 3 (1M-10M), 4 (10M-29M), 5 (29M-99M), 6 (99M+) + - For EUR: 1 (1-300), 2 (301-3K), 3 (3K-30K), 4 (30K-90K), 5 (90K-300K), 6 (300K+) + - For CZK: 1 (1-8K), 2 (8K-80K), 3 (80K-800K), 4 (800K-2.2M), 5 (2.2M-7.7M), 6 (7.7M+) + - For USD: 1 (1-350), 2 (351-3.5K), 3 (3.5K-35K), 4 (35K-100K), 5 (100K-345K), 6 (345K+) - ``FullPixelImplemented``: Indicates if full pixel tracking is implemented (boolean). (required) @@ -813,7 +818,7 @@ barion.createPos({ Email: 'support@example.com' }, PrimaryCurrency: 'HUF', - ExpectedTurnover: { Amount: 100000 }, + ExpectedTurnover: { ExpectedTurnover: 3 }, FullPixelImplemented: false, UseForEInvoicing: false }, function (err, data) { @@ -845,7 +850,7 @@ barion.createPos({ Email: 'support@example.com' }, PrimaryCurrency: 'HUF', - ExpectedTurnover: { Amount: 100000 }, + ExpectedTurnover: { ExpectedTurnover: 3 }, FullPixelImplemented: false, UseForEInvoicing: false }).then(data => { diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index a545036..0ce0da7 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -1,8 +1,8 @@ const Joi = require('joi'); const { CaseInsensitiveSchema } = require('../schema'); const { ShopCategory } = require('./_constraints'); -const ExpectedTurnover = require('./pos/ExpectedTurnover'); const { baseContactSchema, namedContactSchema } = require('./pos/BaseContact'); +const ExpectedTurnover = require('./pos/ExpectedTurnover'); /** * Used to create a shop (POS) in Barion. diff --git a/lib/domain/pos/ExpectedTurnover.js b/lib/domain/pos/ExpectedTurnover.js index 49f8c23..8894c8a 100644 --- a/lib/domain/pos/ExpectedTurnover.js +++ b/lib/domain/pos/ExpectedTurnover.js @@ -4,15 +4,19 @@ const { CaseInsensitiveSchema } = require('../../schema'); /** * This structure represents the expected turnover for a shop in Barion. * + * The ExpectedTurnover field value represents predefined turnover ranges based on currency: + * - For HUF: 1 (1-100K), 2 (100K-1M), 3 (1M-10M), 4 (10M-29M), 5 (29M-99M), 6 (99M+) + * - For EUR: 1 (1-300), 2 (301-3K), 3 (3K-30K), 4 (30K-90K), 5 (90K-300K), 6 (300K+) + * - For CZK: 1 (1-8K), 2 (8K-80K), 3 (80K-800K), 4 (800K-2.2M), 5 (2.2M-7.7M), 6 (7.7M+) + * - For USD: 1 (1-350), 2 (351-3.5K), 3 (3.5K-35K), 4 (35K-100K), 5 (100K-345K), 6 (345K+) + * * @see {@link https://docs.barion.com/ExpectedTurnover|Barion API Documentation} */ const schema = Joi.object({ - MinAmount: Joi.number().optional() - .min(0), - MaxAmount: Joi.number().optional() - .min(0), - Amount: Joi.number().optional() - .min(0) + ExpectedTurnover: Joi.number().required() + .integer() + .min(1) + .max(6) }); module.exports = new CaseInsensitiveSchema(schema); diff --git a/test/barion.test.js b/test/barion.test.js index 1633e89..6c92c2d 100644 --- a/test/barion.test.js +++ b/test/barion.test.js @@ -1384,7 +1384,7 @@ describe('lib/barion.js', function () { Email: 'support@example.com' }, PrimaryCurrency: 'EUR', - ExpectedTurnover: { Amount: 10000 }, + ExpectedTurnover: { ExpectedTurnover: 3 }, FullPixelImplemented: true, UseForEInvoicing: false }; diff --git a/test/integration/test-data.js b/test/integration/test-data.js index 29d21c4..0832164 100644 --- a/test/integration/test-data.js +++ b/test/integration/test-data.js @@ -216,7 +216,7 @@ module.exports = { Email: 'support@example.com' }, PrimaryCurrency: 'HUF', - ExpectedTurnover: { Amount: 100000 }, + ExpectedTurnover: { ExpectedTurnover: 3 }, FullPixelImplemented: false, UseForEInvoicing: false }, From c1695b46913b5b5a885a055a5179970965392bb3 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 06:42:17 +0100 Subject: [PATCH 09/18] fix property names and value case insensitivity --- lib/domain/CreatePos.js | 12 ++++++------ lib/domain/pos/BaseContact.js | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index 0ce0da7..d5af1c9 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -1,7 +1,7 @@ const Joi = require('joi'); -const { CaseInsensitiveSchema } = require('../schema'); -const { ShopCategory } = require('./_constraints'); -const { baseContactSchema, namedContactSchema } = require('./pos/BaseContact'); +const CaseInsensitiveSchema = require('../schema'); +const ShopCategory = require('./_constraints'); +const { BaseContactSchema, NamedContactSchema } = require('./pos/BaseContact'); const ExpectedTurnover = require('./pos/ExpectedTurnover'); /** @@ -26,9 +26,9 @@ const schema = Joi.object({ Category: Joi.array().required() .items(Joi.string().valid(...ShopCategory)) .min(1), - BusinessContact: namedContactSchema.required(), - TechnicalContact: namedContactSchema.required(), - CustomerServiceContact: baseContactSchema.required(), + BusinessContact: NamedContactSchema.required(), + TechnicalContact: NamedContactSchema.required(), + CustomerServiceContact: BaseContactSchema.required(), PrimaryCurrency: Joi.string().required() .valid('HUF', 'CZK', 'EUR', 'USD'), ExpectedTurnover: ExpectedTurnover.required(), diff --git a/lib/domain/pos/BaseContact.js b/lib/domain/pos/BaseContact.js index 30e2f6b..0f5aa42 100644 --- a/lib/domain/pos/BaseContact.js +++ b/lib/domain/pos/BaseContact.js @@ -1,5 +1,5 @@ const Joi = require('joi'); - +const { CaseInsensitiveSchema } = require('../../schema'); /** * Base schema for contact details with common fields. * This serves as the foundation for all contact types in Barion. @@ -20,6 +20,6 @@ const namedContactSchema = baseContactSchema.keys({ }); module.exports = { - baseContactSchema, - namedContactSchema + baseContactSchema: new CaseInsensitiveSchema(baseContactSchema), + namedContactSchema: new CaseInsensitiveSchema(namedContactSchema) }; From 547aa38ad7771163fdc371e93351708859f1b834 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 06:52:02 +0100 Subject: [PATCH 10/18] fix shopcategories in readme --- README.md | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 80c8ef4..479937a 100644 --- a/README.md +++ b/README.md @@ -725,20 +725,32 @@ To create a new POS (shop) in the Barion system, call the ``createPos`` function - ``Logo``: Base64 encoded logo image of the shop (string). (required) - ``Category``: Array of shop categories (string[]). (required)
- Allowed values include: - - ``'FashionAndAccessories'`` - - ``'ChildProducts'`` - - ``'HomeAndGarden'`` - - ``'HealthAndBeauty'`` - - ``'Books'`` - - ``'ToysAndGames'`` - - ``'ElectronicsAndComputers'`` - - ``'FoodAndDrinks'`` - - ``'SportsAndLeisure'`` - - ``'Services'`` - - ``'TravelAndEntertainment'`` - - ``'DigitalContent'`` + Allowed values are: + - ``'Agriculture'`` + - ``'BookNewsPaper'`` + - ``'Ad'`` + - ``'BonusCoupon'`` + - ``'Dating'`` + - ``'Electronics'`` + - ``'FashionClothes'`` + - ``'FoodDrink'`` + - ``'FurnitureAntiquity'`` + - ``'GiftToyFlower'`` + - ``'BeautyHealth'`` + - ``'HomeDesignGarden'`` + - ``'JobEducation'`` + - ``'BuildingMaterialMachine'`` + - ``'Baby'`` + - ``'FilmMusic'`` - ``'Other'`` + - ``'Pet'`` + - ``'Property'`` + - ``'Service'`` + - ``'SportLeisureTravel'`` + - ``'BettingGambling'`` + - ``'Tobacco'`` + - ``'Vehicle'`` + - ``'WatchJewelry'`` - ``BusinessContact``: Contact information for business matters (object). (required) - ``Name``: Contact person name (string). (required) @@ -801,7 +813,7 @@ barion.createPos({ Url: 'https://example.com', Description: 'This is a shop that sells awesome products and provides great service.', Logo: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', - Category: [ 'ElectronicsAndComputers' ], + Category: [ 'Electronics' ], BusinessContact: { Name: 'John Doe', PhoneNumber: '36301234567', @@ -833,7 +845,7 @@ barion.createPos({ Url: 'https://example.com', Description: 'This is a shop that sells awesome products and provides great service.', Logo: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', - Category: [ 'ElectronicsAndComputers' ], + Category: [ 'Electronics' ], BusinessContact: { Name: 'John Doe', PhoneNumber: '36301234567', From 12d423dca2900e96fdb260ba19660aee549146bb Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Tue, 28 Oct 2025 07:11:45 +0100 Subject: [PATCH 11/18] fix import errors --- lib/domain/CreatePos.js | 4 ++-- lib/domain/pos/BaseContact.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index d5af1c9..58e1342 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -1,6 +1,6 @@ const Joi = require('joi'); -const CaseInsensitiveSchema = require('../schema'); -const ShopCategory = require('./_constraints'); +const { CaseInsensitiveSchema } = require('../schema'); +const { ShopCategory } = require('./_constraints'); const { BaseContactSchema, NamedContactSchema } = require('./pos/BaseContact'); const ExpectedTurnover = require('./pos/ExpectedTurnover'); diff --git a/lib/domain/pos/BaseContact.js b/lib/domain/pos/BaseContact.js index 0f5aa42..aea39f2 100644 --- a/lib/domain/pos/BaseContact.js +++ b/lib/domain/pos/BaseContact.js @@ -20,6 +20,6 @@ const namedContactSchema = baseContactSchema.keys({ }); module.exports = { - baseContactSchema: new CaseInsensitiveSchema(baseContactSchema), - namedContactSchema: new CaseInsensitiveSchema(namedContactSchema) + BaseContactSchema: new CaseInsensitiveSchema(baseContactSchema), + NamedContactSchema: new CaseInsensitiveSchema(namedContactSchema) }; From 2bd90a2757280b4a2892316a156734d07d90a2c3 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 19:52:13 +0100 Subject: [PATCH 12/18] fix error in getPosDetails url --- lib/constants.js | 2 +- lib/services.js | 22 +++++++++++++++++++++- test/services.test.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index 4c8f9dc..02cc673 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -100,7 +100,7 @@ module.exports = { }, getPosDetails: { method: 'GET', - path: '/v1/Pos', + path: '/v1/Pos/{PublicKey}', model: GetPosDetails }, createPos: { diff --git a/lib/services.js b/lib/services.js index 22fdc67..3fb50d1 100644 --- a/lib/services.js +++ b/lib/services.js @@ -14,6 +14,25 @@ function getBaseUrl (environment) { return baseUrls[environment]; } +/** + * Replaces path parameters in the path template with values from options. + * @param {String} path - The path template with placeholders like '/api/{id}/details'. + * @param {Object} options - The options object containing parameter values. + * @returns {String} The path with parameters replaced. + */ +function replacePathParameters (path, options) { + let finalPath = path; + const pathParamRegex = /\{(\w+)\}/g; + let match; + while ((match = pathParamRegex.exec(path)) !== null) { + const paramName = match[1]; + if (options[paramName]) { + finalPath = finalPath.replace(match[0], options[paramName]); + } + } + return finalPath; +} + /** * Sends the request to Barion API based on given information. * @param {String} environment - The environment to use ('prod' or 'test'). @@ -24,7 +43,7 @@ function doRequest (environment, endpoint, options) { const { method, path } = endpoint; const url = new URL(getBaseUrl(environment)); - url.pathname = path; + url.pathname = replacePathParameters(path, options); if (endpoint.binary) { return getBinaryFromBarion(url, options); @@ -203,6 +222,7 @@ module.exports = { createPos, _private: { getBaseUrl, + replacePathParameters, doRequest } }; diff --git a/test/services.test.js b/test/services.test.js index cc4f8d4..56c426a 100644 --- a/test/services.test.js +++ b/test/services.test.js @@ -345,4 +345,36 @@ describe('lib/services.js', function () { expect(doRequest).to.throw(/HTTP method/); }); }); + + describe('#replacePathParameters', function () { + const services = require('../lib/services'); + + it('should replace path parameters with values from options', function () { + const path = '/api/{userId}/orders/{orderId}'; + const options = { userId: '123', orderId: '456' }; + const result = services._private.replacePathParameters(path, options); + expect(result).to.equal('/api/123/orders/456'); + }); + + it('should return original path when no parameters are in path', function () { + const path = '/api/orders'; + const options = { userId: '123' }; + const result = services._private.replacePathParameters(path, options); + expect(result).to.equal('/api/orders'); + }); + + it('should not replace parameters that are not in options', function () { + const path = '/api/{userId}/orders/{orderId}'; + const options = { userId: '123' }; + const result = services._private.replacePathParameters(path, options); + expect(result).to.equal('/api/123/orders/{orderId}'); + }); + + it('should handle empty options object', function () { + const path = '/api/{userId}/orders'; + const options = {}; + const result = services._private.replacePathParameters(path, options); + expect(result).to.equal('/api/{userId}/orders'); + }); + }); }); From 790db90e64b3f1f7e822e49a46411e6fdbdf1804 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 19:57:29 +0100 Subject: [PATCH 13/18] add statusCode to BarionError fix minor errors surfaced during testing --- lib/domain/CreatePos.js | 12 +++++++----- lib/domain/pos/ExpectedTurnover.js | 22 ---------------------- lib/errors.js | 4 +++- lib/fetch-api.js | 4 +++- test/barion.test.js | 5 +++-- test/integration/integration.test.js | 14 +++++--------- test/integration/test-data.js | 8 +++++--- 7 files changed, 26 insertions(+), 43 deletions(-) delete mode 100644 lib/domain/pos/ExpectedTurnover.js diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index 58e1342..27027a3 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -1,8 +1,7 @@ const Joi = require('joi'); const { CaseInsensitiveSchema } = require('../schema'); const { ShopCategory } = require('./_constraints'); -const { BaseContactSchema, NamedContactSchema } = require('./pos/BaseContact'); -const ExpectedTurnover = require('./pos/ExpectedTurnover'); +const { NamedContactSchema } = require('./pos/BaseContact'); /** * Used to create a shop (POS) in Barion. @@ -28,10 +27,13 @@ const schema = Joi.object({ .min(1), BusinessContact: NamedContactSchema.required(), TechnicalContact: NamedContactSchema.required(), - CustomerServiceContact: BaseContactSchema.required(), + CustomerServiceContact: NamedContactSchema.required(), PrimaryCurrency: Joi.string().required() .valid('HUF', 'CZK', 'EUR', 'USD'), - ExpectedTurnover: ExpectedTurnover.required(), + ExpectedTurnover: Joi.number().required() + .integer() + .min(1) + .max(6), AverageBasketValue: Joi.number().optional() .integer() .greater(0), @@ -43,7 +45,7 @@ const schema = Joi.object({ .max(100), FullPixelImplemented: Joi.boolean().required(), UseForEInvoicing: Joi.boolean().required(), - CallBackUrl: Joi.string().optional() + CallBackUrl: Joi.string().required() .max(2000) .uri(), ReferenceId: Joi.string().optional(), diff --git a/lib/domain/pos/ExpectedTurnover.js b/lib/domain/pos/ExpectedTurnover.js deleted file mode 100644 index 8894c8a..0000000 --- a/lib/domain/pos/ExpectedTurnover.js +++ /dev/null @@ -1,22 +0,0 @@ -const Joi = require('joi'); -const { CaseInsensitiveSchema } = require('../../schema'); - -/** - * This structure represents the expected turnover for a shop in Barion. - * - * The ExpectedTurnover field value represents predefined turnover ranges based on currency: - * - For HUF: 1 (1-100K), 2 (100K-1M), 3 (1M-10M), 4 (10M-29M), 5 (29M-99M), 6 (99M+) - * - For EUR: 1 (1-300), 2 (301-3K), 3 (3K-30K), 4 (30K-90K), 5 (90K-300K), 6 (300K+) - * - For CZK: 1 (1-8K), 2 (8K-80K), 3 (80K-800K), 4 (800K-2.2M), 5 (2.2M-7.7M), 6 (7.7M+) - * - For USD: 1 (1-350), 2 (351-3.5K), 3 (3.5K-35K), 4 (35K-100K), 5 (100K-345K), 6 (345K+) - * - * @see {@link https://docs.barion.com/ExpectedTurnover|Barion API Documentation} - */ -const schema = Joi.object({ - ExpectedTurnover: Joi.number().required() - .integer() - .min(1) - .max(6) -}); - -module.exports = new CaseInsensitiveSchema(schema); diff --git a/lib/errors.js b/lib/errors.js index 9a860d9..f708d1c 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -2,12 +2,14 @@ * Represents error, sent by Barion API. * @param {String} message - Error message. * @param {Array} errors - Barion's "Errors" array. + * @param {Number} statusCode - HTTP status code. */ class BarionError extends Error { - constructor (message, errors) { + constructor (message, errors, statusCode) { super(message); this.name = this.constructor.name; this.errors = Array.isArray(errors) ? errors : []; + this.statusCode = statusCode; } } diff --git a/lib/fetch-api.js b/lib/fetch-api.js index d270cb8..c7e731f 100644 --- a/lib/fetch-api.js +++ b/lib/fetch-api.js @@ -101,6 +101,7 @@ function isErrorResponse (res) { */ function fetchBarion (url, options, apiKey, binary = false) { let success; + let statusCode; if (apiKey) { options = setApiKeyAuthentication(options, apiKey); @@ -109,6 +110,7 @@ function fetchBarion (url, options, apiKey, binary = false) { return fetch(url, options) .then(res => { success = res.ok; + statusCode = res.status; if (binary && success) { return res.buffer().then(buffer => { @@ -124,7 +126,7 @@ function fetchBarion (url, options, apiKey, binary = false) { .catch(handleError) // response is not a valid JSON or network error occured .then(data => { if (!success || isErrorResponse(data)) { - throw new BarionError('Barion request errored out', data.Errors || data.ErrorList); + throw new BarionError('Barion request errored out', data.Errors || data.ErrorList, statusCode); } return data; diff --git a/test/barion.test.js b/test/barion.test.js index 6c92c2d..cd68b21 100644 --- a/test/barion.test.js +++ b/test/barion.test.js @@ -1384,9 +1384,10 @@ describe('lib/barion.js', function () { Email: 'support@example.com' }, PrimaryCurrency: 'EUR', - ExpectedTurnover: { ExpectedTurnover: 3 }, + ExpectedTurnover: 3, FullPixelImplemented: true, - UseForEInvoicing: false + UseForEInvoicing: false, + CallBackUrl: 'https://example.com/callback' }; it('should answer with callback on success', function (done) { diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 0a7912b..ea3b14e 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -728,13 +728,12 @@ describe('Integration tests', function () { }); }); - it('should answer with BarionModelError when request object is not proper and validation is turned on', + it('should answer with BarionError when request object is not proper and validation is turned on', function (done) { validatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody, (err, res) => { expect(res).to.be.null; expect(err.name).to.equal('BarionError'); - expect(err.errors).to.be.an('array'); - expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode); done(); }); } @@ -756,8 +755,7 @@ describe('Integration tests', function () { notValidatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody, (err, res) => { expect(res).to.be.null; expect(err.name).to.equal('BarionError'); - expect(err.errors).to.be.an('array'); - expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode); done(); }); } @@ -806,8 +804,7 @@ describe('Integration tests', function () { validatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody) .catch(err => { expect(err.name).to.equal('BarionError'); - expect(err.errors).to.be.an('array'); - expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.statusCode); done(); }); } @@ -829,8 +826,7 @@ describe('Integration tests', function () { notValidatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody) .catch(err => { expect(err.name).to.equal('BarionError'); - expect(err.errors).to.be.an('array'); - expect(err.errors[0]).to.deep.include(testData.getPosDetails.expectedError); + expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.statusCode); done(); }); } diff --git a/test/integration/test-data.js b/test/integration/test-data.js index 0832164..18ea2a8 100644 --- a/test/integration/test-data.js +++ b/test/integration/test-data.js @@ -216,9 +216,10 @@ module.exports = { Email: 'support@example.com' }, PrimaryCurrency: 'HUF', - ExpectedTurnover: { ExpectedTurnover: 3 }, + ExpectedTurnover: 3, FullPixelImplemented: false, - UseForEInvoicing: false + UseForEInvoicing: false, + CallBackUrl: 'https://example.com/callback' }, successResponseBody: { Errors: [] @@ -241,9 +242,10 @@ module.exports = { errorRequestBody: { ApiKey, PublicKey: '00000000-0000-0000-0000-000000000000' + }, expectedError: { - ErrorCode: 'ShopNotFound' + StatusCode: '404' } } }; From 0faceeec066616bac64787314be605d5b09ad4cf Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 19:57:52 +0100 Subject: [PATCH 14/18] update readme --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 479937a..2a8b081 100644 --- a/README.md +++ b/README.md @@ -774,13 +774,12 @@ To create a new POS (shop) in the Barion system, call the ``createPos`` function - ``'EUR'`` (Euro) - ``'USD'`` (U.S. dollar) - - ``ExpectedTurnover``: The expected turnover tier for the shop (object). (required) - - ``ExpectedTurnover``: Turnover tier value (number, integer, 1-6). (required)
- The value represents predefined turnover ranges based on PrimaryCurrency: - - For HUF: 1 (1-100K), 2 (100K-1M), 3 (1M-10M), 4 (10M-29M), 5 (29M-99M), 6 (99M+) - - For EUR: 1 (1-300), 2 (301-3K), 3 (3K-30K), 4 (30K-90K), 5 (90K-300K), 6 (300K+) - - For CZK: 1 (1-8K), 2 (8K-80K), 3 (80K-800K), 4 (800K-2.2M), 5 (2.2M-7.7M), 6 (7.7M+) - - For USD: 1 (1-350), 2 (351-3.5K), 3 (3.5K-35K), 4 (35K-100K), 5 (100K-345K), 6 (345K+) + - ``ExpectedTurnover``: The expected turnover tier for the shop (number, integer, 1-6). (required)
+ The value represents predefined turnover ranges based on PrimaryCurrency: + - For HUF: 1 (1-100K), 2 (100K-1M), 3 (1M-10M), 4 (10M-29M), 5 (29M-99M), 6 (99M+) + - For EUR: 1 (1-300), 2 (301-3K), 3 (3K-30K), 4 (30K-90K), 5 (90K-300K), 6 (300K+) + - For CZK: 1 (1-8K), 2 (8K-80K), 3 (80K-800K), 4 (800K-2.2M), 5 (2.2M-7.7M), 6 (7.7M+) + - For USD: 1 (1-350), 2 (351-3.5K), 3 (3.5K-35K), 4 (35K-100K), 5 (100K-345K), 6 (345K+) - ``FullPixelImplemented``: Indicates if full pixel tracking is implemented (boolean). (required) @@ -792,7 +791,7 @@ To create a new POS (shop) in the Barion system, call the ``createPos`` function - ``PercentageOfNonEuCards``: Percentage of non-EU cards (number, 0-100). (optional) - - ``CallBackUrl``: Callback URL for the shop (string, max 2000 characters, valid URI). (optional) + - ``CallBackUrl``: Callback URL for the shop (string, max 2000 characters, valid URI). (required) - ``ReferenceId``: Reference identifier for the shop (string). (optional) @@ -830,9 +829,10 @@ barion.createPos({ Email: 'support@example.com' }, PrimaryCurrency: 'HUF', - ExpectedTurnover: { ExpectedTurnover: 3 }, + ExpectedTurnover: 3, FullPixelImplemented: false, - UseForEInvoicing: false + UseForEInvoicing: false, + CallBackUrl: 'https://example.com/callback' }, function (err, data) { //handle error / process data }); @@ -862,9 +862,10 @@ barion.createPos({ Email: 'support@example.com' }, PrimaryCurrency: 'HUF', - ExpectedTurnover: { ExpectedTurnover: 3 }, + ExpectedTurnover: 3, FullPixelImplemented: false, - UseForEInvoicing: false + UseForEInvoicing: false, + CallBackUrl: 'https://example.com/callback' }).then(data => { //process data }).catch(err => { From e425add3e40a6d92068089cc3a5761df8c73c5f2 Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 20:09:03 +0100 Subject: [PATCH 15/18] fix integration tests --- test/integration/integration.test.js | 4 ++-- test/integration/test-data.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index ea3b14e..f61baa4 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -804,7 +804,7 @@ describe('Integration tests', function () { validatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody) .catch(err => { expect(err.name).to.equal('BarionError'); - expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.statusCode); + expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode); done(); }); } @@ -826,7 +826,7 @@ describe('Integration tests', function () { notValidatedBarion.getPosDetails(testData.getPosDetails.errorRequestBody) .catch(err => { expect(err.name).to.equal('BarionError'); - expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.statusCode); + expect(err.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode); done(); }); } diff --git a/test/integration/test-data.js b/test/integration/test-data.js index 18ea2a8..efe7367 100644 --- a/test/integration/test-data.js +++ b/test/integration/test-data.js @@ -245,7 +245,7 @@ module.exports = { }, expectedError: { - StatusCode: '404' + StatusCode: 404 } } }; From 991f46de479175ac0efd5ef0c8822f3898bffb0b Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 21:47:33 +0100 Subject: [PATCH 16/18] encode path parameters, when replacing --- lib/services.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services.js b/lib/services.js index 3fb50d1..1d184b7 100644 --- a/lib/services.js +++ b/lib/services.js @@ -18,7 +18,7 @@ function getBaseUrl (environment) { * Replaces path parameters in the path template with values from options. * @param {String} path - The path template with placeholders like '/api/{id}/details'. * @param {Object} options - The options object containing parameter values. - * @returns {String} The path with parameters replaced. + * @returns {String} The path with parameters replaced and properly URL encoded. */ function replacePathParameters (path, options) { let finalPath = path; @@ -27,7 +27,7 @@ function replacePathParameters (path, options) { while ((match = pathParamRegex.exec(path)) !== null) { const paramName = match[1]; if (options[paramName]) { - finalPath = finalPath.replace(match[0], options[paramName]); + finalPath = finalPath.replace(match[0], encodeURIComponent(options[paramName])); } } return finalPath; From 0228644c67c5256002dbe0316402fa431d4502bd Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 21:51:29 +0100 Subject: [PATCH 17/18] fix documentation title --- lib/domain/CreatePos.js | 2 +- lib/domain/GetPosDetails.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index 27027a3..c3b838e 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -8,7 +8,7 @@ const { NamedContactSchema } = require('./pos/BaseContact'); * * Note: Uses API key authentication (wallet endpoint). * - * @see {@link https://docs.barion.com/Pos-Create-v1|Barion API Documentation} + * @see {@link https://docs.barion.com/Pos-Create-v1|Barion API: Create a POS in Barion} */ const schema = Joi.object({ ApiKey: Joi.string().required() diff --git a/lib/domain/GetPosDetails.js b/lib/domain/GetPosDetails.js index 0dad96a..4182bfb 100644 --- a/lib/domain/GetPosDetails.js +++ b/lib/domain/GetPosDetails.js @@ -6,7 +6,7 @@ const { CaseInsensitiveSchema } = require('../schema'); * * Note: Uses API key authentication (wallet endpoint). * - * @see {@link https://docs.barion.com/Pos-Get-v1|Barion API Documentation} + * @see {@link https://docs.barion.com/Pos-Get-v1|Barion API: Request all details of a POS} */ const schema = Joi.object({ ApiKey: Joi.string().required() From 1900d004d6528146609ab86cd6d4db83f492f65c Mon Sep 17 00:00:00 2001 From: AronStankovics Date: Wed, 29 Oct 2025 22:03:22 +0100 Subject: [PATCH 18/18] add base64 validation to CreatePose model image --- lib/domain/CreatePos.js | 3 ++- test/barion.test.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js index c3b838e..4946530 100644 --- a/lib/domain/CreatePos.js +++ b/lib/domain/CreatePos.js @@ -21,7 +21,8 @@ const schema = Joi.object({ Description: Joi.string().required() .min(20) .max(200), - Logo: Joi.string().required(), + Logo: Joi.string().required() + .base64(), Category: Joi.array().required() .items(Joi.string().valid(...ShopCategory)) .min(1), diff --git a/test/barion.test.js b/test/barion.test.js index cd68b21..79759e1 100644 --- a/test/barion.test.js +++ b/test/barion.test.js @@ -1366,7 +1366,7 @@ describe('lib/barion.js', function () { Name: 'Test Shop', Url: 'https://example.com', Description: 'This is a test shop for unit testing purposes.', - Logo: 'base64encodedlogo', + Logo: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', Category: [ 'Electronics', 'Other' ], BusinessContact: { Name: 'John Doe',