Refactor and improve code
Clean code to improve readability, allow to use different TPE in the same application, add parameters like mail.master
parent
16a5ad5930
commit
93d0c9262d
|
@ -1 +1,3 @@
|
||||||
|
Gemfile.lock
|
||||||
*.gem
|
*.gem
|
||||||
|
.*.swp
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
require 'paiement_cic/tpe'
|
||||||
|
require 'paiement_cic/railtie' if defined?(Rails)
|
||||||
|
|
||||||
require 'digest/sha1'
|
require 'digest/sha1'
|
||||||
require 'openssl'
|
|
||||||
|
|
||||||
class String
|
class String
|
||||||
|
|
||||||
def ^(other)
|
def ^(other)
|
||||||
raise ArgumentError, "Can't bitwise-XOR a String with a non-String" \
|
raise ArgumentError, "Can't bitwise-XOR a String with a non-String" \
|
||||||
unless other.kind_of? String
|
unless other.kind_of? String
|
||||||
|
@ -13,102 +14,35 @@ class String
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class PaiementCic
|
module PaiementCic
|
||||||
autoload :FormHelper, "paiement_cic/form_helper"
|
API_VERSION = "3.0"
|
||||||
|
DATE_FORMAT = "%d/%m/%Y:%H:%M:%S"
|
||||||
|
|
||||||
@@version = "3.0" # clé extraite grâce à extract2HmacSha1.html fourni par le Crédit Mutuel
|
class << self
|
||||||
cattr_accessor :version
|
attr_accessor :hmac_key, :tpe, :societe, :env
|
||||||
|
attr_writer :target_url
|
||||||
|
|
||||||
@@hmac_key = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" # clé extraite grâce à extract2HmacSha1.html fourni par le Crédit Mutuel
|
def configure(&block)
|
||||||
cattr_accessor :hmac_key
|
yield(self) if block_given?
|
||||||
|
|
||||||
@@target_url = "https://paiement.creditmutuel.fr/test/paiement.cgi" # "https://ssl.paiement.cic-banques.fr/paiement.cgi"
|
|
||||||
cattr_accessor :target_url
|
|
||||||
|
|
||||||
@@tpe = "123456"
|
|
||||||
cattr_accessor :tpe
|
|
||||||
|
|
||||||
@@societe = "masociete"
|
|
||||||
cattr_accessor :societe
|
|
||||||
|
|
||||||
@@url_ok = ""
|
|
||||||
cattr_accessor :url_ok
|
|
||||||
|
|
||||||
def self.date_format
|
|
||||||
"%d/%m/%Y:%H:%M:%S"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.config(amount_in_cents, reference)
|
|
||||||
oa = ActiveSupport::OrderedHash.new
|
|
||||||
oa["version"] = "3.0"
|
|
||||||
oa["TPE"] = tpe
|
|
||||||
oa["date"] = Time.now.strftime(date_format)
|
|
||||||
oa["montant"] = ("%.2f" % amount_in_cents) + "EUR"
|
|
||||||
oa["reference"] = reference
|
|
||||||
oa["texte-libre"] = ""
|
|
||||||
oa["lgue"] = "FR"
|
|
||||||
oa["societe"] = societe
|
|
||||||
oa["mail"] = ""
|
|
||||||
oa
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.mac_string params
|
|
||||||
hmac_key = PaiementCic.new
|
|
||||||
mac_string = [hmac_key.tpe, params["date"], params['montant'], params['reference'], params['texte-libre'], hmac_key.version, params['code-retour'], params['cvx'], params['vld'], params['brand'], params['status3ds'], params['numauto'], params['motifrefus'], params['originecb'], params['bincb'], params['hpancb'], params['ipclient'], params['originetr'], params['veres'], params['pares']].join('*') + "*"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.verify_hmac params
|
|
||||||
hmac_key = PaiementCic.new
|
|
||||||
mac_string = [hmac_key.tpe, params["date"], params['montant'], params['reference'], params['texte-libre'], hmac_key.version, params['code-retour'], params['cvx'], params['vld'], params['brand'], params['status3ds'], params['numauto'], params['motifrefus'], params['originecb'], params['bincb'], params['hpancb'], params['ipclient'], params['originetr'], params['veres'], params['pares']].join('*') + "*"
|
|
||||||
|
|
||||||
hmac_key.valid_hmac?(mac_string, params['MAC'])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if the HMAC matches the HMAC of the data string
|
|
||||||
def valid_hmac?(mac_string, sent_mac)
|
|
||||||
computeHMACSHA1(mac_string) == sent_mac.downcase
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the HMAC for a data string
|
|
||||||
def computeHMACSHA1(data)
|
|
||||||
hmac_sha1(usable_key(self), data).downcase
|
|
||||||
end
|
|
||||||
|
|
||||||
def hmac_sha1(key, data)
|
|
||||||
length = 64
|
|
||||||
|
|
||||||
if (key.length > length)
|
|
||||||
key = [Digest::SHA1.hexdigest(key)].pack("H*")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
key = key.ljust(length, 0.chr)
|
def target_url
|
||||||
ipad = ''.ljust(length, 54.chr)
|
@target_url ||= (env == 'production' ? '' : "https://paiement.creditmutuel.fr/test/paiement.cgi") # "https://ssl.paiement.cic-banques.fr/paiement.cgi"
|
||||||
opad = ''.ljust(length, 92.chr)
|
|
||||||
|
|
||||||
k_ipad = key ^ ipad
|
|
||||||
k_opad = key ^ opad
|
|
||||||
|
|
||||||
#Digest::SHA1.hexdigest(k_opad + [Digest::SHA1.hexdigest(k_ipad + sData)].pack("H*"))
|
|
||||||
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("sha1"), key, data)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
# Return the key to be used in the hmac function
|
|
||||||
def usable_key(payement)
|
|
||||||
|
|
||||||
hex_string_key = payement.hmac_key[0..37]
|
|
||||||
hex_final = payement.hmac_key[38..40] + "00";
|
|
||||||
|
|
||||||
cca0 = hex_final[0].ord
|
|
||||||
|
|
||||||
if cca0 > 70 && cca0 < 97
|
|
||||||
hex_string_key += (cca0 - 23).chr + hex_final[1..2]
|
|
||||||
elsif hex_final[1..2] == "M"
|
|
||||||
hex_string_key += hex_final[0..1] + "0"
|
|
||||||
else
|
|
||||||
hex_string_key += hex_final[0..2]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
[hex_string_key].pack("H*")
|
def hmac_sha1(key, data)
|
||||||
|
length = 64
|
||||||
|
|
||||||
|
if (key.length > length)
|
||||||
|
key = [Digest::SHA1.hexdigest(key)].pack("H*")
|
||||||
|
end
|
||||||
|
|
||||||
|
key = key.ljust(length, 0.chr)
|
||||||
|
|
||||||
|
k_ipad = key ^ ''.ljust(length, 54.chr)
|
||||||
|
k_opad = key ^ ''.ljust(length, 92.chr)
|
||||||
|
|
||||||
|
Digest::SHA1.hexdigest(k_opad + [Digest::SHA1.hexdigest(k_ipad + data)].pack("H*"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,32 +1,23 @@
|
||||||
## refactor this
|
|
||||||
module PaiementCic::FormHelper
|
module PaiementCic::FormHelper
|
||||||
def paiement_cic_hidden_fields(order, price, order_transaction, options = {})
|
def paiement_cic_hidden_fields(reference, price, options = {})
|
||||||
oa = PaiementCic.config(price, order_transaction.reference)
|
oMac = PaiementCic::TPE.new(options)
|
||||||
|
oa = oMac.config(reference, price, options)
|
||||||
|
|
||||||
oMac = PaiementCic.new
|
chaineMAC = oMac.computeHMACSHA1(oa.values.join('*'))
|
||||||
sDate = Time.now.strftime("%d/%m/%Y:%H:%M:%S")
|
|
||||||
chaine = [oMac.tpe, sDate, oa["montant"], oa["reference"].to_s, oa["texte-libre"], oMac.version, "FR", oMac.societe, "", "", "", "", "", "", "", "", "", "", ""].join("*")
|
|
||||||
chaineMAC = oMac.computeHMACSHA1(chaine)
|
|
||||||
|
|
||||||
url_retour = options[:url_retour] || bank_callback_order_transactions_url
|
url_retour = options[:url_retour]
|
||||||
url_retour_ok = options[:url_retour_ok] || bank_callback_order_transactions_url(order)
|
url_retour_ok = options[:url_retour_ok]
|
||||||
url_retour_err = options[:url_retour_err] || bank_err_order_transaction_url(order)
|
url_retour_err = options[:url_retour_err]
|
||||||
|
|
||||||
html = '
|
html = hidden_field_tag('MAC', chaineMAC)
|
||||||
<input type="hidden" name="version" id="version" value="' + oa["version"] + '" />
|
html << hidden_field_tag('url_retour', url_retour)
|
||||||
<input type="hidden" name="TPE" id="TPE" value="' + oa["TPE"] + '" />
|
html << hidden_field_tag('url_retour_ok', url_retour_ok)
|
||||||
<input type="hidden" name="date" id="date" value="' + oa["date"] + '" />
|
html << hidden_field_tag('url_retour_err', url_retour_err)
|
||||||
<input type="hidden" name="montant" id="montant" value="' + oa["montant"] + '" />
|
|
||||||
<input type="hidden" name="reference" id="reference" value="' + oa["reference"].to_s + '" />
|
|
||||||
<input type="hidden" name="MAC" id="MAC" value="' + chaineMAC + '" />
|
|
||||||
<input type="hidden" name="url_retour" id="url_retour" value="' + url_retour + '" />
|
|
||||||
<input type="hidden" name="url_retour_ok" id="url_retour_ok" value="' + url_retour_ok + '" />
|
|
||||||
<input type="hidden" name="url_retour_err" id="url_retour_err" value="' + url_retour_err + '" />
|
|
||||||
<input type="hidden" name="lgue" id="lgue" value="' + oa["lgue"] + '" />
|
|
||||||
<input type="hidden" name="societe" id="societe" value="' + oa["societe"] + '" />
|
|
||||||
<input type="hidden" name="texte-libre" id="texte-libre" value="' + oa["texte-libre"] + '" />
|
|
||||||
<input type="hidden" name="mail" id="mail" value="''" />'
|
|
||||||
|
|
||||||
html.respond_to?(:html_safe) ? html.html_safe : html
|
oa.each do |k,v|
|
||||||
|
html << hidden_field_tag(k, v) unless v.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
html
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
require 'paiement_cic/form_helper'
|
||||||
|
|
||||||
|
module PaiementCic
|
||||||
|
class Railtie < Rails::Railtie
|
||||||
|
initializer "paiement_cic.form_helpers" do
|
||||||
|
ActionView::Base.send :include, FormHelper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,79 @@
|
||||||
|
module PaiementCic
|
||||||
|
class TPE
|
||||||
|
attr_accessor :hmac_key, :tpe, :societe
|
||||||
|
|
||||||
|
def initialize(options = {})
|
||||||
|
self.hmac_key = options[:hmac_key] || PaiementCic.hmac_key
|
||||||
|
self.tpe = options[:tpe] || PaiementCic.tpe
|
||||||
|
self.societe = options[:societe] || PaiementCic.societe
|
||||||
|
end
|
||||||
|
|
||||||
|
def config(reference, amount_in_cents, options = {})
|
||||||
|
{
|
||||||
|
'TPE' => tpe,
|
||||||
|
'date' => Time.now.strftime(PaiementCic::DATE_FORMAT),
|
||||||
|
'montant' => ("%.2f" % amount_in_cents) + "EUR",
|
||||||
|
'reference' => reference.to_s,
|
||||||
|
'texte-libre' => '',
|
||||||
|
'version' => PaiementCic::API_VERSION,
|
||||||
|
'lgue' => 'FR',
|
||||||
|
'societe' => societe,
|
||||||
|
'mail' => options[:mail].to_s,
|
||||||
|
'nbrech' => options[:nbrech].to_s,
|
||||||
|
'dateech1' => options[:dateech1].to_s,
|
||||||
|
'montantech1' => options[:montantech1].to_s,
|
||||||
|
'dateech2' => options[:dateech2].to_s,
|
||||||
|
'montantech2' => options[:montantech2].to_s,
|
||||||
|
'dateech3' => options[:dateech3].to_s,
|
||||||
|
'montantech3' => options[:montantech3].to_s,
|
||||||
|
'dateech4' => options[:dateech4].to_s,
|
||||||
|
'montantech4' => options[:montantech4].to_s,
|
||||||
|
'options' => options[:options].to_s
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def mac_string params
|
||||||
|
[
|
||||||
|
tpe, params["date"], params['montant'], params['reference'], params['texte-libre'],
|
||||||
|
PaiementCic::API_VERSION, params['code-retour'], params['cvx'], params['vld'], params['brand'],
|
||||||
|
params['status3ds'], params['numauto'], params['motifrefus'], params['originecb'],
|
||||||
|
params['bincb'], params['hpancb'], params['ipclient'], params['originetr'],
|
||||||
|
params['veres'], params['pares']
|
||||||
|
].join('*') + "*"
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_hmac params
|
||||||
|
params.has_key?('MAC') && valid_hmac?(mac_string(params), params['MAC'])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the HMAC matches the HMAC of the data string
|
||||||
|
def valid_hmac?(mac_string, sent_mac)
|
||||||
|
computeHMACSHA1(mac_string) == sent_mac.downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the HMAC for a data string
|
||||||
|
def computeHMACSHA1(data)
|
||||||
|
PaiementCic.hmac_sha1(usable_key, data).downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Return the key to be used in the hmac function
|
||||||
|
def usable_key
|
||||||
|
|
||||||
|
hex_string_key = hmac_key[0..37]
|
||||||
|
hex_final = hmac_key[38..40] + "00";
|
||||||
|
|
||||||
|
cca0 = hex_final[0].ord
|
||||||
|
|
||||||
|
if cca0 > 70 && cca0 < 97
|
||||||
|
hex_string_key += (cca0 - 23).chr + hex_final[1..2]
|
||||||
|
elsif hex_final[1..2] == "M"
|
||||||
|
hex_string_key += hex_final[0..1] + "0"
|
||||||
|
else
|
||||||
|
hex_string_key += hex_final[0..2]
|
||||||
|
end
|
||||||
|
|
||||||
|
[hex_string_key].pack("H*")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,3 @@
|
||||||
class PaiementCic
|
module PaiementCic
|
||||||
VERSION = "0.2"
|
VERSION = "0.2"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
$:.push File.expand_path("../lib", __FILE__)
|
lib = File.expand_path('../lib', __FILE__)
|
||||||
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
require "paiement_cic/version"
|
require "paiement_cic/version"
|
||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "paiement_cic"
|
s.name = "paiement_cic"
|
||||||
s.version = PaiementCic::VERSION
|
s.version = PaiementCic::VERSION
|
||||||
s.platform = Gem::Platform::RUBY
|
s.platform = Gem::Platform::RUBY
|
||||||
s.authors = ["Novelys Team"]
|
s.authors = ["Novelys Team", "La Fourmi Immo"]
|
||||||
s.homepage = "https://github.com/novelys/paiementcic"
|
s.homepage = "https://github.com/novelys/paiementcic"
|
||||||
s.summary = %q{CIC / Crédit Mutuel credit card payment toolbox}
|
s.summary = %q{CIC / Crédit Mutuel credit card payment toolbox}
|
||||||
s.description = %q{Paiement CIC is a gem to ease credit card payment with the CIC / Crédit Mutuel banks system. It's a Ruby on Rails port of the connexion kits published by the bank.}
|
s.description = %q{Paiement CIC is a gem to ease credit card payment with the CIC / Crédit Mutuel banks system. It's a Ruby on Rails port of the connexion kits published by the bank.}
|
||||||
|
|
Loading…
Reference in New Issue