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 ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.2
@@ -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
- Controller
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
- TODO
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
- View
133
+ Void
26
134
  ----
27
135
 
28
- TODO
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
  =======
@@ -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
@@ -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/notification'
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::Notification::ViewHelper if defined? ActionView
53
+ ActionView::Base.send :include, Datatrans::Web::ViewHelper if defined? ActionView
@@ -0,0 +1,9 @@
1
+ require 'openssl'
2
+
3
+ module Datatrans::Common
4
+ def sign(*fields)
5
+ key = Datatrans.sign_key.split(/([a-f0-9][a-f0-9])/).reject(&:empty?)
6
+ key = key.pack("H*" * key.size)
7
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::MD5.new, key, fields.join)
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Datatrans
2
- VERSION = "1.0.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -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