diff --git a/.gitignore b/.gitignore index c111b33..4aa6afc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +Gemfile.lock *.gem +.*.swp diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/init.rb b/init.rb deleted file mode 100644 index 3ae6e16..0000000 --- a/init.rb +++ /dev/null @@ -1,2 +0,0 @@ -require "paiement_cic" -require "paiement_cic/form_helper" diff --git a/lib/paiement_cic.rb b/lib/paiement_cic.rb index 3dc5766..d4c7bec 100644 --- a/lib/paiement_cic.rb +++ b/lib/paiement_cic.rb @@ -1,8 +1,9 @@ +require 'paiement_cic/tpe' +require 'paiement_cic/railtie' if defined?(Rails) + require 'digest/sha1' -require 'openssl' class String - def ^(other) raise ArgumentError, "Can't bitwise-XOR a String with a non-String" \ unless other.kind_of? String @@ -13,102 +14,35 @@ class String end end -class PaiementCic - autoload :FormHelper, "paiement_cic/form_helper" +module PaiementCic + 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 - cattr_accessor :version + class << self + 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 - cattr_accessor :hmac_key - - @@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*") + def configure(&block) + yield(self) if block_given? end - key = key.ljust(length, 0.chr) - ipad = ''.ljust(length, 54.chr) - 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] + def target_url + @target_url ||= (env == 'production' ? '' : "https://paiement.creditmutuel.fr/test/paiement.cgi") # "https://ssl.paiement.cic-banques.fr/paiement.cgi" 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 diff --git a/lib/paiement_cic/form_helper.rb b/lib/paiement_cic/form_helper.rb index 429a433..7b8efb9 100644 --- a/lib/paiement_cic/form_helper.rb +++ b/lib/paiement_cic/form_helper.rb @@ -1,32 +1,23 @@ -## refactor this module PaiementCic::FormHelper - def paiement_cic_hidden_fields(order, price, order_transaction, options = {}) - oa = PaiementCic.config(price, order_transaction.reference) + def paiement_cic_hidden_fields(reference, price, options = {}) + oMac = PaiementCic::TPE.new(options) + oa = oMac.config(reference, price, options) - oMac = PaiementCic.new - 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) + chaineMAC = oMac.computeHMACSHA1(oa.values.join('*')) - url_retour = options[:url_retour] || bank_callback_order_transactions_url - url_retour_ok = options[:url_retour_ok] || bank_callback_order_transactions_url(order) - url_retour_err = options[:url_retour_err] || bank_err_order_transaction_url(order) + url_retour = options[:url_retour] + url_retour_ok = options[:url_retour_ok] + url_retour_err = options[:url_retour_err] - html = ' - - - - - - - - - - - - - ' + html = hidden_field_tag('MAC', chaineMAC) + html << hidden_field_tag('url_retour', url_retour) + html << hidden_field_tag('url_retour_ok', url_retour_ok) + html << hidden_field_tag('url_retour_err', url_retour_err) - 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 diff --git a/lib/paiement_cic/railtie.rb b/lib/paiement_cic/railtie.rb new file mode 100644 index 0000000..966bb23 --- /dev/null +++ b/lib/paiement_cic/railtie.rb @@ -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 diff --git a/lib/paiement_cic/tpe.rb b/lib/paiement_cic/tpe.rb new file mode 100644 index 0000000..da5af34 --- /dev/null +++ b/lib/paiement_cic/tpe.rb @@ -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 diff --git a/lib/paiement_cic/version.rb b/lib/paiement_cic/version.rb index e477294..354be3c 100644 --- a/lib/paiement_cic/version.rb +++ b/lib/paiement_cic/version.rb @@ -1,3 +1,3 @@ -class PaiementCic +module PaiementCic VERSION = "0.2" end diff --git a/paiement_cic.gemspec b/paiement_cic.gemspec index bf33c84..cdc77e7 100644 --- a/paiement_cic.gemspec +++ b/paiement_cic.gemspec @@ -1,12 +1,13 @@ # 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" Gem::Specification.new do |s| s.name = "paiement_cic" s.version = PaiementCic::VERSION s.platform = Gem::Platform::RUBY - s.authors = ["Novelys Team"] + s.authors = ["Novelys Team", "La Fourmi Immo"] s.homepage = "https://github.com/novelys/paiementcic" 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.}