Compare commits

..

No commits in common. "master" and "version1.2" have entirely different histories.

13 changed files with 151 additions and 358 deletions

3
.gitignore vendored
View File

@ -1,3 +0,0 @@
Gemfile.lock
*.gem
.*.swp

View File

@ -1,3 +0,0 @@
source 'https://rubygems.org'
gemspec

View File

@ -18,66 +18,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 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 <centrecom@e-i.com>.
#------------------------------------------------------------------------------
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.
#==============================================================================

View File

@ -1,6 +1,6 @@
# Paiement CIC # Paiement CIC
Paiement CIC is a plugin to ease credit card payment with the CIC / Crédit Mutuel banks system version 3.0. Paiement CIC is a plugin 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. It's a Ruby on Rails port of the connexion kits published by the bank.
* The Plugin [site](http://github.com/novelys/cicpayment) * The Plugin [site](http://github.com/novelys/cicpayment)
@ -9,59 +9,46 @@ It's a Ruby on Rails port of the connexion kits published by the bank.
## INSTALL ## INSTALL
gem 'paiement_cic' script/plugin install git://github.com/novelys/paiementcic.git
## USAGE ## USAGE
### in an initializer (`config/initializers/paiement_cic.rb`) : ### in environment.rb :
```ruby
PaiementCic.default_config.configure do |config|
# here the hmac key calculated with the js calculator given by CIC # here the hmac key calculated with the js calculator given by CIC
config.hmac_key = "########################################" PaiementCic.hmac_key = "########################################"
# Here the TPE number # Here the TPE number
config.tpe = "#######" PaiementCic.tpe = "#######"
# Here the Merchant name # Here the Merchant name
config.societe = "xxxxxxxxxxxxx" PaiementCic.societe = "xxxxxxxxxxxxx"
# You can specify the target URL ### in development.rb :
config.target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi"
# Or used predefined ones by specifying environment and bank
config.bank = :cm # or :cic
config.env = :test # or :production
end
```
You can also specify different attributes when initializing `PaiementCic::TPE` and with `paiement_cic_hidden_fields` helper by passing an hash. PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi"
```ruby
{ ### in production.rb :
hmac_key: "########################################",
tpe: "#######", PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/paiement.cgi"
societe: "xxxxxxxxxxxxx",
env: :test, ### in order controller :
}
``` helper :paiement_cic
### in the payment by card view : ### in the payment by card view :
``` - form_tag PaiementCic.target_url do
- form_tag PaiementCic.default_config.target_url do = paiement_cic_hidden_fields(@order, @order_transaction, :url_retour => edit_order_url(order), :url_retour_ok => bank_ok_order_transaction_url(order_transaction), :url_retour_err => bank_err_order_transaction_url(order_transaction))
= paiement_cic_hidden_fields(@order.reference, @order.amount, mail: 'email@example.com', url_retour: edit_order_url(order), url_retour_ok: bank_ok_order_transaction_url(order_transaction), url_retour_err: bank_err_order_transaction_url(order_transaction))
= submit_tag "Accéder au site de la banque", :style => "font-weight: bold;" = submit_tag "Accéder au site de la banque", :style => "font-weight: bold;"
= image_tag "reassuring_pictograms.jpg", :alt => "Pictogrammes rassurants", :style => "width: 157px;" = image_tag "reassuring_pictograms.jpg", :alt => "Pictogrammes rassurants", :style => "width: 157px;"
```
### in a controller for call back from the bank : ### in a controller for call back from the bank :
```ruby class OrderTransactionsController < ApplicationController
class OrderTransactionsController < ApplicationController
protect_from_forgery :except => [:bank_callback] protect_from_forgery :except => [:bank_callback]
def bank_callback def bank_callback
tpe = PaiementCic::TPE.new if PaiementCic.verify_hmac(params)
if tpe.verify_hmac(params)
order_transaction = OrderTransaction.find_by_reference params[:reference], :last order_transaction = OrderTransaction.find_by_reference params[:reference], :last
order = order_transaction.order order = order_transaction.order
@ -84,30 +71,31 @@ class OrderTransactionsController < ApplicationController
order_transaction.update_attribute :success, true order_transaction.update_attribute :success, true
receipt = "0" receipt = "OK"
else else
order.transaction_declined! order.transaction_declined!
order.update_attribute :description, "Document Falsifie." order.update_attribute :description, "Document Falsifie."
order_transaction.update_attribute :success, false order_transaction.update_attribute :success, false
receipt = "1\n#{tpe.mac_string}" receipt = "Document Falsifie"
end end
render :text => "Pragma: no-cache\nContent-type: text/plain\n\nversion=2\ncdr=#{receipt}" render :text => "Version: 1\n#{receipt}\n"
end end
def bank_ok def bank_ok
@order_transaction = OrderTransaction.find params[:id] @order_transaction = OrderTransaction.find params[:id]
@order = @order_transaction.order @order = @order_transaction.order
@order.pay!
end end
def bank_err def bank_err
order_transaction = OrderTransaction.find params[:id] order_transaction = OrderTransaction.find params[:id]
order = order_transaction.order order = order_transaction.order
order.cancel!
end
end end
end
```
## License ## License
Copyright (c) 2008-2012 Novelys Team, released under the MIT license Copyright (c) 2008-2009 Novelys Team, released under the MIT license

2
init.rb 100644
View File

@ -0,0 +1,2 @@
require "paiement_cic"
require "paiement_cic_helper"

View File

@ -1,56 +1,86 @@
require 'paiement_cic/config'
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
raise ArgumentError, "Can't bitwise-XOR strings of different length" \ raise ArgumentError, "Can't bitwise-XOR strings of different length" \
unless self.length == other.length unless self.length == other.length
result = (0..self.length-1).collect { |i| self[i].ord ^ other[i].ord } result = (0..self.length-1).collect { |i| self[i] ^ other[i] }
result.pack("C*") result.pack("C*")
end end
end end
module PaiementCic class PaiementCic
API_VERSION = "3.0" @@hmac_key = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" # clé extraite grâce à extract2HmacSha1.html fourni par le Crédit Mutuel
DATE_FORMAT = "%d/%m/%Y:%H:%M:%S" cattr_accessor :hmac_key
END_POINTS = { @@target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi" # "https://ssl.paiement.cic-banques.fr/paiement.cgi"
cic: { cattr_accessor :target_url
production: 'https://ssl.paiement.cic-banques.fr/paiement.cgi',
test: 'https://ssl.paiement.cic-banques.fr/test/paiement.cgi'
},
cm: {
production: 'https://paiement.creditmutuel.fr/paiement.cgi',
test: 'https://paiement.creditmutuel.fr/test/paiement.cgi'
}
}
DEFAULT_BANK = :cm
DEFAULT_ENV = :test
def self.default_config @@tpe = "123456"
@@default_config ||= PaiementCic::Config.new cattr_accessor :tpe
@@societe = "masociete"
cattr_accessor :societe
def self.date_format
"%d/%m/%Y:%H:%M:%S"
end
def self.config(amount_in_cents, reference)
oa = ActiveSupport::OrderedHash.new
oa["TPE"] = tpe
oa["date"] = Time.now.strftime(date_format)
oa["montant"] = ("%.2f" % (amount_in_cents/100.0)) + "EUR"
oa["reference"] = reference
oa["texte-libre"] = ""
oa["version"] = "1.2open"
oa["lgue"] = "FR"
oa["societe"] = societe
oa
end
def self.calculate_hmac(ordered_hash)
data = ordered_hash.values.join("*") + "*"
hmac(data)
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"
data = retour_plus + [tpe, date, montant, reference, texte_libre, version, code_retour].join("+") + "+"
mac == hmac(data)
end
def self.hmac(data)
pass = "";
k1 = [Digest::SHA1.hexdigest(pass)].pack("H*");
l1 = k1.length
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 end
def self.hmac_sha1(key, data) def self.hmac_sha1(key, data)
length = 64 OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("sha1"), key, data)
if (key.length > length)
key = [Digest::SHA1.hexdigest(key)].pack("H*")
end 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
class UnknownBankError < Exception; end
class UnknownEnvError < Exception; end
end end

View File

@ -1,44 +0,0 @@
module PaiementCic
class Config
attr_accessor :hmac_key, :tpe, :societe
attr_writer :target_url
def initialize(attributes = {}, &block)
if block_given?
configure(&block)
else
attributes.each do |name, value|
setter = "#{name}="
next unless respond_to?(setter)
send(setter, value)
end
end
end
def configure(&block)
yield self
end
def bank
@bank || DEFAULT_BANK
end
def bank=(value)
raise UnknownBankError unless END_POINTS.keys.include?(value.to_sym)
@bank = value
end
def env
@env || DEFAULT_ENV
end
def env=(value)
raise UnknownEnvError unless END_POINTS.first.last.include?(value.to_sym)
@env = value
end
def target_url
@target_url || END_POINTS[self.bank][self.env]
end
end
end

View File

@ -1,23 +0,0 @@
module PaiementCic::FormHelper
def paiement_cic_hidden_fields(reference, price, options = {})
oMac = PaiementCic::TPE.new(options)
oa = oMac.attributes(reference, price, options)
chaineMAC = oMac.compute_hmac_sha1(oa.values.join('*'))
url_retour = options[:url_retour]
url_retour_ok = options[:url_retour_ok]
url_retour_err = options[:url_retour_err]
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)
oa.each do |k,v|
html << hidden_field_tag(k, v) unless v.empty?
end
html
end
end

View File

@ -1,9 +0,0 @@
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

View File

@ -1,79 +0,0 @@
module PaiementCic
class TPE
attr_accessor :config
def initialize(options = nil)
self.config = options.nil? ? PaiementCic.default_config :
PaiementCic::Config.new(options)
end
def attributes(reference, amount_in_cents, options = {})
{
'TPE' => config.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' => config.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
[
config.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)
compute_hmac_sha1(mac_string) == sent_mac.downcase
end
# Return the HMAC for a data string
def compute_hmac_sha1(data)
PaiementCic.hmac_sha1(usable_key, data).downcase
end
alias_method :computeHMACSHA1, :compute_hmac_sha1
private
# Return the key to be used in the hmac function
def usable_key
hex_string_key = config.hmac_key[0..37]
hex_final = config.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

View File

@ -1,3 +0,0 @@
module PaiementCic
VERSION = "0.2"
end

View File

@ -0,0 +1,17 @@
module PaiementCicHelper
def paiement_cic_hidden_fields(order, order_transaction, options = {})
oa = PaiementCic.config(order.amount, 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
end
end

View File

@ -1,17 +0,0 @@
# encoding: utf-8
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", "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.}
s.files = `git ls-files`.split("\n")
s.require_paths = ["lib"]
end