QR Features
QR login and QR card scan in Python
QR Features
Two QR flows, both driven from your server:
- QR login — the customer scans to sign in, and you get their JWT.
- QR card scan — the customer scans at the till, and you get their loyalty card.
Sessions expire after 5 minutes.
Your server opens the session; the browser waits on the realtime channel. That is the path to build — the result lands the instant the customer confirms, with no request per second and no lag at the till. The Python SDK has no subscription helper because the subscription belongs in the page, not on the server.
Receiving the result
The result arrives on a realtime channel, not in a response body. The SDK subscribes for you and blocks until the session resolves:
session = sdk.generate_qr_login(device_name='Till 3', shop_id=shop_id)
# render session['qr_code'], then wait on the channel
result = sdk.subscribe_qr_login(session['session_id'])
if result and result['status'] == 'authenticated':
token, user = result['token'], result['user']Pass a callback to watch every frame instead of only the final one. The helper returns
None/null if nothing resolved within the timeout — sessions last 5 minutes.
Note
The realtime helpers need a WebSocket client: pip install websocket-client. Without it the SDK still works;
only these two methods raise.
Because it blocks, use it from a POS, a CLI or a worker. In a web request, open the session on your server and let the page subscribe — channels, events and payloads are in Realtime.
QR login
Open the session on your server, render the QR code, and let the page wait on the channel above.
session = sdk.generate_qr_login(device_name='Till 3', shop_id=shop_id)
# render session['qr_code'] as a QR image; the page subscribes to
# qr-login.{session['session_id']} and receives the token thereFallback: polling
Only if the page cannot hold a WebSocket open. Never block a worker in a loop — expose the poll as your own endpoint and call it from the browser:
# Django
def qr_status(request, session_id):
return JsonResponse(sdk.poll_qr_login(session_id))The status is authenticated when the customer confirms, and the response then
carries token and user.
| Status | Meaning |
|---|---|
pending | Waiting for the customer to scan |
scanned | Scanned; waiting for confirmation in the app |
confirmed | Confirmed; the token is being issued |
authenticated | Done — token and user are present |
cancelled | The customer declined |
expired | Generate a new session |
If the customer has no app
sdk.send_app_link('+37060000001', shop_id)QR card scan
The POS flow: identify the card without asking for a phone number.
session = sdk.generate_qr_card_session(device_name='Till 3', shop_id=shop_id)
# render session['qr_code']; the till subscribes to qr-card.{session['session_id']}
# and receives card_data the moment the customer scanscard_data carries loyalty_card_id, card_number, points and user. If the till
cannot hold a WebSocket open, sdk.poll_qr_card_status(session['session_id']) returns
the same thing, one request at a time.
Finishing the sale
coupons = sdk.get_card_coupons(shop_id, card_id=card['loyalty_card_id'])
games = sdk.get_card_games(card['loyalty_card_id'], shop_id)
sdk.create_transaction(
user_id=card['user']['id'],
order_id=f'POS-{int(time.time())}',
order_total=total,
currency='EUR',
shop_id=shop_id,
)
for game in games:
sdk.add_stamps(game['id'], card['loyalty_card_id'], shop_id, stamps_count=1)Basic Usage
Awarding points, looking up cards, coupons and games in Python
Which element names the importer recognises GET
Every field the parser reads, with the element names accepted for each. Read this rather than hardcoding the list: the aliases grow as feeds in the wild need them. Only name and price are required per product.