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: 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" # ====================================== def generate_signature(data: dict, passphrase: str) -> str: """ Generate PayFast signature for API call. Sorts parameters, URL-encodes, and appends passphrase. """ data = {k: v for k, v in data.items() if v is not None and v != ""} # Remove empty values sorted_items = sorted(data.items()) # Sort by key query_string = urllib.parse.urlencode(sorted_items) # Encode into query string # Append passphrase if available if passphrase: query_string += f"&passphrase={passphrase}" print(f"Signature prior to encoding: {query_string}") # Hash with MD5 on return return hashlib.md5(query_string.encode("utf-8")).hexdigest() 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"), } # Generate signature signature = generate_signature(params, PASSPHRASE) params["signature"] = signature print("Signature generated:", signature, "Sending cancellation request...") try: response = requests.put(url, data=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) Continue reading...