aktivemerchant 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.
Files changed (193) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1596 -0
  3. data/CONTRIBUTORS +511 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +18 -0
  6. data/lib/active_merchant.rb +108 -0
  7. data/lib/active_merchant/billing.rb +13 -0
  8. data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
  9. data/lib/active_merchant/billing/avs_result.rb +98 -0
  10. data/lib/active_merchant/billing/base.rb +72 -0
  11. data/lib/active_merchant/billing/check.rb +76 -0
  12. data/lib/active_merchant/billing/compatibility.rb +120 -0
  13. data/lib/active_merchant/billing/credit_card.rb +352 -0
  14. data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
  15. data/lib/active_merchant/billing/credit_card_methods.rb +160 -0
  16. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  17. data/lib/active_merchant/billing/gateway.rb +268 -0
  18. data/lib/active_merchant/billing/gateways.rb +17 -0
  19. data/lib/active_merchant/billing/gateways/adyen.rb +209 -0
  20. data/lib/active_merchant/billing/gateways/alfabank.rb +117 -0
  21. data/lib/active_merchant/billing/gateways/app55.rb +176 -0
  22. data/lib/active_merchant/billing/gateways/authorize_net.rb +419 -0
  23. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
  24. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
  25. data/lib/active_merchant/billing/gateways/balanced.rb +256 -0
  26. data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
  27. data/lib/active_merchant/billing/gateways/banwire.rb +105 -0
  28. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +314 -0
  29. data/lib/active_merchant/billing/gateways/barclays_epdq_extra_plus.rb +15 -0
  30. data/lib/active_merchant/billing/gateways/be2bill.rb +131 -0
  31. data/lib/active_merchant/billing/gateways/beanstream.rb +188 -0
  32. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +393 -0
  33. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  34. data/lib/active_merchant/billing/gateways/blue_pay.rb +506 -0
  35. data/lib/active_merchant/billing/gateways/bogus.rb +140 -0
  36. data/lib/active_merchant/billing/gateways/borgun.rb +210 -0
  37. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  38. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  39. data/lib/active_merchant/billing/gateways/braintree_blue.rb +515 -0
  40. data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
  41. data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
  42. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  43. data/lib/active_merchant/billing/gateways/card_stream.rb +220 -0
  44. data/lib/active_merchant/billing/gateways/cashnet.rb +191 -0
  45. data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
  46. data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
  47. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  48. data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
  49. data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
  50. data/lib/active_merchant/billing/gateways/conekta.rb +209 -0
  51. data/lib/active_merchant/billing/gateways/cyber_source.rb +709 -0
  52. data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
  53. data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
  54. data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
  55. data/lib/active_merchant/billing/gateways/epay.rb +275 -0
  56. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  57. data/lib/active_merchant/billing/gateways/eway.rb +214 -0
  58. data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
  59. data/lib/active_merchant/billing/gateways/eway_rapid.rb +524 -0
  60. data/lib/active_merchant/billing/gateways/exact.rb +218 -0
  61. data/lib/active_merchant/billing/gateways/fat_zebra.rb +173 -0
  62. data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
  63. data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
  64. data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
  65. data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
  66. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +355 -0
  67. data/lib/active_merchant/billing/gateways/garanti.rb +257 -0
  68. data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
  69. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  70. data/lib/active_merchant/billing/gateways/hps.rb +288 -0
  71. data/lib/active_merchant/billing/gateways/iats_payments.rb +251 -0
  72. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
  73. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  74. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  75. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
  76. data/lib/active_merchant/billing/gateways/inspire.rb +213 -0
  77. data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
  78. data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
  79. data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
  80. data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
  81. data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
  82. data/lib/active_merchant/billing/gateways/litle.rb +346 -0
  83. data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
  84. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
  85. data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
  86. data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
  87. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
  88. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
  89. data/lib/active_merchant/billing/gateways/mercury.rb +333 -0
  90. data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
  91. data/lib/active_merchant/billing/gateways/migs.rb +265 -0
  92. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  93. data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
  94. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
  95. data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
  96. data/lib/active_merchant/billing/gateways/moneris_us.rb +291 -0
  97. data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
  98. data/lib/active_merchant/billing/gateways/nab_transact.rb +280 -0
  99. data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
  100. data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
  101. data/lib/active_merchant/billing/gateways/netbilling.rb +190 -0
  102. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  103. data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
  104. data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
  105. data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
  106. data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
  107. data/lib/active_merchant/billing/gateways/optimal_payment.rb +313 -0
  108. data/lib/active_merchant/billing/gateways/orbital.rb +803 -0
  109. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
  110. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
  111. data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
  112. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +261 -0
  113. data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
  114. data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
  115. data/lib/active_merchant/billing/gateways/pay_u_latam.rb +462 -0
  116. data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
  117. data/lib/active_merchant/billing/gateways/payex.rb +412 -0
  118. data/lib/active_merchant/billing/gateways/payflow.rb +304 -0
  119. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +209 -0
  120. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  121. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  122. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  123. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  124. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  125. data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
  126. data/lib/active_merchant/billing/gateways/paymill.rb +281 -0
  127. data/lib/active_merchant/billing/gateways/paypal.rb +117 -0
  128. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +670 -0
  129. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
  130. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
  131. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  132. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
  133. data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
  134. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  135. data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
  136. data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
  137. data/lib/active_merchant/billing/gateways/payway.rb +207 -0
  138. data/lib/active_merchant/billing/gateways/pin.rb +197 -0
  139. data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
  140. data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
  141. data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
  142. data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
  143. data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
  144. data/lib/active_merchant/billing/gateways/quickpay.rb +367 -0
  145. data/lib/active_merchant/billing/gateways/realex.rb +298 -0
  146. data/lib/active_merchant/billing/gateways/redsys.rb +391 -0
  147. data/lib/active_merchant/billing/gateways/sage.rb +175 -0
  148. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +87 -0
  149. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +114 -0
  150. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
  151. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +102 -0
  152. data/lib/active_merchant/billing/gateways/sage_pay.rb +398 -0
  153. data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
  154. data/lib/active_merchant/billing/gateways/secure_net.rb +252 -0
  155. data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
  156. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  157. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
  158. data/lib/active_merchant/billing/gateways/skip_jack.rb +452 -0
  159. data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
  160. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
  161. data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
  162. data/lib/active_merchant/billing/gateways/stripe.rb +411 -0
  163. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
  164. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  165. data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
  166. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  167. data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
  168. data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
  169. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  170. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
  171. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +254 -0
  172. data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
  173. data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
  174. data/lib/active_merchant/billing/gateways/vindicia.rb +385 -0
  175. data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
  176. data/lib/active_merchant/billing/gateways/wepay.rb +189 -0
  177. data/lib/active_merchant/billing/gateways/wirecard.rb +421 -0
  178. data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
  179. data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
  180. data/lib/active_merchant/billing/model.rb +30 -0
  181. data/lib/active_merchant/billing/payment_token.rb +21 -0
  182. data/lib/active_merchant/billing/rails.rb +3 -0
  183. data/lib/active_merchant/billing/response.rb +91 -0
  184. data/lib/active_merchant/country.rb +332 -0
  185. data/lib/active_merchant/empty.rb +20 -0
  186. data/lib/active_merchant/errors.rb +29 -0
  187. data/lib/active_merchant/offsite_payments_shim.rb +19 -0
  188. data/lib/active_merchant/version.rb +3 -0
  189. data/lib/activemerchant.rb +1 -0
  190. data/lib/support/gateway_support.rb +71 -0
  191. data/lib/support/outbound_hosts.rb +25 -0
  192. data/lib/support/ssl_verify.rb +93 -0
  193. metadata +400 -0
@@ -0,0 +1,195 @@
1
+ require 'digest/md5'
2
+ require 'rexml/document'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class MerchantWarriorGateway < Gateway
7
+ TOKEN_TEST_URL = 'https://base.merchantwarrior.com/token/'
8
+ TOKEN_LIVE_URL = 'https://api.merchantwarrior.com/token/'
9
+
10
+ POST_TEST_URL = 'https://base.merchantwarrior.com/post/'
11
+ POST_LIVE_URL = 'https://api.merchantwarrior.com/post/'
12
+
13
+ self.supported_countries = ['AU']
14
+ self.supported_cardtypes = [:visa, :master, :american_express,
15
+ :diners_club, :discover]
16
+ self.homepage_url = 'http://www.merchantwarrior.com/'
17
+ self.display_name = 'MerchantWarrior'
18
+
19
+ self.money_format = :dollars
20
+ self.default_currency = 'AUD'
21
+
22
+ def initialize(options = {})
23
+ requires!(options, :merchant_uuid, :api_key, :api_passphrase)
24
+ super
25
+ end
26
+
27
+ def authorize(money, payment_method, options = {})
28
+ post = {}
29
+ add_amount(post, money, options)
30
+ add_product(post, options)
31
+ add_address(post, options)
32
+ add_payment_method(post, payment_method)
33
+ commit('processAuth', post)
34
+ end
35
+
36
+ def purchase(money, payment_method, options = {})
37
+ post = {}
38
+ add_amount(post, money, options)
39
+ add_product(post, options)
40
+ add_address(post, options)
41
+ add_payment_method(post, payment_method)
42
+ commit('processCard', post)
43
+ end
44
+
45
+ def capture(money, identification)
46
+ post = {}
47
+ add_amount(post, money, options)
48
+ add_transaction(post, identification)
49
+ post.merge!('captureAmount' => amount(money))
50
+ commit('processCapture', post)
51
+ end
52
+
53
+ def refund(money, identification)
54
+ post = {}
55
+ add_amount(post, money, options)
56
+ add_transaction(post, identification)
57
+ post['refundAmount'] = amount(money)
58
+ commit('refundCard', post)
59
+ end
60
+
61
+ def store(creditcard, options = {})
62
+ post = {
63
+ 'cardName' => scrub_name(creditcard.name),
64
+ 'cardNumber' => creditcard.number,
65
+ 'cardExpiryMonth' => format(creditcard.month, :two_digits),
66
+ 'cardExpiryYear' => format(creditcard.year, :two_digits)
67
+ }
68
+ commit('addCard', post)
69
+ end
70
+
71
+ private
72
+
73
+ def add_transaction(post, identification)
74
+ post['transactionID'] = identification
75
+ end
76
+
77
+ def add_address(post, options)
78
+ return unless(address = (options[:billing_address] || options[:address]))
79
+
80
+ post['customerName'] = scrub_name(address[:name])
81
+ post['customerCountry'] = address[:country]
82
+ post['customerState'] = address[:state]
83
+ post['customerCity'] = address[:city]
84
+ post['customerAddress'] = address[:address1]
85
+ post['customerPostCode'] = address[:zip]
86
+ end
87
+
88
+ def add_product(post, options)
89
+ post['transactionProduct'] = options[:description]
90
+ end
91
+
92
+ def add_payment_method(post, payment_method)
93
+ if payment_method.respond_to?(:number)
94
+ add_creditcard(post, payment_method)
95
+ else
96
+ add_token(post, payment_method)
97
+ end
98
+ end
99
+
100
+ def add_token(post, token)
101
+ post['cardID'] = token
102
+ end
103
+
104
+ def add_creditcard(post, creditcard)
105
+ post['paymentCardNumber'] = creditcard.number
106
+ post['paymentCardName'] = scrub_name(creditcard.name)
107
+ post['paymentCardExpiry'] = creditcard.expiry_date.expiration.strftime("%m%y")
108
+ post['paymentCardCSC'] = creditcard.verification_value if creditcard.verification_value?
109
+ end
110
+
111
+ def scrub_name(name)
112
+ name.gsub(/[^a-zA-Z\. -]/, '')
113
+ end
114
+
115
+ def add_amount(post, money, options)
116
+ currency = (options[:currency] || currency(money))
117
+
118
+ post['transactionAmount'] = amount(money)
119
+ post['transactionCurrency'] = currency
120
+ post['hash'] = verification_hash(amount(money), currency)
121
+ end
122
+
123
+ def verification_hash(money, currency)
124
+ Digest::MD5.hexdigest(
125
+ (
126
+ @options[:api_passphrase].to_s +
127
+ @options[:merchant_uuid].to_s +
128
+ money.to_s +
129
+ currency
130
+ ).downcase
131
+ )
132
+ end
133
+
134
+ def parse(body)
135
+ xml = REXML::Document.new(body)
136
+
137
+ response = {}
138
+ xml.root.elements.to_a.each do |node|
139
+ parse_element(response, node)
140
+ end
141
+ response
142
+ end
143
+
144
+ def parse_element(response, node)
145
+ if node.has_elements?
146
+ node.elements.each{|element| parse_element(response, element)}
147
+ else
148
+ response[node.name.underscore.to_sym] = node.text
149
+ end
150
+ end
151
+
152
+ def commit(action, post)
153
+ add_auth(action, post)
154
+
155
+ response = parse(ssl_post(url_for(action, post), post_data(post)))
156
+
157
+ Response.new(
158
+ success?(response),
159
+ response[:response_message],
160
+ response,
161
+ :test => test?,
162
+ :authorization => (response[:card_id] || response[:transaction_id])
163
+ )
164
+ end
165
+
166
+ def add_auth(action, post)
167
+ post['merchantUUID'] = @options[:merchant_uuid]
168
+ post['apiKey'] = @options[:api_key]
169
+ unless token?(post)
170
+ post['method'] = action
171
+ end
172
+ end
173
+
174
+ def url_for(action, post)
175
+ if token?(post)
176
+ [(test? ? TOKEN_TEST_URL : TOKEN_LIVE_URL), action].join("/")
177
+ else
178
+ (test? ? POST_TEST_URL : POST_LIVE_URL)
179
+ end
180
+ end
181
+
182
+ def token?(post)
183
+ (post["cardID"] || post["cardName"])
184
+ end
185
+
186
+ def success?(response)
187
+ (response[:response_code] == '0')
188
+ end
189
+
190
+ def post_data(post)
191
+ post.collect{|k,v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&")
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,333 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # The Mercury gateway integration by default requires that the Mercury
4
+ # account being used has tokenization turned. This enables the use of
5
+ # capture/refund/void without having to pass the credit card back in each
6
+ # time. Only the "OneTime" tokenization is used; there is no use of
7
+ # "Recurring" tokenization.
8
+ #
9
+ # If you don't wish to enable Mercury tokenization, you can pass
10
+ # <code>:tokenization => false</code> as an option when creating the
11
+ # gateway. If you do so, then passing a +:credit_card+ option to +capture+
12
+ # and +refund+ will become mandatory.
13
+ class MercuryGateway < Gateway
14
+ URLS = {
15
+ :test => 'https://w1.mercurydev.net/ws/ws.asmx',
16
+ :live => 'https://w1.mercurypay.com/ws/ws.asmx'
17
+ }
18
+
19
+ self.homepage_url = 'http://www.mercurypay.com'
20
+ self.display_name = 'Mercury'
21
+ self.supported_countries = ['US']
22
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
23
+ self.default_currency = 'USD'
24
+
25
+ STANDARD_ERROR_CODE_MAPPING = {
26
+ '100204' => STANDARD_ERROR_CODE[:invalid_number],
27
+ '100205' => STANDARD_ERROR_CODE[:invalid_expiry_date],
28
+ '000000' => STANDARD_ERROR_CODE[:card_declined]
29
+ }
30
+
31
+ def initialize(options = {})
32
+ requires!(options, :login, :password)
33
+ @use_tokenization = (!options.has_key?(:tokenization) || options[:tokenization])
34
+ super
35
+ end
36
+
37
+ def purchase(money, credit_card, options = {})
38
+ requires!(options, :order_id)
39
+
40
+ request = build_non_authorized_request('Sale', money, credit_card, options)
41
+ commit('Sale', request)
42
+ end
43
+
44
+ def credit(money, credit_card, options = {})
45
+ requires!(options, :order_id)
46
+
47
+ request = build_non_authorized_request('Return', money, credit_card, options)
48
+ commit('Return', request)
49
+ end
50
+
51
+ def authorize(money, credit_card, options = {})
52
+ requires!(options, :order_id)
53
+
54
+ request = build_non_authorized_request('PreAuth', money, credit_card, options.merge(:authorized => money))
55
+ commit('PreAuth', request)
56
+ end
57
+
58
+ def capture(money, authorization, options = {})
59
+ requires!(options, :credit_card) unless @use_tokenization
60
+
61
+ request = build_authorized_request('PreAuthCapture', money, authorization, options[:credit_card], options.merge(:authorized => money))
62
+ commit('PreAuthCapture', request)
63
+ end
64
+
65
+ def refund(money, authorization, options = {})
66
+ requires!(options, :credit_card) unless @use_tokenization
67
+
68
+ request = build_authorized_request('Return', money, authorization, options[:credit_card], options)
69
+ commit('Return', request)
70
+ end
71
+
72
+ def void(authorization, options={})
73
+ requires!(options, :credit_card) unless @use_tokenization
74
+
75
+ if options[:try_reversal]
76
+ request = build_authorized_request('VoidSale', nil, authorization, options[:credit_card], options.merge(:reversal => true))
77
+ response = commit('VoidSale', request)
78
+
79
+ return response if response.success?
80
+ end
81
+
82
+ request = build_authorized_request('VoidSale', nil, authorization, options[:credit_card], options)
83
+ commit('VoidSale', request)
84
+ end
85
+
86
+ def store(credit_card, options={})
87
+ request = build_card_lookup_request(credit_card, options)
88
+ commit('CardLookup', request)
89
+ end
90
+
91
+ private
92
+
93
+ def build_non_authorized_request(action, money, credit_card, options)
94
+ xml = Builder::XmlMarkup.new
95
+
96
+ xml.tag! "TStream" do
97
+ xml.tag! "Transaction" do
98
+ xml.tag! 'TranType', 'Credit'
99
+ xml.tag! 'TranCode', action
100
+ if action == 'PreAuth' || action == 'Sale'
101
+ xml.tag! "PartialAuth", "Allow"
102
+ end
103
+ add_invoice(xml, options[:order_id], nil, options)
104
+ add_reference(xml, "RecordNumberRequested")
105
+ add_customer_data(xml, options)
106
+ add_amount(xml, money, options)
107
+ add_credit_card(xml, credit_card, action)
108
+ add_address(xml, options) unless credit_card.track_data.present?
109
+ end
110
+ end
111
+ xml = xml.target!
112
+ end
113
+
114
+ def build_authorized_request(action, money, authorization, credit_card, options)
115
+ xml = Builder::XmlMarkup.new
116
+
117
+ invoice_no, ref_no, auth_code, acq_ref_data, process_data, record_no, amount = split_authorization(authorization)
118
+ ref_no = "1" if options[:reversal] #filler value for preauth voids -- not used by mercury but will reject if missing or not numeric
119
+
120
+ xml.tag! "TStream" do
121
+ xml.tag! "Transaction" do
122
+ xml.tag! 'TranType', 'Credit'
123
+ if action == 'PreAuthCapture'
124
+ xml.tag! "PartialAuth", "Allow"
125
+ end
126
+ xml.tag! 'TranCode', (@use_tokenization ? (action + "ByRecordNo") : action)
127
+ add_invoice(xml, invoice_no, ref_no, options)
128
+ add_reference(xml, record_no)
129
+ add_customer_data(xml, options)
130
+ add_amount(xml, (money || amount.to_i), options)
131
+ add_credit_card(xml, credit_card, action) if credit_card
132
+ add_address(xml, options)
133
+ xml.tag! 'TranInfo' do
134
+ xml.tag! "AuthCode", auth_code
135
+ xml.tag! "AcqRefData", acq_ref_data if options[:reversal]
136
+ xml.tag! "ProcessData", process_data if options[:reversal]
137
+ end
138
+ end
139
+ end
140
+ xml = xml.target!
141
+ end
142
+
143
+ def build_card_lookup_request(credit_card, options)
144
+ xml = Builder::XmlMarkup.new
145
+
146
+ xml.tag! "TStream" do
147
+ xml.tag! "Transaction" do
148
+ xml.tag! 'TranType', 'CardLookup'
149
+ xml.tag! 'RecordNo', 'RecordNumberRequested'
150
+ xml.tag! 'Frequency', 'OneTime'
151
+
152
+ xml.tag! 'Memo', options[:description]
153
+ add_customer_data(xml, options)
154
+ add_credit_card(xml, credit_card, options)
155
+ end
156
+ end
157
+ xml.target!
158
+ end
159
+
160
+ def add_invoice(xml, invoice_no, ref_no, options)
161
+ xml.tag! 'InvoiceNo', invoice_no
162
+ xml.tag! 'RefNo', (ref_no || invoice_no)
163
+ xml.tag! 'OperatorID', options[:merchant] if options[:merchant]
164
+ xml.tag! 'Memo', options[:description] if options[:description]
165
+ end
166
+
167
+ def add_reference(xml, record_no)
168
+ if @use_tokenization
169
+ xml.tag! "Frequency", "OneTime"
170
+ xml.tag! "RecordNo", record_no
171
+ end
172
+ end
173
+
174
+ def add_customer_data(xml, options)
175
+ xml.tag! 'IpAddress', options[:ip] if options[:ip]
176
+ if options[:customer]
177
+ xml.tag! "TranInfo" do
178
+ xml.tag! 'CustomerCode', options[:customer]
179
+ end
180
+ end
181
+ xml.tag! 'MerchantID', @options[:login]
182
+ end
183
+
184
+ def add_amount(xml, money, options = {})
185
+ xml.tag! 'Amount' do
186
+ xml.tag! 'Purchase', amount(money)
187
+ xml.tag! 'Tax', options[:tax] if options[:tax]
188
+ xml.tag! 'Authorize', amount(options[:authorized]) if options[:authorized]
189
+ xml.tag! 'Gratuity', amount(options[:tip]) if options[:tip]
190
+ end
191
+ end
192
+
193
+ CARD_CODES = {
194
+ 'visa' => 'VISA',
195
+ 'master' => 'M/C',
196
+ 'american_express' => 'AMEX',
197
+ 'discover' => 'DCVR',
198
+ 'diners_club' => 'DCLB',
199
+ 'jcb' => 'JCB'
200
+ }
201
+
202
+ def add_credit_card(xml, credit_card, action)
203
+ xml.tag! 'Account' do
204
+ if credit_card.track_data.present?
205
+ xml.tag! 'Track1', credit_card.track_data
206
+ else
207
+ xml.tag! 'AcctNo', credit_card.number
208
+ xml.tag! 'ExpDate', expdate(credit_card)
209
+ end
210
+ end
211
+ xml.tag! 'CardType', CARD_CODES[credit_card.brand] if credit_card.brand
212
+
213
+ include_cvv = !%w(Return PreAuthCapture).include?(action) && !credit_card.track_data.present?
214
+ xml.tag! 'CVVData', credit_card.verification_value if(include_cvv && credit_card.verification_value)
215
+ end
216
+
217
+ def add_address(xml, options)
218
+ if billing_address = options[:billing_address] || options[:address]
219
+ xml.tag! 'AVS' do
220
+ xml.tag! 'Address', billing_address[:address1]
221
+ xml.tag! 'Zip', billing_address[:zip]
222
+ end
223
+ end
224
+ end
225
+
226
+ def parse(action, body)
227
+ response = {}
228
+ hashify_xml!(unescape_xml(body), response)
229
+ response
230
+ end
231
+
232
+ def hashify_xml!(xml, response)
233
+ xml = REXML::Document.new(xml)
234
+
235
+ xml.elements.each("//CmdResponse/*") do |node|
236
+ response[node.name.underscore.to_sym] = node.text
237
+ end
238
+
239
+ xml.elements.each("//TranResponse/*") do |node|
240
+ if node.name.to_s == "Amount"
241
+ node.elements.each do |amt|
242
+ response[amt.name.underscore.to_sym] = amt.text
243
+ end
244
+ else
245
+ response[node.name.underscore.to_sym] = node.text
246
+ end
247
+ end
248
+ end
249
+
250
+ def endpoint_url
251
+ URLS[test? ? :test : :live]
252
+ end
253
+
254
+ def build_soap_request(body)
255
+ xml = Builder::XmlMarkup.new
256
+
257
+ xml.instruct!
258
+ xml.tag! 'soap:Envelope', ENVELOPE_NAMESPACES do
259
+ xml.tag! 'soap:Body' do
260
+ xml.tag! 'CreditTransaction', 'xmlns' => homepage_url do
261
+ xml.tag! 'tran' do
262
+ xml << escape_xml(body)
263
+ end
264
+ xml.tag! 'pw', @options[:password]
265
+ end
266
+ end
267
+ end
268
+ xml.target!
269
+ end
270
+
271
+ def build_header
272
+ {
273
+ "SOAPAction" => "http://www.mercurypay.com/CreditTransaction",
274
+ "Content-Type" => "text/xml; charset=utf-8"
275
+ }
276
+ end
277
+
278
+ SUCCESS_CODES = [ 'Approved', 'Success' ]
279
+
280
+ def commit(action, request)
281
+ response = parse(action, ssl_post(endpoint_url, build_soap_request(request), build_header))
282
+
283
+ success = SUCCESS_CODES.include?(response[:cmd_status])
284
+ message = success ? 'Success' : message_from(response)
285
+
286
+ Response.new(success, message, response,
287
+ :test => test?,
288
+ :authorization => authorization_from(response),
289
+ :avs_result => { :code => response[:avs_result] },
290
+ :cvv_result => response[:cvv_result],
291
+ :error_code => success ? nil : STANDARD_ERROR_CODE_MAPPING[response[:dsix_return_code]])
292
+ end
293
+
294
+ def message_from(response)
295
+ response[:text_response]
296
+ end
297
+
298
+ def authorization_from(response)
299
+ dollars, cents = (response[:purchase] || "").split(".").collect{|e| e.to_i}
300
+ dollars ||= 0
301
+ cents ||= 0
302
+ [
303
+ response[:invoice_no],
304
+ response[:ref_no],
305
+ response[:auth_code],
306
+ response[:acq_ref_data],
307
+ response[:process_data],
308
+ response[:record_no],
309
+ ((dollars * 100) + cents).to_s
310
+ ].join(";")
311
+ end
312
+
313
+ def split_authorization(authorization)
314
+ invoice_no, ref_no, auth_code, acq_ref_data, process_data, record_no, amount = authorization.split(";")
315
+ [invoice_no, ref_no, auth_code, acq_ref_data, process_data, record_no, amount]
316
+ end
317
+
318
+ ENVELOPE_NAMESPACES = {
319
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
320
+ 'xmlns:soap' => "http://schemas.xmlsoap.org/soap/envelope/",
321
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
322
+ }
323
+
324
+ def escape_xml(xml)
325
+ "\n<![CDATA[\n#{xml}\n]]>\n"
326
+ end
327
+
328
+ def unescape_xml(escaped_xml)
329
+ escaped_xml.gsub(/\&gt;/,'>').gsub(/\&lt;/,'<')
330
+ end
331
+ end
332
+ end
333
+ end