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,291 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+
6
+ # To learn more about the Moneris (US) gateway, please contact
7
+ # ussales@moneris.com for a copy of their integration guide. For
8
+ # information on remote testing, please see "Test Environment Penny Value
9
+ # Response Table", and "Test Environment eFraud (AVS and CVD) Penny
10
+ # Response Values", available at Moneris' {eSelect Plus Documentation
11
+ # Centre}[https://www3.moneris.com/connect/en/documents/index.html].
12
+ class MonerisUsGateway < Gateway
13
+ self.test_url = 'https://esplusqa.moneris.com/gateway_us/servlet/MpgRequest'
14
+ self.live_url = 'https://esplus.moneris.com/gateway_us/servlet/MpgRequest'
15
+
16
+ self.supported_countries = ['US']
17
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover]
18
+ self.homepage_url = 'http://www.monerisusa.com/'
19
+ self.display_name = 'Moneris (US)'
20
+
21
+ # Initialize the Gateway
22
+ #
23
+ # The gateway requires that a valid login and password be passed
24
+ # in the +options+ hash.
25
+ #
26
+ # ==== Options
27
+ #
28
+ # * <tt>:login</tt> -- Your Store ID
29
+ # * <tt>:password</tt> -- Your API Token
30
+ # * <tt>:cvv_enabled</tt> -- Specify that you would like the CVV passed to the gateway.
31
+ # Only particular account types at Moneris will allow this.
32
+ # Defaults to false. (optional)
33
+ def initialize(options = {})
34
+ requires!(options, :login, :password)
35
+ @cvv_enabled = options[:cvv_enabled]
36
+ @avs_enabled = options[:avs_enabled]
37
+ options = { :crypt_type => 7 }.merge(options)
38
+ super
39
+ end
40
+
41
+ # Referred to as "PreAuth" in the Moneris integration guide, this action
42
+ # verifies and locks funds on a customer's card, which then must be
43
+ # captured at a later date.
44
+ #
45
+ # Pass in +order_id+ and optionally a +customer+ parameter.
46
+ def authorize(money, creditcard_or_datakey, options = {})
47
+ requires!(options, :order_id)
48
+ post = {}
49
+ add_payment_source(post, creditcard_or_datakey, options)
50
+ post[:amount] = amount(money)
51
+ post[:order_id] = options[:order_id]
52
+ post[:address] = options[:billing_address] || options[:address]
53
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
54
+ action = (post[:data_key].blank?) ? 'us_preauth' : 'us_res_preauth_cc'
55
+ commit(action, post)
56
+ end
57
+
58
+ # This action verifies funding on a customer's card and readies them for
59
+ # deposit in a merchant's account.
60
+ #
61
+ # Pass in <tt>order_id</tt> and optionally a <tt>customer</tt> parameter
62
+ def purchase(money, creditcard_or_datakey, options = {})
63
+ requires!(options, :order_id)
64
+ post = {}
65
+ add_payment_source(post, creditcard_or_datakey, options)
66
+ post[:amount] = amount(money)
67
+ post[:order_id] = options[:order_id]
68
+ post[:address] = options[:billing_address] || options[:address]
69
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
70
+ action = (post[:data_key].blank?) ? 'us_purchase' : 'us_res_purchase_cc'
71
+ commit(action, post)
72
+ end
73
+
74
+ # This method retrieves locked funds from a customer's account (from a
75
+ # PreAuth) and prepares them for deposit in a merchant's account.
76
+ #
77
+ # Note: Moneris requires both the order_id and the transaction number of
78
+ # the original authorization. To maintain the same interface as the other
79
+ # gateways the two numbers are concatenated together with a ; separator as
80
+ # the authorization number returned by authorization
81
+ def capture(money, authorization, options = {})
82
+ commit 'us_completion', crediting_params(authorization, :comp_amount => amount(money))
83
+ end
84
+
85
+ # Voiding requires the original transaction ID and order ID of some open
86
+ # transaction. Closed transactions must be refunded. Note that the only
87
+ # methods which may be voided are +capture+ and +purchase+.
88
+ #
89
+ # Concatenate your transaction number and order_id by using a semicolon
90
+ # (';'). This is to keep the Moneris interface consistent with other
91
+ # gateways. (See +capture+ for details.)
92
+ def void(authorization, options = {})
93
+ commit 'us_purchasecorrection', crediting_params(authorization)
94
+ end
95
+
96
+ # Performs a refund. This method requires that the original transaction
97
+ # number and order number be included. Concatenate your transaction
98
+ # number and order_id by using a semicolon (';'). This is to keep the
99
+ # Moneris interface consistent with other gateways. (See +capture+ for
100
+ # details.)
101
+ def credit(money, authorization, options = {})
102
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
103
+ refund(money, authorization, options)
104
+ end
105
+
106
+ def refund(money, authorization, options = {})
107
+ commit 'us_refund', crediting_params(authorization, :amount => amount(money))
108
+ end
109
+
110
+ def store(credit_card, options = {})
111
+ post = {}
112
+ post[:pan] = credit_card.number
113
+ post[:expdate] = expdate(credit_card)
114
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
115
+ commit('us_res_add_cc', post)
116
+ end
117
+
118
+ def unstore(data_key, options = {})
119
+ post = {}
120
+ post[:data_key] = data_key
121
+ commit('us_res_delete', post)
122
+ end
123
+
124
+ def update(data_key, credit_card, options = {})
125
+ post = {}
126
+ post[:pan] = credit_card.number
127
+ post[:expdate] = expdate(credit_card)
128
+ post[:data_key] = data_key
129
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
130
+ commit('us_res_update_cc', post)
131
+ end
132
+
133
+ private # :nodoc: all
134
+
135
+ def expdate(creditcard)
136
+ sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month)
137
+ end
138
+
139
+ def add_payment_source(post, source, options)
140
+ if source.is_a?(String)
141
+ post[:data_key] = source
142
+ post[:cust_id] = options[:customer]
143
+ else
144
+ post[:pan] = source.number
145
+ post[:expdate] = expdate(source)
146
+ post[:cvd_value] = source.verification_value if source.verification_value?
147
+ post[:cust_id] = options[:customer] || source.name
148
+ end
149
+ end
150
+
151
+ # Common params used amongst the +credit+, +void+ and +capture+ methods
152
+ def crediting_params(authorization, options = {})
153
+ {
154
+ :txn_number => split_authorization(authorization).first,
155
+ :order_id => split_authorization(authorization).last,
156
+ :crypt_type => options[:crypt_type] || @options[:crypt_type]
157
+ }.merge(options)
158
+ end
159
+
160
+ # Splits an +authorization+ param and retrieves the order id and
161
+ # transaction number in that order.
162
+ def split_authorization(authorization)
163
+ if authorization.nil? || authorization.empty? || authorization !~ /;/
164
+ raise ArgumentError, 'You must include a valid authorization code (e.g. "1234;567")'
165
+ else
166
+ authorization.split(';')
167
+ end
168
+ end
169
+
170
+ def commit(action, parameters = {})
171
+ data = post_data(action, parameters)
172
+ url = test? ? self.test_url : self.live_url
173
+ raw = ssl_post(url, data)
174
+ response = parse(raw)
175
+
176
+ Response.new(successful?(response), message_from(response[:message]), response,
177
+ :test => test?,
178
+ :avs_result => { :code => response[:avs_result_code] },
179
+ :cvv_result => response[:cvd_result_code] && response[:cvd_result_code][-1,1],
180
+ :authorization => authorization_from(response)
181
+ )
182
+ end
183
+
184
+ # Generates a Moneris authorization string of the form 'trans_id;receipt_id'.
185
+ def authorization_from(response = {})
186
+ if response[:trans_id] && response[:receipt_id]
187
+ "#{response[:trans_id]};#{response[:receipt_id]}"
188
+ end
189
+ end
190
+
191
+ # Tests for a successful response from Moneris' servers
192
+ def successful?(response)
193
+ response[:response_code] &&
194
+ response[:complete] &&
195
+ (0..49).include?(response[:response_code].to_i)
196
+ end
197
+
198
+ def parse(xml)
199
+ response = { :message => "Global Error Receipt", :complete => false }
200
+ hashify_xml!(xml, response)
201
+ response
202
+ end
203
+
204
+ def hashify_xml!(xml, response)
205
+ xml = REXML::Document.new(xml)
206
+ return if xml.root.nil?
207
+ xml.elements.each('//receipt/*') do |node|
208
+ response[node.name.underscore.to_sym] = normalize(node.text)
209
+ end
210
+ end
211
+
212
+ def post_data(action, parameters = {})
213
+ xml = REXML::Document.new
214
+ root = xml.add_element("request")
215
+ root.add_element("store_id").text = options[:login]
216
+ root.add_element("api_token").text = options[:password]
217
+ root.add_element(transaction_element(action, parameters))
218
+
219
+ xml.to_s
220
+ end
221
+
222
+ def transaction_element(action, parameters)
223
+ transaction = REXML::Element.new(action)
224
+
225
+ # Must add the elements in the correct order
226
+ actions[action].each do |key|
227
+ case key
228
+ when :avs_info
229
+ transaction.add_element(avs_element(parameters[:address])) if @avs_enabled && parameters[:address]
230
+ when :cvd_info
231
+ transaction.add_element(cvd_element(parameters[:cvd_value])) if @cvv_enabled
232
+ else
233
+ transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank?
234
+ end
235
+ end
236
+
237
+ transaction
238
+ end
239
+
240
+ def avs_element(address)
241
+ full_address = "#{address[:address1]} #{address[:address2]}"
242
+ tokens = full_address.split(/\s+/)
243
+
244
+ element = REXML::Element.new('avs_info')
245
+ element.add_element('avs_street_number').text = tokens.select{|x| x =~ /\d/}.join(' ')
246
+ element.add_element('avs_street_name').text = tokens.reject{|x| x =~ /\d/}.join(' ')
247
+ element.add_element('avs_zipcode').text = address[:zip]
248
+ element
249
+ end
250
+
251
+ def cvd_element(cvd_value)
252
+ element = REXML::Element.new('cvd_info')
253
+ if cvd_value
254
+ element.add_element('cvd_indicator').text = "1"
255
+ element.add_element('cvd_value').text = cvd_value
256
+ else
257
+ element.add_element('cvd_indicator').text = "0"
258
+ end
259
+ element
260
+ end
261
+
262
+ def message_from(message)
263
+ return 'Unspecified error' if message.blank?
264
+ message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
265
+ end
266
+
267
+ def actions
268
+ {
269
+ "us_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :avs_info, :cvd_info],
270
+ "us_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :avs_info, :cvd_info],
271
+ "us_command" => [:order_id],
272
+ "us_refund" => [:order_id, :amount, :txn_number, :crypt_type],
273
+ "us_indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
274
+ "us_completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
275
+ "us_purchasecorrection" => [:order_id, :txn_number, :crypt_type],
276
+ "us_cavvpurcha" => [:order_id, :cust_id, :amount, :pan, :expdate, :cav],
277
+ "us_cavvpreaut" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
278
+ "us_transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
279
+ "us_Batchcloseall" => [],
280
+ "us_opentotals" => [:ecr_number],
281
+ "us_batchclose" => [:ecr_number],
282
+ "us_res_add_cc" => [:pan, :expdate, :crypt_type],
283
+ "us_res_delete" => [:data_key],
284
+ "us_res_update_cc" => [:data_key, :pan, :expdate, :crypt_type],
285
+ "us_res_purchase_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type],
286
+ "us_res_preauth_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type]
287
+ }
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,152 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class MoneyMoversGateway < Gateway
4
+ self.live_url = self.test_url = 'https://secure.mmoagateway.com/api/transact.php'
5
+
6
+ APPROVED, DECLINED, ERROR = 1, 2, 3
7
+
8
+ self.homepage_url = 'http://mmoa.us/'
9
+ self.display_name = 'MoneyMovers'
10
+ self.supported_countries = ['US']
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+
13
+ def initialize(options = {})
14
+ requires!(options, :login, :password)
15
+ @options = options
16
+ super
17
+ end
18
+
19
+ def purchase(money, creditcard, options = {})
20
+ post = {}
21
+ add_invoice(post, options)
22
+ add_creditcard(post, creditcard)
23
+ add_address(post, options)
24
+ add_customer_data(post, options)
25
+ commit('sale', money, post)
26
+ end
27
+
28
+ def authorize(money, creditcard, options = {})
29
+ post = {}
30
+ add_invoice(post, options)
31
+ add_creditcard(post, creditcard)
32
+ add_address(post, options)
33
+ add_customer_data(post, options)
34
+ commit('auth', money, post)
35
+ end
36
+
37
+ def capture(money, authorization, options = {})
38
+ options[:transactionid] = authorization
39
+ commit('capture', money, options)
40
+ end
41
+
42
+ def void(authorization, options = {})
43
+ options[:transactionid] = authorization
44
+ commit('void', nil, options)
45
+ end
46
+
47
+ def refund(money, authorization, options = {})
48
+ commit('refund', money, options.merge(:transactionid => authorization))
49
+ end
50
+
51
+ def credit(money, authorization, options = {})
52
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
53
+ refund(money, authorization, options)
54
+ end
55
+
56
+ private
57
+
58
+ def add_customer_data(post, options)
59
+ post[:firstname] = options[:first_name]
60
+ post[:lastname] = options[:last_name]
61
+
62
+ post[:email] = options[:email]
63
+ end
64
+
65
+ def add_address(post, options)
66
+ if address = (options[:billing_address] || options[:address])
67
+ post[:company] = address[:company]
68
+ post[:address1] = address[:address1]
69
+ post[:address2] = address[:address2]
70
+ post[:city] = address[:city]
71
+ post[:state] = address[:state]
72
+ post[:zip] = address[:zip]
73
+ post[:country] = address[:country]
74
+ post[:phone] = address[:phone]
75
+ end
76
+ if address = options[:shipping_address]
77
+ post[:shipping_firstname] = address[:first_name]
78
+ post[:shipping_lastname] = address[:last_name]
79
+ post[:shipping_company] = address[:company]
80
+ post[:shipping_address1] = address[:address1]
81
+ post[:shipping_address2] = address[:address2]
82
+ post[:shipping_city] = address[:city]
83
+ post[:shipping_state] = address[:state]
84
+ post[:shipping_zip] = address[:zip]
85
+ post[:shipping_country] = address[:country]
86
+ post[:shipping_email] = address[:email]
87
+ end
88
+ end
89
+
90
+ def add_invoice(post, options)
91
+ post[:orderid] = options[:order_id]
92
+ post[:orderdescription] = options[:description]
93
+ end
94
+
95
+ def add_creditcard(post, creditcard)
96
+ post[:ccnumber] = creditcard.number
97
+ post[:ccexp] = expdate(creditcard)
98
+ post[:cvv] = creditcard.verification_value
99
+ end
100
+
101
+ def parse(body)
102
+ body.split('&').inject({}) do |memo, x|
103
+ k, v = x.split('=')
104
+ memo[k] = v
105
+ memo
106
+ end
107
+ end
108
+
109
+ def commit(action, money, parameters)
110
+ parameters[:amount] = amount(money)
111
+
112
+ data = ssl_post(self.live_url, post_data(action, parameters))
113
+ response = parse(data)
114
+ message = message_from(response)
115
+
116
+ Response.new(success?(response), message, response,
117
+ :test => test?,
118
+ :authorization => response['transactionid'],
119
+ :avs_result => {:code => response['avsresponse']},
120
+ :cvv_result => response['cvvresponse']
121
+ )
122
+ end
123
+
124
+ def success?(response)
125
+ response['response'] == '1'
126
+ end
127
+
128
+ def test?
129
+ (@options[:login].eql?('demo')) && (@options[:password].eql?('password'))
130
+ end
131
+
132
+ def message_from(response)
133
+ case response['response'].to_i
134
+ when APPROVED
135
+ "Transaction Approved"
136
+ when DECLINED
137
+ "Transaction Declined"
138
+ else
139
+ "Error in transaction data or system error"
140
+ end
141
+ end
142
+
143
+ def post_data(action, parameters = {})
144
+ parameters[:type] = action
145
+ parameters[:username] = @options[:login]
146
+ parameters[:password] = @options[:password]
147
+ parameters.map{|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
148
+ end
149
+ end
150
+ end
151
+ end
152
+
@@ -0,0 +1,280 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # The National Australia Bank provide a payment gateway that seems to
6
+ # be a rebadged Securepay Australia service, though some differences exist.
7
+ class NabTransactGateway < Gateway
8
+ API_VERSION = 'xml-4.2'
9
+ PERIODIC_API_VERSION = "spxml-4.2"
10
+
11
+ class_attribute :test_periodic_url, :live_periodic_url
12
+
13
+ self.test_url = 'https://transact.nab.com.au/test/xmlapi/payment'
14
+ self.live_url = 'https://transact.nab.com.au/live/xmlapi/payment'
15
+ self.test_periodic_url = 'https://transact.nab.com.au/xmlapidemo/periodic'
16
+ self.live_periodic_url = 'https://transact.nab.com.au/xmlapi/periodic'
17
+
18
+ self.supported_countries = ['AU']
19
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
20
+
21
+ self.homepage_url = 'http://transact.nab.com.au'
22
+ self.display_name = 'NAB Transact'
23
+ self.money_format = :cents
24
+ self.default_currency = 'AUD'
25
+
26
+ #Transactions currently accepted by NAB Transact XML API
27
+ TRANSACTIONS = {
28
+ :purchase => 0, #Standard Payment
29
+ :refund => 4, #Refund
30
+ :void => 6, #Client Reversal (Void)
31
+ :unmatched_refund => 666, #Unmatched Refund
32
+ :authorization => 10, #Preauthorise
33
+ :capture => 11 #Preauthorise Complete (Advice)
34
+ }
35
+
36
+ PERIODIC_TYPES = {
37
+ :addcrn => 5,
38
+ :editcrn => 5,
39
+ :deletecrn => 5,
40
+ :trigger => 8
41
+ }
42
+
43
+ SUCCESS_CODES = [ '00', '08', '11', '16', '77' ]
44
+
45
+
46
+ def initialize(options = {})
47
+ requires!(options, :login, :password)
48
+ super
49
+ end
50
+
51
+ def purchase(money, credit_card_or_stored_id, options = {})
52
+ if credit_card_or_stored_id.respond_to?(:number)
53
+ #Credit card for instant payment
54
+ commit :purchase, build_purchase_request(money, credit_card_or_stored_id, options)
55
+ else
56
+ #Triggered payment for an existing stored credit card
57
+ options[:billing_id] = credit_card_or_stored_id.to_s
58
+ commit_periodic build_periodic_item(:trigger, money, nil, options)
59
+ end
60
+ end
61
+
62
+ def credit(money, credit_card, options = {})
63
+ commit :unmatched_refund, build_purchase_request(money, credit_card, options)
64
+ end
65
+
66
+ def refund(money, authorization, options = {})
67
+ commit :refund, build_reference_request(money, authorization, options)
68
+ end
69
+
70
+ def authorize(money, credit_card, options = {})
71
+ commit :authorization, build_purchase_request(money, credit_card, options)
72
+ end
73
+
74
+ def capture(money, authorization, options = {})
75
+ commit :capture, build_reference_request(money, authorization, options)
76
+ end
77
+
78
+ def store(creditcard, options = {})
79
+ requires!(options, :billing_id, :amount)
80
+ commit_periodic(build_periodic_item(:addcrn, options[:amount], creditcard, options))
81
+ end
82
+
83
+ def unstore(identification, options = {})
84
+ options[:billing_id] = identification
85
+ commit_periodic(build_periodic_item(:deletecrn, options[:amount], nil, options))
86
+ end
87
+
88
+ private
89
+
90
+ def add_metadata(xml, options)
91
+ if options[:merchant_name] || options[:merchant_location]
92
+ xml.tag! 'metadata' do
93
+ xml.tag! 'meta', :name => 'ca_name', :value => options[:merchant_name] if options[:merchant_name]
94
+ xml.tag! 'meta', :name => 'ca_location', :value => options[:merchant_location] if options[:merchant_location]
95
+ end
96
+ end
97
+ end
98
+
99
+ def build_purchase_request(money, credit_card, options)
100
+ xml = Builder::XmlMarkup.new
101
+ xml.tag! 'amount', amount(money)
102
+ xml.tag! 'currency', options[:currency] || currency(money)
103
+ xml.tag! 'purchaseOrderNo', options[:order_id].to_s.gsub(/[ ']/, '')
104
+
105
+ xml.tag! 'CreditCardInfo' do
106
+ xml.tag! 'cardNumber', credit_card.number
107
+ xml.tag! 'expiryDate', expdate(credit_card)
108
+ xml.tag! 'cvv', credit_card.verification_value if credit_card.verification_value?
109
+ end
110
+
111
+ add_metadata(xml, options)
112
+
113
+ xml.target!
114
+ end
115
+
116
+ def build_reference_request(money, reference, options)
117
+ xml = Builder::XmlMarkup.new
118
+
119
+ transaction_id, order_id, preauth_id, original_amount = reference.split('*')
120
+
121
+ xml.tag! 'amount', (money ? amount(money) : original_amount)
122
+ xml.tag! 'currency', options[:currency] || currency(money)
123
+ xml.tag! 'txnID', transaction_id
124
+ xml.tag! 'purchaseOrderNo', order_id
125
+ xml.tag! 'preauthID', preauth_id
126
+
127
+ add_metadata(xml, options)
128
+
129
+ xml.target!
130
+ end
131
+
132
+ #Generate payment request XML
133
+ # - API is set to allow multiple Txn's but currently only allows one
134
+ # - txnSource = 23 - (XML)
135
+ def build_request(action, body)
136
+ xml = Builder::XmlMarkup.new
137
+ xml.instruct!
138
+ xml.tag! 'NABTransactMessage' do
139
+ xml.tag! 'MessageInfo' do
140
+ xml.tag! 'messageID', SecureRandom.hex(15)
141
+ xml.tag! 'messageTimestamp', generate_timestamp
142
+ xml.tag! 'timeoutValue', request_timeout
143
+ xml.tag! 'apiVersion', API_VERSION
144
+ end
145
+
146
+ xml.tag! 'MerchantInfo' do
147
+ xml.tag! 'merchantID', @options[:login]
148
+ xml.tag! 'password', @options[:password]
149
+ end
150
+
151
+ xml.tag! 'RequestType', 'Payment'
152
+ xml.tag! 'Payment' do
153
+ xml.tag! 'TxnList', "count" => 1 do
154
+ xml.tag! 'Txn', "ID" => 1 do
155
+ xml.tag! 'txnType', TRANSACTIONS[action]
156
+ xml.tag! 'txnSource', 23
157
+ xml << body
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ xml.target!
164
+ end
165
+
166
+ def build_periodic_item(action, money, credit_card, options)
167
+ xml = Builder::XmlMarkup.new
168
+
169
+ xml.tag! 'actionType', action.to_s
170
+ xml.tag! 'periodicType', PERIODIC_TYPES[action] if PERIODIC_TYPES[action]
171
+ xml.tag! 'currency', options[:currency] || currency(money)
172
+ xml.tag! 'crn', options[:billing_id]
173
+
174
+ if credit_card
175
+ xml.tag! 'CreditCardInfo' do
176
+ xml.tag! 'cardNumber', credit_card.number
177
+ xml.tag! 'expiryDate', expdate(credit_card)
178
+ xml.tag! 'cvv', credit_card.verification_value if credit_card.verification_value?
179
+ end
180
+ end
181
+ xml.tag! 'amount', amount(money)
182
+
183
+ xml.target!
184
+ end
185
+
186
+ def build_periodic_request(body)
187
+ xml = Builder::XmlMarkup.new
188
+ xml.instruct!
189
+ xml.tag! 'NABTransactMessage' do
190
+ xml.tag! 'MessageInfo' do
191
+ xml.tag! 'messageID', SecureRandom.hex(15)
192
+ xml.tag! 'messageTimestamp', generate_timestamp
193
+ xml.tag! 'timeoutValue', request_timeout
194
+ xml.tag! 'apiVersion', PERIODIC_API_VERSION
195
+ end
196
+
197
+ xml.tag! 'MerchantInfo' do
198
+ xml.tag! 'merchantID', @options[:login]
199
+ xml.tag! 'password', @options[:password]
200
+ end
201
+
202
+ xml.tag! 'RequestType', 'Periodic'
203
+ xml.tag! 'Periodic' do
204
+ xml.tag! 'PeriodicList', "count" => 1 do
205
+ xml.tag! 'PeriodicItem', "ID" => 1 do
206
+ xml << body
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ xml.target!
213
+ end
214
+
215
+ def commit(action, request)
216
+ response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(action, request)))
217
+
218
+ Response.new(success?(response), message_from(response), response,
219
+ :test => test?,
220
+ :authorization => authorization_from(response)
221
+ )
222
+ end
223
+
224
+ def commit_periodic(request)
225
+ response = parse(ssl_post(test? ? self.test_periodic_url : self.live_periodic_url, build_periodic_request(request)))
226
+ Response.new(success?(response), message_from(response), response,
227
+ :test => test?,
228
+ :authorization => authorization_from(response)
229
+ )
230
+ end
231
+
232
+ def success?(response)
233
+ SUCCESS_CODES.include?(response[:response_code])
234
+ end
235
+
236
+ def authorization_from(response)
237
+ [response[:txn_id], response[:purchase_order_no], response[:preauth_id], response[:amount]].join('*')
238
+ end
239
+
240
+ def message_from(response)
241
+ response[:response_text] || response[:status_description]
242
+ end
243
+
244
+ def expdate(credit_card)
245
+ "#{format(credit_card.month, :two_digits)}/#{format(credit_card.year, :two_digits)}"
246
+ end
247
+
248
+ def parse(body)
249
+ xml = REXML::Document.new(body)
250
+
251
+ response = {}
252
+
253
+ xml.root.elements.to_a.each do |node|
254
+ parse_element(response, node)
255
+ end
256
+
257
+ response
258
+ end
259
+
260
+ def parse_element(response, node)
261
+ if node.has_elements?
262
+ node.elements.each{|element| parse_element(response, element) }
263
+ else
264
+ response[node.name.underscore.to_sym] = node.text
265
+ end
266
+ end
267
+
268
+ # YYYYDDMMHHNNSSKKK000sOOO
269
+ def generate_timestamp
270
+ time = Time.now.utc
271
+ time.strftime("%Y%d%m%H%M%S#{time.usec}+000")
272
+ end
273
+
274
+ def request_timeout
275
+ @options[:request_timeout] || 60
276
+ end
277
+
278
+ end
279
+ end
280
+ end