""" Ссылка на оплату NextCoin. Формат один для всех клиентов, чтобы QR из кошелька на сайте читался мобильным приложением и наоборот. Сделан по образцу давно устоявшегося BIP-21 у биткоина, чтобы не изобретать своё там, где есть привычное людям. nextcoin:nxt1abc... nextcoin:nxt1abc...?amount=1.5 nextcoin:nxt1abc...?amount=1.5&label=Кофе Сумма всегда в NXT с точкой, как её видит человек, а не в базовых единицах: ссылку может собрать кто угодно вручную, и она должна читаться глазами. """ from urllib.parse import urlencode, urlparse, parse_qs, quote, unquote SCHEME = "nextcoin" def build(address: str, amount_base: int = None, label: str = None, message: str = None) -> str: """Собрать ссылку. amount_base в базовых единицах, наружу уходит в NXT.""" from chain.units import format_nxt from chain.nxtaddr import is_valid_address if not is_valid_address(address): raise ValueError("адрес не похож на адрес NextCoin") params = {} if amount_base: if amount_base <= 0: raise ValueError("сумма должна быть больше нуля") params["amount"] = format_nxt(amount_base) if label: params["label"] = label[:64] if message: params["message"] = message[:128] tail = ("?" + urlencode(params, quote_via=quote)) if params else "" return f"{SCHEME}:{address}{tail}" def parse(uri: str) -> dict: """ Разобрать ссылку или голый адрес. Голый адрес тоже принимаем: люди копируют именно его, и заставлять их искать правильный префикс было бы издевательством. """ from chain.units import parse_nxt from chain.nxtaddr import is_valid_address if not isinstance(uri, str): raise ValueError("пустая ссылка") text = uri.strip() if not text: raise ValueError("пустая ссылка") if ":" not in text: address, query = text, "" else: scheme, rest = text.split(":", 1) if scheme.lower() != SCHEME: raise ValueError(f"чужая ссылка: {scheme}") address, _, query = rest.partition("?") address = unquote(address).strip() if not is_valid_address(address): raise ValueError("в ссылке нет правильного адреса NextCoin") q = parse_qs(query, keep_blank_values=False) out = {"address": address, "amount": None, "label": None, "message": None} if "amount" in q: try: out["amount"] = parse_nxt(q["amount"][0]) except ValueError as e: raise ValueError(f"сумма в ссылке неверна: {e}") if "label" in q: out["label"] = q["label"][0][:64] if "message" in q: out["message"] = q["message"][0][:128] return out