active_merchant_sermepa 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/CHANGELOG +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +54 -0
- data/MIT-LICENSE +20 -0
- data/Manifest +15 -0
- data/README.rdoc +158 -0
- data/Rakefile +18 -0
- data/active_merchant_sermepa.gemspec +27 -0
- data/lib/active_merchant/billing/integrations/sermepa.rb +158 -0
- data/lib/active_merchant/billing/integrations/sermepa/helper.rb +187 -0
- data/lib/active_merchant/billing/integrations/sermepa/notification.rb +146 -0
- data/lib/active_merchant/billing/integrations/sermepa/return.rb +10 -0
- data/lib/active_merchant_sermepa.rb +10 -0
- data/lib/activemerchant_sermepa.rb +3 -0
- data/test/test_helper.rb +105 -0
- data/test/unit/integrations/helpers/sermepa_helper_test.rb +73 -0
- data/test/unit/integrations/notifications/sermepa_notification_test.rb +64 -0
- data/test/unit/integrations/sermepa_module_text.rb +39 -0
- metadata +145 -0
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
active_merchant_sermepa (0.0.1)
|
5
|
+
activemerchant (>= 1.9.4)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
abstract (1.0.0)
|
11
|
+
actionpack (3.0.4)
|
12
|
+
activemodel (= 3.0.4)
|
13
|
+
activesupport (= 3.0.4)
|
14
|
+
builder (~> 2.1.2)
|
15
|
+
erubis (~> 2.6.6)
|
16
|
+
i18n (~> 0.4)
|
17
|
+
rack (~> 1.2.1)
|
18
|
+
rack-mount (~> 0.6.13)
|
19
|
+
rack-test (~> 0.5.7)
|
20
|
+
tzinfo (~> 0.3.23)
|
21
|
+
activemerchant (1.11.0)
|
22
|
+
activesupport (>= 2.3.8)
|
23
|
+
braintree (>= 2.0.0)
|
24
|
+
builder (>= 2.0.0)
|
25
|
+
activemodel (3.0.4)
|
26
|
+
activesupport (= 3.0.4)
|
27
|
+
builder (~> 2.1.2)
|
28
|
+
i18n (~> 0.4)
|
29
|
+
activesupport (3.0.4)
|
30
|
+
braintree (2.8.0)
|
31
|
+
builder
|
32
|
+
builder (2.1.2)
|
33
|
+
erubis (2.6.6)
|
34
|
+
abstract (>= 1.0.0)
|
35
|
+
i18n (0.5.0)
|
36
|
+
mocha (0.9.12)
|
37
|
+
money (3.5.5)
|
38
|
+
i18n (~> 0.4)
|
39
|
+
rack (1.2.1)
|
40
|
+
rack-mount (0.6.13)
|
41
|
+
rack (>= 1.0.0)
|
42
|
+
rack-test (0.5.7)
|
43
|
+
rack (>= 1.0)
|
44
|
+
tzinfo (0.3.24)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
actionpack (~> 3.0.3)
|
51
|
+
active_merchant_sermepa!
|
52
|
+
activemerchant (>= 1.9.4)
|
53
|
+
mocha (~> 0.9.10)
|
54
|
+
money (~> 3.5.4)
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010-2011 Samuel Lown
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
MIT-LICENSE
|
3
|
+
Manifest
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
active_merchant_sermepa.gemspec
|
7
|
+
lib/active_merchant/billing/integrations/sermepa.rb
|
8
|
+
lib/active_merchant/billing/integrations/sermepa/helper.rb
|
9
|
+
lib/active_merchant/billing/integrations/sermepa/notification.rb
|
10
|
+
lib/active_merchant/billing/integrations/sermepa/return.rb
|
11
|
+
lib/active_merchant_sermepa.rb
|
12
|
+
test/test_helper.rb
|
13
|
+
test/unit/integrations/helpers/sermepa_helper_test.rb
|
14
|
+
test/unit/integrations/notifications/sermepa_notification_test.rb
|
15
|
+
test/unit/integrations/sermepa_module_text.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
= Active Merchant Sermepa Plugin
|
2
|
+
|
3
|
+
Basic support for the Spanish SERMEPA Virtual POS payment gateway provided by Servired.
|
4
|
+
Used by many banks in Spain and known locally as a "TPV Virtual".
|
5
|
+
|
6
|
+
Include this library along with the standard active merchant package and a new
|
7
|
+
Integration payment gateway will be available.
|
8
|
+
|
9
|
+
|
10
|
+
== Install
|
11
|
+
|
12
|
+
Add to your Gemfile as follows:
|
13
|
+
|
14
|
+
gem 'activemerchant_sermepa'
|
15
|
+
|
16
|
+
That should automatically install a usable version of active merchant as a dependency.
|
17
|
+
|
18
|
+
== Usage
|
19
|
+
|
20
|
+
This is an integrated payment gateway and as such requires all credit card details
|
21
|
+
to be provided outside of your own website's pages. The basic purchase process is:
|
22
|
+
|
23
|
+
1. HTML form shown on website with payment details
|
24
|
+
2. Form is submitted directly to Sermepa's servers
|
25
|
+
3. User provides credit card and completes validatation proceedures (this can be a pain in the *** for your clients if they don't have the necessary login details, ask your bank to disable it if you get lots of complaints.)
|
26
|
+
4. Optional: website receives notification from sermepa of sale result
|
27
|
+
5. User forwarded back to your website. If the "Parameters in URL" (Parámetros en las URLs) option is set in the point of sale's configuration, the user will be forwarded with a set of parameters that can be used to validate the purchase.
|
28
|
+
6. Confirm that the purchase is successful using either notification or parameters in URL.
|
29
|
+
|
30
|
+
Sermepa, unlike the similar BBVA system, does not support manual requests to confirm
|
31
|
+
the success of a sale. Validation must be done using the parameters received either
|
32
|
+
from the notification or paremeters in forwarded URL.
|
33
|
+
|
34
|
+
|
35
|
+
=== Configuration
|
36
|
+
|
37
|
+
Your bank should provide you with three keys, known as the "terminal id", "commercial id",
|
38
|
+
and "secret key". These when combined allow requests to be signed and incoming confirmations
|
39
|
+
to be validated. A final option is available to set the the type of key used in the
|
40
|
+
transactions. The "key type" can be set to either "sha1_complete" or "sha1_extended
|
41
|
+
according to whatever is set by your bank.
|
42
|
+
|
43
|
+
These configuration options can either be set globally or on a per-request basis. The
|
44
|
+
following would be included in an initializer to prepare the gateway for use:
|
45
|
+
|
46
|
+
ActiveMerchant::Billing::Integrations::Sermepa::Helper.credentials = {
|
47
|
+
:terminal_id => '9',
|
48
|
+
:commercial_id => ''999008881,
|
49
|
+
:secret_key => 'qwertyasdf0123456789',
|
50
|
+
:key_type => 'sha1_complete'
|
51
|
+
}
|
52
|
+
|
53
|
+
If the credentials are not set this way, they'll need to be included with each call to
|
54
|
+
the library's methods.
|
55
|
+
|
56
|
+
=== Form
|
57
|
+
|
58
|
+
Active Merchant provides a helper called "payment_service_for" which handles the preparation
|
59
|
+
of the request to the gateway. The following code sample shows what this might look like:
|
60
|
+
|
61
|
+
= payment_service_for @invoice.transactions.last.code, 'The Shop', :amount => @invoice.total.cents, :currency => 'EUR', :service => :sermepa do |service|
|
62
|
+
- service.description "Some description of the purchase"
|
63
|
+
- service.customer_name @invoice.client.name
|
64
|
+
- service.notify_url notify_invoice_url(@invoice)
|
65
|
+
- service.success_url complete_invoice_url(@invoice)
|
66
|
+
- service.failure_url complete_invoice_url(@invoice)
|
67
|
+
|
68
|
+
= submit_tag "Go to payment gateway!"
|
69
|
+
|
70
|
+
A few important things to bare in mind:
|
71
|
+
|
72
|
+
- Each request to the service *must* have a unique transaction or order id. This is the first parameter provided to the helper.
|
73
|
+
- If a purchase fails, the order id *must* be updated.
|
74
|
+
- As per the Sermepa documentation the transaction id must be between 4 and 12 digits long and always start with 4 numbers.
|
75
|
+
- The credentials can be provided in the ":credentials" option to the helper if preferred.
|
76
|
+
- The URLs set where the user will be sent after the purchase.
|
77
|
+
|
78
|
+
|
79
|
+
=== Notification and Confirmation
|
80
|
+
|
81
|
+
While confirming the purchase is optional, it is highly recommended as it allows you
|
82
|
+
to let the client know instantly that the transaction has completed successfully.
|
83
|
+
|
84
|
+
If HTTP notification has been enabled in the Sermepa configuration, a private
|
85
|
+
request will be sent from the gateway to your website confirming the success of
|
86
|
+
the transaction. The notification URL can be provided either in the form's parameters
|
87
|
+
or in the administrator configuration. You'll most likely not be able to test this
|
88
|
+
during development on your local machine for obvious reasons.
|
89
|
+
|
90
|
+
Ensuring that the parameters are provided from the returning user is much more
|
91
|
+
convenient for testing and allows for an instant response to the user.
|
92
|
+
|
93
|
+
Both types work using the same parameters in the URL so can be handled with the same method.
|
94
|
+
|
95
|
+
Your controller might have a actions like in the following example:
|
96
|
+
|
97
|
+
# Receive a direct notification from the gateway
|
98
|
+
def notify
|
99
|
+
notify = ActiveMerchant::Billing::Integrations::Sermepa.notification(request.query_parameters)
|
100
|
+
if notify.acknowledge
|
101
|
+
# Do something useful
|
102
|
+
end
|
103
|
+
render :text => 'OK'
|
104
|
+
end
|
105
|
+
|
106
|
+
# Handle the incoming user
|
107
|
+
def confirm
|
108
|
+
notify = ActiveMerchant::Billing::Integrations::Sermepa.notification(request.query_parameters)
|
109
|
+
if notify.acknowledge
|
110
|
+
# do something useful
|
111
|
+
render :action => 'success'
|
112
|
+
else
|
113
|
+
render :action => 'failure'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
These examples are greatly simplified. It would be much better to handle these operations in
|
118
|
+
a model and provide support for recording each event that takes place.
|
119
|
+
|
120
|
+
The acknowledge method also accepts a hash of credentials should you prefer this
|
121
|
+
to setting them globally. It checks to see if by combining the received parameters
|
122
|
+
the same signature can be generated using our secret key. Assuming the operation
|
123
|
+
successful and the signature is valid, the purchase will be successful.
|
124
|
+
|
125
|
+
|
126
|
+
== Authors
|
127
|
+
|
128
|
+
Written by Sam Lown.
|
129
|
+
|
130
|
+
Special thanks go to {rentages.es}[http://www.rentages.es] for supporting the initial development.
|
131
|
+
|
132
|
+
Thanks should also go to {floresfrescas.com}[http://www.floresfrescas.com] for supporting the development of the BBVA TPV gateway method on which this is heavily based.
|
133
|
+
|
134
|
+
And of course, many thanks to the ActiveMerchant[http://www.activemerchant.org] Shopify[http://www.shopify.com] team for releasing this library in the first place!
|
135
|
+
|
136
|
+
== License
|
137
|
+
|
138
|
+
Released under the MIT license.
|
139
|
+
|
140
|
+
Copyright (c) 2010-2011 Samuel Lown
|
141
|
+
|
142
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
143
|
+
of this software and associated documentation files (the "Software"), to deal
|
144
|
+
in the Software without restriction, including without limitation the rights
|
145
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
146
|
+
copies of the Software, and to permit persons to whom the Software is
|
147
|
+
furnished to do so, subject to the following conditions:
|
148
|
+
|
149
|
+
The above copyright notice and this permission notice shall be included in
|
150
|
+
all copies or substantial portions of the Software.
|
151
|
+
|
152
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
153
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
154
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
155
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
156
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
157
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
158
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require 'rake'
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
desc "Run the unit test suite"
|
9
|
+
task :default => 'test:units'
|
10
|
+
|
11
|
+
namespace :test do
|
12
|
+
Rake::TestTask.new(:units) do |t|
|
13
|
+
t.pattern = 'test/unit/**/*_test.rb'
|
14
|
+
t.ruby_opts << '-rubygems'
|
15
|
+
t.libs << 'test'
|
16
|
+
t.verbose = true
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{active_merchant_sermepa}
|
5
|
+
s.version = "0.1.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.1") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Sam Lown"]
|
9
|
+
s.date = %q{2011-02-25}
|
10
|
+
s.description = %q{Add support to ActiveMerchant for the Sermepa payment gateway by Servired used by many banks in Spain}
|
11
|
+
s.summary = "ActiveMerchant support for Servired's Sermepa payment gateway"
|
12
|
+
s.email = %q{me@samlown.com}
|
13
|
+
s.extra_rdoc_files = ['MIT-LICENSE', 'CHANGELOG', 'README.rdoc']
|
14
|
+
s.homepage = %q{http://github.com/samlown/active_merchant_sermepa}
|
15
|
+
s.rubygems_version = "1.3.7"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency("activemerchant", ">= 1.9.4")
|
23
|
+
s.add_development_dependency("mocha", "~> 0.9.10")
|
24
|
+
s.add_development_dependency("money", "~> 3.5.4")
|
25
|
+
s.add_development_dependency("actionpack", "~> 3.0.3")
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ActiveMerchant #:nodoc:
|
3
|
+
module Billing #:nodoc:
|
4
|
+
module Integrations #:nodoc:
|
5
|
+
# See the BbvaTpv::Helper class for more generic information on usage of
|
6
|
+
# this integrated payment method.
|
7
|
+
module Sermepa
|
8
|
+
|
9
|
+
autoload :Helper, 'active_merchant/billing/integrations/sermepa/helper.rb'
|
10
|
+
autoload :Return, 'active_merchant/billing/integrations/sermepa/return.rb'
|
11
|
+
autoload :Notification, 'active_merchant/billing/integrations/sermepa/notification.rb'
|
12
|
+
|
13
|
+
mattr_accessor :service_test_url
|
14
|
+
self.service_test_url = "https://sis-t.sermepa.es:25443/sis/realizarPago"
|
15
|
+
mattr_accessor :service_production_url
|
16
|
+
self.service_production_url = "https://sis.sermepa.es/sis/realizarPago"
|
17
|
+
|
18
|
+
mattr_accessor :operations_test_url
|
19
|
+
self.operations_test_url = "https://sis-t.sermepa.es:25443/sis/operaciones"
|
20
|
+
mattr_accessor :operations_production_url
|
21
|
+
self.operations_production_url = "https://sis.sermepa.es/sis/operaciones"
|
22
|
+
|
23
|
+
|
24
|
+
def self.service_url
|
25
|
+
mode = ActiveMerchant::Billing::Base.integration_mode
|
26
|
+
case mode
|
27
|
+
when :production
|
28
|
+
self.service_production_url
|
29
|
+
when :test
|
30
|
+
self.service_test_url
|
31
|
+
else
|
32
|
+
raise StandardError, "Integration mode set to an invalid value: #{mode}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.operations_url
|
37
|
+
mode = ActiveMerchant::Billing::Base.integration_mode
|
38
|
+
case mode
|
39
|
+
when :production
|
40
|
+
self.operations_production_url
|
41
|
+
when :test
|
42
|
+
self.operations_test_url
|
43
|
+
else
|
44
|
+
raise StandardError, "Integration mode set to an invalid value: #{mode}"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.notification(post)
|
50
|
+
Notification.new(post)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def self.currency_code( name )
|
55
|
+
row = supported_currencies.assoc(name)
|
56
|
+
row.nil? ? supported_currencies.first[1] : row[1]
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.currency_from_code( code )
|
60
|
+
row = supported_currencies.rassoc(code)
|
61
|
+
row.nil? ? supported_currencies.first[0] : row[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.language_code(name)
|
65
|
+
row = supported_languages.assoc(name.to_s.downcase.to_sym)
|
66
|
+
row.nil? ? supported_languages.first[1] : row[1]
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.language_from_code( code )
|
70
|
+
row = supported_languages.rassoc(code)
|
71
|
+
row.nil? ? supported_languages.first[0] : row[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.transaction_code(name)
|
75
|
+
row = supported_transactions.assoc(name.to_sym)
|
76
|
+
row.nil? ? supported_transactions.first[1] : row[1]
|
77
|
+
end
|
78
|
+
def self.transaction_from_code(code)
|
79
|
+
row = supported_transactions.rassoc(code.to_s)
|
80
|
+
row.nil? ? supported_languages.first[0] : row[0]
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.supported_currencies
|
84
|
+
[ ['EUR', '978'] ]
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.supported_languages
|
88
|
+
[
|
89
|
+
[:es, '001'],
|
90
|
+
[:en, '002'],
|
91
|
+
[:ca, '003'],
|
92
|
+
[:fr, '004'],
|
93
|
+
[:de, '005'],
|
94
|
+
[:pt, '009']
|
95
|
+
]
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.supported_transactions
|
99
|
+
[
|
100
|
+
[:authorization, '0'],
|
101
|
+
[:preauthorization, '1'],
|
102
|
+
[:confirmation, '2'],
|
103
|
+
[:automatic_return, '3'],
|
104
|
+
[:reference_payment, '4'],
|
105
|
+
[:recurring_transaction, '5'],
|
106
|
+
[:successive_transaction, '6'],
|
107
|
+
[:authentication, '7'],
|
108
|
+
[:confirm_authentication, '8'],
|
109
|
+
[:cancel_preauthorization, '9'],
|
110
|
+
[:deferred_authorization, 'O'],
|
111
|
+
[:confirm_deferred_authorization, 'P'],
|
112
|
+
[:cancel_deferred_authorization, 'Q'],
|
113
|
+
[:inicial_recurring_authorization, 'R'],
|
114
|
+
[:successive_recurring_authorization, 'S']
|
115
|
+
]
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.response_code_message(code)
|
119
|
+
case code.to_i
|
120
|
+
when 0..99
|
121
|
+
nil
|
122
|
+
when 900
|
123
|
+
"Transacción autorizada para devoluciones y confirmaciones"
|
124
|
+
when 101
|
125
|
+
"Tarjeta caducada"
|
126
|
+
when 102
|
127
|
+
"Tarjeta en excepción transitoria o bajo sospecha de fraude"
|
128
|
+
when 104
|
129
|
+
"Operación no permitida para esa tarjeta o terminal"
|
130
|
+
when 116
|
131
|
+
"Disponible insuficiente"
|
132
|
+
when 118
|
133
|
+
"Tarjeta no registrada o Método de pago no disponible para su tarjeta"
|
134
|
+
when 129
|
135
|
+
"Código de seguridad (CVV2/CVC2) incorrecto"
|
136
|
+
when 180
|
137
|
+
"Tarjeta no válida o Tarjeta ajena al servicio o Error en la llamada al MPI sin controlar."
|
138
|
+
when 184
|
139
|
+
"Error en la autenticación del titular"
|
140
|
+
when 190
|
141
|
+
"Denegación sin especificar Motivo"
|
142
|
+
when 191
|
143
|
+
"Fecha de caducidad errónea"
|
144
|
+
when 202
|
145
|
+
"Tarjeta en excepción transitoria o bajo sospecha de fraude con retirada de tarjeta"
|
146
|
+
when 912,9912
|
147
|
+
"Emisor no disponible"
|
148
|
+
when 913
|
149
|
+
"Pedido repetido"
|
150
|
+
else
|
151
|
+
"Transacción denegada"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ActiveMerchant #:nodoc:
|
3
|
+
module Billing #:nodoc:
|
4
|
+
module Integrations #:nodoc:
|
5
|
+
module Sermepa
|
6
|
+
# Sermepa/Servired Spanish Virtual POS Gateway
|
7
|
+
#
|
8
|
+
# Support for the Spanish payment gateway provided by Sermepa, part of Servired,
|
9
|
+
# one of the main providers in Spain to Banks and Cajas.
|
10
|
+
#
|
11
|
+
# Requires the :terminal_id, :commercial_id, and :secret_key to be set in the credentials
|
12
|
+
# before the helper can be used. Credentials may be overwriten when instantiating the helper
|
13
|
+
# if required or instead of the global variable. Optionally, the :key_type can also be set to
|
14
|
+
# either 'sha1_complete' or 'sha1_extended', where the later is the default case. This
|
15
|
+
# is a configurable option in the Sermepa admin which you may or may not be able to access.
|
16
|
+
# If nothing seems to work, try changing this.
|
17
|
+
#
|
18
|
+
# Ensure the gateway is configured correctly. Synchronization should be set to Asynchronous
|
19
|
+
# and the parameters in URL option (Parámetros en las URLs) should be set to true unless
|
20
|
+
# the notify_url is provided. During development on localhost ensuring this option is set
|
21
|
+
# is especially important as there is no other way to confirm a successful purchase.
|
22
|
+
#
|
23
|
+
# Your view for a payment form might look something like the following:
|
24
|
+
#
|
25
|
+
# <%= payment_service_for @transaction.id, 'Company name', :amount => @transaction.amount, :currency => 'EUR', :service => :sermepa do |service| %>
|
26
|
+
# <% service.description @sale.description %>
|
27
|
+
# <% service.customer_name @sale.client.name %>
|
28
|
+
# <% service.notify_url notify_sale_url(@sale) %>
|
29
|
+
# <% service.success_url win_sale_url(@sale) %>
|
30
|
+
# <% service.failure_url fail_sale_url(@sale) %>
|
31
|
+
#
|
32
|
+
# <%= submit_tag "PAY!" %>
|
33
|
+
# <% end %>
|
34
|
+
#
|
35
|
+
#
|
36
|
+
#
|
37
|
+
class Helper < ActiveMerchant::Billing::Integrations::Helper
|
38
|
+
include PostsData
|
39
|
+
|
40
|
+
class << self
|
41
|
+
# Credentials should be set as a hash containing the fields:
|
42
|
+
# :terminal_id, :commercial_id, :secret_key, :key_type (optional)
|
43
|
+
attr_accessor :credentials
|
44
|
+
end
|
45
|
+
|
46
|
+
mapping :account, 'Ds_Merchant_MerchantName'
|
47
|
+
|
48
|
+
mapping :currency, 'Ds_Merchant_Currency'
|
49
|
+
mapping :amount, 'Ds_Merchant_Amount'
|
50
|
+
|
51
|
+
mapping :order, 'Ds_Merchant_Order'
|
52
|
+
mapping :description, 'Ds_Merchant_ProductDescription'
|
53
|
+
mapping :client, 'Ds_Merchant_Titular'
|
54
|
+
|
55
|
+
mapping :notify_url, 'Ds_Merchant_MerchantURL'
|
56
|
+
mapping :success_url, 'Ds_Merchant_UrlOK'
|
57
|
+
mapping :failure_url, 'Ds_Merchant_UrlKO'
|
58
|
+
|
59
|
+
mapping :language, 'Ds_Merchant_ConsumerLanguage'
|
60
|
+
|
61
|
+
mapping :transaction_type, 'Ds_Merchant_TransactionType'
|
62
|
+
|
63
|
+
mapping :customer_name, 'Ds_Merchant_Titular'
|
64
|
+
|
65
|
+
#### Special Request Specific Fields ####
|
66
|
+
mapping :signature, 'Ds_Merchant_MerchantSignature'
|
67
|
+
########
|
68
|
+
|
69
|
+
# ammount should always be provided in cents!
|
70
|
+
def initialize(order, account, options = {})
|
71
|
+
self.credentials = options.delete(:credentials) if options[:credentials]
|
72
|
+
super(order, account, options)
|
73
|
+
|
74
|
+
add_field 'Ds_Merchant_MerchantCode', credentials[:commercial_id]
|
75
|
+
add_field 'Ds_Merchant_Terminal', credentials[:terminal_id]
|
76
|
+
#add_field mappings[:transaction_type], '0' # Default Transaction Type
|
77
|
+
self.transaction_type = :authorization
|
78
|
+
end
|
79
|
+
|
80
|
+
# Allow credentials to be overwritten if needed
|
81
|
+
def credentials
|
82
|
+
@credentials || self.class.credentials
|
83
|
+
end
|
84
|
+
def credentials=(creds)
|
85
|
+
@credentials = (self.class.credentials || {}).dup.merge(creds)
|
86
|
+
end
|
87
|
+
|
88
|
+
def amount=(money)
|
89
|
+
cents = money.respond_to?(:cents) ? money.cents : money
|
90
|
+
if money.is_a?(String) || cents.to_i <= 0
|
91
|
+
raise ArgumentError, 'money amount must be either a Money object or a positive integer in cents.'
|
92
|
+
end
|
93
|
+
add_field mappings[:amount], cents.to_i
|
94
|
+
end
|
95
|
+
|
96
|
+
def order=(order_id)
|
97
|
+
order_id = order_id.to_s
|
98
|
+
if order_id !~ /^[0-9]{4}/ && order_id.length <= 8
|
99
|
+
order_id = ('0' * 4) + order_id
|
100
|
+
end
|
101
|
+
regexp = /^[0-9]{4}[0-9a-zA-Z]{0,8}$/
|
102
|
+
raise "Invalid order number format! First 4 digits must be numbers" if order_id !~ regexp
|
103
|
+
add_field mappings[:order], order_id
|
104
|
+
end
|
105
|
+
|
106
|
+
def currency=( value )
|
107
|
+
add_field mappings[:currency], Sermepa.currency_code(value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def language=(lang)
|
111
|
+
add_field mappings[:language], Sermepa.language_code(lang)
|
112
|
+
end
|
113
|
+
|
114
|
+
def transaction_type=(type)
|
115
|
+
add_field mappings[:transaction_type], Sermepa.transaction_code(type)
|
116
|
+
end
|
117
|
+
|
118
|
+
def form_fields
|
119
|
+
add_field mappings[:signature], sign_request
|
120
|
+
@fields
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Send a manual request for the currently prepared transaction.
|
125
|
+
# This is an alternative to the normal view helper and is useful
|
126
|
+
# for special types of transaction.
|
127
|
+
def send_transaction
|
128
|
+
body = build_xml_request
|
129
|
+
|
130
|
+
headers = { }
|
131
|
+
headers['Content-Length'] = body.size.to_s
|
132
|
+
headers['User-Agent'] = "Active Merchant -- http://activemerchant.org"
|
133
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
134
|
+
|
135
|
+
# Return the raw response data
|
136
|
+
ssl_post(Sermepa.operations_url, "entrada="+CGI.escape(body), headers)
|
137
|
+
end
|
138
|
+
|
139
|
+
protected
|
140
|
+
|
141
|
+
def build_xml_request
|
142
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
143
|
+
xml.DATOSENTRADA do
|
144
|
+
xml.DS_Version 0.1
|
145
|
+
xml.DS_MERCHANT_CURRENCY @fields['Ds_Merchant_Currency']
|
146
|
+
xml.DS_MERCHANT_AMOUNT @fields['Ds_Merchant_Amount']
|
147
|
+
xml.DS_MERCHANT_MERCHANTURL @fields['Ds_Merchant_MerchantURL']
|
148
|
+
xml.DS_MERCHANT_TRANSACTIONTYPE @fields['Ds_Merchant_TransactionType']
|
149
|
+
xml.DS_MERCHANT_MERCHANTDATA @fields['Ds_Merchant_Product_Description']
|
150
|
+
xml.DS_MERCHANT_TERMINAL credentials[:terminal_id]
|
151
|
+
xml.DS_MERCHANT_MERCHANTCODE credentials[:commercial_id]
|
152
|
+
xml.DS_MERCHANT_ORDER @fields['Ds_Merchant_Order']
|
153
|
+
xml.DS_MERCHANT_MERCHANTSIGNATURE sign_request
|
154
|
+
end
|
155
|
+
xml.target!
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
# Generate a signature authenticating the current request.
|
160
|
+
# Values included in the signature are determined by the the type of
|
161
|
+
# transaction.
|
162
|
+
def sign_request
|
163
|
+
str = @fields['Ds_Merchant_Amount'].to_s +
|
164
|
+
@fields['Ds_Merchant_Order'].to_s +
|
165
|
+
@fields['Ds_Merchant_MerchantCode'].to_s +
|
166
|
+
@fields['Ds_Merchant_Currency'].to_s
|
167
|
+
|
168
|
+
case Sermepa.transaction_from_code(@fields['Ds_Merchant_TransactionType'])
|
169
|
+
when :recurring_transaction
|
170
|
+
str += @fields['Ds_Merchant_SumTotal']
|
171
|
+
end
|
172
|
+
|
173
|
+
if credentials[:key_type].blank? || credentials[:key_type] == 'sha1_extended'
|
174
|
+
str += @fields['Ds_Merchant_TransactionType'].to_s +
|
175
|
+
@fields['Ds_Merchant_MerchantURL'].to_s # may be blank!
|
176
|
+
end
|
177
|
+
|
178
|
+
str += credentials[:secret_key]
|
179
|
+
|
180
|
+
Digest::SHA1.hexdigest(str)
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ActiveMerchant #:nodoc:
|
3
|
+
module Billing #:nodoc:
|
4
|
+
module Integrations #:nodoc:
|
5
|
+
module Sermepa
|
6
|
+
class Notification < ActiveMerchant::Billing::Integrations::Notification
|
7
|
+
include PostsData
|
8
|
+
|
9
|
+
def complete?
|
10
|
+
status == 'Completed'
|
11
|
+
end
|
12
|
+
|
13
|
+
def transaction_id
|
14
|
+
params['ds_order']
|
15
|
+
end
|
16
|
+
|
17
|
+
# When was this payment received by the client.
|
18
|
+
def received_at
|
19
|
+
if params['ds_date']
|
20
|
+
(day, month, year) = params['ds_date'].split('/')
|
21
|
+
Time.parse("#{year}-#{month}-#{day} #{params['ds_hour']}")
|
22
|
+
else
|
23
|
+
Time.now # Not provided!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# the money amount we received in cents in X.2 format
|
28
|
+
def gross
|
29
|
+
sprintf("%.2f", params['ds_amount'].to_f / 100)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Was this a test transaction?
|
33
|
+
def test?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def currency
|
38
|
+
Sermepa.currency_from_code( params['ds_currency'] )
|
39
|
+
end
|
40
|
+
|
41
|
+
# Status of transaction. List of possible values:
|
42
|
+
# <tt>Completed</tt>
|
43
|
+
# <tt>Failed</tt>
|
44
|
+
# <tt>Pending</tt>
|
45
|
+
def status
|
46
|
+
case error_code.to_i
|
47
|
+
when 0..99
|
48
|
+
'Completed'
|
49
|
+
when 900
|
50
|
+
'Pending'
|
51
|
+
else
|
52
|
+
'Failed'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def error_code
|
57
|
+
params['ds_response']
|
58
|
+
end
|
59
|
+
|
60
|
+
def error_message
|
61
|
+
msg = Sermepa.response_code_message(error_code)
|
62
|
+
error_code.to_s + ' - ' + (msg.nil? ? 'Operación Aceptada' : msg)
|
63
|
+
end
|
64
|
+
|
65
|
+
def secure_payment?
|
66
|
+
params['ds_securepayment'] == '1'
|
67
|
+
end
|
68
|
+
|
69
|
+
# Acknowledge the transaction.
|
70
|
+
#
|
71
|
+
# Validate the details provided by the gateway by ensuring that the signature
|
72
|
+
# matches up with the details provided.
|
73
|
+
#
|
74
|
+
# Optionally, a set of credentials can be provided that should contain a
|
75
|
+
# :secret_key instead of using the global credentials defined in the Sermepa::Helper.
|
76
|
+
#
|
77
|
+
# Example:
|
78
|
+
#
|
79
|
+
# def notify
|
80
|
+
# notify = Sermepa::Notification.new(request.query_parameters)
|
81
|
+
#
|
82
|
+
# if notify.acknowledge
|
83
|
+
# ... process order ... if notify.complete?
|
84
|
+
# else
|
85
|
+
# ... log possible hacking attempt ...
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
#
|
89
|
+
def acknowledge(credentials = nil)
|
90
|
+
return false if params['ds_signature'].blank?
|
91
|
+
str =
|
92
|
+
params['ds_amount'].to_s +
|
93
|
+
params['ds_order'].to_s +
|
94
|
+
params['ds_merchantcode'].to_s +
|
95
|
+
params['ds_currency'].to_s +
|
96
|
+
params['ds_response'].to_s
|
97
|
+
if xml?
|
98
|
+
str += params['ds_transactiontype'].to_s + params['ds_securepayment'].to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
str += (credentials || Sermepa::Helper.credentials)[:secret_key]
|
102
|
+
sig = Digest::SHA1.hexdigest(str)
|
103
|
+
sig.upcase == params['ds_signature'].to_s.upcase
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def xml?
|
109
|
+
!params['code'].blank?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Take the posted data and try to extract the parameters.
|
113
|
+
#
|
114
|
+
# Posted data can either be a parameters hash, XML string or CGI data string
|
115
|
+
# of parameters.
|
116
|
+
#
|
117
|
+
def parse(post)
|
118
|
+
if post.is_a?(Hash)
|
119
|
+
post.each { |key, value| params[key.downcase] = value }
|
120
|
+
elsif post.to_s =~ /<retornoxml>/i
|
121
|
+
# XML source
|
122
|
+
self.params = xml_response_to_hash(@raw)
|
123
|
+
else
|
124
|
+
for line in post.to_s.split('&')
|
125
|
+
key, value = *line.scan( %r{^([A-Za-z0-9_.]+)\=(.*)$} ).flatten
|
126
|
+
params[key.downcase] = CGI.unescape(value)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
@raw = post.inspect.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
def xml_response_to_hash(xml)
|
133
|
+
result = { }
|
134
|
+
doc = Nokogiri::XML(xml)
|
135
|
+
doc.css('RETORNOXML OPERACION').children().each do |child|
|
136
|
+
result[child.name.downcase] = child.inner_text
|
137
|
+
end
|
138
|
+
result['code'] = doc.css('RETORNOXML CODIGO').inner_text
|
139
|
+
result
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'money'
|
10
|
+
require 'mocha'
|
11
|
+
require 'yaml'
|
12
|
+
require 'active_merchant_sermepa'
|
13
|
+
|
14
|
+
require 'action_controller'
|
15
|
+
require 'action_view/template'
|
16
|
+
begin
|
17
|
+
require 'action_dispatch/testing/test_process'
|
18
|
+
rescue LoadError
|
19
|
+
require 'action_controller/test_process'
|
20
|
+
end
|
21
|
+
require 'active_merchant/billing/integrations/action_view_helper'
|
22
|
+
|
23
|
+
ActiveMerchant::Billing::Base.mode = :test
|
24
|
+
|
25
|
+
|
26
|
+
## Assertions copied from ActiveMerchant. This is lame, they should have them in a seperate file!
|
27
|
+
|
28
|
+
module ActiveMerchant
|
29
|
+
module Assertions
|
30
|
+
AssertionClass = RUBY_VERSION > '1.9' ? MiniTest::Assertion : Test::Unit::AssertionFailedError
|
31
|
+
|
32
|
+
def assert_field(field, value)
|
33
|
+
clean_backtrace do
|
34
|
+
assert_equal value, @helper.fields[field]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Allows the testing of you to check for negative assertions:
|
39
|
+
#
|
40
|
+
# # Instead of
|
41
|
+
# assert !something_that_is_false
|
42
|
+
#
|
43
|
+
# # Do this
|
44
|
+
# assert_false something_that_should_be_false
|
45
|
+
#
|
46
|
+
# An optional +msg+ parameter is available to help you debug.
|
47
|
+
def assert_false(boolean, message = nil)
|
48
|
+
message = build_message message, '<?> is not false or nil.', boolean
|
49
|
+
|
50
|
+
clean_backtrace do
|
51
|
+
assert_block message do
|
52
|
+
not boolean
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# A handy little assertion to check for a successful response:
|
58
|
+
#
|
59
|
+
# # Instead of
|
60
|
+
# assert_success response
|
61
|
+
#
|
62
|
+
# # DRY that up with
|
63
|
+
# assert_success response
|
64
|
+
#
|
65
|
+
# A message will automatically show the inspection of the response
|
66
|
+
# object if things go afoul.
|
67
|
+
def assert_success(response)
|
68
|
+
clean_backtrace do
|
69
|
+
assert response.success?, "Response failed: #{response.inspect}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# The negative of +assert_success+
|
74
|
+
def assert_failure(response)
|
75
|
+
clean_backtrace do
|
76
|
+
assert_false response.success?, "Response expected to fail: #{response.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def assert_valid(validateable)
|
81
|
+
clean_backtrace do
|
82
|
+
assert validateable.valid?, "Expected to be valid"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def assert_not_valid(validateable)
|
87
|
+
clean_backtrace do
|
88
|
+
assert_false validateable.valid?, "Expected to not be valid"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def clean_backtrace(&block)
|
94
|
+
yield
|
95
|
+
rescue AssertionClass => e
|
96
|
+
path = File.expand_path(__FILE__)
|
97
|
+
raise AssertionClass, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
Test::Unit::TestCase.class_eval do
|
103
|
+
include ActiveMerchant::Assertions
|
104
|
+
end
|
105
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.expand_path('../../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class SermepaHelperTest < Test::Unit::TestCase
|
4
|
+
include ActiveMerchant::Billing::Integrations
|
5
|
+
|
6
|
+
def setup
|
7
|
+
Sermepa::Helper.credentials = {
|
8
|
+
:terminal_id => '9',
|
9
|
+
:commercial_id => '999008881',
|
10
|
+
:secret_key => 'qwertyasdf0123456789'
|
11
|
+
}
|
12
|
+
@helper = Sermepa::Helper.new('070803113316', 'Comercio Pruebas', :amount => 825, :currency => 'EUR')
|
13
|
+
@helper.description = "Alfombrilla para raton"
|
14
|
+
@helper.customer_name = "Sermepa"
|
15
|
+
@helper.notify_url = "https://sis-t.sermepa.es:25443/sis/pruebaCom.jsp"
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_credentials_accessible
|
19
|
+
assert_instance_of Hash, @helper.credentials
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_credentials_overwritable
|
23
|
+
@helper = Sermepa::Helper.new(29292929, 'cody@example.com', :amount => 1235, :currency => 'EUR',
|
24
|
+
:credentials => {:terminal_id => 12})
|
25
|
+
assert_field 'Ds_Merchant_Terminal', '12'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_basic_helper_fields
|
29
|
+
assert_field 'Ds_Merchant_MerchantCode', '999008881'
|
30
|
+
assert_field 'Ds_Merchant_Amount', '825'
|
31
|
+
assert_field 'Ds_Merchant_Order', '070803113316'
|
32
|
+
assert_field 'Ds_Merchant_ProductDescription', 'Alfombrilla para raton'
|
33
|
+
assert_field 'Ds_Merchant_Currency', '978'
|
34
|
+
assert_field 'Ds_Merchant_TransactionType', '0'
|
35
|
+
assert_field 'Ds_Merchant_MerchantName', 'Comercio Pruebas'
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_unknown_mapping
|
39
|
+
assert_nothing_raised do
|
40
|
+
@helper.company_address :address => '500 Dwemthy Fox Road'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_padding_on_order_id
|
45
|
+
@helper.order = 101
|
46
|
+
assert_field 'Ds_Merchant_Order', "0000101"
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_no_padding_on_valid_order_id
|
50
|
+
@helper.order = 1010
|
51
|
+
assert_field 'Ds_Merchant_Order', "1010"
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_error_raised_on_invalid_order_id
|
55
|
+
assert_raise RuntimeError do
|
56
|
+
@helper.order = "A0000000ABC"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_basic_signing_request
|
61
|
+
assert sig = @helper.send(:sign_request)
|
62
|
+
assert_equal "ca2bd747d365b4f0a87c670b270cc390b79670ce", sig
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_build_xml_confirmation_request
|
66
|
+
# This also tests signing the request for differnet transactions
|
67
|
+
data = @helper.send(:build_xml_request)
|
68
|
+
assert data =~ /<DS_MERCHANT_TRANSACTIONTYPE>0<\/DS_MERCHANT_TRANSACTIONTYPE>/
|
69
|
+
assert data =~ /<DS_MERCHANT_MERCHANTCODE>999008881<\/DS_MERCHANT_MERCHANTCODE>/
|
70
|
+
assert data =~ /<DS_MERCHANT_MERCHANTSIGNATURE>ca2bd747d365b4f0a87c670b270cc390b79670ce<\/DS_MERCHANT_MERCHANTSIGNATURE>/
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.expand_path('../../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class SermepaNotificationTest < Test::Unit::TestCase
|
4
|
+
include ActiveMerchant::Billing::Integrations
|
5
|
+
|
6
|
+
def setup
|
7
|
+
Sermepa::Helper.credentials = {
|
8
|
+
:terminal_id => '1',
|
9
|
+
:commercial_id => '999008881',
|
10
|
+
:secret_key => 'qwertyasdf0123456789'
|
11
|
+
}
|
12
|
+
@sermepa = Sermepa::Notification.new(raw_params_data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_accessors
|
16
|
+
assert @sermepa.complete?
|
17
|
+
assert_equal "Completed", @sermepa.status
|
18
|
+
assert_equal "070820124150", @sermepa.transaction_id
|
19
|
+
assert_equal "0.45", @sermepa.gross
|
20
|
+
assert_equal "EUR", @sermepa.currency
|
21
|
+
assert_equal Time.parse("2007-08-20 12:47"), @sermepa.received_at
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_compositions
|
25
|
+
assert_equal Money.new(45, 'EUR'), @sermepa.amount
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_respond_to_acknowledge
|
29
|
+
assert @sermepa.respond_to?(:acknowledge)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Replace with real successful acknowledgement code
|
33
|
+
def test_acknowledgement
|
34
|
+
assert @sermepa.acknowledge
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_acknowledgement_with_xml
|
38
|
+
# Fake the presence of xml!
|
39
|
+
@sermepa.params['code'] = '123'
|
40
|
+
@sermepa.params["ds_signature"] = "49A8A907D86FE4763890180061E7907589DBE96A"
|
41
|
+
assert @sermepa.acknowledge
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def raw_params_data
|
46
|
+
{
|
47
|
+
"Ds_AuthorisationCode" => "004022",
|
48
|
+
"Ds_SecurePayment" => "1",
|
49
|
+
"Ds_Hour" => "12:47",
|
50
|
+
"Ds_MerchantData" => "",
|
51
|
+
"Ds_Terminal" => "001",
|
52
|
+
"Ds_Card_Country" => "724",
|
53
|
+
"Ds_Response" => "0000",
|
54
|
+
"Ds_Currency" => "978",
|
55
|
+
"Ds_MerchantCode" => "999008881",
|
56
|
+
"Ds_ConsumerLanguage" => "1",
|
57
|
+
"Ds_TransactionType" => "0",
|
58
|
+
"Ds_Signature" => "E2E5A14D690B869183CF3BA36E2B6005BB21F9C5",
|
59
|
+
"Ds_Order" => "070820124150",
|
60
|
+
"Ds_Amount" => "45",
|
61
|
+
"Ds_Date" => "20/08/2007"
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class SermepaModuleTest < Test::Unit::TestCase
|
4
|
+
include ActiveMerchant::Billing::Integrations
|
5
|
+
|
6
|
+
def test_notification_method
|
7
|
+
assert_instance_of Sermepa::Notification, Sermepa.notification('name=cody')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_currency_code
|
11
|
+
assert_equal '978', Sermepa.currency_code('EUR')
|
12
|
+
end
|
13
|
+
def test_currency_from_code
|
14
|
+
assert_equal 'EUR', Sermepa.currency_from_code('978')
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_language_code
|
18
|
+
assert_equal Sermepa.language_code('es'), '001'
|
19
|
+
assert_equal Sermepa.language_code('CA'), '003'
|
20
|
+
assert_equal Sermepa.language_code(:pt), '009'
|
21
|
+
end
|
22
|
+
def test_language_from_code
|
23
|
+
assert_equal :ca, Sermepa.language_from_code('003')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_transaction_code
|
27
|
+
assert_equal '2', Sermepa.transaction_code(:confirmation)
|
28
|
+
end
|
29
|
+
def test_transaction_from_code
|
30
|
+
assert_equal :confirmation, Sermepa.transaction_from_code(2)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_response_code_message
|
34
|
+
assert_equal nil, Sermepa.response_code_message(23)
|
35
|
+
assert_equal nil, Sermepa.response_code_message('23')
|
36
|
+
assert_equal "Tarjeta caducada", Sermepa.response_code_message(101)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_merchant_sermepa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Sam Lown
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-02-25 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activemerchant
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 9
|
31
|
+
- 4
|
32
|
+
version: 1.9.4
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mocha
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
- 9
|
46
|
+
- 10
|
47
|
+
version: 0.9.10
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: money
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 3
|
60
|
+
- 5
|
61
|
+
- 4
|
62
|
+
version: 3.5.4
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: actionpack
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 3
|
75
|
+
- 0
|
76
|
+
- 3
|
77
|
+
version: 3.0.3
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
80
|
+
description: Add support to ActiveMerchant for the Sermepa payment gateway by Servired used by many banks in Spain
|
81
|
+
email: me@samlown.com
|
82
|
+
executables: []
|
83
|
+
|
84
|
+
extensions: []
|
85
|
+
|
86
|
+
extra_rdoc_files:
|
87
|
+
- MIT-LICENSE
|
88
|
+
- CHANGELOG
|
89
|
+
- README.rdoc
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- CHANGELOG
|
93
|
+
- Gemfile
|
94
|
+
- Gemfile.lock
|
95
|
+
- MIT-LICENSE
|
96
|
+
- Manifest
|
97
|
+
- README.rdoc
|
98
|
+
- Rakefile
|
99
|
+
- active_merchant_sermepa.gemspec
|
100
|
+
- lib/active_merchant/billing/integrations/sermepa.rb
|
101
|
+
- lib/active_merchant/billing/integrations/sermepa/helper.rb
|
102
|
+
- lib/active_merchant/billing/integrations/sermepa/notification.rb
|
103
|
+
- lib/active_merchant/billing/integrations/sermepa/return.rb
|
104
|
+
- lib/active_merchant_sermepa.rb
|
105
|
+
- lib/activemerchant_sermepa.rb
|
106
|
+
- test/test_helper.rb
|
107
|
+
- test/unit/integrations/helpers/sermepa_helper_test.rb
|
108
|
+
- test/unit/integrations/notifications/sermepa_notification_test.rb
|
109
|
+
- test/unit/integrations/sermepa_module_text.rb
|
110
|
+
has_rdoc: true
|
111
|
+
homepage: http://github.com/samlown/active_merchant_sermepa
|
112
|
+
licenses: []
|
113
|
+
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options: []
|
116
|
+
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
segments:
|
133
|
+
- 1
|
134
|
+
- 3
|
135
|
+
- 1
|
136
|
+
version: 1.3.1
|
137
|
+
requirements: []
|
138
|
+
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 1.3.7
|
141
|
+
signing_key:
|
142
|
+
specification_version: 3
|
143
|
+
summary: ActiveMerchant support for Servired's Sermepa payment gateway
|
144
|
+
test_files: []
|
145
|
+
|