-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathmtn.py
More file actions
120 lines (104 loc) · 4.22 KB
/
Copy pathmtn.py
File metadata and controls
120 lines (104 loc) · 4.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import httpx
from typing import Dict, Any, Optional
class MTNMobileMoneyAdapter:
"""
MTN Mobile Money API Adapter
--------------------------------
Handles:
- Authentication (OAuth2)
- Request-to-Pay (Collections)
- Transfers (Disbursements)
- Transaction Status
- Refunds (if supported)
"""
BASE_URL = "https://api.mtn.com/v1/" # Change to sandbox for testing
def __init__(self, api_key: str, subscription_key: str, environment: str = "sandbox"):
"""
:param api_key: API Key from MTN Developer Portal
:param subscription_key: Subscription Key (Ocp-Apim-Subscription-Key)
:param environment: "sandbox" or "production"
"""
self.api_key = api_key
self.subscription_key = subscription_key
self.environment = environment
self.access_token = None
async def authenticate(self) -> str:
"""
Obtain OAuth2 access token from MTN API.
"""
url = f"{self.BASE_URL}token/"
headers = {
"Ocp-Apim-Subscription-Key": self.subscription_key,
}
async with httpx.AsyncClient() as client:
resp = await client.post(url, headers=headers, auth=(self.api_key, ""))
resp.raise_for_status()
data = resp.json()
self.access_token = data.get("access_token")
return self.access_token
async def send_payment(self, amount: float, phone_number: str, currency: str, reference: str) -> Dict[str, Any]:
"""
Initiate a 'Request to Pay' (Collection) transaction.
"""
if not self.access_token:
await self.authenticate()
url = f"{self.BASE_URL}collection/request-to-pay"
headers = {
"Authorization": f"Bearer {self.access_token}",
"X-Reference-Id": reference,
"X-Target-Environment": self.environment,
"Content-Type": "application/json",
"Ocp-Apim-Subscription-Key": self.subscription_key,
}
payload = {
"amount": str(amount),
"currency": currency,
"externalId": reference,
"payer": {
"partyIdType": "MSISDN",
"partyId": phone_number,
},
"payerMessage": "EasySwitch Payment",
"payeeNote": "Thank you for using EasySwitch",
}
async with httpx.AsyncClient() as client:
resp = await client.post(url, headers=headers, json=payload)
return {"status_code": resp.status_code, "reference": reference}
async def check_transaction_status(self, reference: str) -> Dict[str, Any]:
"""
Check the status of a transaction by reference ID.
"""
if not self.access_token:
await self.authenticate()
url = f"{self.BASE_URL}transaction/status/{reference}"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Ocp-Apim-Subscription-Key": self.subscription_key,
}
async with httpx.AsyncClient() as client:
resp = await client.get(url, headers=headers)
resp.raise_for_status()
return resp.json()
async def process_refund(self, original_reference: str, amount: float, currency: str) -> Dict[str, Any]:
"""
Process refund for a completed transaction (if supported).
"""
if not self.access_token:
await self.authenticate()
url = f"{self.BASE_URL}disbursement/transfer"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Ocp-Apim-Subscription-Key": self.subscription_key,
"Content-Type": "application/json",
}
payload = {
"amount": str(amount),
"currency": currency,
"externalId": f"refund_{original_reference}",
"payee": {"partyIdType": "MSISDN", "partyId": "<customer_number>"},
"payerMessage": "Refund Processed",
"payeeNote": "Refund from EasySwitch",
}
async with httpx.AsyncClient() as client:
resp = await client.post(url, headers=headers, json=payload)
return {"status_code": resp.status_code, "refund_reference": original_reference}