diff --git a/README.md b/README.md
index 1c1de61..cd3abe8 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)
- [Get transaction history](#get-transaction-history---bariongethistoryoptions-callback)
- [Handling errors](#handling-errors)
- [Secure vs. insecure mode](#secure-vs-insecure-mode)
@@ -85,6 +87,8 @@ barion.getPaymentState({
- get existing accounts of the user
- send money to existing Barion account or email address
- download monthly or daily statements of your account
+- get details of a specific POS (shop)
+- create a new POS (shop) in Barion
- get transaction history for wallet accounts.
> **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.
@@ -678,6 +682,205 @@ 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 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)
+ - ``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 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)
+
+ - ``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). (required)
+
+ - ``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: [ 'Electronics' ],
+ 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: 3,
+ FullPixelImplemented: false,
+ UseForEInvoicing: false,
+ CallBackUrl: 'https://example.com/callback'
+}, 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: [ 'Electronics' ],
+ 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: 3,
+ FullPixelImplemented: false,
+ UseForEInvoicing: false,
+ CallBackUrl: 'https://example.com/callback'
+}).then(data => {
+ //process data
+}).catch(err => {
+ //handle error
+});
+```
+
### Get transaction history - barion.getHistory(options, \[callback\])
To query the transaction history of a wallet account, call the ``getHistory`` function. [[Barion Docs](https://docs.barion.com/UserHistory-GetHistory-v3)]
diff --git a/lib/barion.js b/lib/barion.js
index 6312dff..8264d3a 100644
--- a/lib/barion.js
+++ b/lib/barion.js
@@ -14,6 +14,8 @@ const {
StartPaymentWithGoogleToken,
StartPaymentWithAppleToken,
ValidateApplePaySession,
+ GetPosDetails,
+ CreatePos,
GetHistory
} = require('./domain');
const { buildRequest, buildRequestWithoutValidation } = require('./build');
@@ -259,8 +261,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({
@@ -301,6 +304,36 @@ 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);
+};
+
+/**
+ * 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);
+};
+
/**
* Gets the transaction history of a wallet account.
* @param {Object} options - Options that are required to get history.
diff --git a/lib/constants.js b/lib/constants.js
index 0d762bc..edda927 100644
--- a/lib/constants.js
+++ b/lib/constants.js
@@ -13,6 +13,8 @@ const {
StartPaymentWithAppleToken,
ValidateApplePaySession,
StartPaymentWithGoogleToken,
+ GetPosDetails,
+ CreatePos,
GetHistory
} = require('./domain');
@@ -97,6 +99,16 @@ module.exports = {
path: '/v2/ApplePay/ValidateSession',
model: ValidateApplePaySession
},
+ getPosDetails: {
+ method: 'GET',
+ path: '/v1/Pos/{PublicKey}',
+ model: GetPosDetails
+ },
+ createPos: {
+ method: 'POST',
+ path: '/v1/Pos',
+ model: CreatePos
+ },
getHistory: {
method: 'GET',
path: '/v3/UserHistory/GetHistory',
diff --git a/lib/domain/CreatePos.js b/lib/domain/CreatePos.js
new file mode 100644
index 0000000..4946530
--- /dev/null
+++ b/lib/domain/CreatePos.js
@@ -0,0 +1,58 @@
+const Joi = require('joi');
+const { CaseInsensitiveSchema } = require('../schema');
+const { ShopCategory } = require('./_constraints');
+const { NamedContactSchema } = require('./pos/BaseContact');
+
+/**
+ * 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: Create a POS in Barion}
+ */
+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()
+ .base64(),
+ Category: Joi.array().required()
+ .items(Joi.string().valid(...ShopCategory))
+ .min(1),
+ BusinessContact: NamedContactSchema.required(),
+ TechnicalContact: NamedContactSchema.required(),
+ CustomerServiceContact: NamedContactSchema.required(),
+ PrimaryCurrency: Joi.string().required()
+ .valid('HUF', 'CZK', 'EUR', 'USD'),
+ ExpectedTurnover: Joi.number().required()
+ .integer()
+ .min(1)
+ .max(6),
+ 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().required()
+ .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/GetPosDetails.js b/lib/domain/GetPosDetails.js
new file mode 100644
index 0000000..4182bfb
--- /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: Request all details of a POS}
+ */
+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 ee7fa9c..91f2f7c 100644
--- a/lib/domain/_constraints.js
+++ b/lib/domain/_constraints.js
@@ -110,5 +110,49 @@ module.exports = {
'NoPreference',
'ChallengeRequired',
'NoChallengeNeeded'
+ ],
+ ShopStatus: [
+ 'Unspecified',
+ 'NewUnderApproval',
+ 'NewForbidden',
+ 'Open',
+ 'OpenWaitForModificationApproval',
+ 'OpenModificationForbidden',
+ 'TemporaryClosed',
+ 'TemporaryClosedWaitForModificationApproval',
+ 'TemporaryClosedModificationForbidden',
+ 'PermanentlyClosed',
+ 'PermanentlyClosedByUser',
+ 'PermanentlyClosedByOfficer',
+ '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 859ff1b..91dc08b 100644
--- a/lib/domain/index.js
+++ b/lib/domain/index.js
@@ -13,6 +13,8 @@ const StatementDownload = require('./StatementDownload');
const StartPaymentWithGoogleToken = require('./StartPaymentWithGoogleToken');
const StartPaymentWithAppleToken = require('./StartPaymentWithAppleToken');
const ValidateApplePaySession = require('./ValidateApplePaySession');
+const GetPosDetails = require('./GetPosDetails');
+const CreatePos = require('./CreatePos');
const GetHistory = require('./GetHistory');
module.exports = {
@@ -31,5 +33,7 @@ module.exports = {
StartPaymentWithGoogleToken,
StartPaymentWithAppleToken,
ValidateApplePaySession,
+ GetPosDetails,
+ CreatePos,
GetHistory
};
diff --git a/lib/domain/pos/BaseContact.js b/lib/domain/pos/BaseContact.js
new file mode 100644
index 0000000..aea39f2
--- /dev/null
+++ b/lib/domain/pos/BaseContact.js
@@ -0,0 +1,25 @@
+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.
+ */
+const baseContactSchema = Joi.object({
+ PhoneNumber: Joi.string().required()
+ .max(30),
+ Email: Joi.string().required()
+ .email()
+});
+
+/**
+ * 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().required()
+});
+
+module.exports = {
+ BaseContactSchema: new CaseInsensitiveSchema(baseContactSchema),
+ NamedContactSchema: new CaseInsensitiveSchema(namedContactSchema)
+};
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/lib/services.js b/lib/services.js
index 7b86f4e..d6cd7c3 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 and properly URL encoded.
+ */
+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], encodeURIComponent(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);
@@ -144,7 +163,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 +172,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,10 +181,28 @@ 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);
}
+/**
+ * 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);
+}
+
+/**
+ * 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);
+}
+
/**
* Gets the transaction history of a wallet account.
* @param {String} environment - The environment to use ('test' or 'prod').
@@ -190,9 +227,12 @@ module.exports = {
startPaymentWithGoogleToken,
startPaymentWithAppleToken,
validateApplePaySession,
+ getPosDetails,
+ createPos,
getHistory,
_private: {
getBaseUrl,
+ replacePathParameters,
doRequest
}
};
diff --git a/test/barion.test.js b/test/barion.test.js
index ead3d6c..d6932ce 100644
--- a/test/barion.test.js
+++ b/test/barion.test.js
@@ -56,6 +56,8 @@ const Barions = {
startPaymentWithGoogleToken: returnSuccess,
startPaymentWithAppleToken: returnSuccess,
validateApplePaySession: returnSuccess,
+ getPosDetails: returnSuccess,
+ createPos: returnSuccess,
getHistory: returnSuccess
}
}),
@@ -75,6 +77,8 @@ const Barions = {
startPaymentWithGoogleToken: returnError,
startPaymentWithAppleToken: returnError,
validateApplePaySession: returnError,
+ getPosDetails: returnError,
+ createPos: returnError,
getHistory: returnError
}
}),
@@ -94,6 +98,8 @@ const Barions = {
startPaymentWithGoogleToken: returnSuccess,
startPaymentWithAppleToken: returnSuccess,
validateApplePaySession: returnSuccess,
+ getPosDetails: returnSuccess,
+ createPos: returnSuccess,
getHistory: returnSuccess
},
'./build': {
@@ -116,6 +122,8 @@ const Barions = {
startPaymentWithGoogleToken: returnSuccess,
startPaymentWithAppleToken: returnSuccess,
validateApplePaySession: returnSuccess,
+ getPosDetails: returnSuccess,
+ createPos: returnSuccess,
getHistory: returnSuccess
},
'./build': {
@@ -1021,7 +1029,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) {
@@ -1283,6 +1292,174 @@ describe('lib/barion.js', function () {
});
});
+ 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);
+ });
+ });
+
+ 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: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
+ 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: 3,
+ FullPixelImplemented: true,
+ UseForEInvoicing: false,
+ CallBackUrl: 'https://example.com/callback'
+ };
+
+ 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);
+ });
+ });
+
describe('#getHistory(options, [callback])', function () {
const request = {
ApiKey: '277a6ae112b041928e6cbc7d0612afa1'
diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js
index c68d983..be78d8f 100644
--- a/test/integration/integration.test.js
+++ b/test/integration/integration.test.js
@@ -581,6 +581,258 @@ 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 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.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode);
+ 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.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode);
+ 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.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode);
+ 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.statusCode).to.equal(testData.getPosDetails.expectedError.StatusCode);
+ done();
+ });
+ }
+ );
+ });
+
describe('Get history (callback)', function () {
it('should get transaction history when validation is turned on', function (done) {
validatedBarion.getHistory(testData.getHistory.successRequestBody, (err, res) => {
diff --git a/test/integration/test-data.js b/test/integration/test-data.js
index 26aaf64..27c32a8 100644
--- a/test/integration/test-data.js
+++ b/test/integration/test-data.js
@@ -192,6 +192,62 @@ module.exports = {
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: 3,
+ FullPixelImplemented: false,
+ UseForEInvoicing: false,
+ CallBackUrl: 'https://example.com/callback'
+ },
+ 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: {
+ StatusCode: 404
+ }
+ },
getHistory: {
successRequestBody: {
ApiKey,
diff --git a/test/services.test.js b/test/services.test.js
index e5713bc..196db2a 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('#getHistory(environment, options)', function () {
it('should return a Promise on success', function (done) {
const services = serviceMocks.okService;
@@ -331,4 +359,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');
+ });
+ });
});