Compare commits

...

19 Commits

Author SHA1 Message Date
Guillaume DOTT e77eb75f07 Move Config attributes to a separate class
This allows to modify the target URL, the environment or any attribute
for a specific TPE or use default config.
2014-04-09 14:38:27 +02:00
Guillaume DOTT fd565f3e6f Update README 2013-06-11 14:12:57 +02:00
Guillaume DOTT 7b428b5b61 Add predefined URLs for CIC and CM and both environments 2013-06-11 11:51:22 +02:00
Guillaume DOTT 93d0c9262d Refactor and improve code
Clean code to improve readability, allow to use different TPE in the
same application, add parameters like mail.
2013-06-05 15:33:46 +02:00
Guillaume DOTT 16a5ad5930 Remove trailing whitespaces and tabs 2013-06-05 10:28:56 +02:00
David Bourguignon 442be992a0 change readme 2012-01-30 13:55:44 +01:00
David Bourguignon 5cf5026e07 bump version 2012-01-30 11:28:14 +01:00
David Bourguignon cf9df67cd9 Merge branch 'master' of github.com:novelys/paiementcic 2012-01-30 11:26:41 +01:00
David Bourguignon 910b448448 use options hash for return urls 2012-01-30 11:26:07 +01:00
David Bourguignon 0e61bba15a Merge pull request #3 from bgarret/ruby-1.9.2
Ruby 1.9 support
2012-01-30 11:25:54 +01:00
David Bourguignon 52704a5aba Merge pull request #3 from bgarret/ruby-1.9.2
Ruby 1.9 support
2012-01-30 00:39:50 -08:00
David Bourguignon d07822c628 still need support for Rails 2.3 2012-01-30 09:38:45 +01:00
David Bourguignon d4bc57df1a Merge branch 'rails3' of https://github.com/bgarret/paiementcic into bgarret-rails3 2012-01-30 09:36:43 +01:00
David Bourguignon 57fa761418 Merge pull request #1 from bgarret/gem
Gemify the plugin
2012-01-30 00:15:29 -08:00
Benoit Garret eb8033c57c Rails 3 treats all strings as unsafe by default 2011-06-24 17:50:57 +02:00
Benoit Garret 948ad95235 Explicitly convert chars to integers 2011-06-24 17:47:54 +02:00
Benoit Garret 7be9c5f2d8 Gemify the plugin 2011-06-23 16:38:31 +02:00
Benoit Garret cca9e3a7da Move lib/paiement_cic_helper to lib/paiement_cic/form_helper 2011-06-23 16:04:13 +02:00
Toutie d9c53853ed modifications to support version 3 2009-12-21 11:24:45 +01:00
13 changed files with 361 additions and 154 deletions

3
.gitignore vendored 100644
View File

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

3
Gemfile 100644
View File

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

View File

@ -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 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. 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. 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,46 +9,59 @@ It's a Ruby on Rails port of the connexion kits published by the bank.
## INSTALL ## INSTALL
script/plugin install git://github.com/novelys/paiementcic.git gem 'paiement_cic'
## USAGE ## USAGE
### in environment.rb : ### in an initializer (`config/initializers/paiement_cic.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
PaiementCic.hmac_key = "########################################" config.hmac_key = "########################################"
# Here the TPE number # Here the TPE number
PaiementCic.tpe = "#######" config.tpe = "#######"
# Here the Merchant name # Here the Merchant name
PaiementCic.societe = "xxxxxxxxxxxxx" config.societe = "xxxxxxxxxxxxx"
### in development.rb : # You can specify the target URL
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
```
PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi" You can also specify different attributes when initializing `PaiementCic::TPE` and with `paiement_cic_hidden_fields` helper by passing an hash.
```ruby
### in production.rb : {
hmac_key: "########################################",
PaiementCic.target_url = "https://ssl.paiement.cic-banques.fr/paiement.cgi" tpe: "#######",
societe: "xxxxxxxxxxxxx",
### in order controller : env: :test,
}
helper :paiement_cic ```
### in the payment by card view : ### in the payment by card view :
- form_tag PaiementCic.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)) - form_tag PaiementCic.default_config.target_url do
= 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
if PaiementCic.verify_hmac(params) tpe = PaiementCic::TPE.new
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
@ -71,31 +84,30 @@ script/plugin install git://github.com/novelys/paiementcic.git
order_transaction.update_attribute :success, true order_transaction.update_attribute :success, true
receipt = "OK" receipt = "0"
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 = "Document Falsifie" receipt = "1\n#{tpe.mac_string}"
end end
render :text => "Version: 1\n#{receipt}\n" render :text => "Pragma: no-cache\nContent-type: text/plain\n\nversion=2\ncdr=#{receipt}"
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-2009 Novelys Team, released under the MIT license Copyright (c) 2008-2012 Novelys Team, released under the MIT license

View File

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

View File

@ -1,86 +1,56 @@
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] ^ other[i] } result = (0..self.length-1).collect { |i| self[i].ord ^ other[i].ord }
result.pack("C*") result.pack("C*")
end end
end end
class PaiementCic module PaiementCic
@@hmac_key = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" # clé extraite grâce à extract2HmacSha1.html fourni par le Crédit Mutuel API_VERSION = "3.0"
cattr_accessor :hmac_key DATE_FORMAT = "%d/%m/%Y:%H:%M:%S"
@@target_url = "https://ssl.paiement.cic-banques.fr/test/paiement.cgi" # "https://ssl.paiement.cic-banques.fr/paiement.cgi" END_POINTS = {
cattr_accessor :target_url cic: {
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
@@tpe = "123456" def self.default_config
cattr_accessor :tpe @@default_config ||= PaiementCic::Config.new
@@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)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("sha1"), key, data) length = 64
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

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

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

@ -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

View File

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

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

View File

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

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