-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpaymentsense.py
422 lines (367 loc) · 14.6 KB
/
paymentsense.py
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# coding: utf-8
import re
import hashlib
import datetime
import pytz
import requests
# Function to test whether a variable is a number or not
def is_number(s):
try:
float(s)
return True
except ValueError:
return False
# All variables with None are obviously not required by the API, but must
# be passed as None in order for the hashing to work as expected.
def build_hash(PreSharedKey, MerchantID, Password, Amount,
CurrencyCode, EchoAVSCheckResult, EchoCV2CheckResult,
EchoThreeDSecureAuthenticationCheckResult, EchoCardType,
OrderID, TransactionType, CallbackURL, EmailAddressEditable,
PhoneNumberEditable, CV2Mandatory, Address1Mandatory, CityMandatory,
PostCodeMandatory, StateMandatory, CountryMandatory, ResultDeliveryMethod,
PaymentFormDisplaysResult, TransactionDateTime,
OrderDescription, CustomerName, Address1, Address2,
Address3, Address4, City, State, PostCode,
CountryCode, EmailAddress, PhoneNumber,
ServerResultURL):
# This is the order the variables must be passed to payment sense,
# where None has been defined, these are the non required variables
# PreSharedKey needs to validation
if len(MerchantID) > 15:
raise Exception("MerchantID must be no more than 15 characters")
# Password needs to validation
amount_test = is_number(Amount)
if amount_test == False:
raise Exception("Amount need to be a numerical value")
# We hard code CurrencyCode to 826/GBP
CurrencyCode = 826
echo_avs_check = isinstance(EchoAVSCheckResult, bool)
if echo_avs_check == False:
raise Exception("EchoAVSCheckResult accepts True or False")
echo_cv2_check = isinstance(EchoCV2CheckResult, bool)
if echo_cv2_check == False:
raise Exception("EchoCV2CheckResult accepts True or False")
echo_3dcheck = isinstance(EchoThreeDSecureAuthenticationCheckResult, bool)
if echo_3dcheck == False:
raise Exception(
"EchoThreeDSecureAuthenticationCheckResult accepts True or False")
echo_card_type_check = isinstance(EchoCardType, bool)
if echo_card_type_check == False:
raise Exception("EchoCardType accepts True or False")
if len(OrderID) > 50:
raise Exception("OrderID must be no more than 15 characters")
if TransactionType == "SALE" or "PREAUTH":
pass
else:
raise Exception("TransactionType accepts 'SALE' or 'PREAUTH'")
# CallbackURL doesn't really need any validation
if ServerResultURL == None:
ServerResultURL = ""
if OrderDescription == None:
OrderDescription = ""
elif len(OrderDescription) > 256:
raise Exception(
"OrderDescription must be no more than 256 characters")
if CustomerName == None:
CustomerName = ""
elif len(CustomerName) > 100:
raise Exception(
"CustomerName must be no more than 100 characters")
if Address1 == None:
Address1 = ""
elif len(Address1) > 100:
raise Exception(
"Address1 must be no more than 100 characters")
if Address2 == None:
Address2 = ""
elif len(Address2) > 50:
raise Exception(
"Address1 must be no more than 50 characters")
if Address3 == None:
Address3 = ""
elif len(Address3) > 50:
raise Exception(
"Address3 must be no more than 50 characters")
if Address4 == None:
Address4 = ""
elif len(Address4) > 50:
raise Exception(
"Address4 must be no more than 50 characters")
if City == None:
City = ""
elif len(City) > 50:
raise Exception(
"City must be no more than 50 characters")
if State == None:
State = ""
elif len(State) > 50:
raise Exception(
"State must be no more than 50 characters")
if PostCode == None:
PostCode = ""
elif len(PostCode) > 50:
raise Exception(
"PostCode must be no more than 50 characters")
if CountryCode == None:
CountryCode = ""
elif len(CountryCode) > 3:
raise Exception(
"CountryCode must be no more than 50 characters")
else:
cc_test = is_number(CountryCode)
if cc_test == False:
raise Exception("CountryCode need to be a numerical value")
if EmailAddress == None:
EmailAddress = ""
elif len(EmailAddress) > 100:
raise Exception(
"EmailAddress must be no more than 100 characters")
else:
email_test = re.match(r'\b[\w.-]+@[\w.-]+.\w{2,4}\b',
EmailAddress)
# if email is invalid, send to luke and he can investigate
if email_test == None:
raise Exception("EmailAddress is an invalid format")
if PhoneNumber == None:
PhoneNumber = ""
elif len(PhoneNumber) > 30:
raise Exception(
"PostCode must be no more than 30 characters")
email_edit_check = isinstance(EmailAddressEditable, bool)
if email_edit_check == False:
raise Exception("EmailAddressEditable accepts True or False")
phone_edit_check = isinstance(PhoneNumberEditable, bool)
if phone_edit_check == False:
raise Exception("PhoneNumberEditable accepts True or False")
cv2_man_check = isinstance(CV2Mandatory, bool)
if cv2_man_check == False:
raise Exception("CV2Mandatory accepts True or False")
addr1_man_check = isinstance(Address1Mandatory, bool)
if addr1_man_check == False:
raise Exception("Address1Mandatory accepts True or False")
city_man_check = isinstance(CityMandatory, bool)
if city_man_check == False:
raise Exception("CityMandatory accepts True or False")
postc_man_check = isinstance(PostCodeMandatory, bool)
if postc_man_check == False:
raise Exception("PostCodeMandatory accepts True or False")
state_man_check = isinstance(StateMandatory, bool)
if state_man_check == False:
raise Exception("StateMandatory accepts True or False")
country_man_check = isinstance(CountryMandatory, bool)
if country_man_check == False:
raise Exception("CountryMandatory accepts True or False")
if ResultDeliveryMethod == "POST" or "SERVER" or "SERVER_PULL":
pass
else:
raise Exception(
"ResultDeliveryMethod accepts 'POST', 'SERVER' or 'SERVER_PULL'")
# ServerResultURL requires no validation
pay_form_check = isinstance(PaymentFormDisplaysResult, bool)
if pay_form_check == False:
raise Exception("PaymentFormDisplaysResult accepts True or False")
# Pre hash must be in this order
# Can't split, no PEP8 awards to be won here
prehash_skeleton = """PreSharedKey={PreSharedKey}&MerchantID={MerchantID}&Password={Password}&Amount={Amount}&CurrencyCode={CurrencyCode}&EchoAVSCheckResult={EchoAVSCheckResult}&EchoCV2CheckResult={EchoCV2CheckResult}&EchoThreeDSecureAuthenticationCheckResult={EchoThreeDSecureAuthenticationCheckResult}&EchoCardType={EchoCardType}&OrderID={OrderID}&TransactionType={TransactionType}&TransactionDateTime={TransactionDateTime}&CallbackURL={CallbackURL}&OrderDescription={OrderDescription}&CustomerName={CustomerName}&Address1={Address1}&Address2={Address2}&Address3={Address3}&Address4={Address4}&City={City}&State={State}&PostCode={PostCode}&CountryCode={CountryCode}&EmailAddress={EmailAddress}&PhoneNumber={PhoneNumber}&EmailAddressEditable={EmailAddressEditable}&PhoneNumberEditable={PhoneNumberEditable}&CV2Mandatory={CV2Mandatory}&Address1Mandatory={Address1Mandatory}&CityMandatory={CityMandatory}&PostCodeMandatory={PostCodeMandatory}&StateMandatory={StateMandatory}&CountryMandatory={CountryMandatory}&ResultDeliveryMethod={ResultDeliveryMethod}&ServerResultURL={ServerResultURL}&PaymentFormDisplaysResult={PaymentFormDisplaysResult}"""
# Format the string
prehash = prehash_skeleton.format(
PreSharedKey=PreSharedKey,
MerchantID=MerchantID,
Password=Password,
Amount=Amount,
CurrencyCode=CurrencyCode,
EchoAVSCheckResult=EchoAVSCheckResult,
EchoCV2CheckResult=EchoCV2CheckResult,
EchoThreeDSecureAuthenticationCheckResult=
EchoThreeDSecureAuthenticationCheckResult,
EchoCardType=EchoCardType,
OrderID=OrderID,
TransactionType=TransactionType,
TransactionDateTime=TransactionDateTime,
CallbackURL=CallbackURL,
OrderDescription=OrderDescription,
CustomerName=CustomerName,
Address1=Address1,
Address2=Address2,
Address3=Address3,
Address4=Address4,
City=City,
State=State,
PostCode=PostCode,
CountryCode=CountryCode,
EmailAddress=EmailAddress,
PhoneNumber=PhoneNumber,
EmailAddressEditable=EmailAddressEditable,
PhoneNumberEditable=PhoneNumberEditable,
CV2Mandatory=CV2Mandatory,
Address1Mandatory=Address1Mandatory,
CityMandatory=CityMandatory,
PostCodeMandatory=PostCodeMandatory,
StateMandatory=StateMandatory,
CountryMandatory=CountryMandatory,
ResultDeliveryMethod=ResultDeliveryMethod,
ServerResultURL=ServerResultURL,
PaymentFormDisplaysResult=PaymentFormDisplaysResult
)
# Format the hash to what payment sense expects
prehash_remove_none = prehash.replace("None","")
# Replace None with empty string, then remove any \n instances
prehash_clean = prehash_remove_none.replace("\n","")
# lastly generate the sha1 string to pass to the URL
sha1_string = hashlib.sha1(prehash_clean).hexdigest()
# In the PaymentSense admin console you can change the default hash method
# to MD5, HMACMD5 or HMACSHA1, please adjust the hash line accordingly.
# Return sha1 hash of all values
return dict(
sha1_string=sha1_string,
PreSharedKey=PreSharedKey,
MerchantID=MerchantID,
Password=Password,
Amount=Amount,
CurrencyCode=CurrencyCode,
EchoAVSCheckResult=EchoAVSCheckResult,
EchoCV2CheckResult=EchoCV2CheckResult,
EchoThreeDSecureAuthenticationCheckResult=
EchoThreeDSecureAuthenticationCheckResult,
EchoCardType=EchoCardType,
OrderID=OrderID,
TransactionType=TransactionType,
TransactionDateTime=TransactionDateTime,
CallbackURL=CallbackURL,
OrderDescription=OrderDescription,
CustomerName=CustomerName,
Address1=Address1,
Address2=Address2,
Address3=Address3,
Address4=Address4,
City=City,
State=State,
PostCode=PostCode,
CountryCode=CountryCode,
EmailAddress=EmailAddress,
PhoneNumber=PhoneNumber,
EmailAddressEditable=EmailAddressEditable,
PhoneNumberEditable=PhoneNumberEditable,
CV2Mandatory=CV2Mandatory,
Address1Mandatory=Address1Mandatory,
CityMandatory=CityMandatory,
PostCodeMandatory=PostCodeMandatory,
StateMandatory=StateMandatory,
CountryMandatory=CountryMandatory,
ResultDeliveryMethod=ResultDeliveryMethod,
ServerResultURL=ServerResultURL,
PaymentFormDisplaysResult=PaymentFormDisplaysResult
)
def get_paymenturl(PreSharedKey, MerchantID, Password, Amount, CurrencyCode,
EchoAVSCheckResult, EchoCardType, OrderID, TransactionType,
CallbackURL, EmailAddressEditable, PhoneNumberEditable,
CV2Mandatory, Address1Mandatory, CityMandatory,
PostCodeMandatory, StateMandatory, CountryMandatory,
ResultDeliveryMethod, PaymentFormDisplaysResult,
EchoCV2CheckResult, EchoThreeDSecureAuthenticationCheckResult,
ServerResultURL, OrderDescription, CustomerName, Address1,
Address2, Address3, Address4, City, State, PostCode,
CountryCode, EmailAddress, PhoneNumber, datetime_tz,
post_addr):
# Datetime check, we set this manually, it is not passed to the function
# TransactionDateTime needs to be in the format of
# “YYYY-MM-DD HH:MM:SS +00:00”, with the time in 24hr format,
# where 00:00 is the offset from UTC - e.g. “2013-07-22 13:46 +01:00”
# Define the output format
fmt = '%Y-%m-%d %H:%M:%S %z'
# Add your own timezone here
TransactionDateTime_unclean = datetime_tz.strftime(fmt)
# Now the python datetime method doesnt include a colon in the UTC offset
# which means we need this lovely messy code below
last_c1 = TransactionDateTime_unclean[-1]
last_c2 = TransactionDateTime_unclean[-2]
end_str = ":" + last_c2 + last_c1
TransactionDateTime_remove = TransactionDateTime_unclean[:-2]
TransactionDateTime = TransactionDateTime_remove + end_str
# Now build call the hash build function, this variable will become a
# dict with all cleaned variables
sha1_hash = build_hash(
PreSharedKey = PreSharedKey,
MerchantID = MerchantID,
Password = Password,
Amount = Amount,
CurrencyCode = CurrencyCode,
EchoAVSCheckResult = EchoAVSCheckResult,
EchoCV2CheckResult = EchoCV2CheckResult,
EchoThreeDSecureAuthenticationCheckResult =\
EchoThreeDSecureAuthenticationCheckResult,
EchoCardType = EchoCardType,
OrderID = OrderID,
TransactionType = TransactionType,
TransactionDateTime=TransactionDateTime,
CallbackURL = CallbackURL,
EmailAddressEditable = EmailAddressEditable,
PhoneNumberEditable = PhoneNumberEditable,
CV2Mandatory = CV2Mandatory,
Address1Mandatory = Address1Mandatory,
CityMandatory = CityMandatory,
PostCodeMandatory = PostCodeMandatory,
StateMandatory = StateMandatory,
CountryMandatory = CountryMandatory,
ResultDeliveryMethod=ResultDeliveryMethod,
PaymentFormDisplaysResult = PaymentFormDisplaysResult,
ServerResultURL=ServerResultURL,
OrderDescription=OrderDescription,
CustomerName=CustomerName,
Address1=Address1,
Address2=Address2,
Address3=Address3,
Address4=Address4,
City=City,
State=State,
PostCode=PostCode,
CountryCode=CountryCode,
EmailAddress=EmailAddress,
PhoneNumber=PhoneNumber)
# Now we have the hash, build the request data pairs with the clean
# variables we have received from the build_hash() function
data = {
'HashDigest': sha1_hash['sha1_string'],
'MerchantID': sha1_hash['MerchantID'],
'Amount': sha1_hash['Amount'],
'CurrencyCode': sha1_hash['CurrencyCode'],
'EchoAVSCheckResult': sha1_hash['EchoAVSCheckResult'],
'EchoCV2CheckResult': sha1_hash['EchoCV2CheckResult'],
'EchoThreeDSecureAuthenticationCheckResult':
sha1_hash['EchoThreeDSecureAuthenticationCheckResult'],
'EchoCardType': sha1_hash['EchoCardType'],
'OrderID': sha1_hash['OrderID'],
'TransactionType': sha1_hash['TransactionType'],
'TransactionDateTime': sha1_hash['TransactionDateTime'],
'CallbackURL': sha1_hash['CallbackURL'],
'OrderDescription': sha1_hash['OrderDescription'],
'CustomerName': sha1_hash['CustomerName'],
'Address1': sha1_hash['Address1'],
'Address2': sha1_hash['Address2'],
'Address3': sha1_hash['Address3'],
'Address4': sha1_hash['Address4'],
'City': sha1_hash['City'],
'State': sha1_hash['State'],
'PostCode': sha1_hash['PostCode'],
'CountryCode': sha1_hash['CountryCode'],
'EmailAddress': sha1_hash['EmailAddress'],
'PhoneNumber': sha1_hash['PhoneNumber'],
'EmailAddressEditable': sha1_hash['EmailAddressEditable'],
'PhoneNumberEditable': sha1_hash['PhoneNumberEditable'],
'CV2Mandatory': sha1_hash['CV2Mandatory'],
'Address1Mandatory': sha1_hash['Address1Mandatory'],
'CityMandatory': sha1_hash['CityMandatory'],
'PostCodeMandatory': sha1_hash['PostCodeMandatory'],
'StateMandatory': sha1_hash['StateMandatory'],
'CountryMandatory': sha1_hash['CountryMandatory'],
'ResultDeliveryMethod': sha1_hash['ResultDeliveryMethod'],
'ServerResultURL': sha1_hash['ServerResultURL'],
'PaymentFormDisplaysResult':sha1_hash['PaymentFormDisplaysResult'],
}
r = requests.post(post_addr, params=data)
url = r.url
url_clean = url.replace("None","")
# Return the clean url, your application can then direct the user
# to this url, once payment is completed the user will be sent to the
# 'CallbackURL' given to the function
return r.url