A minimal MonCash client in Python (Django) with token caching

poze a month ago343 gadeen

0

I'd rather not depend on an unmaintained package. What does a small, correct MonCash client look like in Python for a Django project?

Anviwònman:Django 5.1Python 3.12

Dyagnostik IA

Se IA ki fè l. Se pa yon repons — kominote a anba a konfime oswa korije l. Toujou verifye anvan ou fye l.

1 Repons

4
✓
✓ Repons yo aksepte

A small client is ~40 lines, and you'll understand every one of them. That matters when money is involved.

# payments/moncash.py
import time
import requests
from django.conf import settings
 
class MonCash:
    def __init__(self):
        self.api = settings.MONCASH_API          # sandbox or live
        self.gateway = settings.MONCASH_GATEWAY
        self._token = None
        self._expires_at = 0.0
        self.http = requests.Session()
 
    def _token_value(self) -> str:
        if self._token and time.time() < self._expires_at - 10:
            return self._token
        r = self.http.post(
            f"{self.api}/oauth/token",
            auth=(settings.MONCASH_CLIENT_ID, settings.MONCASH_CLIENT_SECRET),  # HTTP Basic
            data={"scope": "read,write", "grant_type": "client_credentials"},    # form-encoded
            headers={"Accept": "application/json"},
            timeout=15,
        )
        r.raise_for_status()
        body = r.json()
        self._token, self._expires_at = body["access_token"], time.time() + body["expires_in"]
        return self._token
 
    def _post(self, path: str, payload: dict) -> dict:
        r = self.http.post(f"{self.api}{path}", json=payload, timeout=20,
                           headers={"Authorization": f"Bearer {self._token_value()}"})
        if r.status_code == 401:            # token expired between check and call
            self._token = None
            return self._post(path, payload)
        r.raise_for_status()
        return r.json()
 
    def create_payment(self, order_id: str, amount: int) -> str:
        token = self._post("/v1/CreatePayment", {"amount": amount, "orderId": order_id})["payment_token"]["token"]
        return f"{self.gateway}/Payment/Redirect?token={token}"
 
    def transaction(self, transaction_id: str) -> dict | None:
        return self._post("/v1/RetrieveTransactionPayment", {"transactionId": transaction_id}).get("payment")
 
    def order(self, order_id: str) -> dict | None:
        return self._post("/v1/RetrieveOrderPayment", {"orderId": order_id}).get("payment")
 
moncash = MonCash()

Always set timeouts. A requests call without one can hang a worker forever when the network is unstable. And note the 401 retry above only retries once, because the recursive call gets a fresh token.

Konekte pou di lòt moun si li te mache.

answered a month ago

Sign in and verify your email to post an answer.