n8_activemerchant 1.9.3

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.
Files changed (158) hide show
  1. data/CHANGELOG +574 -0
  2. data/CONTRIBUTORS +175 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +151 -0
  5. data/gem-public_cert.pem +20 -0
  6. data/lib/active_merchant.rb +49 -0
  7. data/lib/active_merchant/billing.rb +9 -0
  8. data/lib/active_merchant/billing/avs_result.rb +98 -0
  9. data/lib/active_merchant/billing/base.rb +57 -0
  10. data/lib/active_merchant/billing/check.rb +68 -0
  11. data/lib/active_merchant/billing/credit_card.rb +161 -0
  12. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  13. data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
  14. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  15. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  16. data/lib/active_merchant/billing/gateway.rb +169 -0
  17. data/lib/active_merchant/billing/gateways.rb +18 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net.rb +654 -0
  19. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +736 -0
  20. data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
  21. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +244 -0
  22. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  23. data/lib/active_merchant/billing/gateways/bogus.rb +102 -0
  24. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  25. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  26. data/lib/active_merchant/billing/gateways/braintree_blue.rb +210 -0
  27. data/lib/active_merchant/billing/gateways/braintree_orange.rb +17 -0
  28. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  29. data/lib/active_merchant/billing/gateways/cyber_source.rb +406 -0
  30. data/lib/active_merchant/billing/gateways/data_cash.rb +593 -0
  31. data/lib/active_merchant/billing/gateways/efsnet.rb +229 -0
  32. data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
  33. data/lib/active_merchant/billing/gateways/epay.rb +263 -0
  34. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  35. data/lib/active_merchant/billing/gateways/exact.rb +222 -0
  36. data/lib/active_merchant/billing/gateways/first_pay.rb +172 -0
  37. data/lib/active_merchant/billing/gateways/garanti.rb +222 -0
  38. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  39. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  40. data/lib/active_merchant/billing/gateways/iridium.rb +253 -0
  41. data/lib/active_merchant/billing/gateways/jetpay.rb +270 -0
  42. data/lib/active_merchant/billing/gateways/linkpoint.rb +449 -0
  43. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +154 -0
  44. data/lib/active_merchant/billing/gateways/merchant_ware.rb +283 -0
  45. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  46. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  47. data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
  48. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  49. data/lib/active_merchant/billing/gateways/netaxept.rb +234 -0
  50. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  51. data/lib/active_merchant/billing/gateways/ogone.rb +279 -0
  52. data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
  53. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  54. data/lib/active_merchant/billing/gateways/paybox_direct.rb +203 -0
  55. data/lib/active_merchant/billing/gateways/payflow.rb +236 -0
  56. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
  57. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  58. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  59. data/lib/active_merchant/billing/gateways/payflow_express.rb +138 -0
  60. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  61. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  62. data/lib/active_merchant/billing/gateways/payment_express.rb +230 -0
  63. data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
  64. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +326 -0
  65. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +38 -0
  66. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  67. data/lib/active_merchant/billing/gateways/paypal_express.rb +145 -0
  68. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
  69. data/lib/active_merchant/billing/gateways/paysimple/paysimple.rb +333 -0
  70. data/lib/active_merchant/billing/gateways/paysimple/usaepay.wsdl +1596 -0
  71. data/lib/active_merchant/billing/gateways/plugnpay.rb +292 -0
  72. data/lib/active_merchant/billing/gateways/psigate.rb +214 -0
  73. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  74. data/lib/active_merchant/billing/gateways/quickpay.rb +213 -0
  75. data/lib/active_merchant/billing/gateways/realex.rb +200 -0
  76. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  77. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  78. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  79. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  80. data/lib/active_merchant/billing/gateways/sage_pay.rb +315 -0
  81. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  82. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  83. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  84. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +157 -0
  85. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  86. data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
  87. data/lib/active_merchant/billing/gateways/smart_ps.rb +265 -0
  88. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  89. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  90. data/lib/active_merchant/billing/gateways/trust_commerce.rb +418 -0
  91. data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
  92. data/lib/active_merchant/billing/gateways/usa_epay_soap.rb +154 -0
  93. data/lib/active_merchant/billing/gateways/verifi.rb +228 -0
  94. data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
  95. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  96. data/lib/active_merchant/billing/integrations.rb +17 -0
  97. data/lib/active_merchant/billing/integrations/action_view_helper.rb +68 -0
  98. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  99. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  100. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  101. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  102. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  103. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  104. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  105. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  106. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  107. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +188 -0
  108. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  109. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  110. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  111. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  112. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  113. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  114. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  115. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  116. data/lib/active_merchant/billing/integrations/helper.rb +96 -0
  117. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  118. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  119. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  120. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  121. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  122. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  123. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  124. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  125. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  126. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  127. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  128. data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
  129. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  130. data/lib/active_merchant/billing/integrations/quickpay.rb +17 -0
  131. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
  132. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  133. data/lib/active_merchant/billing/integrations/return.rb +37 -0
  134. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  135. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  136. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +109 -0
  137. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +204 -0
  138. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +27 -0
  139. data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
  140. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  141. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  142. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  143. data/lib/active_merchant/billing/response.rb +32 -0
  144. data/lib/active_merchant/common.rb +14 -0
  145. data/lib/active_merchant/common/connection.rb +162 -0
  146. data/lib/active_merchant/common/country.rb +328 -0
  147. data/lib/active_merchant/common/error.rb +26 -0
  148. data/lib/active_merchant/common/post_data.rb +24 -0
  149. data/lib/active_merchant/common/posts_data.rb +61 -0
  150. data/lib/active_merchant/common/requires_parameters.rb +16 -0
  151. data/lib/active_merchant/common/utils.rb +18 -0
  152. data/lib/active_merchant/common/validateable.rb +76 -0
  153. data/lib/active_merchant/version.rb +3 -0
  154. data/lib/activemerchant.rb +1 -0
  155. data/lib/certs/cacert.pem +7815 -0
  156. data/lib/support/gateway_support.rb +58 -0
  157. data/lib/support/outbound_hosts.rb +25 -0
  158. metadata +270 -0
@@ -0,0 +1,189 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class ViaklixGateway < Gateway
4
+ class_inheritable_accessor :test_url, :live_url, :delimiter, :actions
5
+
6
+ self.test_url = 'https://demo.viaklix.com/process.asp'
7
+ self.live_url = 'https://www.viaklix.com/process.asp'
8
+ self.delimiter = "\r\n"
9
+
10
+ self.actions = {
11
+ :purchase => 'SALE',
12
+ :credit => 'CREDIT'
13
+ }
14
+
15
+ APPROVED = '0'
16
+
17
+ self.supported_cardtypes = [:visa, :master, :american_express]
18
+ self.supported_countries = ['US']
19
+ self.display_name = 'ViaKLIX'
20
+ self.homepage_url = 'http://viaklix.com'
21
+
22
+ # Initialize the Gateway
23
+ #
24
+ # The gateway requires that a valid login and password be passed
25
+ # in the +options+ hash.
26
+ #
27
+ # ==== Options
28
+ #
29
+ # * <tt>:login</tt> -- Merchant ID
30
+ # * <tt>:password</tt> -- PIN
31
+ # * <tt>:user</tt> -- Specify a subuser of the account (optional)
32
+ # * <tt>:test => +true+ or +false+</tt> -- Force test transactions
33
+ def initialize(options = {})
34
+ requires!(options, :login, :password)
35
+ @options = options
36
+ super
37
+ end
38
+
39
+ # Make a purchase
40
+ def purchase(money, creditcard, options = {})
41
+ form = {}
42
+ add_invoice(form, options)
43
+ add_creditcard(form, creditcard)
44
+ add_address(form, options)
45
+ add_customer_data(form, options)
46
+ commit(:purchase, money, form)
47
+ end
48
+
49
+ # Make a credit to a card (Void can only be done from the virtual terminal)
50
+ # Viaklix does not support credits by reference. You must pass in the credit card
51
+ def credit(money, creditcard, options = {})
52
+ if creditcard.is_a?(String)
53
+ raise ArgumentError, "Reference credits are not supported. Please supply the original credit card"
54
+ end
55
+
56
+ form = {}
57
+ add_invoice(form, options)
58
+ add_creditcard(form, creditcard)
59
+ add_address(form, options)
60
+ add_customer_data(form, options)
61
+ commit(:credit, money, form)
62
+ end
63
+
64
+ private
65
+ def add_customer_data(form, options)
66
+ form[:email] = options[:email].to_s.slice(0, 100) unless options[:email].blank?
67
+ form[:customer_code] = options[:customer].to_s.slice(0, 10) unless options[:customer].blank?
68
+ end
69
+
70
+ def add_invoice(form,options)
71
+ form[:invoice_number] = (options[:order_id] || options[:invoice]).to_s.slice(0, 10)
72
+ form[:description] = options[:description].to_s.slice(0, 255)
73
+ end
74
+
75
+ def add_address(form,options)
76
+ billing_address = options[:billing_address] || options[:address]
77
+
78
+ if billing_address
79
+ form[:avs_address] = billing_address[:address1].to_s.slice(0, 30)
80
+ form[:address2] = billing_address[:address2].to_s.slice(0, 30)
81
+ form[:avs_zip] = billing_address[:zip].to_s.slice(0, 10)
82
+ form[:city] = billing_address[:city].to_s.slice(0, 30)
83
+ form[:state] = billing_address[:state].to_s.slice(0, 10)
84
+ form[:company] = billing_address[:company].to_s.slice(0, 50)
85
+ form[:phone] = billing_address[:phone].to_s.slice(0, 20)
86
+ form[:country] = billing_address[:country].to_s.slice(0, 50)
87
+ end
88
+
89
+ if shipping_address = options[:shipping_address]
90
+ first_name, last_name = parse_first_and_last_name(shipping_address[:name])
91
+ form[:ship_to_first_name] = first_name.to_s.slice(0, 20)
92
+ form[:ship_to_last_name] = last_name.to_s.slice(0, 30)
93
+ form[:ship_to_address] = shipping_address[:address1].to_s.slice(0, 30)
94
+ form[:ship_to_city] = shipping_address[:city].to_s.slice(0, 30)
95
+ form[:ship_to_state] = shipping_address[:state].to_s.slice(0, 10)
96
+ form[:ship_to_company] = shipping_address[:company].to_s.slice(0, 50)
97
+ form[:ship_to_country] = shipping_address[:country].to_s.slice(0, 50)
98
+ form[:ship_to_zip] = shipping_address[:zip].to_s.slice(0, 10)
99
+ end
100
+ end
101
+
102
+ def parse_first_and_last_name(value)
103
+ name = value.to_s.split(' ')
104
+
105
+ last_name = name.pop || ''
106
+ first_name = name.join(' ')
107
+ [ first_name, last_name ]
108
+ end
109
+
110
+ def add_creditcard(form, creditcard)
111
+ form[:card_number] = creditcard.number
112
+ form[:exp_date] = expdate(creditcard)
113
+
114
+ if creditcard.verification_value?
115
+ add_verification_value(form, creditcard)
116
+ end
117
+
118
+ form[:first_name] = creditcard.first_name.to_s.slice(0, 20)
119
+ form[:last_name] = creditcard.last_name.to_s.slice(0, 30)
120
+ end
121
+
122
+ def add_verification_value(form, creditcard)
123
+ form[:cvv2cvc2] = creditcard.verification_value
124
+ form[:cvv2] = 'present'
125
+ end
126
+
127
+ def preamble
128
+ result = {
129
+ 'merchant_id' => @options[:login],
130
+ 'pin' => @options[:password],
131
+ 'show_form' => 'false',
132
+ 'test_mode' => test? ? 'TRUE' : 'FALSE',
133
+ 'result_format' => 'ASCII',
134
+ }
135
+
136
+ result['user_id'] = @options[:user] unless @options[:user].blank?
137
+ result
138
+ end
139
+
140
+ def test?
141
+ @options[:test] || super
142
+ end
143
+
144
+ def commit(action, money, parameters)
145
+ parameters[:amount] = amount(money)
146
+ parameters[:transaction_type] = self.actions[action]
147
+
148
+ response = parse( ssl_post(test? ? self.test_url : self.live_url, post_data(parameters)) )
149
+
150
+ Response.new(response['result'] == APPROVED, message_from(response), response,
151
+ :test => @options[:test] || test?,
152
+ :authorization => authorization_from(response),
153
+ :avs_result => { :code => response['avs_response'] },
154
+ :cvv_result => response['cvv2_response']
155
+ )
156
+ end
157
+
158
+ def authorization_from(response)
159
+ response['txn_id']
160
+ end
161
+
162
+ def message_from(response)
163
+ response['result_message']
164
+ end
165
+
166
+ def post_data(parameters)
167
+ result = preamble
168
+ result.merge!(parameters)
169
+ result.collect { |key, value| "ssl_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
170
+ end
171
+
172
+ def expdate(creditcard)
173
+ year = sprintf("%.4i", creditcard.year)
174
+ month = sprintf("%.2i", creditcard.month)
175
+ "#{month}#{year[2..3]}"
176
+ end
177
+
178
+ # Parse the response message
179
+ def parse(msg)
180
+ resp = {}
181
+ msg.split(self.delimiter).collect{|li|
182
+ key, value = li.split("=")
183
+ resp[key.strip.gsub(/^ssl_/, '')] = value.to_s.strip
184
+ }
185
+ resp
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,318 @@
1
+ require 'base64'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class WirecardGateway < Gateway
6
+ # Test server location
7
+ TEST_URL = 'https://c3-test.wirecard.com/secure/ssl-gateway'
8
+
9
+ # Live server location
10
+ LIVE_URL = 'https://c3.wirecard.com/secure/ssl-gateway'
11
+
12
+ # The Namespaces are not really needed, because it just tells the System, that there's actually no namespace used.
13
+ # It's just specified here for completeness.
14
+ ENVELOPE_NAMESPACES = {
15
+ 'xmlns:xsi' => 'http://www.w3.org/1999/XMLSchema-instance',
16
+ 'xsi:noNamespaceSchemaLocation' => 'wirecard.xsd'
17
+ }
18
+
19
+ PERMITTED_TRANSACTIONS = %w[ AUTHORIZATION CAPTURE_AUTHORIZATION PURCHASE ]
20
+
21
+ RETURN_CODES = %w[ ACK NOK ]
22
+
23
+ # Wirecard only allows phone numbers with a format like this: +xxx(yyy)zzz-zzzz-ppp, where:
24
+ # xxx = Country code
25
+ # yyy = Area or city code
26
+ # zzz-zzzz = Local number
27
+ # ppp = PBX extension
28
+ # For example, a typical U.S. or Canadian number would be "+1(202)555-1234-739" indicating PBX extension 739 at phone
29
+ # number 5551234 within area code 202 (country code 1).
30
+ VALID_PHONE_FORMAT = /\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/
31
+
32
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
33
+ # TODO: Check supported countries
34
+ self.supported_countries = ['DE']
35
+
36
+ # Wirecard supports all major credit and debit cards:
37
+ # Visa, Mastercard, American Express, Diners Club,
38
+ # JCB, Switch, VISA Carte Bancaire, Visa Electron and UATP cards.
39
+ # They also support the latest anti-fraud systems such as Verified by Visa or Master Secure Code.
40
+ self.supported_cardtypes = [
41
+ :visa, :master, :american_express, :diners_club, :jcb, :switch
42
+ ]
43
+
44
+ # The homepage URL of the gateway
45
+ self.homepage_url = 'http://www.wirecard.com'
46
+
47
+ # The name of the gateway
48
+ self.display_name = 'Wirecard'
49
+
50
+ # The currency should normally be EUROs
51
+ self.default_currency = 'EUR'
52
+
53
+ # 100 is 1.00 Euro
54
+ self.money_format = :cents
55
+
56
+ def initialize(options = {})
57
+ # verify that username and password are supplied
58
+ requires!(options, :login, :password)
59
+ # unfortunately Wirecard also requires a BusinessCaseSignature in the XML request
60
+ requires!(options, :signature)
61
+ @options = options
62
+ super
63
+ end
64
+
65
+ # Should run against the test servers or not?
66
+ def test?
67
+ @options[:test] || super
68
+ end
69
+
70
+ # Authorization
71
+ def authorize(money, creditcard, options = {})
72
+ prepare_options_hash(options)
73
+ @options[:credit_card] = creditcard
74
+ request = build_request(:authorization, money, @options)
75
+ commit(request)
76
+ end
77
+
78
+
79
+ # Capture Authorization
80
+ def capture(money, authorization, options = {})
81
+ prepare_options_hash(options)
82
+ @options[:authorization] = authorization
83
+ request = build_request(:capture_authorization, money, @options)
84
+ commit(request)
85
+ end
86
+
87
+
88
+ # Purchase
89
+ def purchase(money, creditcard, options = {})
90
+ prepare_options_hash(options)
91
+ @options[:credit_card] = creditcard
92
+ request = build_request(:purchase, money, @options)
93
+ commit(request)
94
+ end
95
+
96
+ private
97
+
98
+ def prepare_options_hash(options)
99
+ @options.update(options)
100
+ setup_address_hash!(options)
101
+ end
102
+
103
+ # Create all address hash key value pairs so that
104
+ # it still works if only provided with one or two of them
105
+ def setup_address_hash!(options)
106
+ options[:billing_address] = options[:billing_address] || options[:address] || {}
107
+ options[:shipping_address] = options[:shipping_address] || {}
108
+ # Include Email in address-hash from options-hash
109
+ options[:billing_address][:email] = options[:email] if options[:email]
110
+ end
111
+
112
+ # Contact WireCard, make the XML request, and parse the
113
+ # reply into a Response object
114
+ def commit(request)
115
+ headers = { 'Content-Type' => 'text/xml',
116
+ 'Authorization' => encoded_credentials }
117
+
118
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, request, headers))
119
+ # Pending Status also means Acknowledged (as stated in their specification)
120
+ success = response[:FunctionResult] == "ACK" || response[:FunctionResult] == "PENDING"
121
+ message = response[:Message]
122
+ authorization = (success && @options[:action] == :authorization) ? response[:GuWID] : nil
123
+
124
+ Response.new(success, message, response,
125
+ :test => test?,
126
+ :authorization => authorization,
127
+ :avs_result => { :code => response[:avsCode] },
128
+ :cvv_result => response[:cvCode]
129
+ )
130
+ end
131
+
132
+ # Generates the complete xml-message, that gets sent to the gateway
133
+ def build_request(action, money, options = {})
134
+ xml = Builder::XmlMarkup.new :indent => 2
135
+ xml.instruct!
136
+ xml.tag! 'WIRECARD_BXML' do
137
+ xml.tag! 'W_REQUEST' do
138
+ xml.tag! 'W_JOB' do
139
+ # TODO: OPTIONAL, check what value needs to be insert here
140
+ xml.tag! 'JobID', 'test dummy data'
141
+ # UserID for this transaction
142
+ xml.tag! 'BusinessCaseSignature', options[:signature] || options[:login]
143
+ # Create the whole rest of the message
144
+ add_transaction_data(xml, action, money, options)
145
+ end
146
+ end
147
+ end
148
+ xml.target!
149
+ end
150
+
151
+ # Includes the whole transaction data (payment, creditcard, address)
152
+ def add_transaction_data(xml, action, money, options = {})
153
+ options[:action] = action
154
+ # TODO: require order_id instead of auto-generating it if not supplied
155
+ options[:order_id] ||= generate_unique_id
156
+ transaction_type = action.to_s.upcase
157
+
158
+ xml.tag! "FNC_CC_#{transaction_type}" do
159
+ # TODO: OPTIONAL, check which param should be used here
160
+ xml.tag! 'FunctionID', options[:description] || 'Test dummy FunctionID'
161
+
162
+ xml.tag! 'CC_TRANSACTION' do
163
+ xml.tag! 'TransactionID', options[:order_id]
164
+ if [:authorization, :purchase].include?(action)
165
+ add_invoice(xml, money, options)
166
+ add_creditcard(xml, options[:credit_card])
167
+ add_address(xml, options[:billing_address])
168
+ elsif action == :capture_authorization
169
+ xml.tag! 'GuWID', options[:authorization] if options[:authorization]
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ # Includes the payment (amount, currency, country) to the transaction-xml
176
+ def add_invoice(xml, money, options)
177
+ xml.tag! 'Amount', amount(money)
178
+ xml.tag! 'Currency', options[:currency] || currency(money)
179
+ xml.tag! 'CountryCode', options[:billing_address][:country]
180
+ xml.tag! 'RECURRING_TRANSACTION' do
181
+ xml.tag! 'Type', options[:recurring] || 'Single'
182
+ end
183
+ end
184
+
185
+ # Includes the credit-card data to the transaction-xml
186
+ def add_creditcard(xml, creditcard)
187
+ raise "Creditcard must be supplied!" if creditcard.nil?
188
+ xml.tag! 'CREDIT_CARD_DATA' do
189
+ xml.tag! 'CreditCardNumber', creditcard.number
190
+ xml.tag! 'CVC2', creditcard.verification_value
191
+ xml.tag! 'ExpirationYear', creditcard.year
192
+ xml.tag! 'ExpirationMonth', format(creditcard.month, :two_digits)
193
+ xml.tag! 'CardHolderName', [creditcard.first_name, creditcard.last_name].join(' ')
194
+ end
195
+ end
196
+
197
+ # Includes the IP address of the customer to the transaction-xml
198
+ def add_customer_data(xml, options)
199
+ return unless options[:ip]
200
+ xml.tag! 'CONTACT_DATA' do
201
+ xml.tag! 'IPAddress', options[:ip]
202
+ end
203
+ end
204
+
205
+ # Includes the address to the transaction-xml
206
+ def add_address(xml, address)
207
+ return if address.nil?
208
+ xml.tag! 'CORPTRUSTCENTER_DATA' do
209
+ xml.tag! 'ADDRESS' do
210
+ xml.tag! 'Address1', address[:address1]
211
+ xml.tag! 'Address2', address[:address2] if address[:address2]
212
+ xml.tag! 'City', address[:city]
213
+ xml.tag! 'ZipCode', address[:zip]
214
+
215
+ if address[:state] =~ /[A-Za-z]{2}/ && address[:country] =~ /^(us|ca)$/i
216
+ xml.tag! 'State', address[:state].upcase
217
+ end
218
+
219
+ xml.tag! 'Country', address[:country]
220
+ xml.tag! 'Phone', address[:phone] if address[:phone] =~ VALID_PHONE_FORMAT
221
+ xml.tag! 'Email', address[:email]
222
+ end
223
+ end
224
+ end
225
+
226
+
227
+ # Read the XML message from the gateway and check if it was successful,
228
+ # and also extract required return values from the response.
229
+ def parse(xml)
230
+ basepath = '/WIRECARD_BXML/W_RESPONSE'
231
+ response = {}
232
+
233
+ xml = REXML::Document.new(xml)
234
+ if root = REXML::XPath.first(xml, "#{basepath}/W_JOB")
235
+ parse_response(response, root)
236
+ elsif root = REXML::XPath.first(xml, "//ERROR")
237
+ parse_error(response, root)
238
+ else
239
+ response[:Message] = "No valid XML response message received. \
240
+ Propably wrong credentials supplied with HTTP header."
241
+ end
242
+
243
+ response
244
+ end
245
+
246
+ # Parse the <ProcessingStatus> Element which containts all important information
247
+ def parse_response(response, root)
248
+ status = nil
249
+ # get the root element for this Transaction
250
+ root.elements.to_a.each do |node|
251
+ if node.name =~ /FNC_CC_/
252
+ status = REXML::XPath.first(node, "CC_TRANSACTION/PROCESSING_STATUS")
253
+ end
254
+ end
255
+ message = ""
256
+ if status
257
+ if info = status.elements['Info']
258
+ message << info.text
259
+ end
260
+ # Get basic response information
261
+ status.elements.to_a.each do |node|
262
+ response[node.name.to_sym] = (node.text || '').strip
263
+ end
264
+ end
265
+ parse_error(root, message)
266
+ response[:Message] = message
267
+ end
268
+
269
+ # Parse a generic error response from the gateway
270
+ def parse_error(root, message = "")
271
+ # Get errors if available and append them to the message
272
+ errors = errors_to_string(root)
273
+ unless errors.strip.blank?
274
+ message << ' - ' unless message.strip.blank?
275
+ message << errors
276
+ end
277
+ message
278
+ end
279
+
280
+ # Parses all <ERROR> elements in the response and converts the information
281
+ # to a single string
282
+ def errors_to_string(root)
283
+ # Get context error messages (can be 0..*)
284
+ errors = []
285
+ REXML::XPath.each(root, "//ERROR") do |error_elem|
286
+ error = {}
287
+ error[:Advice] = []
288
+ error[:Message] = error_elem.elements['Message'].text
289
+ error_elem.elements.each('Advice') do |advice|
290
+ error[:Advice] << advice.text
291
+ end
292
+ errors << error
293
+ end
294
+ # Convert all messages to a single string
295
+ string = ''
296
+ errors.each do |error|
297
+ string << error[:Message]
298
+ error[:Advice].each_with_index do |advice, index|
299
+ string << ' (' if index == 0
300
+ string << "#{index+1}. #{advice}"
301
+ string << ' and ' if index < error[:Advice].size - 1
302
+ string << ')' if index == error[:Advice].size - 1
303
+ end
304
+ end
305
+ string
306
+ end
307
+
308
+ # Encode login and password in Base64 to supply as HTTP header
309
+ # (for http basic authentication)
310
+ def encoded_credentials
311
+ credentials = [@options[:login], @options[:password]].join(':')
312
+ "Basic " << Base64.encode64(credentials).strip
313
+ end
314
+
315
+ end
316
+ end
317
+ end
318
+