datatrans 1.0.0 → 2.0.0
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/.rvmrc +1 -0
- data/README.markdown +153 -9
- data/datatrans.gemspec +5 -0
- data/lib/datatrans.rb +9 -3
- data/lib/datatrans/common.rb +9 -0
- data/lib/datatrans/version.rb +1 -1
- data/lib/datatrans/web/transaction.rb +55 -0
- data/lib/datatrans/web/transaction/authorize.rb +68 -0
- data/lib/datatrans/xml/transaction.rb +47 -0
- data/lib/datatrans/xml/transaction/authorize.rb +83 -0
- data/lib/datatrans/xml/transaction/capture.rb +68 -0
- data/lib/datatrans/xml/transaction/request.rb +39 -0
- data/lib/datatrans/xml/transaction/response.rb +13 -0
- data/lib/datatrans/xml/transaction/void.rb +69 -0
- data/spec/common_spec.rb +20 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/web/authorize_spec.rb +128 -0
- data/spec/xml/authorizate_spec.rb +114 -0
- data/spec/xml/capture_spec.rb +103 -0
- data/spec/xml/void_spec.rb +103 -0
- metadata +105 -47
- data/lib/datatrans/notification.rb +0 -81
- data/lib/datatrans/transaction.rb +0 -79
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ruby-1.9.2
|
data/README.markdown
CHANGED
@@ -3,29 +3,173 @@ Datatrans
|
|
3
3
|
|
4
4
|
Ruby adapter for the Datatrans payment gateway (http://www.datatrans.ch).
|
5
5
|
|
6
|
-
Usage
|
7
|
-
=====
|
8
|
-
|
9
6
|
Configuration
|
10
7
|
-------------
|
11
8
|
|
9
|
+
Set your datatrans credentials in your environment.
|
10
|
+
|
12
11
|
Datatrans.configure do |config|
|
13
12
|
config.merchant_id = '1234567'
|
14
|
-
config.sign_key = '...'
|
13
|
+
config.sign_key = 'ab739fd5b7c2a1...'
|
15
14
|
config.environment = :production
|
16
15
|
end
|
17
16
|
|
18
17
|
Possible values for the environment: `:production`, `:development`
|
19
18
|
|
20
|
-
|
21
|
-
|
19
|
+
Web Authorization
|
20
|
+
=================
|
21
|
+
|
22
|
+
If you want to process a credit card the first time a web authorization is
|
23
|
+
necessary. Add the following code to a controller action that shows the form.
|
24
|
+
You need to pass at least `amount`, `currency` and `refno` (order number).
|
25
|
+
|
26
|
+
@transaction = Datatrans::Web::Transaction.new({
|
27
|
+
:amount => 1000, # in cents!
|
28
|
+
:currency => 'CHF',
|
29
|
+
:refno => 'ABCDEF',
|
30
|
+
:uppCustomerEmail => 'customer@email.com'
|
31
|
+
# feel free to add more upp infos here ...
|
32
|
+
})
|
33
|
+
|
34
|
+
In your View your show the credit card form with a convenient helper:
|
35
|
+
|
36
|
+
In this example we use just ECA (Mastercard) as paymentmethod. Feel free to
|
37
|
+
provide an appropriate select field to offer more payment methods. This is the
|
38
|
+
form you will show in your "new" method.
|
39
|
+
|
40
|
+
= form_tag Datatrans.web_authorize_url do
|
41
|
+
|
42
|
+
= text_field_tag :paymentmethod, 'ECA'
|
43
|
+
= text_field_tag :cardno
|
44
|
+
= text_field_tag :expm
|
45
|
+
= text_field_tag :expy
|
46
|
+
= text_field_tag :cvv
|
47
|
+
|
48
|
+
= hidden_field_tag :successUrl, <your_application_return_url>
|
49
|
+
= hidden_field_tag :cancelUrl, <your_application_return_url>
|
50
|
+
= hidden_field_tag :errorUrl, <your_application_return_url>
|
51
|
+
|
52
|
+
= datatrans_notification_request_hidden_fields(@transaction)
|
53
|
+
|
54
|
+
= submit_tag "send"
|
55
|
+
|
56
|
+
In this example we use just ECA (Mastercard) as paymentmethod. Feel free to
|
57
|
+
provide an appropriate select field to offer more payment methods. Don't forget
|
58
|
+
to add `successUrl`, `cancelUrl` and `errorUrl`. We recommend to set them all
|
59
|
+
to the same value.
|
60
|
+
|
61
|
+
After you submit the request to Datatrans they redirect back to your application.
|
62
|
+
Now you can process the transaction like this:
|
63
|
+
|
64
|
+
begin
|
65
|
+
transaction = Datatrans::Web::Transaction.new(params)
|
66
|
+
|
67
|
+
if transaction.authorize
|
68
|
+
# transaction was successful, access the following attributes
|
69
|
+
# transaction.transaction_id
|
70
|
+
# transaction.creditcard_alias
|
71
|
+
# transaction.masked_cc
|
72
|
+
# transaction.authorization_code
|
73
|
+
# ...
|
74
|
+
|
75
|
+
else
|
76
|
+
# transaction was not successful, accces the error details
|
77
|
+
# transaction.error_code, transaction.error_message, transaction.error_detail
|
78
|
+
|
79
|
+
end
|
80
|
+
rescue Datatrans::InvalidSignatureError => exception
|
81
|
+
# the signature was wrong, the request may have been compromised...
|
82
|
+
end
|
83
|
+
|
84
|
+
XML Transactions
|
85
|
+
================
|
86
|
+
|
87
|
+
If you have already a credit card alias or an authorized transaction you can
|
88
|
+
use the convenient XML methods to process payments.
|
89
|
+
|
90
|
+
Authorize
|
91
|
+
---------
|
92
|
+
|
93
|
+
transaction = Datatrans::XML::Transaction.new(
|
94
|
+
:refno => 'ABCDEF',
|
95
|
+
:amount => 1000, # in cents!
|
96
|
+
:currency => 'CHF',
|
97
|
+
:aliasCC => '8383843729284848348',
|
98
|
+
:expm => 12,
|
99
|
+
:expy => 15
|
100
|
+
)
|
101
|
+
|
102
|
+
if transaction.authorize
|
103
|
+
# ok, the transaction is authorized...
|
104
|
+
# access same values as in the web authorization (e.g. transaction.transaction_id)
|
105
|
+
else
|
106
|
+
# transaction.error_code, transaction.error_message, transaction.error_detail
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
Capture
|
111
|
+
-------
|
112
|
+
|
113
|
+
To capture an authorized transaction you use the following code:
|
22
114
|
|
23
|
-
|
115
|
+
transaction = Datatrans::XML::Transaction.new(
|
116
|
+
:refno => 'ABCDEF',
|
117
|
+
:amount => 1000, # in cents!
|
118
|
+
:currency => 'CHF',
|
119
|
+
:transaction_id => 19834324987349723948729834
|
120
|
+
)
|
121
|
+
|
122
|
+
begin
|
123
|
+
if transaction.capture
|
124
|
+
# ok, the money is yours...
|
125
|
+
else
|
126
|
+
# transaction.error_code, transaction.error_message, transaction.error_detail
|
127
|
+
end
|
128
|
+
rescue Datatrans::InvalidSignatureError => exception
|
129
|
+
# the signature was wrong, the request may have been compromised...
|
130
|
+
end
|
131
|
+
|
24
132
|
|
25
|
-
|
133
|
+
Void
|
26
134
|
----
|
27
135
|
|
28
|
-
|
136
|
+
To make an authorized transaction invalid use void.
|
137
|
+
|
138
|
+
transaction = Datatrans::XML::Transaction.new(
|
139
|
+
:refno => 'ABCDEF',
|
140
|
+
:amount => 1000, # in cents!
|
141
|
+
:currency => 'CHF',
|
142
|
+
:transaction_id => 19834324987349723948729834
|
143
|
+
)
|
144
|
+
|
145
|
+
if transaction.void
|
146
|
+
# ok, the transaction is not longer valid...
|
147
|
+
else
|
148
|
+
# transaction.error_code, transaction.error_message, transaction.error_detail
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
Todo
|
153
|
+
====
|
154
|
+
|
155
|
+
* allow signing of xml transactions
|
156
|
+
* allow signing with different keys
|
157
|
+
* add credit method to reverse already captured transactions
|
158
|
+
* add purchase method to authorize and capture in one step
|
159
|
+
* dry code more
|
160
|
+
|
161
|
+
|
162
|
+
Contribute
|
163
|
+
==========
|
164
|
+
|
165
|
+
* Fork the project.
|
166
|
+
* Make your feature addition or bug fix.
|
167
|
+
* Add specs for it. This is important so we don't break it in a
|
168
|
+
future version unintentionally.
|
169
|
+
* Commit, do not mess with rakefile, version, or history.
|
170
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself we can ignore when we pull)
|
171
|
+
* Send us a pull request. Bonus points for topic branches.
|
172
|
+
|
29
173
|
|
30
174
|
Credits
|
31
175
|
=======
|
data/datatrans.gemspec
CHANGED
@@ -20,4 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_dependency 'httparty'
|
22
22
|
s.add_dependency 'activesupport', '>= 3.0.0'
|
23
|
+
s.add_dependency 'i18n'
|
24
|
+
s.add_dependency 'builder'
|
25
|
+
|
26
|
+
s.add_development_dependency 'rspec'
|
27
|
+
s.add_development_dependency 'actionpack', '>= 3.0.0'
|
23
28
|
end
|
data/lib/datatrans.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'active_support/core_ext/module'
|
2
|
+
require 'action_view'
|
3
|
+
|
1
4
|
module Datatrans
|
2
5
|
BASE_URL = 'https://payment.datatrans.biz'
|
3
6
|
WEB_AUTHORIZE_URL = "#{BASE_URL}/upp/jsp/upStart.jsp"
|
@@ -38,10 +41,13 @@ module Datatrans
|
|
38
41
|
raise "Unknown environment '#{environment}'. Available: :development, :production."
|
39
42
|
end
|
40
43
|
end
|
44
|
+
|
45
|
+
class InvalidSignatureError < StandardError; end
|
41
46
|
end
|
42
47
|
|
43
48
|
require 'datatrans/version'
|
44
|
-
require 'datatrans/
|
45
|
-
require 'datatrans/transaction'
|
49
|
+
require 'datatrans/common'
|
50
|
+
require 'datatrans/xml/transaction'
|
51
|
+
require 'datatrans/web/transaction'
|
46
52
|
|
47
|
-
ActionView::Base.send :include, Datatrans::
|
53
|
+
ActionView::Base.send :include, Datatrans::Web::ViewHelper if defined? ActionView
|
data/lib/datatrans/version.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_support/core_ext/hash'
|
2
|
+
|
3
|
+
module Datatrans::Web
|
4
|
+
class Transaction
|
5
|
+
include Datatrans::Common
|
6
|
+
|
7
|
+
attr_accessor :request
|
8
|
+
attr_reader :response, :params
|
9
|
+
|
10
|
+
def initialize(params)
|
11
|
+
raise 'Please define Datatrans.sign_key!' unless Datatrans.sign_key.present?
|
12
|
+
|
13
|
+
params = params.to_hash
|
14
|
+
params.symbolize_keys!
|
15
|
+
params.reverse_merge!({ :reqtype => 'NOA', :useAlias => 'Yes', :hiddenMode => 'Yes' })
|
16
|
+
@params = params
|
17
|
+
end
|
18
|
+
|
19
|
+
def signature
|
20
|
+
sign(Datatrans.merchant_id, params[:amount], params[:currency], params[:refno])
|
21
|
+
end
|
22
|
+
|
23
|
+
def authorize
|
24
|
+
@response = AuthorizeResponse.new(params)
|
25
|
+
@response.successful?
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(method, *args, &block)
|
29
|
+
if response.respond_to? method.to_sym
|
30
|
+
response.send(method)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module ViewHelper
|
38
|
+
def datatrans_notification_request_hidden_fields(transaction)
|
39
|
+
[
|
40
|
+
hidden_field_tag(:merchantId, Datatrans.merchant_id),
|
41
|
+
hidden_field_tag(:hiddenMode, transaction.params[:hiddenMode]),
|
42
|
+
hidden_field_tag(:reqtype, transaction.params[:reqtype]),
|
43
|
+
hidden_field_tag(:amount, transaction.params[:amount]),
|
44
|
+
hidden_field_tag(:currency, transaction.params[:currency]),
|
45
|
+
hidden_field_tag(:useAlias, transaction.params[:useAlias]),
|
46
|
+
hidden_field_tag(:sign, transaction.signature),
|
47
|
+
hidden_field_tag(:refno, transaction.params[:refno]),
|
48
|
+
hidden_field_tag(:uppCustomerName, transaction.params[:uppCustomerName]),
|
49
|
+
hidden_field_tag(:uppCustomerEmail, transaction.params[:uppCustomerEmail])
|
50
|
+
].join.html_safe
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'datatrans/web/transaction/authorize'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Datatrans::Web::Transaction
|
2
|
+
class AuthorizeResponse
|
3
|
+
attr_accessor :params
|
4
|
+
|
5
|
+
def initialize(params)
|
6
|
+
@params = params
|
7
|
+
end
|
8
|
+
|
9
|
+
def successful?
|
10
|
+
raise Datatrans::InvalidSignatureError unless valid_signature?
|
11
|
+
response_code == '01' && response_message == 'Authorized' && !errors_occurred?
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_signature?
|
15
|
+
return true if errors_occurred? # no sign2 sent on error
|
16
|
+
sign(Datatrans.merchant_id, params[:amount], params[:currency], params[:uppTransactionId]) == params[:sign2]
|
17
|
+
end
|
18
|
+
|
19
|
+
def response_code
|
20
|
+
params[:responseCode] rescue nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def response_message
|
24
|
+
params[:responseMessage] rescue nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def transaction_id
|
28
|
+
params[:uppTransactionId] rescue nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def reference_number
|
32
|
+
params[:refno] rescue nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def authorization_code
|
36
|
+
params[:authorizationCode] rescue nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def masked_cc
|
40
|
+
params[:maskedCC] rescue nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def creditcard_alias
|
44
|
+
params[:aliasCC] rescue nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def error_code
|
48
|
+
params[:errorCode] rescue nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def error_message
|
52
|
+
params[:errorMessage] rescue nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def error_detail
|
56
|
+
params[:errorDetail] rescue nil
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def errors_occurred?
|
63
|
+
error_code || error_message || error_detail
|
64
|
+
end
|
65
|
+
|
66
|
+
include Datatrans::Common
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support/core_ext/hash'
|
2
|
+
|
3
|
+
module Datatrans::XML
|
4
|
+
class Transaction
|
5
|
+
attr_accessor :request
|
6
|
+
attr_reader :response, :params
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@params = params.symbolize_keys
|
10
|
+
end
|
11
|
+
|
12
|
+
def authorize
|
13
|
+
self.request = AuthorizeRequest.new(params)
|
14
|
+
@response = AuthorizeResponse.new(request.process)
|
15
|
+
@response.successful?
|
16
|
+
end
|
17
|
+
|
18
|
+
def void
|
19
|
+
self.request = VoidRequest.new(params)
|
20
|
+
@response = VoidResponse.new(request.process)
|
21
|
+
@response.successful?
|
22
|
+
end
|
23
|
+
|
24
|
+
def capture
|
25
|
+
self.request = CaptureRequest.new(params)
|
26
|
+
@response = CaptureResponse.new(request.process)
|
27
|
+
@response.successful?
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO: purchase, credit methods
|
31
|
+
|
32
|
+
|
33
|
+
def method_missing(method, *args, &block)
|
34
|
+
if response.respond_to? method.to_sym
|
35
|
+
response.send(method)
|
36
|
+
elsif request.respond_to? method.to_sym
|
37
|
+
request.send(method)
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'datatrans/xml/transaction/authorize'
|
46
|
+
require 'datatrans/xml/transaction/void'
|
47
|
+
require 'datatrans/xml/transaction/capture'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'datatrans/xml/transaction/request'
|
2
|
+
require 'datatrans/xml/transaction/response'
|
3
|
+
|
4
|
+
class Datatrans::XML::Transaction
|
5
|
+
class AuthorizeRequest < Request
|
6
|
+
|
7
|
+
def process
|
8
|
+
self.class.post(Datatrans.xml_authorize_url,
|
9
|
+
:headers => { 'Content-Type' => 'text/xml' },
|
10
|
+
:body => build_authorize_request.to_s).parsed_response
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_authorize_request
|
17
|
+
build_xml_request(:authorization) do |xml|
|
18
|
+
xml.amount params[:amount]
|
19
|
+
xml.currency params[:currency]
|
20
|
+
xml.aliasCC params[:aliasCC]
|
21
|
+
xml.expm params[:expm]
|
22
|
+
xml.expy params[:expy]
|
23
|
+
xml.sign sign(Datatrans.merchant_id, params[:amount], params[:currency], params[:refno])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class AuthorizeResponse < Response
|
30
|
+
|
31
|
+
def successful?
|
32
|
+
response_code == '01' && response_message == 'Authorized'
|
33
|
+
end
|
34
|
+
|
35
|
+
def response_code
|
36
|
+
params_root_node['response']['responseCode'] rescue nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def response_message
|
40
|
+
params_root_node['response']['responseMessage'] rescue nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def transaction_id
|
44
|
+
params_root_node['response']['uppTransactionId'] rescue nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def reference_number
|
48
|
+
params_root_node['refno'] rescue nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def authorization_code
|
52
|
+
params_root_node['response']['authorizationCode'] rescue nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def masked_cc
|
56
|
+
params_root_node['response']['maskedCC'] rescue nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def creditcard_alias
|
60
|
+
params_root_node['request']['aliasCC'] rescue nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def error_code
|
64
|
+
params_root_node['error']['errorCode'] rescue nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def error_message
|
68
|
+
params_root_node['error']['errorMessage'] rescue nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def error_detail
|
72
|
+
params_root_node['error']['errorDetail'] rescue nil
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def params_root_node
|
79
|
+
params['authorizationService']['body']['transaction']
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|