datatrans 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|