@emaraplay/nobles 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./src/index');
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@emaraplay/nobles",
3
+ "version": "1.0.1",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "mocha test --recursive --timeout 5000 --reporter mochawesome --exit",
7
+ "test:cov": "nyc --reporter lcov mocha test --recursive --timeout 5000 --reporter mochawesome --exit"
8
+ },
9
+ "author": "",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "@emaraplay/error-codes": "0.0.8",
13
+ "@emaraplay/utils": "1.0.20",
14
+ "@sentry/node": "^6.1.0",
15
+ "dayjs": "^1.11.10",
16
+ "got": "^11.8.6",
17
+ "xlsx": "^0.18.5",
18
+ "xss": "^1.0.8"
19
+ },
20
+ "devDependencies": {
21
+ "chai": "^4.3.0",
22
+ "mocha": "^8.2.1",
23
+ "mochawesome": "^6.2.1",
24
+ "moxios": "^0.4.0",
25
+ "nyc": "^15.1.0"
26
+ }
27
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ exports.games = require('./list.method');
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ const dayjs = require('dayjs');
4
+ const got = require('got');
5
+
6
+ function mapCategoryToEnum(category) {
7
+ if (typeof category !== 'string') return undefined;
8
+ const raw = category.trim().toLowerCase();
9
+ const normalized = raw.replace(/[\s/_-]+/g, '');
10
+
11
+ const MAP = {
12
+ slot: 'SLOT',
13
+ slots: 'SLOT',
14
+ videoslot: 'SLOT',
15
+ videoslots: 'SLOT',
16
+ megaways: 'SLOT',
17
+
18
+ pool: 'POOL',
19
+
20
+ roulette: 'ROULETTE',
21
+
22
+ blackjack: 'BLACKJACK',
23
+
24
+ baccarat: 'BACCARAT',
25
+ crash: 'CRASH',
26
+
27
+ instant: 'INSTANT',
28
+ casual: 'INSTANT',
29
+ arcade: 'INSTANT',
30
+ mines: 'INSTANT',
31
+ plinko: 'INSTANT',
32
+ dice: 'INSTANT',
33
+
34
+ scratch: 'SCRATCH',
35
+ scratchcard: 'SCRATCH',
36
+ scratchcards: 'SCRATCH',
37
+
38
+ poker: 'POKER',
39
+ videopoker: 'POKER',
40
+ holdem: 'POKER',
41
+ threecardpoker: 'POKER',
42
+
43
+ bingo: 'BINGO',
44
+ keno: 'BINGO',
45
+ lotto: 'BINGO',
46
+ lottery: 'BINGO',
47
+
48
+ virtual: 'VIRTUAL',
49
+ virtualsports: 'VIRTUAL',
50
+
51
+ table: 'TABLE',
52
+ tablegame: 'TABLE',
53
+ tablegames: 'TABLE',
54
+ card: 'TABLE',
55
+ cards: 'TABLE',
56
+ board: 'TABLE'
57
+ };
58
+
59
+ if (MAP[normalized]) return MAP[normalized];
60
+
61
+ if (/roulette/.test(raw)) return 'ROULETTE';
62
+ if (/blackjack/.test(raw)) return 'BLACKJACK';
63
+ if (/crash/.test(raw)) return 'CRASH';
64
+ if (/poker/.test(raw)) return 'POKER';
65
+ if (/bingo|keno|lottery|lotto/.test(raw)) return 'BINGO';
66
+ if (/virtual/.test(raw)) return 'VIRTUAL';
67
+ if (/scratch/.test(raw)) return 'SCRATCH';
68
+ if (/table|card|cards|board/.test(raw)) return 'TABLE';
69
+ if (/slot|megaways/.test(raw)) return 'SLOT';
70
+ if (/instant|arcade|mines|plinko|dice/.test(raw)) return 'INSTANT';
71
+
72
+ return 'OTHER';
73
+ }
74
+
75
+ async function getGames(provider) {
76
+ try {
77
+ if (!provider || !provider.urls) {
78
+ throw new Error('Proveedor inválido o sin configuración de URLs');
79
+ }
80
+ if (!provider.urls.gameList) {
81
+ throw new Error('Proveedor no tiene configurado el endpoint de lista de juegos');
82
+ }
83
+
84
+ const endpoint = provider.urls.gameList;
85
+ if (!endpoint) {
86
+ throw new Error('Proveedor sin endpoint de lista de juegos configurado');
87
+ }
88
+
89
+ const response = await got.get(endpoint, {
90
+ responseType: 'json',
91
+ headers: {
92
+ 'Content-Type': 'application/json'
93
+ }
94
+ });
95
+
96
+ const gamesData = response.body;
97
+ if (!Array.isArray(gamesData)) {
98
+ throw new Error('La respuesta del endpoint no es un array de juegos');
99
+ }
100
+
101
+ const processedGames = [];
102
+ let skippedGamesCount = 0;
103
+
104
+ for (const game of gamesData) {
105
+ try {
106
+ if (!game.gameMode || !game.title) {
107
+ skippedGamesCount++;
108
+ continue;
109
+ }
110
+
111
+ const gameType = mapCategoryToEnum(game.category) || 'OTHER';
112
+
113
+ const hasFreespins = Array.isArray(game.bonusTypes) && game.bonusTypes.length > 0;
114
+
115
+ const gameData = {
116
+ alias: game.gameMode,
117
+ name: game.title.trim(),
118
+ freespins: hasFreespins,
119
+ fun: true,
120
+ type: gameType,
121
+ platform: {
122
+ mobile: true,
123
+ desktop: true,
124
+ },
125
+ params: {
126
+ description: game.description || '',
127
+ multiplayer: game.multiplayer || false
128
+ }
129
+ };
130
+
131
+ if (game.rtp) {
132
+ const rtpVal = Number(game.rtp);
133
+ if (!Number.isNaN(rtpVal)) {
134
+ const normalizedRtp = rtpVal <= 1 ? rtpVal * 100 : rtpVal;
135
+ if (normalizedRtp > 0 && normalizedRtp <= 100) {
136
+ gameData.rtp = normalizedRtp;
137
+ }
138
+ }
139
+ }
140
+
141
+ gameData.launchDate = dayjs().toDate();
142
+
143
+ processedGames.push(gameData);
144
+ } catch (gameError) {
145
+ console.error('Error procesando juego:', gameError);
146
+ skippedGamesCount++;
147
+ }
148
+ }
149
+
150
+ if (skippedGamesCount > 0) {
151
+ console.log(`Se omitieron ${skippedGamesCount} juegos por datos inválidos`);
152
+ }
153
+
154
+ return processedGames;
155
+ } catch (error) {
156
+ console.error('Error obteniendo juegos de InOut desde API:', error.message);
157
+ throw error;
158
+ }
159
+ }
160
+
161
+ module.exports = {
162
+ getGames
163
+ };
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ exports.game = require('./games');
4
+ exports.utils = require('./utils');
5
+ exports.wallet = require('./wallet');
6
+ exports.launcher = require('./launcher');
7
+ exports.rounds = require('./rounds');
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ const xss = require('xss');
4
+ const crypto = require('crypto');
5
+ const got = require('got');
6
+ const { getHMAC } = require('../utils/sign.helper');
7
+
8
+ function sortPlainObjectKeysAsc(obj) {
9
+ const sortedKeys = Object.keys(obj).sort();
10
+ const sortedObj = {};
11
+ for (const key of sortedKeys) {
12
+ sortedObj[key] = obj[key];
13
+ }
14
+ return sortedObj;
15
+ }
16
+
17
+ module.exports = async function(request, provider, aUser, game, sessionId, mode) {
18
+ try {
19
+ const launchUrl = provider?.urls?.launcher;
20
+ if (!launchUrl) throw new Error('URL de lanzamiento (launcher) no configurada para el proveedor.');
21
+ const operatorId = provider?.k2;
22
+ if (!operatorId) throw new Error('operatorId (k2) no configurado para el proveedor.');
23
+ const gameMode = game?.a;
24
+ if (!gameMode) throw new Error('El ID del juego (game.a) es inválido.');
25
+
26
+ const lng = xss(request.data?.lang || aUser?.data?.language || request.body?.lang || 'es-ES');
27
+ const lobbyUrl = xss(request.query.lobby) || xss(request.bookmaker?.url?.l);
28
+ const callbackUrl = xss(provider.params?.callBack) || lobbyUrl;
29
+ const isDemo = mode === 'fun' || mode === 'demo';
30
+ const user = isDemo ? 'DemoUser' : String(aUser?.data?.internal || '');
31
+ const balance = isDemo ? 10000 : (aUser?.data?.balance || 0);
32
+ const currency = isDemo ? 'USD' : xss(aUser?.data?.currency || 'ARS');
33
+ let demoUrl;
34
+ let body;
35
+
36
+ if (mode === 'fun' || mode === 'demo') {
37
+ demoUrl = `${provider.urls.baseUrl}/demo/sessions`;
38
+ const demoAuthToken = crypto.randomBytes(16).toString('hex');
39
+ body = {
40
+ gameCode: gameMode,
41
+ playerId: user,
42
+ operatorCode: operatorId,
43
+ callbackUrl: callbackUrl,
44
+ lobbyUrl: lobbyUrl,
45
+ sessionId: demoAuthToken,
46
+ locale: lng.substring(0, 2),
47
+ currency: currency || 'USD',
48
+ initialBalance: balance
49
+ };
50
+ } else {
51
+ body = {
52
+ gameCode: gameMode,
53
+ playerId: user,
54
+ operatorCode: operatorId,
55
+ callbackUrl: callbackUrl,
56
+ lobbyUrl: lobbyUrl,
57
+ sessionId: String(sessionId),
58
+ locale: lng.substring(0, 2),
59
+ currency: currency,
60
+ initialBalance: balance
61
+ };
62
+
63
+ }
64
+
65
+ const sortedBody = sortPlainObjectKeysAsc(body);
66
+ const signature = getHMAC(sortedBody, provider.k1);
67
+
68
+ const response = await got.post(isDemo ? demoUrl : launchUrl, {
69
+ json: body,
70
+ headers: {
71
+ 'x-request-sign': signature
72
+ }
73
+ });
74
+
75
+ const result = JSON.parse(response.body);
76
+ return result.url;
77
+ } catch (error) {
78
+ console.error(`Error en Nobles launcher: ${error.message}`);
79
+ throw new Error(`Nobles launcher falló: ${error.message}`);
80
+ }
81
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports.get = require('./get.method');
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ const xss = require('xss');
4
+ const got = require('got');
5
+ const { getHMAC } = require('../utils/sign.helper');
6
+ const ERROR = require('@emaraplay/error-codes');
7
+
8
+ const sanitize = (value) => xss(value)?.trim();
9
+
10
+ const validateParams = ({ host, apiKey, casinoId, roundId }) => {
11
+ if (!host || !apiKey) {
12
+ return { ...ERROR.ERROR_CODES.UNAUTHORIZED, description: 'UNAUTHORIZED' };
13
+ }
14
+ if (!casinoId) {
15
+ return { ...ERROR.ERROR_CODES.INVALID_PARAMETERS, description: 'Casino Required' };
16
+ }
17
+ if (!roundId) {
18
+ return { ...ERROR.ERROR_CODES.INVALID_PARAMETERS, description: 'Round Required' };
19
+ }
20
+ return null;
21
+ };
22
+
23
+ const buildPayload = ({ casinoId, roundId, timeZone }) => ({
24
+ casino_id: casinoId,
25
+ round_id: roundId,
26
+ ...(timeZone ? { time_zone: timeZone } : {}),
27
+ });
28
+
29
+ const sendSignedRequest = async (endpoint, payload, secret) => {
30
+ const body = JSON.stringify(payload);
31
+ const signature = getHMAC(body, secret);
32
+
33
+ const response = await got.post(endpoint, {
34
+ body,
35
+ responseType: 'json',
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ 'x-request-sign': signature,
39
+ },
40
+ });
41
+ return response.body;
42
+ };
43
+
44
+ module.exports = async (host, apiKey, casino, round, timeZone = null) => {
45
+ const sanitizedParams = {
46
+ host: sanitize(host),
47
+ apiKey: sanitize(apiKey),
48
+ casinoId: sanitize(casino),
49
+ roundId: sanitize(round),
50
+ timeZone: timeZone ? sanitize(timeZone) : null,
51
+ };
52
+
53
+ const validationError = validateParams(sanitizedParams);
54
+ if (validationError) return validationError;
55
+
56
+ const endpoint = sanitizedParams.host
57
+
58
+ try {
59
+ const payload = buildPayload({
60
+ casinoId: sanitizedParams.casinoId,
61
+ roundId: sanitizedParams.roundId,
62
+ timeZone: sanitizedParams.timeZone,
63
+ });
64
+
65
+ const response = await sendSignedRequest(endpoint, payload, sanitizedParams.apiKey);
66
+ if (!response?.details_url) {
67
+ return {
68
+ error: 404,
69
+ description: 'Round details URL not provided',
70
+ raw: response,
71
+ };
72
+ }
73
+
74
+ return {
75
+ error: 0,
76
+ description: 'Success',
77
+ result: {
78
+ type: 'url',
79
+ value: response.details_url,
80
+ },
81
+ };
82
+ } catch (err) {
83
+ console.error(err);
84
+ return {
85
+ error: 500,
86
+ description: err?.response?.body?.error || err.message,
87
+ raw: err?.response?.body || err,
88
+ };
89
+ }
90
+ };
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports.details = require('./details.method');
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports.PROVIDER_CODE = 'NB';
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ exports.currencyHelper = (bCurrency, uCurrency) => {
4
+ if(!bCurrency || !uCurrency) {
5
+ return false;
6
+ }
7
+ if(bCurrency === uCurrency) {
8
+ return true;
9
+ } else {
10
+ return false
11
+ }
12
+ }
@@ -0,0 +1,225 @@
1
+ 'use strict';
2
+
3
+ const ERRORS = {
4
+ NONE: {
5
+ error: 0,
6
+ httpCode: 200,
7
+ message: 'Success',
8
+ },
9
+ TEMPORARY_ERROR: {
10
+ error: 1,
11
+ httpCode: 500,
12
+ errorNobles: 'ERROR1',
13
+ message: 'An internal error occurred',
14
+ },
15
+ INVALID_TOKEN: {
16
+ error: 4,
17
+ httpCode: 401,
18
+ errorNobles: 'ERROR4',
19
+ message: 'Player authentication failed',
20
+ },
21
+ ACCOUNT_LOCKED: {
22
+ error: 6,
23
+ httpCode: 401,
24
+ errorNobles: 'ERROR4',
25
+ message: 'Player authentication failed',
26
+ },
27
+ ACCOUNT_INVALID: {
28
+ error: 2,
29
+ httpCode: 401,
30
+ errorNobles: 'ERROR4',
31
+ message: 'Player authentication failed',
32
+ },
33
+ UNKNOWN_ERROR: {
34
+ error: 100,
35
+ httpCode: 500,
36
+ errorNobles: 'ERROR1',
37
+ message: 'An internal error occurred',
38
+ },
39
+ GAME_DISABLED: {
40
+ error: 8,
41
+ httpCode: 500,
42
+ errorNobles: 'ERROR1',
43
+ message: 'An internal error occurred',
44
+ },
45
+ INSUFFICIENT_FUNDS: {
46
+ error: 1,
47
+ httpCode: 400,
48
+ errorNobles: 'ERROR2',
49
+ message: 'Not enough balance to complete the action',
50
+ },
51
+ CHECKS_FAIL: {
52
+ error: 3,
53
+ httpCode: 401,
54
+ errorNobles: 'ERROR3',
55
+ message: 'Unauthorized operation attempted',
56
+ },
57
+ DEBIT_TRANSACTION_NOT_FOUND: {
58
+ error: 14,
59
+ httpCode: 401,
60
+ errorNobles: 'ERROR3',
61
+ message: 'Unauthorized operation attempted',
62
+ },
63
+ INVALID_HASH: {
64
+ error: 5,
65
+ httpCode: 401,
66
+ errorNobles: 'ERROR3',
67
+ message: 'Unauthorized operation attempted',
68
+ },
69
+ MISSING_SIGNATURE: {
70
+ error: 16,
71
+ httpCode: 401,
72
+ errorNobles: 'ERROR3',
73
+ message: 'Unauthorized operation attempted'
74
+ },
75
+ INVALID_PARAMS: {
76
+ error: 7,
77
+ httpCode: 500,
78
+ errorNobles: 'ERROR1',
79
+ message: 'An internal error occurred',
80
+ },
81
+ DUPLICATED_TRANSACTION: {
82
+ error: 11,
83
+ httpCode: 401,
84
+ errorNobles: 'ERROR3',
85
+ message: 'Unauthorized operation attempted',
86
+ },
87
+ INVALID_TRANSACTION: {
88
+ error: 12,
89
+ httpCode: 401,
90
+ errorNobles: 'ERROR3',
91
+ message: 'Unauthorized operation attempted',
92
+ },
93
+ ROUND_ALREADY_STARTED: {
94
+ error: 13,
95
+ httpCode: 401,
96
+ errorNobles: 'ERROR3',
97
+ message: 'Unauthorized operation attempted',
98
+ },
99
+ ROUND_ALREADY_CLOSED: {
100
+ error: 15,
101
+ httpCode: 401,
102
+ errorNobles: 'ERROR3',
103
+ message: 'Unauthorized operation attempted',
104
+ },
105
+ ROUND_NOT_FOUND: {
106
+ error: 16,
107
+ httpCode: 401,
108
+ errorNobles: 'ERROR3',
109
+ message: 'Unauthorized operation attempted',
110
+ },
111
+ INVALID_AMOUNT: {
112
+ error: 21,
113
+ httpCode: 401,
114
+ errorNobles: 'ERROR3',
115
+ message: 'Unauthorized operation attempted',
116
+ },
117
+ PROVIDER_NOT_FOUND: {
118
+ error: 30,
119
+ httpCode: 500,
120
+ errorNobles: 'ERROR1',
121
+ message: 'An internal error occurred',
122
+ },
123
+ INTERNAL_SERVER_ERROR_1: {
124
+ error: 120,
125
+ httpCode: 500,
126
+ errorNobles: 'ERROR1',
127
+ message: 'An internal error occurred',
128
+ },
129
+ INTERNAL_SERVER_ERROR_2: {
130
+ error: 120,
131
+ httpCode: 500,
132
+ errorNobles: 'ERROR1',
133
+ message: 'An internal error occurred',
134
+ },
135
+ BET_OUT_OF_LIMIT: {
136
+ error: 310,
137
+ httpCode: 401,
138
+ errorNobles: 'ERROR3',
139
+ message: 'Unauthorized operation attempted',
140
+ },
141
+ UNAUTHORIZED: {
142
+ error: 401,
143
+ httpCode: 401,
144
+ errorNobles: 'ERROR3',
145
+ message: 'Unauthorized operation attempted',
146
+ },
147
+ NOT_IMPLEMENTED: {
148
+ error: 501,
149
+ httpCode: 500,
150
+ errorNobles: 'ERROR1',
151
+ message: 'An internal error occurred',
152
+ },
153
+ EMPTY_SESSION_ID: {
154
+ error: 1000,
155
+ httpCode: 401,
156
+ errorNobles: 'ERROR4',
157
+ message: 'Player authentication failed',
158
+ },
159
+ INVALID_USER: {
160
+ error: 1001,
161
+ httpCode: 401,
162
+ errorNobles: 'ERROR4',
163
+ message: 'Player authentication failed',
164
+ },
165
+ INVALID_CASINO_ID: {
166
+ error: 1002,
167
+ httpCode: 500,
168
+ errorNobles: 'ERROR1',
169
+ message: 'An internal error occurred',
170
+ },
171
+ INVALID_GAME_ID: {
172
+ error: 1003,
173
+ httpCode: 500,
174
+ errorNobles: 'ERROR1',
175
+ message: 'An internal error occurred',
176
+ },
177
+ INVALID_USER_CURRENCY: {
178
+ error: 1004,
179
+ httpCode: 400,
180
+ errorNobles: 'ERROR6',
181
+ message: 'Unsupported currency',
182
+ },
183
+ }
184
+
185
+ const getErrorByNumber = (errorNumber) => {
186
+ const k = Object.keys(ERRORS).find(key => ERRORS[key].error === errorNumber);
187
+ return ERRORS[k] || {
188
+ error: errorNumber,
189
+ httpCode: 500,
190
+ errorNobles: 'ERROR1',
191
+ message: 'An internal error occurred'
192
+ };
193
+ };
194
+
195
+ function buildHttpError(codeOrKey) {
196
+ let error;
197
+
198
+ if (typeof codeOrKey === 'number') {
199
+ error = getErrorByNumber(codeOrKey);
200
+ }
201
+ else if (typeof codeOrKey === 'string' && ERRORS[codeOrKey]) {
202
+ error = ERRORS[codeOrKey];
203
+ }
204
+ else {
205
+ error = {
206
+ error: codeOrKey,
207
+ httpCode: 500,
208
+ errorNobles: 'ERROR1',
209
+ message: 'An internal error occurred'
210
+ };
211
+ }
212
+
213
+ return {
214
+ httpCode: error.httpCode,
215
+ body: {
216
+ error: {
217
+ code: error.errorNobles,
218
+ message: error.message
219
+ }
220
+ }
221
+ };
222
+ }
223
+
224
+
225
+ module.exports = { ERRORS, buildHttpError };
@@ -0,0 +1,5 @@
1
+ module.exports.normalize = require('./normalize.helper');
2
+ module.exports.getError = require('./error.helper');
3
+ module.exports.constants = require('./constants');
4
+ module.exports.security = require('./sign.helper');
5
+ module.exports.currencyHelper = require('./currency.helper');
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const xss = require('xss');
4
+
5
+ module.exports = (params, request) => {
6
+ const normalized = {
7
+ lang: xss(request.data.lang) || "",
8
+ ip: xss((request.data.ip || {}).ip) || "",
9
+ provider: xss(request.provider) || "",
10
+
11
+ game: xss(params?.gameCode),
12
+ transaction: xss(params?.trxUid),
13
+ user: xss(request.user?.r),
14
+ userInternal: xss(request.user?._id),
15
+ currency: xss(params?.currency),
16
+ gameCycle: xss(params?.roundUid),
17
+ gameCycleClosed: params?.isRoundClosed,
18
+ originalTransaction: xss(params?.data?.debitId),
19
+ amount: xss((params?.bet && params?.win) ?
20
+ (Number(params?.win || 0) - Number(params?.bet || 0)).toString() :
21
+ (params?.bet)
22
+ ),
23
+ betAmount: xss(params?.bet),
24
+ winAmount: xss(params?.win),
25
+ typeToRollback: ['betwin'],
26
+
27
+ // promoId: xss(params?.eventSource),
28
+ raw: params,
29
+ };
30
+ return normalized;
31
+ };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ exports.getHMAC = (json, secret) => {
6
+ const data = (typeof json === 'string' ? json : JSON.stringify(json));
7
+ const hmacData = crypto.createHmac('sha256', secret).update(data).digest('hex');
8
+ return hmacData;
9
+ };
10
+
11
+ exports.isValidSign = (sign, body, secret) => {
12
+ const check = this.getHMAC(body, secret);
13
+ return sign === check;
14
+ };
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ const Sentry = require('@sentry/node');
4
+ const { normalize } = require('../utils');
5
+ const utils = require('@emaraplay/utils');
6
+ const { ERRORS, buildHttpError } = require('../utils/error.helper');
7
+ const { isValidSign } = require('../utils/sign.helper');
8
+ const { currencyHelper } = require('../utils/currency.helper');
9
+
10
+ const validateAuthenticateRequest = (params) => {
11
+ if (!params.user) return buildHttpError(ERRORS.INVALID_USER.error);
12
+ if (!params.currency) return buildHttpError(ERRORS.INVALID_USER_CURRENCY.error);
13
+ return null;
14
+ };
15
+
16
+ module.exports = async function(controller, fastify, request) {
17
+ try {
18
+ const signature = request.headers['x-request-sign'];
19
+ if (!signature) return buildHttpError(ERRORS.MISSING_SIGNATURE.error);
20
+ const query = JSON.stringify(request.query);
21
+ const secretKey = request.provider.k1;
22
+ if (!isValidSign(signature, query, secretKey)) {
23
+ return buildHttpError(ERRORS.INVALID_HASH.error);
24
+ }
25
+
26
+ const body = normalize(request.query, request);
27
+ const sameCurrency = currencyHelper(body.currency, request.user.currency);
28
+ if (!sameCurrency) return buildHttpError(ERRORS.INVALID_USER_CURRENCY.error);
29
+
30
+ const validationError = validateAuthenticateRequest(body);
31
+ if (validationError) return validationError;
32
+
33
+ const payload = utils.wallet.balance(request, {
34
+ user: body.user,
35
+ currency: body.currency,
36
+ });
37
+
38
+ const response = await controller(fastify, payload, request);
39
+ if (response.error) {
40
+ return buildHttpError(response.code);
41
+ }
42
+
43
+ return {
44
+ balance: Number(response.data.balance),
45
+ playerId: body.userInternal,
46
+ currency: response.data.currency,
47
+ };
48
+ } catch (error) {
49
+ Sentry.captureException(error);
50
+ return buildHttpError(ERRORS.INTERNAL_SERVER_ERROR_2.error);
51
+ }
52
+ };
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const Sentry = require('@sentry/node');
4
+ const { ERRORS, buildHttpError } = require('../utils/error.helper');
5
+ const { normalize } = require('../utils');
6
+ const { isValidSign } = require('../utils/sign.helper');
7
+ const { currencyHelper } = require('../utils/currency.helper');
8
+ const utils = require('@emaraplay/utils');
9
+
10
+ const validateBetRequest = (params) => {
11
+ if (!params.gameCycle) return buildHttpError(ERRORS.INVALID_PARAMS.error);
12
+ if (!params.transaction) return buildHttpError(ERRORS.INVALID_PARAMS.error);
13
+ if (params.amount === undefined || params.amount === null || Number(params.amount) < 0) return buildHttpError(ERRORS.INVALID_PARAMS.error);
14
+ if (params.betAmount === undefined || params.betAmount === null || Number(params.betAmount) < 0) return buildHttpError(ERRORS.INVALID_PARAMS.error);
15
+ if (params.winAmount === undefined || params.winAmount === null || Number(params.winAmount) < 0) return buildHttpError(ERRORS.INVALID_PARAMS.error);
16
+ if (!params.userInternal) return buildHttpError(ERRORS.INVALID_PARAMS.error);
17
+ if (!params.game) return buildHttpError(ERRORS.INVALID_GAME_ID.error);
18
+ if (!params.currency) return buildHttpError(ERRORS.INVALID_PARAMS.error);
19
+ if (!params.user) return buildHttpError(ERRORS.INVALID_USER.error);
20
+ return null;
21
+ };
22
+
23
+ module.exports = async function (controller, fastify, request) {
24
+ try {
25
+ const signature = request.headers['x-request-sign'];
26
+ if (!signature) {
27
+ return buildHttpError(ERRORS.MISSING_SIGNATURE.error);
28
+ }
29
+ const secretKey = request.provider.k1;
30
+ if (!isValidSign(signature, request.rawBody, secretKey)) {
31
+ return buildHttpError(ERRORS.INVALID_HASH.error)
32
+ }
33
+ const body = normalize(request.body, request);
34
+ const sameCurrency = currencyHelper(body.currency, request.user.currency)
35
+ if(!sameCurrency) {
36
+ return buildHttpError(ERRORS.INVALID_USER_CURRENCY.error);
37
+ }
38
+
39
+ const validationError = validateBetRequest(body);
40
+ if (validationError) return validationError;
41
+
42
+ const failOnExist = !request.provider.p?.cfg?.rst;
43
+ const checkRound = !request.provider.p?.cfg?.amb;
44
+
45
+ const payload = utils.casino.stakeAndSettle(request, body, failOnExist, checkRound);
46
+ const response = await controller(fastify, payload, request);
47
+ if (response && response.error) {
48
+ return buildHttpError(response.error);
49
+ }
50
+ const balance = Number(response.data?.balance);
51
+
52
+ return {
53
+ balance,
54
+ playerId: body.userInternal,
55
+ trxId: response.data.transaction || response.data.txid,
56
+ currency: body.currency
57
+ };
58
+ } catch (err) {
59
+ console.error('ERROR in bet:', err);
60
+ Sentry.captureException(err);
61
+ return buildHttpError(ERRORS.INTERNAL_SERVER_ERROR_2.error);
62
+ }
63
+ };
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ module.exports.balance = require('./balance.method');
4
+ module.exports.betWin = require('./bet-win.method');
5
+ module.exports.rollback = require('./rollback.method');
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ const Sentry = require('@sentry/node');
4
+ const { ERRORS, buildHttpError } = require('../utils/error.helper');
5
+ const { normalize } = require('../utils');
6
+ const { isValidSign } = require('../utils/sign.helper');
7
+ const { currencyHelper } = require('../utils/currency.helper');
8
+ const utils = require('@emaraplay/utils');
9
+
10
+ const validateRollbackRequest = (params) => {
11
+ if (!params.gameCycle) return buildHttpError(ERRORS.INVALID_PARAMS.error);
12
+ if (!params.transaction) return buildHttpError(ERRORS.INVALID_PARAMS.error);
13
+ if (!params.game) return buildHttpError(ERRORS.INVALID_GAME_ID.error);
14
+ if (!params.user) return buildHttpError(ERRORS.INVALID_USER.error);
15
+ if (!params.currency) return buildHttpError(ERRORS.INVALID_USER_CURRENCY.error);
16
+ if (params.amount === undefined || params.amount === null) return buildHttpError(ERRORS.INVALID_PARAMS.error);
17
+ const amount = Number(params.amount);
18
+ if (Number.isNaN(amount) || amount < 0) {
19
+ return buildHttpError(ERRORS.INVALID_AMOUNT.error);
20
+ }
21
+ return null;
22
+ };
23
+
24
+ module.exports = async function (controller, fastify, request) {
25
+ try {
26
+ const signature = request.headers['x-request-sign'];
27
+ const secretKey = request.provider.k1;
28
+ const body = normalize(request.body, request);
29
+ const sameCurrency = currencyHelper(body.currency, request.user.currency)
30
+ if(!sameCurrency) {
31
+ return buildHttpError(ERRORS.INVALID_USER_CURRENCY.error);
32
+ }
33
+
34
+ if (!signature) return buildHttpError(ERRORS.MISSING_SIGNATURE.error);
35
+ if (!isValidSign(signature, request.rawBody, secretKey)) {
36
+ return buildHttpError(ERRORS.INVALID_HASH.error);
37
+ }
38
+
39
+ const validationError = validateRollbackRequest(body);
40
+ if (validationError) return validationError;
41
+
42
+ const failOnExist = !request.provider.p?.cfg?.rst;
43
+ const checkRound = !request.provider.p?.cfg?.amb;
44
+
45
+ const payload = utils.casino.rollback(request, body, body.gameCycleClosed, failOnExist, checkRound);
46
+ const response = await controller(fastify, payload, request);
47
+
48
+ if (response && response.error) {
49
+ return buildHttpError(response.error);
50
+ }
51
+
52
+ const balance = Number(response.data?.balance);
53
+
54
+ return {
55
+ balance,
56
+ playerId: body.userInternal,
57
+ trxId: response.data.transaction || response.data.txid,
58
+ currency: body.currency
59
+ };
60
+ } catch (err) {
61
+ console.error('ERROR in rollback/refund:', err);
62
+ Sentry.captureException(err);
63
+ return buildHttpError(ERRORS.INTERNAL_SERVER_ERROR_1.error)
64
+ }
65
+ };