I am trying to cancel a PayFast subscription (in the sandbox) using the API tools, in Python.
Looking at the PayFast documentation for recurring billing cancellations it appears to be a PUT request with minimal params.
Here is my minimal working example (with obfuscated credentials), trying to cancel a recurring billing given known merchant_id, passphrase, subscription_id values:
[Edit: it occurred to me that the signature is probably the existing signature for the recurring billing in place, rather than one generated at runtime of this cancellation. Script updated to pass the known signature forward from PayFast's notify_url using signature = data.get("signature")]
import requests
import hashlib
import datetime
import urllib.parse
# ====== HARDCODED VALUES FOR EXAMPLE ==
MERCHANT_ID = "1003XXXX"
PASSPHRASE = "XXXXXXXXXXXX"
SUBSCRIPTION_ID = "4a113e9b-4bda-4007-af87-xxxxxxxxxxxx"
SIGNATURE = "fe8d0a15218b2cab41be372a5cXXXXXX"
# ======================================
def cancel_subscription():
url = f"https://sandbox.payfast.co.za/subscriptions/{SUBSCRIPTION_ID}/cancel"
# Required PayFast params for the header
params = {
"merchant-id": MERCHANT_ID,
"version": "v1",
"timestamp": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
"signature": SIGNATURE
}
try:
response = requests.put(url, headers=params, timeout=10)
response.raise_for_status()
print("Success:", response.status_code)
print("Response JSON:", response.json())
except requests.exceptions.HTTPError as e:
print("HTTP Error:", e)
print("Response content:", response.text)
except Exception as e:
print("Other Error:", e)
if __name__ == "__main__":
cancel_subscription()
And I get this output error:
HTTP Error: 419 Client Error: unknown status for url: https://sandbox.payfast.co.za/subscriptions/4a113e9b-4bda-4007-af87-xxxxxxxxxxxx/cancel
(Obviously without the "xxxxxxxxxxxx")
Unknown status is a bit worrying as it suggests a complete failure at the endpoint. I've tried workarounds such as:
- Using
/api/in the url:https://sandbox.payfast.co.za/api/subscriptions/{SUBSCRIPTION_ID}/cancel- but this does not allow PUT operations - Various ChatGPT and Gemini suggestions which try DELETE requests instead of PUT and sending POST requests with HTTPBasicAuth in the headers.
These have yielded responses, which is a step forward. But, I'm a little out of my depth in terms of debugging and moving away from what the documentation says is required, so I would appreciate any wisdom on whether I'm missing obvious fixes!
I have noticed in the past that the PayFast API docs aren't 100% inline with the necessary steps to perform successful transaction API calls, and do require some playing around in the dark.
Extra info:
I know the signature generation is okay as it's worked for single payments and setting up recurring billing payments. For clarity, when successfully setting up payment requests, I have generated signatures in the same way as above with these params:
date_today = date.today().strftime("%Y-%m-%d")
params = {
"m_payment_id": transaction_id,
"merchant_id": merchant_id,
"merchant_key": merchant_key,
"return_url": return_url,
"cancel_url": cancel_url,
"notify_url": notify_url,
"email_address": email_address,
"amount": amount,
"item_name": item_name,
"item_description": f'Purchasing of the product entitled: {item_name}, for my mobile app',
"custom_str1": item_category,
"billing_date": date_today,
}
signature = generate_signature(params, passphrase)