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,292 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class QbmsGateway < Gateway
4
+ API_VERSION = '4.0'
5
+
6
+ class_attribute :test_url, :live_url
7
+
8
+ self.test_url = "https://webmerchantaccount.ptc.quickbooks.com/j/AppGateway"
9
+ self.live_url = "https://webmerchantaccount.quickbooks.com/j/AppGateway"
10
+
11
+ self.homepage_url = 'http://payments.intuit.com/'
12
+ self.display_name = 'QuickBooks Merchant Services'
13
+ self.default_currency = 'USD'
14
+ self.supported_cardtypes = [ :visa, :master, :discover, :american_express, :diners_club, :jcb ]
15
+ self.supported_countries = [ 'US' ]
16
+
17
+ TYPES = {
18
+ :authorize => 'CustomerCreditCardAuth',
19
+ :capture => 'CustomerCreditCardCapture',
20
+ :purchase => 'CustomerCreditCardCharge',
21
+ :refund => 'CustomerCreditCardTxnVoidOrRefund',
22
+ :void => 'CustomerCreditCardTxnVoid',
23
+ :query => 'MerchantAccountQuery',
24
+ }
25
+
26
+ # Creates a new QbmsGateway
27
+ #
28
+ # The gateway requires that a valid app id, app login, and ticket be passed
29
+ # in the +options+ hash.
30
+ #
31
+ # ==== Options
32
+ #
33
+ # * <tt>:login</tt> -- The App Login (REQUIRED)
34
+ # * <tt>:ticket</tt> -- The Connection Ticket. (REQUIRED)
35
+ # * <tt>:pem</tt> -- The PEM-encoded SSL client key and certificate. (REQUIRED)
36
+ # * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
37
+ # Otherwise, perform transactions against the production server.
38
+ #
39
+ def initialize(options = {})
40
+ requires!(options, :login, :ticket)
41
+ super
42
+ end
43
+
44
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
45
+ # charge the card.
46
+ #
47
+ # ==== Parameters
48
+ #
49
+ # * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
50
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
51
+ # * <tt>options</tt> -- A hash of optional parameters.
52
+ #
53
+ def authorize(money, creditcard, options = {})
54
+ commit(:authorize, money, options.merge(:credit_card => creditcard))
55
+ end
56
+
57
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
58
+ #
59
+ # ==== Parameters
60
+ #
61
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
62
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
63
+ # * <tt>options</tt> -- A hash of optional parameters.
64
+ #
65
+ def purchase(money, creditcard, options = {})
66
+ commit(:purchase, money, options.merge(:credit_card => creditcard))
67
+ end
68
+
69
+ # Captures the funds from an authorized transaction.
70
+ #
71
+ # ==== Parameters
72
+ #
73
+ # * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
74
+ # * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
75
+ #
76
+ def capture(money, authorization, options = {})
77
+ commit(:capture, money, options.merge(:transaction_id => authorization))
78
+ end
79
+
80
+ # Void a previous transaction
81
+ #
82
+ # ==== Parameters
83
+ #
84
+ # * <tt>authorization</tt> - The authorization returned from the previous authorize request.
85
+ #
86
+ def void(authorization, options = {})
87
+ commit(:void, nil, options.merge(:transaction_id => authorization))
88
+ end
89
+
90
+ # Credit an account.
91
+ #
92
+ # This transaction is also referred to as a Refund and indicates to the gateway that
93
+ # money should flow from the merchant to the customer.
94
+ #
95
+ # ==== Parameters
96
+ #
97
+ # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
98
+ # * <tt>identification</tt> -- The ID of the original transaction against which the credit is being issued.
99
+ # * <tt>options</tt> -- A hash of parameters.
100
+ #
101
+ #
102
+ def credit(money, identification, options = {})
103
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
104
+ refund(money, identification, options = {})
105
+ end
106
+
107
+ def refund(money, identification, options = {})
108
+ commit(:refund, money, options.merge(:transaction_id => identification))
109
+ end
110
+
111
+ # Query the merchant account status
112
+ def query
113
+ commit(:query, nil, {})
114
+ end
115
+
116
+ private
117
+
118
+ def hosted?
119
+ @options[:pem]
120
+ end
121
+
122
+ def commit(action, money, parameters)
123
+ url = test? ? self.test_url : self.live_url
124
+
125
+ type = TYPES[action]
126
+ parameters[:trans_request_id] ||= SecureRandom.hex(10)
127
+
128
+ req = build_request(type, money, parameters)
129
+
130
+ data = ssl_post(url, req, "Content-Type" => "application/x-qbmsxml")
131
+ response = parse(type, data)
132
+ message = (response[:status_message] || '').strip
133
+
134
+ Response.new(success?(response), message, response,
135
+ :test => test?,
136
+ :authorization => response[:credit_card_trans_id],
137
+ :fraud_review => fraud_review?(response),
138
+ :avs_result => { :code => avs_result(response) },
139
+ :cvv_result => cvv_result(response)
140
+ )
141
+ end
142
+
143
+ def success?(response)
144
+ response[:status_code] == 0
145
+ end
146
+
147
+ def fraud_review?(response)
148
+ [10100, 10101].member? response[:status_code]
149
+ end
150
+
151
+ def parse(type, body)
152
+ xml = REXML::Document.new(body)
153
+
154
+ signon = REXML::XPath.first(xml, "//SignonMsgsRs/#{hosted? ? 'SignonAppCertRs' : 'SignonDesktopRs'}")
155
+ status_code = signon.attributes["statusCode"].to_i
156
+
157
+ if status_code != 0
158
+ return {
159
+ :status_code => status_code,
160
+ :status_message => signon.attributes["statusMessage"],
161
+ }
162
+ end
163
+
164
+ response = REXML::XPath.first(xml, "//QBMSXMLMsgsRs/#{type}Rs")
165
+
166
+ results = {
167
+ :status_code => response.attributes["statusCode"].to_i,
168
+ :status_message => response.attributes["statusMessage"],
169
+ }
170
+
171
+ response.elements.each do |e|
172
+ name = e.name.underscore.to_sym
173
+ value = e.text()
174
+
175
+ if old_value = results[name]
176
+ results[name] = [old_value] if !old_value.kind_of?(Array)
177
+ results[name] << value
178
+ else
179
+ results[name] = value
180
+ end
181
+ end
182
+
183
+ results
184
+ end
185
+
186
+ def build_request(type, money, parameters = {})
187
+ xml = Builder::XmlMarkup.new(:indent => 0)
188
+
189
+ xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
190
+ xml.instruct!(:qbmsxml, :version => API_VERSION)
191
+
192
+ xml.tag!("QBMSXML") do
193
+ xml.tag!("SignonMsgsRq") do
194
+ xml.tag!(hosted? ? "SignonAppCertRq" : "SignonDesktopRq") do
195
+ xml.tag!("ClientDateTime", Time.now.xmlschema)
196
+ xml.tag!("ApplicationLogin", @options[:login])
197
+ xml.tag!("ConnectionTicket", @options[:ticket])
198
+ end
199
+ end
200
+
201
+ xml.tag!("QBMSXMLMsgsRq") do
202
+ xml.tag!("#{type}Rq") do
203
+ method("build_#{type}").call(xml, money, parameters)
204
+ end
205
+ end
206
+ end
207
+
208
+ xml.target!
209
+ end
210
+
211
+ def build_CustomerCreditCardAuth(xml, money, parameters)
212
+ cc = parameters[:credit_card]
213
+ name = "#{cc.first_name} #{cc.last_name}"[0...30]
214
+
215
+ xml.tag!("TransRequestID", parameters[:trans_request_id])
216
+ xml.tag!("CreditCardNumber", cc.number)
217
+ xml.tag!("ExpirationMonth", cc.month)
218
+ xml.tag!("ExpirationYear", cc.year)
219
+ xml.tag!("IsECommerce", "true")
220
+ xml.tag!("Amount", amount(money))
221
+ xml.tag!("NameOnCard", name)
222
+ add_address(xml, parameters)
223
+ xml.tag!("CardSecurityCode", cc.verification_value) if cc.verification_value?
224
+ end
225
+
226
+ def build_CustomerCreditCardCapture(xml, money, parameters)
227
+ xml.tag!("TransRequestID", parameters[:trans_request_id])
228
+ xml.tag!("CreditCardTransID", parameters[:transaction_id])
229
+ xml.tag!("Amount", amount(money))
230
+ end
231
+
232
+ def build_CustomerCreditCardCharge(xml, money, parameters)
233
+ cc = parameters[:credit_card]
234
+ name = "#{cc.first_name} #{cc.last_name}"[0...30]
235
+
236
+ xml.tag!("TransRequestID", parameters[:trans_request_id])
237
+ xml.tag!("CreditCardNumber", cc.number)
238
+ xml.tag!("ExpirationMonth", cc.month)
239
+ xml.tag!("ExpirationYear", cc.year)
240
+ xml.tag!("IsECommerce", "true")
241
+ xml.tag!("Amount", amount(money))
242
+ xml.tag!("NameOnCard", name)
243
+ add_address(xml, parameters)
244
+ xml.tag!("CardSecurityCode", cc.verification_value) if cc.verification_value?
245
+ end
246
+
247
+ def build_CustomerCreditCardTxnVoidOrRefund(xml, money, parameters)
248
+ xml.tag!("TransRequestID", parameters[:trans_request_id])
249
+ xml.tag!("CreditCardTransID", parameters[:transaction_id])
250
+ xml.tag!("Amount", amount(money))
251
+ end
252
+
253
+ def build_CustomerCreditCardTxnVoid(xml, money, parameters)
254
+ xml.tag!("TransRequestID", parameters[:trans_request_id])
255
+ xml.tag!("CreditCardTransID", parameters[:transaction_id])
256
+ end
257
+
258
+ # Called reflectively by build_request
259
+ def build_MerchantAccountQuery(xml, money, parameters)
260
+ end
261
+
262
+ def add_address(xml, parameters)
263
+ if address = parameters[:billing_address] || parameters[:address]
264
+ xml.tag!("CreditCardAddress", (address[:address1] || "")[0...30])
265
+ xml.tag!("CreditCardPostalCode", (address[:zip] || "")[0...9])
266
+ end
267
+ end
268
+
269
+ def cvv_result(response)
270
+ case response[:card_security_code_match]
271
+ when "Pass" then 'M'
272
+ when "Fail" then 'N'
273
+ when "NotAvailable" then 'P'
274
+ end
275
+ end
276
+
277
+ def avs_result(response)
278
+ case "#{response[:avs_street]}|#{response[:avs_zip]}"
279
+ when "Pass|Pass" then "D"
280
+ when "Pass|Fail" then "A"
281
+ when "Pass|NotAvailable" then "B"
282
+ when "Fail|Pass" then "Z"
283
+ when "Fail|Fail" then "C"
284
+ when "Fail|NotAvailable" then "N"
285
+ when "NotAvailable|Pass" then "P"
286
+ when "NotAvailable|Fail" then "N"
287
+ when "NotAvailable|NotAvailable" then "U"
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,276 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # ActiveMerchant Implementation for Quantum Gateway XML Requester Service
4
+ # Based on API Doc from 8/6/2009
5
+ #
6
+ # Important Notes
7
+ # * Support is included for a customer id via the :customer option, invoice number via :invoice option, invoice description via :merchant option and memo via :description option
8
+ # * You can force email of receipt with :email_receipt => true
9
+ # * You can force email of merchant receipt with :merchant_receipt => true
10
+ # * You can exclude CVV with :ignore_cvv => true
11
+ # * All transactions use dollar values.
12
+ class QuantumGateway < Gateway
13
+ self.live_url = self.test_url = 'https://secure.quantumgateway.com/cgi/xml_requester.php'
14
+
15
+ # visa, master, american_express, discover
16
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
17
+ self.supported_countries = ['US']
18
+ self.default_currency = 'USD'
19
+ self.money_format = :dollars
20
+ self.homepage_url = 'http://www.quantumgateway.com'
21
+ self.display_name = 'Quantum Gateway'
22
+
23
+ # These are the options that can be used when creating a new Quantum Gateway object.
24
+ #
25
+ # :login => Your Quantum Gateway Gateway ID
26
+ #
27
+ # :password => Your Quantum Gateway Vault Key or Restrict Key
28
+ #
29
+ # NOTE: For testing supply your test GatewayLogin and GatewayKey
30
+ #
31
+ # :email_receipt => true if you want a receipt sent to the customer (false be default)
32
+ #
33
+ # :merchant_receipt => true if you want to override receiving the merchant receipt
34
+ #
35
+ # :ignore_avs => true ignore both AVS and CVV verification
36
+ # :ignore_cvv => true don't want to use CVV so continue processing even if CVV would have failed
37
+ #
38
+ def initialize(options = {})
39
+ requires!(options, :login, :password)
40
+ super
41
+ end
42
+
43
+ # Request an authorization for an amount from CyberSource
44
+ #
45
+ def authorize(money, creditcard, options = {})
46
+ setup_address_hash(options)
47
+ commit(build_auth_request(money, creditcard, options), options )
48
+ end
49
+
50
+ # Capture an authorization that has previously been requested
51
+ def capture(money, authorization, options = {})
52
+ setup_address_hash(options)
53
+ commit(build_capture_request(money, authorization, options), options)
54
+ end
55
+
56
+ # Purchase is an auth followed by a capture
57
+ # You must supply an order_id in the options hash
58
+ def purchase(money, creditcard, options = {})
59
+ setup_address_hash(options)
60
+ commit(build_purchase_request(money, creditcard, options), options)
61
+ end
62
+
63
+ def void(identification, options = {})
64
+ commit(build_void_request(identification, options), options)
65
+ end
66
+
67
+ def refund(money, identification, options = {})
68
+ commit(build_credit_request(money, identification, options), options)
69
+ end
70
+
71
+ def credit(money, identification, options = {})
72
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
73
+ refund(money, identification, options)
74
+ end
75
+
76
+ private
77
+
78
+ def setup_address_hash(options)
79
+ options[:billing_address] = options[:billing_address] || options[:address] || {}
80
+ end
81
+
82
+ def build_auth_request(money, creditcard, options)
83
+ xml = Builder::XmlMarkup.new
84
+ add_common_credit_card_info(xml,'AUTH_ONLY')
85
+ add_purchase_data(xml, money)
86
+ add_creditcard(xml, creditcard)
87
+ add_address(xml, creditcard, options[:billing_address], options)
88
+ add_invoice_details(xml, options)
89
+ add_customer_details(xml, options)
90
+ add_memo(xml, options)
91
+ add_business_rules_data(xml)
92
+ xml.target!
93
+ end
94
+
95
+ def build_capture_request(money, authorization, options)
96
+ xml = Builder::XmlMarkup.new
97
+ add_common_credit_card_info(xml,'PREVIOUS_SALE')
98
+ transaction_id, _ = authorization_parts_from(authorization)
99
+ add_transaction_id(xml, transaction_id)
100
+ xml.target!
101
+ end
102
+
103
+ def build_purchase_request(money, creditcard, options)
104
+ xml = Builder::XmlMarkup.new
105
+ add_common_credit_card_info(xml, @options[:ignore_avs] || @options[:ignore_cvv] ? 'SALES' : 'AUTH_CAPTURE')
106
+ add_address(xml, creditcard, options[:billing_address], options)
107
+ add_purchase_data(xml, money)
108
+ add_creditcard(xml, creditcard)
109
+ add_invoice_details(xml, options)
110
+ add_customer_details(xml, options)
111
+ add_memo(xml, options)
112
+ add_business_rules_data(xml)
113
+ xml.target!
114
+ end
115
+
116
+ def build_void_request(authorization, options)
117
+ xml = Builder::XmlMarkup.new
118
+ add_common_credit_card_info(xml,'VOID')
119
+ transaction_id, _ = authorization_parts_from(authorization)
120
+ add_transaction_id(xml, transaction_id)
121
+ xml.target!
122
+ end
123
+
124
+ def build_credit_request(money, authorization, options)
125
+ xml = Builder::XmlMarkup.new
126
+ add_common_credit_card_info(xml,'RETURN')
127
+ add_purchase_data(xml, money)
128
+ transaction_id, cc = authorization_parts_from(authorization)
129
+ add_transaction_id(xml, transaction_id)
130
+ xml.tag! 'CreditCardNumber', cc
131
+ xml.target!
132
+ end
133
+
134
+ def add_common_credit_card_info(xml, process_type)
135
+ xml.tag! 'RequestType', 'ProcessSingleTransaction'
136
+ xml.tag! 'TransactionType', 'CREDIT'
137
+ xml.tag! 'PaymentType', 'CC'
138
+ xml.tag! 'ProcessType', process_type
139
+ end
140
+
141
+ def add_business_rules_data(xml)
142
+ xml.tag!('CustomerEmail', @options[:email_receipt] ? 'Y' : 'N')
143
+ xml.tag!('MerchantEmail', @options[:merchant_receipt] ? 'Y' : 'N')
144
+ end
145
+
146
+ def add_invoice_details(xml, options)
147
+ xml.tag! 'InvoiceNumber', options[:invoice]
148
+ xml.tag! 'InvoiceDescription', options[:merchant]
149
+ end
150
+
151
+ def add_customer_details(xml, options)
152
+ xml.tag! 'CustomerID', options[:customer]
153
+ end
154
+
155
+ def add_transaction_id(xml, transaction_id)
156
+ xml.tag! 'TransactionID', transaction_id
157
+ end
158
+
159
+ def add_memo(xml, options)
160
+ xml.tag! 'Memo', options[:description]
161
+ end
162
+
163
+ def add_purchase_data(xml, money = 0)
164
+ xml.tag! 'Amount', amount(money)
165
+ xml.tag! 'TransactionDate', Time.now
166
+ end
167
+
168
+ def add_address(xml, creditcard, address, options, shipTo = false)
169
+ xml.tag! 'FirstName', creditcard.first_name
170
+ xml.tag! 'LastName', creditcard.last_name
171
+ xml.tag! 'Address', address[:address1] # => there is no support for address2 in quantum
172
+ xml.tag! 'City', address[:city]
173
+ xml.tag! 'State', address[:state]
174
+ xml.tag! 'ZipCode', address[:zip]
175
+ xml.tag! 'Country', address[:country]
176
+ xml.tag! 'EmailAddress', options[:email]
177
+ xml.tag! 'IPAddress', options[:ip]
178
+ end
179
+
180
+ def add_creditcard(xml, creditcard)
181
+ xml.tag! 'PaymentType', 'CC'
182
+ xml.tag! 'CreditCardNumber', creditcard.number
183
+ xml.tag! 'ExpireMonth', format(creditcard.month, :two_digits)
184
+ xml.tag! 'ExpireYear', format(creditcard.year, :four_digits)
185
+ xml.tag!('CVV2', creditcard.verification_value) unless (@options[:ignore_cvv] || creditcard.verification_value.blank? )
186
+ end
187
+
188
+ # Where we actually build the full SOAP request using builder
189
+ def build_request(body, options)
190
+ xml = Builder::XmlMarkup.new
191
+ xml.instruct!
192
+ xml.tag! 'QGWRequest' do
193
+ xml.tag! 'Authentication' do
194
+ xml.tag! 'GatewayLogin', @options[:login]
195
+ xml.tag! 'GatewayKey', @options[:password]
196
+ end
197
+ xml.tag! 'Request' do
198
+ xml << body
199
+ end
200
+ end
201
+ xml.target!
202
+ end
203
+
204
+ # Contact CyberSource, make the SOAP request, and parse the reply into a Response object
205
+ def commit(request, options)
206
+ headers = { 'Content-Type' => 'text/xml' }
207
+ response = parse(ssl_post(self.live_url, build_request(request, options), headers))
208
+
209
+ success = response[:request_status] == "Success"
210
+ message = response[:request_message]
211
+
212
+ if success # => checking for connectivity success first
213
+ success = %w(APPROVED FORCED VOIDED).include?(response[:Status])
214
+ message = response[:StatusDescription]
215
+ authorization = success ? authorization_for(response) : nil
216
+ end
217
+
218
+ Response.new(success, message, response,
219
+ :test => test?,
220
+ :authorization => authorization,
221
+ :avs_result => { :code => response[:AVSResponseCode] },
222
+ :cvv_result => response[:CVV2ResponseCode]
223
+ )
224
+ end
225
+
226
+ # Parse the SOAP response
227
+ # Technique inspired by the Paypal Gateway
228
+ def parse(xml)
229
+ reply = {}
230
+
231
+ begin
232
+ xml = REXML::Document.new(xml)
233
+
234
+ root = REXML::XPath.first(xml, "//QGWRequest/ResponseSummary")
235
+ parse_element(reply, root)
236
+ reply[:request_status] = reply[:Status]
237
+ reply[:request_message] = "#{reply[:Status]}: #{reply[:StatusDescription]}"
238
+
239
+ if root = REXML::XPath.first(xml, "//QGWRequest/Result")
240
+ root.elements.to_a.each do |node|
241
+ parse_element(reply, node)
242
+ end
243
+ end
244
+ rescue Exception
245
+ reply[:request_status] = 'Failure'
246
+ reply[:request_message] = "Failure: There was a problem parsing the response XML"
247
+ end
248
+
249
+ return reply
250
+ end
251
+
252
+ def parse_element(reply, node)
253
+ if node.has_elements?
254
+ node.elements.each{|e| parse_element(reply, e) }
255
+ else
256
+ if node.parent.name =~ /item/
257
+ parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
258
+ reply[(parent + '_' + node.name).to_sym] = node.text
259
+ else
260
+ reply[node.name.to_sym] = node.text
261
+ end
262
+ end
263
+ return reply
264
+ end
265
+
266
+ def authorization_for(reply)
267
+ "#{reply[:TransactionID]};#{reply[:CreditCardNumber]}"
268
+ end
269
+
270
+ def authorization_parts_from(authorization)
271
+ authorization.split(/;/)
272
+ end
273
+
274
+ end
275
+ end
276
+ end