Skip to content

Commit

Permalink
feat: Add initiate transaction logic - paytm
Browse files Browse the repository at this point in the history
feat: added checksum file from paytm

Added condition for sandbox & live

added paytm in schema choices
  • Loading branch information
mrsaicharan1 committed Aug 17, 2019
1 parent c576251 commit 66e58c0
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 2 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="))
16 changes: 16 additions & 0 deletions app/api/helpers/payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
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
Expand Down Expand Up @@ -274,3 +276,17 @@ def charge_payment(order_identifier, token):
},
)
return charge


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

@staticmethod
def generate_checksum(paytm_params):
if get_settings()['paytm_mode'] == 'sandbox':
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)
48 changes: 47 additions & 1 deletion app/api/orders.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import logging
import json
import pytz
from datetime import datetime
import requests


import omise
from flask import request, jsonify, Blueprint, url_for, redirect
Expand All @@ -10,6 +13,7 @@
from marshmallow_jsonapi.flask import Schema
from sqlalchemy.orm.exc import NoResultFound

from app.settings import get_settings
from app.api.bootstrap import api
from app.api.data_layers.ChargesLayer import ChargesLayer
from app.api.helpers.db import save_to_db, safe_query, safe_query_without_soft_deleted_entries
Expand All @@ -22,7 +26,7 @@
send_notif_ticket_cancel
from app.api.helpers.order import delete_related_attendees_for_order, set_expiry_for_order, \
create_pdf_tickets_for_holder, create_onsite_attendees_for_order
from app.api.helpers.payment import AliPayPaymentsManager, OmisePaymentsManager
from app.api.helpers.payment import AliPayPaymentsManager, OmisePaymentsManager, PaytmPaymentsManager
from app.api.helpers.payment import PayPalPaymentsManager
from app.api.helpers.permission_manager import has_access
from app.api.helpers.permissions import jwt_required
Expand Down Expand Up @@ -606,3 +610,45 @@ def omise_checkout(order_identifier):
logging.info(f"Successful charge: {charge.id}. Order ID: {order_identifier}")

return redirect(make_frontend_url('orders/{}/view'.format(order_identifier)))


@order_misc_routes.route('/orders/<string:order_identifier>/paytm/initiate-transaction', methods=['POST', 'GET'])
def initiate_transaction(order_identifier):
"""
Initiating a PayTM transaction to obtain the txn token
:param order_identifier:
:return: JSON response containing the signature & txn token
"""
order = safe_query(db, Order, 'identifier', order_identifier, 'identifier')
paytm_mode = get_settings()['paytm_mode']
paytm_params = {}
# body parameters
paytm_params["body"] = {
"requestType": "Payment",
"mid": (get_settings()['paytm_sandbox_merchant'] if paytm_mode == 'sandbox'
else get_settings()['paytm_live_merchant']),
"websiteName": "eventyay",
"orderId": order.id,
"callbackUrl": "",
"txnAmount": {
"value": order.amount,
"currency": order.event.payment_currency,
},
"userInfo": {
"custId": order.user.id,
},
}
checksum = PaytmPaymentsManager.generate_checksum(paytm_params)
# head parameters
paytm_params["head"] = {
"signature" : checksum
}
post_data = json.dumps(paytm_params)
if paytm_mode == 'sandbox':
url = "https://securegw-stage.paytm.in/theia/api/v1/initiateTransaction?mid={}&orderId={}".\
format(get_settings()['paytm_sandbox_merchant'], order.id)
else:
url = "https://securegw.paytm.in/theia/api/v1/initiateTransaction?mid={}&orderId={}".\
format(get_settings()['paytm_sandbox_merchant'], order.id)
response = requests.post(url, data=post_data, headers={"Content-type": "application/json"})
return response.json()
2 changes: 1 addition & 1 deletion app/api/schema/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def initial_values(self, data):
payment_mode = fields.Str(
default="free",
validate=validate.OneOf(choices=["free", "stripe", "paypal", "bank",
"cheque", "onsite", "omise", "alipay"]),
"cheque", "onsite", "omise", "alipay", "paytm"]),
allow_none=True)
paid_via = fields.Str(dump_only=True)
is_billing_enabled = fields.Boolean(default=False)
Expand Down
1 change: 1 addition & 0 deletions requirements/common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ stripe~=2.35.0
xhtml2pdf~=0.2
flask-caching~=1.4
forex-python~=1.5
pycrypto~=2.6.1
oauth2~=1.9
qrcode~=6.1
python-magic~=0.4
Expand Down

0 comments on commit 66e58c0

Please sign in to comment.