Jan 13, 2020
15 minute read
What is the Paytm Payment Gateway?
Even though Drupal is quite a secure platform for the business, it is still not completely secured from the hands of spambots and hackers. Being an open-source makes Drupal more of a security thing and it is taken care of by a number of security experts who work continuously to check for weaknesses in the core or for vulnerabilities and its modules. Once the security team finds such an issue then they will quickly come up with a security update or a patch for the module in question and release it soon.
Terms you need to know before proceeding further
- MID: This is a unique identifier provided to every merchant by Paytm. MID is part of your account credentials and is different in the staging and production environments. Your staging MID is available here and production MID will be available once your activation is complete.
- Merchant Key: This is a unique secret key used for secure encryption of every request. This needs to be kept on the server-side and should not be shared with anyone.
- CHANNEL_ID:This parameter is used to control the theme of the payment page. Based on the channel passed, Paytm will render the layout suitable for that specific platform For websites, the value is WEB For Mobile websites/App, the value is WAP
- WEBSITE: For staging environment: WEBSTAGING,
For production environment: Will be available here once your activation is completeli> - INDUSTRY_TYPE_ID:For staging environment: “Retail”,For production environment: Will be available here once your activation is complete.
- CALLBACK_URL:On completion of the transaction, the Paytm payment gateway will send the response on this URL. Sample URL is – https://merchant.com/callback/
- Payment_url:when start payment it’s redirecting to payment gateway. In that, we can not go back or not refresh the page.
These are the steps to integrate Paytm as a payment gateway in your rails app
- Step 1:Need to create developer account from here: create account
- Â
- Step 2:After successful account creation, Grab below credentials from Paytm and place it inside secrets.yml or credentials.yml
- Test Merchant ID = ”
- Test Merchant Key = ”
- Website = WEBSTAGING
- Industry Type = Retail
- Channel ID (For Website) = WEB
- Channel ID (For Mobile Apps) = WAP
- transaction_url: https://securegw-stage.paytm.in/theia/processTransaction
- Â
- Step 3:We need to process and verify the payment. Create one controller named paytm_controller.rb
class PaytmController < ApplicationController
include PaytmHelper
skip_before_action :verify_authenticity_token
def start_payment
start_payment = Paytm::StartPaymentService.new(params, request)
start_payment.call
@checksum = start_payment.checksum
@paytmParam = start_payment.paytmParam
end
def verify_payment
paytm_verify = Paytm::VerifyPaymentService.new(params, @cart)
paytm_verify.call
@paytmparams = paytm_verify.paytmparams
@is_valid_checksum = paytm_verify.is_valid_checksum
if @is_valid_checksum == true
if @paytmparams["STATUS"] == "TXN_SUCCESS"
respond_to do |format|
format.html
end
else
respond_to do |format|
format.html
end
end
end
end
end
- Step 4:Add service in your app. We make services for controller code refactoraccount
start_payment_service.rb
include PaytmHelper
module Paytm
class StartPaymentService
attr :request,
:paytmParam,
:checksum
def initialize(attribute, request)
@request = request
end
def call
get_param_list
end
private
def get_param_list
paytmParam = Hash.new
paytmParam["MID"] = Rails.application.credentials[:merchant_ID]
paytmParam["ORDER_ID"] = "#{Time.now.to_i.to_s}"
paytmParam["CUST_ID"] = "#{Time.now.to_i.to_s}"
paytmParam["INDUSTRY_TYPE_ID"] = Rails.application.credentials[:industry_type]
paytmParam["CHANNEL_ID"] = Rails.application.credentials[:channel_web_ID]
paytmParam["TXN_AMOUNT"] = 1
paytmParam["WEBSITE"] = Rails.application.credentials[:website]
paytmParam["CALLBACK_URL"] =
"#{request.protocol + request.host_with_port}/confirm_payment"
@paytmParam=paytmParam
@checksum=generate_checksum()
end
end
end
verify_payment_service.rb
include PaytmHelper
module Paytm
class VerifyPaymentService
attr_reader :keys,
:paytmparams,
:is_valid_checksum
def initialize(attribute, cart)
@attribute = attribute
@keys = attribute.keys
end
def call
paytm_params
end
private
def paytm_params
paytmparams = Hash.new
@keys.each do |k|
paytmparams[k] = @attribute[k]
end
@checksum_hash = paytmparams["CHECKSUMHASH"]
paytmparams.delete("CHECKSUMHASH")
paytmparams.delete("controller")
paytmparams.delete("action")
@paytmparams = paytmparams
@is_valid_checksum = verify_checksum()
end
end
end
- Step 5:Add routes in routes.rb file
match '/paytm_payment' => 'paytm#start_payment', via: [:post], :as => :paytm_payment
match '/confirm_payment' => 'paytm#verify_payment', via: [:post]
match '/paytm_payment' => 'paytm#start_payment', via: [:post], :as => :paytm_payment match '/confirm_payment' => 'paytm#verify_payment', via: [:post]
- Step 6:Reduce logic from view file, create one helper named paytm_helper.rb
module PaytmHelper
require 'paytm/encryption_new_pg.rb'
include EncryptionNewPG
def generate_checksum
new_pg_checksum(@paytmParam, Rails.application.credentials[:merchant_key]).gsub("\n",'')
end
def verify_checksum
new_pg_verify_checksum(@paytmparams, @checksum_hash, Rails.application.credentials[:merchant_key])
end
end
- Step 7:We will add encyption_new_pg.rb file for encrypt data in module named encryption_new_pg.rb inside helpers/paytm folder
######################################## Encryption module #########################################
############################ AES 128-bit encryption with SHA256 Hash ###############################
####################################### New PG Encryption ##########################################
module EncryptionNewPG
require 'openssl'
require 'base64'
require 'digest'
require 'securerandom'
### function returns dictionary of encrypted data ###
### accepts a dictionary with data and key to encrypt with ###
### can accept multiple key value pairs in the dictionary ###
def new_pg_encrypt(paytmparams)
if (paytmparams.class != Hash) || (paytmparams.keys == [])
return false
end
if !paytmparams.has_key?(:key)
return false
end
encrypted_data = Hash[]
key = paytmparams.delete(:key)
keys = paytmparams.keys
###aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
aes = OpenSSL::Cipher::AES.new('128-CBC')
begin
keys.each do |k|
data = paytmparams[k]
aes.encrypt
aes.key = key
aes.iv = '@@@@&&&###$$$$'
encrypted_k = aes.update(k.to_s) + aes.final
encrypted_k = Base64.encode64(encrypted_k.to_s)
aes.encrypt
aes.key = key
aes.iv = '@@@@&&&###$$$$'
encrypted_data[encrypted_k] = aes.update(data.to_s) + aes.final
encrypted_data[encrypted_k] = Base64.encode64(encrypted_data[encrypted_k])
end
rescue Exception => e
return false
end
return encrypted_data
end
### function returns a single encrypted value ###
### input data -> value to be encrypted ###
### key -> key to use for encryption ###
def new_pg_encrypt_variable(data, key)
##aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
aes = OpenSSL::Cipher::AES.new('128-CBC')
aes.encrypt
aes.key = key
aes.iv = '@@@@&&&###$$$$'
encrypted_data = nil
begin
encrypted_data = aes.update(data.to_s) + aes.final
encrypted_data = Base64.encode64(encrypted_data)
rescue Exception => e
return false
end
return encrypted_data
end
### function returns dictionary of decrypted data ###
### accepts a dictionary with data and key to decrypt with ###
### can accept multiple key value pairs in the dictionary ###
def new_pg_decrypt(paytmparams)
if (paytmparams.class != Hash) || (paytmparams.keys == [])
return false
end
if !paytmparams.has_key?(:key)
return false
end
decrypted_data = Hash[]
key = paytmparams.delete(:key)
keys = paytmparams.keys
##aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
aes = OpenSSL::Cipher::AES.new('128-CBC')
begin
keys.each do |k|
data = paytmparams[k]
aes.decrypt
aes.key = key
aes.iv = '@@@@&&&###$$$$'
decrypted_k = Base64.decode64(k.to_s)
decrypted_k = aes.update(decrypted_k.to_s) + aes.final
if data.empty?
decrypted_data[decrypted_k] = ""
next
end
aes.decrypt
aes.key = key
aes.iv = '@@@@&&&###$$$$'
data = Base64.decode64(data)
decrypted_data[decrypted_k] = aes.update(data) + aes.final
end
rescue Exception => e
return false
end
return decrypted_data
end
### function returns a single decrypted value ###
### input data -> value to be decrypted ###
### key -> key to use for decryption ###
def new_pg_decrypt_variable(data, key)
##aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
aes = OpenSSL::Cipher::AES.new('128-CBC')
aes.decrypt
aes.key = key
aes.iv = '@@@@&&&###$$$$'
decrypted_data = nil
begin
decrypted_data = Base64.decode64(data.to_s)
decrypted_data = aes.update(decrypted_data) + aes.final
rescue Exception => e
return false
end
return decrypted_data
end
def new_pg_generate_salt(length)
salt = SecureRandom.urlsafe_base64(length*(3.0/4.0))
return salt.to_s
end
### function returns checksum of given key value pairs ###
### accepts a hash with key value pairs ###
### calculates sha256 checksum of given values ###
def new_pg_checksum(paytmparams, key, salt_length = 4)
if paytmparams.class != Hash
return false
end
if key.empty?
return false
end
salt = new_pg_generate_salt(salt_length)
keys = paytmparams.keys
str = nil
keys = keys.sort
keys.each do |k|
if str.nil?
str = paytmparams[k].to_s
next
end
str = str + '|' + paytmparams[k].to_s
end
str = str + '|' + salt
check_sum = Digest::SHA256.hexdigest(str)
check_sum = check_sum + salt
### encrypting checksum ###
check_sum = new_pg_encrypt_variable(check_sum, key)
check_sum = check_sum.gsub("\r\n",'')
check_sum = check_sum.gsub("\n",'')
return check_sum
end
### function returns checksum of given String ###
### accepts a hash with String ###
### calculates sha256 checksum of given values ###
def new_pg_checksum_by_String(strdata, key, salt_length = 4)
if strdata.empty?
return false
end
if key.empty?
return false
end
salt = new_pg_generate_salt(salt_length)
str = nil
str = strdata + '|' + salt
check_sum = Digest::SHA256.hexdigest(str)
check_sum = check_sum + salt
### encrypting checksum ###
check_sum = new_pg_encrypt_variable(check_sum, key)
check_sum = check_sum.gsub("\r\n",'')
check_sum = check_sum.gsub("\n",'')
return check_sum
end
### function returns checksum of given key value pairs ###
### accepts a hash with key value pairs ###
### calculates sha256 checksum of given values ###
def new_pg_refund_checksum(paytmparams, key, salt_length = 4)
keys = paytmparams.keys
keys.each do |k|
if ! paytmparams[k].empty?
#if params[k].to_s.include? "REFUND"
unless paytmparams[k].to_s.include? "|"
next
end
paytmparams[k] = paytmparams[k]
end
end
if paytmparams.class != Hash
return false
end
if key.empty?
return false
end
salt = new_pg_generate_salt(salt_length)
keys = paytmparams.keys
str = nil
keys = keys.sort
keys.each do |k|
if str.nil?
str = paytmparams[k].to_s
next
end
str = str + '|' + paytmparams[k].to_s
end
str = str + '|' + salt
check_sum = Digest::SHA256.hexdigest(str)
check_sum = check_sum + salt
### encrypting checksum ###
check_sum = new_pg_encrypt_variable(check_sum, key)
check_sum = check_sum.gsub("\r\n",'')
check_sum = check_sum.gsub("\n",'')
return check_sum
end
### function returns checksum of given key value pairs (must contain the :checksum key) ###
### accepts a hash with key value pairs ###
### calculates sha256 checksum of given values ###
### returns true if checksum is consistent ###
### returns false in case of inconsistency ###
def new_pg_verify_checksum_by_String(strdata, check_sum, key, salt_length = 4)
if strdata.empty?
return false
end
if key.empty?
return false
end
if check_sum.nil? || check_sum.empty?
return false
end
generated_check_sum = nil
check_sum = check_sum.gsub("\r\n",'')
check_sum = check_sum.gsub("\n",'')
check_sum = new_pg_decrypt_variable(check_sum, key)
if check_sum == false
return false
end
begin
salt = check_sum[(check_sum.length-salt_length), (check_sum.length)]
str = strdata + '|' + salt
generated_check_sum = Digest::SHA256.hexdigest(str)
generated_check_sum = generated_check_sum + salt
rescue Exception => e
return false
end
if check_sum == generated_check_sum
return true
else
return false
end
end
### function returns checksum of given key value pairs (must contain the :checksum key) ###
### accepts a hash with key value pairs ###
### calculates sha256 checksum of given values ###
### returns true if checksum is consistent ###
### returns false in case of inconsistency ###
def new_pg_verify_checksum(paytmparams, check_sum, key, salt_length = 4)
if paytmparams.class != Hash
return false
end
if key.empty?
return false
end
if check_sum.nil? || check_sum.empty?
return false
end
generated_check_sum = nil
check_sum = check_sum.gsub("\r\n",'')
check_sum = check_sum.gsub("\n",'')
check_sum = new_pg_decrypt_variable(check_sum, key)
if check_sum == false
return false
end
begin
salt = check_sum[(check_sum.length-salt_length), (check_sum.length)]
keys = paytmparams.keys
str = nil
keys = keys.sort
keys.each do |k|
if str.nil?
str = paytmparams[k].to_s
next
end
str = str + '|' + paytmparams[k].to_s
end
str = str + '|' + salt
generated_check_sum = Digest::SHA256.hexdigest(str)
generated_check_sum = generated_check_sum + salt
rescue Exception => e
return false
end
if check_sum == generated_check_sum
return true
else
return false
end
end
end
- Step 8:Now we can create a view for start and verify the payment. For that, we can add 2 files named start_payment.html.erb and verify_payment.html.erb
start_payment.html.erb
class PaytmController < ApplicationController
include PaytmHelper
skip_before_action :verify_authenticity_token
def start_payment
start_payment = Paytm::StartPaymentService.new(params, request)
start_payment.call
@checksum = start_payment.checksum
@paytmParam = start_payment.paytmParam
end
def verify_payment
paytm_verify = Paytm::VerifyPaymentService.new(params, @cart)
paytm_verify.call
@paytmparams = paytm_verify.paytmparams
@is_valid_checksum = paytm_verify.is_valid_checksum
if @is_valid_checksum == true
if @paytmparams["STATUS"] == "TXN_SUCCESS"
respond_to do |format|
format.html
end
else
respond_to do |format|
format.html
end
end
end
end
end
verify_payment.html.erb
<%= @paytmparams["STATUS"] %>
<%= @paytmparams["RESPMSG"] %>
- Step 9:Add the Paytm payment button which initializes payment.
<%= form_tag paytm_payment_path, method: :post, remote: false, :style => "" do %>
<% end %>
<%= form_tag paytm_payment_path, method: :post, remote: false, :style => "" do %>
<button>Start Payment</button>
<% end %>
Everything is done now you will have a complete working payment gateway on your web app. Any question regarding the process or if any issue in implementing the above steps you can comment and we will be glad to help. Thank you!