Implement Moyasar payments flow with webhooks
This commit is contained in:
@@ -2,15 +2,35 @@ import os
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class PaymentGatewayError(RuntimeError):
|
||||
def __init__(self, message: str, status_code: Optional[int] = None, payload: Optional[dict] = None) -> None:
|
||||
super().__init__(message)
|
||||
self.status_code = status_code
|
||||
self.payload = payload or {}
|
||||
|
||||
|
||||
@dataclass
|
||||
class PaymentInitResult:
|
||||
external_id: str
|
||||
status: Optional[str]
|
||||
redirect_url: Optional[str]
|
||||
payload: dict
|
||||
|
||||
|
||||
class BasePaymentGateway:
|
||||
def create_payment(self, amount: str, currency: str, description: str) -> PaymentInitResult:
|
||||
def create_payment(
|
||||
self,
|
||||
amount: int,
|
||||
currency: str,
|
||||
description: str,
|
||||
source: dict,
|
||||
callback_url: Optional[str],
|
||||
given_id: str,
|
||||
metadata: dict,
|
||||
) -> PaymentInitResult:
|
||||
raise NotImplementedError
|
||||
|
||||
def capture_payment(self, external_id: str) -> None:
|
||||
@@ -30,9 +50,56 @@ class MoyasarGateway(BasePaymentGateway):
|
||||
if not self.secret_key or not self.publishable_key:
|
||||
raise ValueError("Moyasar credentials are not configured")
|
||||
|
||||
def create_payment(self, amount: str, currency: str, description: str) -> PaymentInitResult:
|
||||
def create_payment(
|
||||
self,
|
||||
amount: int,
|
||||
currency: str,
|
||||
description: str,
|
||||
source: dict,
|
||||
callback_url: Optional[str],
|
||||
given_id: str,
|
||||
metadata: dict,
|
||||
) -> PaymentInitResult:
|
||||
self._assert_config()
|
||||
raise NotImplementedError("Moyasar gateway integration not implemented yet")
|
||||
url = f"{self.base_url}/v1/payments"
|
||||
payload = {
|
||||
"amount": amount,
|
||||
"currency": currency,
|
||||
"description": description,
|
||||
"source": source,
|
||||
"given_id": given_id,
|
||||
"metadata": metadata,
|
||||
}
|
||||
if callback_url:
|
||||
payload["callback_url"] = callback_url
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=payload, auth=(self.secret_key, ""), timeout=10)
|
||||
except requests.RequestException as exc:
|
||||
raise PaymentGatewayError("Failed to reach Moyasar") from exc
|
||||
|
||||
try:
|
||||
data = response.json() if response.content else {}
|
||||
except ValueError as exc:
|
||||
raise PaymentGatewayError("Invalid response from Moyasar") from exc
|
||||
|
||||
if response.status_code not in (200, 201):
|
||||
raise PaymentGatewayError(
|
||||
"Moyasar returned an error",
|
||||
status_code=response.status_code,
|
||||
payload=data,
|
||||
)
|
||||
redirect_url = None
|
||||
source_payload = data.get("source") or {}
|
||||
if isinstance(source_payload, dict):
|
||||
redirect_url = source_payload.get("transaction_url")
|
||||
|
||||
return PaymentInitResult(
|
||||
external_id=data.get("id"),
|
||||
status=data.get("status"),
|
||||
redirect_url=redirect_url,
|
||||
payload=data,
|
||||
)
|
||||
|
||||
def capture_payment(self, external_id: str) -> None:
|
||||
self._assert_config()
|
||||
|
||||
Reference in New Issue
Block a user