Skip to content

Commit

Permalink
feat: Added otp validation & process transaction logic
Browse files Browse the repository at this point in the history
Added url method paytm

Finalized fetch payment options

Tested all endpoints for responses
  • Loading branch information
mrsaicharan1 committed Aug 20, 2019
1 parent fcf5275 commit ebe9502
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 60 deletions.
139 changes: 139 additions & 0 deletions app/api/helpers/checksum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Checksum module from https://github.com/Paytm-Payments/Paytm_Web_Sample_Kit_Python

import base64
import string
import random
import hashlib

from Crypto.Cipher import AES


IV = "@@@@&&&&####$$$$"
BLOCK_SIZE = 16


def generate_checksum(param_dict, merchant_key, salt=None):
params_string = __get_param_string__(param_dict)
salt = salt if salt else __id_generator__(4)
final_string = '%s|%s' % (params_string, salt)

hasher = hashlib.sha256(final_string.encode())
hash_string = hasher.hexdigest()

hash_string += salt

return __encode__(hash_string, IV, merchant_key)


def generate_refund_checksum(param_dict, merchant_key, salt=None):
for i in param_dict:
if("|" in param_dict[i]):
param_dict = {}
exit()
params_string = __get_param_string__(param_dict)
salt = salt if salt else __id_generator__(4)
final_string = '%s|%s' % (params_string, salt)

hasher = hashlib.sha256(final_string.encode())
hash_string = hasher.hexdigest()

hash_string += salt

return __encode__(hash_string, IV, merchant_key)


def generate_checksum_by_str(param_str, merchant_key, salt=None):
params_string = param_str
salt = salt if salt else __id_generator__(4)
final_string = '%s|%s' % (params_string, salt)

hasher = hashlib.sha256(final_string.encode())
hash_string = hasher.hexdigest()

hash_string += salt

return __encode__(hash_string, IV, merchant_key)


def verify_checksum(param_dict, merchant_key, checksum):
# Remove checksum
if 'CHECKSUMHASH' in param_dict:
param_dict.pop('CHECKSUMHASH')

# Get salt
paytm_hash = __decode__(checksum, IV, merchant_key)
salt = paytm_hash[-4:]
calculated_checksum = generate_checksum(param_dict, merchant_key, salt=salt)
return calculated_checksum == checksum


def verify_checksum_by_str(param_str, merchant_key, checksum):
# Get salt
paytm_hash = __decode__(checksum, IV, merchant_key)
salt = paytm_hash[-4:]
calculated_checksum = generate_checksum_by_str(param_str, merchant_key, salt=salt)
return calculated_checksum == checksum


def __id_generator__(size=6, chars=string.ascii_uppercase + string.digits + string.ascii_lowercase):
return ''.join(random.choice(chars) for _ in range(size))


def __get_param_string__(params):
params_string = []
for key in sorted(params.keys()):
if("REFUND" in params[key] or "|" in params[key]):
exit()
value = params[key]
params_string.append('' if value == 'null' else str(value))
return '|'.join(params_string)


def __pad__(string_literal):
return (string_literal + (BLOCK_SIZE - len(string_literal) % BLOCK_SIZE) *
chr(BLOCK_SIZE - len(string_literal) % BLOCK_SIZE))


def __unpad__(string_literal):
return string_literal[0:-ord(string_literal[-1])]


def __encode__(to_encode, iv, key):
# Pad
to_encode = __pad__(to_encode)
# Encrypt
c = AES.new(key, AES.MODE_CBC, iv)
to_encode = c.encrypt(to_encode)
# Encode
to_encode = base64.b64encode(to_encode)
return to_encode.decode("UTF-8")


def __decode__(to_decode, iv, key):
# Decode
to_decode = base64.b64decode(to_decode)
# Decrypt
c = AES.new(key, AES.MODE_CBC, iv)
to_decode = c.decrypt(to_decode)
if type(to_decode) == bytes:
# convert bytes array to str.
to_decode = to_decode.decode()
# remove pad
return __unpad__(to_decode)


if __name__ == "__main__":
params = {
"MID": "mid",
"ORDER_ID": "order_id",
"CUST_ID": "cust_id",
"TXN_AMOUNT": "1",
"CHANNEL_ID": "WEB",
"INDUSTRY_TYPE_ID": "Retail",
"WEBSITE": "xxxxxxxxxxx"
}

print(verify_checksum(
params, 'xxxxxxxxxxxxxxxx',
"CD5ndX8VVjlzjWbbYoAtKQIlvtXPypQYOg0Fi2AUYKXZA5XSHiRF0FDj7vQu6\
6S8MHx9NaDZ/uYm3WBOWHf+sDQAmTyxqUipA7i1nILlxrk="))
67 changes: 25 additions & 42 deletions app/api/helpers/payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from forex_python.converter import CurrencyRates

from app.api.helpers.cache import cache
from app.settings import get_settings
from app.api.helpers import checksum
from app.api.helpers.exceptions import ForbiddenException, ConflictException
from app.api.helpers.utilities import represents_int
from app.models.stripe_authorization import StripeAuthorization
from app.settings import get_settings, Environment
from app.api.helpers.db import safe_query, save_to_db
from app.api.helpers.db import safe_query
from app.models import db
from app.models.order import Order

Expand Down Expand Up @@ -204,47 +206,6 @@ def create_payment(order, return_url, cancel_url):
else:
return False, payment.error

@staticmethod
def verify_payment(payment_id, order):
"""
Verify Paypal payment one more time for paying with Paypal in mobile client
"""
try:
payment_server = paypalrestsdk.Payment.find(payment_id)
if payment_server.state != 'approved':
return False, 'Payment has not been approved yet. Status is ' + payment_server.state + '.'

# Get the most recent transaction
transaction = payment_server.transactions[0]
amount_server = transaction.amount.total
currency_server = transaction.amount.currency
sale_state = transaction.related_resources[0].sale.state

if amount_server != order.amount:
return False, 'Payment amount does not match order'
elif currency_server != order.event.payment_currency:
return False, 'Payment currency does not match order'
if sale_state != 'completed':
return False, 'Sale not completed'
elif PayPalPaymentsManager.used_payment(payment_id, order):
return False, 'Payment already been verified'
else:
return True, None
except paypalrestsdk.ResourceNotFound:
return False, 'Payment Not Found'

@staticmethod
def used_payment(payment_id, order):
"""
Function to check for recycling of payment IDs
"""
if Order.query.filter(Order.paypal_token == payment_id).first() is None:
order.paypal_token = payment_id
save_to_db(order)
return False
else:
return True

@staticmethod
def execute_payment(paypal_payer_id, paypal_payment_id):
"""
Expand Down Expand Up @@ -315,3 +276,25 @@ def charge_payment(order_identifier, token):
},
)
return charge


class PaytmPaymentsManager(object):
"""
Class to manage PayTM payments
"""

@property
def paytm_endpoint():
if get_settings()['paytm_mode'] == 'test':
url = "https://securegw-stage.paytm.in/theia/api/v1/" # for staging
else:
url = "https://securegw.paytm.in/theia/api/v1/" # for production
return url

@staticmethod
def generate_checksum(paytm_params):
if get_settings()['paytm_mode'] == 'test':
merchant_key = get_settings()['paytm_sandbox_secret']
else:
merchant_key = get_settings()['paytm_live_secret']
return checksum.generate_checksum_by_str(json.dumps(paytm_params["body"]), merchant_key)
Loading

0 comments on commit ebe9502

Please sign in to comment.