diff --git a/MIT-LICENSE b/MIT-LICENSE index 48c13ea..71a0151 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -18,3 +18,66 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#============================================================================== +# +# "Open source" kit for P@iement CM-CIC(TM). +# Integration sample in a merchant site for Ruby +# +# Author : Euro-Information/e-Commerce (contact: centrecom@e-i.com) +# Version : 1.0 +# Date : 01/01/2009 +# +# Copyright: (c) 2009 Euro-Information. All rights reserved. +# +#============================================================================== + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + - Redistributions of source code must retain the above copyright + notice and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice and the following disclaimer in the documentation and/or + other materials provided with the distribution. + - Neither the name of Euro-Information nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Note: Euro-Information does not provide person-to-person technical + support for tryout of CM-CIC P@iement examples. We do however + welcome your feedback which can be sent to . + +#------------------------------------------------------------------------------ + +This software uses RSA Data Security, Inc. MD5 Message-Digest Algorithm. + +License to copy and use this software is granted provided that it is +identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software or +this function. +License is also granted to make and use derivative works provided that +such works are identified as "derived from the RSA Data Security, +Inc. MD5 Message-Digest Algorithm" in all material mentioning or +referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the +merchantability of this software or the suitability of this software +for any particular purpose. It is provided "as is" without express or +implied warranty of any kind. +These notices must be retained in any copies of any part of this +documentation and/or software. + +#============================================================================== diff --git a/README.markdown b/README.markdown index 6b2c667..5dbbd6d 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,6 @@ # Paiement CIC -Paiement CIC is a plugin to ease credit card payment with the CIC / Crédit Mutuel banks system. +Paiement CIC is a plugin to ease credit card payment with the CIC / Crédit Mutuel banks system version 3.0. It's a Ruby on Rails port of the connexion kits published by the bank. * The Plugin [site](http://github.com/novelys/cicpayment) @@ -24,11 +24,11 @@ script/plugin install git://github.com/novelys/paiementcic.git ### in development.rb : - PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi" + PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi" # or https://paiement.creditmutuel.fr/test/paiement.cgi ### in production.rb : - PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/paiement.cgi" + PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/paiement.cgi" # or https://paiement.creditmutuel.fr/paiement.cgi ### in order controller : @@ -71,15 +71,15 @@ script/plugin install git://github.com/novelys/paiementcic.git order_transaction.update_attribute :success, true - receipt = "OK" + receipt = "0" else order.transaction_declined! order.update_attribute :description, "Document Falsifie." order_transaction.update_attribute :success, false - receipt = "Document Falsifie" + receipt = "1\n#{PaiementCic.mac_string}" end - render :text => "Version: 1\n#{receipt}\n" + render :text => "Pragma: no-cache\nContent-type: text/plain\n\nversion=2\ncdr=#{receipt}" end def bank_ok diff --git a/lib/paiement_cic.rb b/lib/paiement_cic.rb index 8d1a898..487a4dc 100644 --- a/lib/paiement_cic.rb +++ b/lib/paiement_cic.rb @@ -14,10 +14,13 @@ class String end class PaiementCic + @@version = "3.0" # clé extraite grâce à extract2HmacSha1.html fourni par le Crédit Mutuel + cattr_accessor :version + @@hmac_key = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" # clé extraite grâce à extract2HmacSha1.html fourni par le Crédit Mutuel cattr_accessor :hmac_key - @@target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi" # "https://ssl.paiement.cic-banques.fr/paiement.cgi" + @@target_url = "https://paiement.creditmutuel.fr/test/paiement.cgi" # "https://ssl.paiement.cic-banques.fr/paiement.cgi" cattr_accessor :target_url @@tpe = "123456" @@ -26,61 +29,84 @@ class PaiementCic @@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/100.0)) + "EUR" + oa["montant"] = ("%.2f" % amount_in_cents) + "EUR" oa["reference"] = reference oa["texte-libre"] = "" - oa["version"] = "1.2open" oa["lgue"] = "FR" oa["societe"] = societe + oa["mail"] = "" oa end - def self.calculate_hmac(ordered_hash) - data = ordered_hash.values.join("*") + "*" - hmac(data) + 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 - tpe = params[:TPE] - date = params[:date] - montant = params[:montant] - reference = params[:reference] - mac = params[:MAC].downcase - texte_libre = params['texte-libre'] - code_retour = params['code-retour'] - retour_plus = params['retourPLUS'] - version = "1.2open" + 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('*') + "*" - data = retour_plus + [tpe, date, montant, reference, texte_libre, version, code_retour].join("+") + "+" - - mac == hmac(data) + 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 self.hmac(data) - pass = ""; - k1 = [Digest::SHA1.hexdigest(pass)].pack("H*"); - l1 = k1.length + def hmac_sha1(key, data) + length = 64 - k2 = [hmac_key].pack("H*") - l2 = k2.length - if (l1 > l2) - k2 = k2.ljust(l1, 0.chr) - elsif (l2 > l1) - k1 = k1.ljust(l2, 0.chr) - end - xor_res = k1 ^ k2 - hmac_sha1(xor_res, data).downcase - end + if (key.length > length) + key = [Digest::SHA1.hexdigest(key)].pack("H*") + end - def self.hmac_sha1(key, data) - OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("sha1"), key, data) - end -end \ No newline at end of file + 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] + + 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 diff --git a/lib/paiement_cic_helper.rb b/lib/paiement_cic_helper.rb index 9563654..7cec90e 100644 --- a/lib/paiement_cic_helper.rb +++ b/lib/paiement_cic_helper.rb @@ -1,17 +1,27 @@ +## refactor this module PaiementCicHelper - def paiement_cic_hidden_fields(order, order_transaction, options = {}) - oa = PaiementCic.config(order.amount, order_transaction.reference) + def paiement_cic_hidden_fields(order, price, order_transaction, options = {}) + oa = PaiementCic.config(price, order_transaction.reference) - hsh = oa.dup - hsh["url_retour"] = options[:url_retour] || edit_order_url(order) - hsh["url_retour_ok"] = options[:url_retour_ok] || bank_ok_order_transaction_url(order_transaction) - hsh["url_retour_err"] = options[:url_retour_err] || bank_err_order_transaction_url(order_transaction) - hsh["MAC"] = PaiementCic.calculate_hmac(oa) - - res = "\n" - hsh.each{|key, value| - res << hidden_field_tag(key, value) << "\n" - } - res + 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) + + html = ' + + + + + + + + + + + + + ' + html end end