active_merchant_sermepa 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|