Skip to main content

Python

Uses PyJWT with the cryptography backend, and requests.

pip install "PyJWT[crypto]" requests

Token client​

ryno_token.py
import os
import time
import uuid

import jwt
import requests

CLIENT_ID = os.environ["RYNO_CLIENT_ID"] # from the API Credentials page
TOKEN_URL = os.environ["RYNO_TOKEN_URL"] # e.g. https://keycloak.sandbox.rynopay.io/realms/ryno/protocol/openid-connect/token

with open(os.environ["RYNO_PRIVATE_KEY_PATH"], "rb") as f:
PRIVATE_KEY = f.read()

_cached = {"token": None, "expires_at": 0.0}


def build_assertion() -> str:
"""Sign a single-use client assertion, valid for 60 seconds."""
now = int(time.time())
claims = {
"iss": CLIENT_ID,
"sub": CLIENT_ID,
"aud": TOKEN_URL,
"jti": str(uuid.uuid4()),
"iat": now,
"exp": now + 60,
}
return jwt.encode(claims, PRIVATE_KEY, algorithm="RS256", headers={"typ": "JWT"})


def get_access_token(scope: str = "partner.read partner.write") -> str:
"""Return a cached access token, requesting a new one 30 seconds before expiry."""
if _cached["token"] and time.time() < _cached["expires_at"] - 30:
return _cached["token"]

response = requests.post(
TOKEN_URL,
data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": build_assertion(),
"scope": scope,
},
timeout=10,
)
body = response.json()
if not response.ok:
raise RuntimeError(f"Token request failed: {body.get('error')} {body.get('error_description', '')}")

_cached["token"] = body["access_token"]
_cached["expires_at"] = time.time() + body["expires_in"]
return _cached["token"]

Call the API​

example.py
import requests

from ryno_token import get_access_token

API_BASE_URL = "https://gateway.sandbox.rynopay.io/api"

response = requests.get(
f"{API_BASE_URL}/partner/v1/business-profiles",
params={"page": 1, "pageSize": 20},
headers={"Authorization": f"Bearer {get_access_token()}"},
timeout=10,
)
print(response.status_code, response.json())