essence-header-logo
Jan 13, 2020
15 minute read
Paytm integration with ROR

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

Paytm Terms needs to know
  • 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

            
                <div>
  <h1>
    <%= @paytmparams["STATUS"] %>
  </h1>
  <h1>
    <%= @paytmparams["RESPMSG"] %>
  </h1>
</div>
            
        
  • Step 9:Add the Paytm payment button which initializes payment.
            
                <%= form_tag paytm_payment_path, method: :post, remote: false, :style => "" do %>
     <button>Start Payment</button>
<% 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!

Sachiin Gevariya

Sachin Gevariya is a Founder and Technical Director at Essence Solusoft. He is dedicated to making the best use of modern technologies to craft end-to-end solutions. He also has a vast knowledge of Cloud management. He loves to do coding so still doing the coding. Also, help employees for quality based solutions to clients. Always eager to learn new technology and implement for best solutions.