# How to calculate a PayFast signature using Python
Wed Feb 08 2023
# Introduction
To initiate a payment through PayFast using their custom integration (opens new window), you need to calculate a security signature (opens new window) that gets calculated from the data in your checkout form.
# Issues in the provided example code
The provided Python code in the PayFast documentation is incorrect at the time of writing and will return the incorrect signature.
The three issues in their provided code are:
- Plus signs (
+
s) are replaced by spaces, instead of spaces which should be replaced by plus signs. This can be fixed by removing the.replace("+", " ")
call. Theurllib.parse.quote_plus
will automatically replace spaces with pluses. - Blank variables aren't automatically removed.
- The variables aren't automatically ordered in the correct order. The documentation is a bit vague on the order, but it should be ordered in the same order in which the parameters are listed in the documentation under "Create your checkout form" (opens new window).
# Solution code
Below you can find a Python script that worked for me to generate a Payfast security signature.
import logging
import urllib
from hashlib import md5
logger = logging.getLogger(__name__)
CHECKOUT_SIGNATURE_FIELD_ORDER = [
# Merchant Details
"merchant_id",
"merchant_key",
"return_url",
"cancel_url",
"notify_url",
# Buyer Detail
"name_first",
"name_last",
"email_address",
"cell_number",
# Transaction Details
"m_payment_id",
"amount",
"item_name",
"item_description",
"custom_int1",
"custom_int2",
"custom_int3",
"custom_int4",
"custom_int5",
"custom_str1",
"custom_str2",
"custom_str3",
"custom_str4",
"custom_str5",
# Transaction Options
"email_confirmation",
"confirmation_address",
# Set Payment Method
"payment_method",
# Recurring Billing Details
"subscription_type",
"billing_date",
"recurring_amount",
"frequency",
"cycles",
]
def sort_by_priority_list(values, priority):
"""
Sorts an iterable according to a list of priority items.
Usage:
>>> sort_by_priority_list(values=[1,2,2,3], priority=[2,3,1])
[2, 2, 3, 1]
>>> sort_by_priority_list(values=set([1,2,3]), priority=[2,3])
[2, 3, 1]
"""
priority_dict = {k: i for i, k in enumerate(priority)}
def priority_getter(value):
return priority_dict.get(value, len(values))
return sorted(values, key=priority_getter)
def calculate_signature(input_data, passphrase):
"""
Calculate a Payfast signature according to the documentation:
https://developers.payfast.co.za/docs#step_2_signature
"""
output_data = {}
for item in input_data.items():
# Filter out blank values and remove surrounding spaces
if item[1].strip():
output_data[item[0]] = item[1].strip()
# Sort the keys in the required order
keys = sort_by_priority_list(output_data.keys(), CHECKOUT_SIGNATURE_FIELD_ORDER)
# Prepare the parameter string
payfast_string = ""
for key in keys:
if key != "signature":
payfast_string += (
key + "=" + urllib.parse.quote_plus(output_data[key]) + "&"
)
# Append passphrase
payfast_string = payfast_string[:-1]
payfast_string += f"&passphrase={passphrase}"
# Hash the concatenated string
return md5(payfast_string.encode()).hexdigest()
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
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
Newsletter
If you'd like to subscribe to my blog, please enter your details below. You can unsubscribe at any time.