Skip to content

Instantly share code, notes, and snippets.

@toxicantidote
Last active November 7, 2023 22:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save toxicantidote/0e2028a3cb6afffdc9374d25fc4ba219 to your computer and use it in GitHub Desktop.
Save toxicantidote/0e2028a3cb6afffdc9374d25fc4ba219 to your computer and use it in GitHub Desktop.
Proof of concept for logging in to Nayax and getting the XML machine list
###
username = input('Username? ')
password = input('Password? ')
import requests
import re
import sqlite3
import json
## Request wrapper
def make_request(path, type = 'POST', cookies = {}, headers = {}, data = {}, json = {}):
url = 'https://my.nayax.com/DCS/' + path
if type == 'POST':
request = requests.post(url, cookies = cookies, headers = headers, data = data, json = json)
else:
request = requests.get(url, cookies = cookies, headers = headers)
return request
## Login to Nayax
def login(username, password):
## 1: Get the signin token from the login page.
print('Getting login page')
login_page = make_request('LoginPage.aspx', type = 'GET')
regexp_token = re.search(r'var token = \'(.+)\'\;', login_page.text)
if regexp_token:
token = regexp_token.group(1)
print('Login token: ' + str(token))
## 2: Do the login. This redirects back to the login page, but we have the
## session cookies.
print('Performing login')
login_post = make_request('LoginPage.aspx?ReturnUrl=%2fdcs%2fpublic%2fdefault.aspx', type = 'POST', headers = {'signin-token': token, 'Host': 'my.nayax.com', 'X-Requested-With': 'XMLHttpRequest', 'Origin': 'https://my.nayax.com', 'Referer': 'https://my.nayax.com/DCS/LoginPage.aspx?ReturnUrl=%2fdcs%2fpublic%2fdefault.aspx'}, json = {'userName': username, 'password': password, 'action': 'signin', 'newPassword': '', 'oldPassword': '', 'verifyPassword': ''})
## 3: Get the dashboard. We can get the X-Nayax-Validation-Token value from this
print('Getting dashboard')
dashboard = make_request('public/facade.aspx?model=reports/dashboard', type = 'GET', headers = {'Host': 'my.nayax.com', 'Origin': 'https://my.nayax.com'}, cookies = login_post.cookies)
regexp_nvtoken = re.search(r'var token = \'(.+)\'\;', dashboard.text)
if regexp_nvtoken:
nvtoken = regexp_nvtoken.group(1)
print('NV token: ' + nvtoken)
return nvtoken, login_post.cookies
## Initialise an in-memory SQlite3 database for storing data
def init_db():
connection = sqlite3.connect(':memory:')
cursor = connection.cursor()
cursor.execute('CREATE table machines (id integer, parent integer, name text)')
cursor.execute('CREATE table operators (id integer, parent integer, name text)')
connection.commit()
return connection, cursor
## translate actor name to machine name
def actor_to_name(connection, cursor, actor):
for row in db_cursor.execute('SELECT name FROM operators WHERE id=?', [actor]):
return row
## get sales for the given machine
def get_sales(auth_token, auth_cookies, start, end, actor):
print('Requesting cash sales for ' + actor_to_name(actor))
sales_cash = make_request('public/facade.aspx?responseType=json&model=reports/SalesSummary&action=SalesSummary_Report&&actor_id=' + actor +'&num_of_rows=1000&with_cash=1&with_cashless_external=0&time_period=57&start_date=' + start + 'T00%3A00%3A00&end_date=' + end + 'T23%3A59%3A59.997&report_type=2', type = 'POST', headers = {'X-Nayax-Validation-Token': auth_token, 'Host': 'my.nayax.com', 'X-Requested-With': 'XMLHttpRequest', 'Origin': 'https://my.nayax.com', 'Referer': 'https://my.nayax.com/dcs/public/facade.aspx?model=reports/SalesSummary'}, cookies = auth_cookies)
sales_card = make_request('public/facade.aspx?responseType=json&model=reports/SalesSummary&action=SalesSummary_Report&&actor_id=' + actor +'&num_of_rows=1000&with_cash=1&with_cashless_external=0&time_period=57&start_date=' + start + 'T00%3A00%3A00&end_date=' + end + 'T23%3A59%3A59.997&report_type=2', type = 'POST', headers = {'X-Nayax-Validation-Token': auth_token, 'Host': 'my.nayax.com', 'X-Requested-With': 'XMLHttpRequest', 'Origin': 'https://my.nayax.com', 'Referer': 'https://my.nayax.com/dcs/public/facade.aspx?model=reports/SalesSummary'}, cookies = auth_cookies)
json_cash = json.loads(sales_cash.text)
json_card = json.loads(sales_card.text)
## todo: split out sales to return them. make sure we only get passed a lowest level actor to avoid big, slow requests.
## 4: Get the machine list in XML format.
print('Getting machine list')
auth_token, auth_cookies = login(username, password)
machines = make_request('public/facade.aspx?model=operations/machine&action=Machine.Machines_Search', type = 'POST', headers = {'X-Nayax-Validation-Token': auth_token, 'Host': 'my.nayax.com', 'X-Requested-With': 'XMLHttpRequest', 'Origin': 'https://my.nayax.com', 'Referer': 'https://my.nayax.com/dcs/public/facade.aspx?model=operations/machine'}, cookies = auth_cookies)
## Init database
db_connection, db_cursor = init_db()
## Pass the machine list to the XML parser
print('Parsing machine list')
for line in machines.text.split('/>'):
regexp_machine = re.search(r'parent_id=\"(\d+)\" title=\"(.+)\" machine_id=\"(\d+)\"', line)
regexp_operator = re.search(r'id=\"(\d+)\" parent_id=\"(\d+)\" title=\"(.+)\" actor_type_id', line)
if regexp_machine:
parent = regexp_machine.group(1)
name = regexp_machine.group(2)
id = regexp_machine.group(3)
db_cursor.execute('INSERT INTO machines (id, parent, name) VALUES (?, ?, ?)', [id, parent, name])
elif regexp_operator:
parent = regexp_operator.group(2)
name = regexp_operator.group(3)
id = regexp_operator.group(1)
db_cursor.execute('INSERT INTO operators (id, parent, name) VALUES (?, ?, ?)', [id, parent, name])
db_connection.commit()
print('-' * 50)
print('Operators: ')
operators = list(db_cursor.execute('SELECT * FROM operators'))
for row in operators:
print(row)
print('-' * 50)
print('Machines: ')
machines = list(db_cursor.execute('SELECT * FROM machines'))
for row in machines:
print(row)
db_connection.close()
print('-' * 50)
print('Located ' + str(len(machines)) + ' machines under ' + str(len(operators)) + ' operators')
print('All done!')
@JodieM
Copy link

JodieM commented Jun 28, 2022

Hi, I can see this is just a PoC but have you done anything more about this? I am trying to find a way of getting Nayax reports in a more reliable way.

@toxicantidote
Copy link
Author

@JodieM I've had a look around and dug up an old sales reporting tool for Nayax I made a few years ago and put it in a new public gist here.

I don't work with Nayax products anymore, so I don't know if this will still work (it's a few years old now). It looks like I put a bunch of comments through it so hopefully that helps.

When this was in use, it was used to make reports of sales for operators so that you could work out commissions to charge them.

A couple of pain points I recall in working with the Nayax interface when doing this:

  • Event logs are always missing entries. Don't rely on them to reconstruct a series of events or check for a machine condition.
  • The API won't reliably return machine lists with more than 500 entries.

@JodieM
Copy link

JodieM commented Jun 28, 2022

Wow, thank you! Yes, I am all too well aware of the missing entries etc. It is a PITA to work with. Thanks for your code, I will take a look and see what I can do with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment