Skip to main content

Reversal

Reverse completed M-Pesa transactions by initiating a reversal request and handling asynchronous result notifications.

User Stories

  • As a fintech product owner, I want to programmatically reverse M-Pesa transactions so that I can handle customer requests efficiently.
  • As an integrations developer, I want a simple client and clear webhook callbacks so I can implement reliable end-to-end flows with minimal boilerplate.
  • As a billing operations engineer, I want result and timeout notifications with acknowledgements so I can reconcile transactions and trigger retries or alerts when needed.
  • As a reseller partner, I want a tested SDK and examples so I can onboard quickly and reduce integration defects.

Parameters Definition

ParameterTypeDescription
Initiatorrequired
str
StringName of the initiating user (must be pre-approved by Safaricom).
SecurityCredentialrequired
str
StringEncrypted credential of the user (base64 encoded).
TransactionIDrequired
str
StringUnique M-Pesa transaction ID to reverse.
Amountrequired
float
FloatAmount to reverse (should match original transaction).
ReceiverPartyrequired
int
IntegerParty receiving the reversed funds (shortcode or MSISDN).
ResultURLrequired
str
StringHTTPS endpoint that will receive the result notification.
QueueTimeOutURLrequired
str
StringHTTPS endpoint that will receive timeout notifications.
Remarksrequired
str
StringReason for the reversal (<= 100 characters).
Occasion
str
StringOptional additional information (<= 100 characters).
OriginatorConversationID
str
StringUnique identifier for the conversation (returned in response).
ConversationID
str
StringUnique conversation ID assigned by M-Pesa (returned in response).
ResponseCode
str
StringStatus code of the reversal request (returned in response).
ResponseDescription
str
StringDescription of the reversal request status (returned in response).

Overview

Reversal allows you to cancel or reverse a previously completed M-Pesa transaction. It is an asynchronous operation with callbacks for results and timeouts.

Quick Setup

Python
# Example: initiate a reversal using the high-level client
from mpesakit import MpesaClient
client = MpesaClient(consumer_key="...", consumer_secret="...", environment="sandbox")
resp = client.reversal.reverse(
initiator="TestInit610",
security_credential="encrypted_credential",
transaction_id="LKXXXX1234",
amount=100,
receiver_party=600610,
result_url="https://your.example/result",
queue_timeout_url="https://your.example/timeout",
remarks="Wrong recipient",
occasion="Refund"
)
if resp.is_successful():
print("Reversal initiated successfully")
else:
print("Reversal failed:", resp.ResponseDescription)

Webhook Handling (Result & Timeout)

Python
# Example: simple FastAPI endpoints for Reversal Result and Timeout
from fastapi import FastAPI, Request, HTTPException
from mpesakit.reversal import ReversalResultCallback, ReversalResultCallbackResponse, ReversalTimeoutCallback, ReversalTimeoutCallbackResponse
from mpesakit.security.ip_whitelist import is_mpesa_ip_allowed
app = FastAPI()
@app.post("/reversal/result")
async def reversal_result(request: Request):
payload = await request.json()
caller_ip = (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip()
if not is_mpesa_ip_allowed(caller_ip):
raise HTTPException(status_code=403, detail="forbidden")
data = ReversalResultCallback(**payload) # will validate incoming fields
# process the result (update database, notify user, etc.)
ack = ReversalResultCallbackResponse()
return ack.model_dump(mode="json")
@app.post("/reversal/timeout")
async def reversal_timeout(request: Request):
payload = await request.json()
caller_ip = (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip()
if not is_mpesa_ip_allowed(caller_ip):
raise HTTPException(status_code=403, detail="forbidden")
# process timeout notification (log, retry logic, etc.)
ack = ReversalTimeoutCallbackResponse()
return ack.model_dump(mode="json")

Responses & Helpers

Example Reversal Success Response
{
"OriginatorConversationID": "71840-27539181-07",
"ConversationID": "AG_20210709_12346c8e6f8858d7b70a",
"ResponseCode": "0",
"ResponseDescription": "Accept the service request successfully."
}

Error Handling

Python
# Handle errors when calling the service
try:
resp = client.reversal.reverse(...)
except Exception as exc:
# The underlying HTTP client may raise exceptions on network errors; log and retry as appropriate
print("Reversal failed:", exc)

Testing & Expected Behaviors

  • Reversal:

    • The service posts to /mpesa/reversal/v1/request with Authorization header set via TokenManager.
    • Responses are returned as ReversalResponse instances.
    • The implementation tolerates a common provider typo ("OriginatorCoversationID") and maps it to OriginatorConversationID.
  • Validation:

    • Incoming payloads are validated against ReversalResultCallback and ReversalTimeoutCallback. Missing required fields or invalid formats will raise validation errors.
    • Use the provided response schemas for acknowledgements; invalid codes are rejected by the model validator.

Next Steps