"""Python 3.10+, standard library only. Run on the store server.""" import json import re import urllib.error import urllib.parse import urllib.request class _NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None class DidbanClient: def __init__(self, base_url: str, api_key: str): u = urllib.parse.urlsplit(base_url) if (not u.hostname or u.username or u.password or u.query or u.fragment or u.path not in ('', '/') or not (u.scheme == 'https' or u.scheme == 'http' and u.hostname in ('127.0.0.1', 'localhost'))): raise ValueError('Use an HTTPS origin or localhost.') if not re.fullmatch(r'db_(test|live)_[A-Za-z0-9_-]{43}', api_key): raise ValueError('Invalid API key.') self.base_url = base_url.rstrip('/') self.api_key = api_key self.opener = urllib.request.build_opener(_NoRedirect()) def create_invoice(self, request_id: str, amount_rial: str, ttl_minutes: int | None = None, presentation: str = "hosted") -> dict: return self._request('/api/v1/invoices', dict(request_id=request_id, amount_rial=amount_rial, ttl_minutes=ttl_minutes, presentation=presentation)) def get_invoice(self, invoice_id: str) -> dict: if not re.fullmatch(r'[0-9a-f-]{36}', invoice_id): raise ValueError('Invalid invoice ID.') return self._request('/api/v1/invoices/' + invoice_id) def get_payment_data(self, invoice_id: str) -> dict: if not re.fullmatch(r'[0-9a-f-]{36}', invoice_id): raise ValueError('Invalid invoice ID.') return self._request('/api/v1/invoices/' + invoice_id + '/payment-data') def get_wallet(self) -> dict: return self._request('/api/v1/wallet') def _request(self, path: str, body: dict | None = None) -> dict: request = urllib.request.Request(self.base_url + path, data=json.dumps(body, ensure_ascii=False).encode('utf-8') if body is not None else None, headers={'Authorization': 'Bearer ' + self.api_key, 'Content-Type': 'application/json', 'Accept': 'application/json'}) try: with self.opener.open(request, timeout=20) as response: result = json.load(response) if not isinstance(result, dict): raise RuntimeError('Invalid API response.') return result except urllib.error.HTTPError as error: raise RuntimeError(f'Didban request failed (HTTP {error.code}).') from None except (urllib.error.URLError, TimeoutError): raise RuntimeError('Connection failed; retry only with the same request_id.') from None if __name__ == '__main__': import os client = DidbanClient(os.environ.get('DIDBAN_API_URL', 'http://127.0.0.1:4321'), os.environ['DIDBAN_API_KEY']) invoice = client.create_invoice('PY-ORDER-1001', '550680') # Persist invoice['order_id'], invoice['id'] and invoice['amount_rial'] with your local order before showing payment. print('Payment URL:', invoice['payment_url']) state = client.get_invoice(invoice['id']) print('Status:', state['status'], '| Requires review:', state['requires_review']) # Only live invoices with a verified bank approval can report paid=true. Never fulfill an order from test_payment.