gonow-activemerchant 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +690 -0
- data/CONTRIBUTORS +237 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +165 -0
- data/gem-public_cert.pem +20 -0
- data/lib/active_merchant.rb +47 -0
- data/lib/active_merchant/billing.rb +9 -0
- data/lib/active_merchant/billing/avs_result.rb +98 -0
- data/lib/active_merchant/billing/base.rb +57 -0
- data/lib/active_merchant/billing/check.rb +68 -0
- data/lib/active_merchant/billing/credit_card.rb +178 -0
- data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
- data/lib/active_merchant/billing/credit_card_methods.rb +125 -0
- data/lib/active_merchant/billing/cvv_result.rb +38 -0
- data/lib/active_merchant/billing/expiry_date.rb +34 -0
- data/lib/active_merchant/billing/gateway.rb +170 -0
- data/lib/active_merchant/billing/gateways.rb +18 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +664 -0
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +858 -0
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
- data/lib/active_merchant/billing/gateways/beanstream.rb +139 -0
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +282 -0
- data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
- data/lib/active_merchant/billing/gateways/blue_pay.rb +11 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +132 -0
- data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +293 -0
- data/lib/active_merchant/billing/gateways/braintree_orange.rb +17 -0
- data/lib/active_merchant/billing/gateways/braspag.rb +188 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +430 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
- data/lib/active_merchant/billing/gateways/elavon.rb +134 -0
- data/lib/active_merchant/billing/gateways/epay.rb +268 -0
- data/lib/active_merchant/billing/gateways/eway.rb +277 -0
- data/lib/active_merchant/billing/gateways/eway_managed.rb +231 -0
- data/lib/active_merchant/billing/gateways/exact.rb +222 -0
- data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
- data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
- data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
- data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
- data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
- data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
- data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
- data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +454 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +156 -0
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
- data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +209 -0
- data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
- data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
- data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
- data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +292 -0
- data/lib/active_merchant/billing/gateways/orbital.rb +321 -0
- data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +392 -0
- data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +207 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +253 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +207 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +222 -0
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
- data/lib/active_merchant/billing/gateways/payment_express.rb +235 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +121 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +351 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
- data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +177 -0
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +25 -0
- data/lib/active_merchant/billing/gateways/plugnpay.rb +298 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
- data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
- data/lib/active_merchant/billing/gateways/qbms.rb +295 -0
- data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +218 -0
- data/lib/active_merchant/billing/gateways/realex.rb +311 -0
- data/lib/active_merchant/billing/gateways/sage.rb +146 -0
- data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
- data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
- data/lib/active_merchant/billing/gateways/sage_pay.rb +320 -0
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
- data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +193 -0
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
- data/lib/active_merchant/billing/gateways/skip_jack.rb +453 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +271 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +212 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
- data/lib/active_merchant/billing/gateways/transax.rb +25 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +423 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +194 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +189 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
- data/lib/active_merchant/billing/integrations.rb +17 -0
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +68 -0
- data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
- data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
- data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
- data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
- data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
- data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
- data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
- data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
- data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
- data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
- data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
- data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
- data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
- data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
- data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
- data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
- data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
- data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
- data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/helper.rb +96 -0
- data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
- data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
- data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
- data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
- data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
- data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
- data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
- data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
- data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/notification.rb +62 -0
- data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +154 -0
- data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
- data/lib/active_merchant/billing/integrations/quickpay/helper.rb +72 -0
- data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
- data/lib/active_merchant/billing/integrations/return.rb +42 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +111 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
- data/lib/active_merchant/billing/integrations/two_checkout.rb +23 -0
- data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
- data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
- data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
- data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
- data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
- data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
- data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
- data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
- data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
- data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
- data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
- data/lib/active_merchant/billing/response.rb +32 -0
- data/lib/active_merchant/common.rb +14 -0
- data/lib/active_merchant/common/connection.rb +177 -0
- data/lib/active_merchant/common/country.rb +328 -0
- data/lib/active_merchant/common/error.rb +26 -0
- data/lib/active_merchant/common/post_data.rb +24 -0
- data/lib/active_merchant/common/posts_data.rb +63 -0
- data/lib/active_merchant/common/requires_parameters.rb +16 -0
- data/lib/active_merchant/common/utils.rb +22 -0
- data/lib/active_merchant/common/validateable.rb +81 -0
- data/lib/active_merchant/version.rb +3 -0
- data/lib/activemerchant.rb +1 -0
- data/lib/certs/cacert.pem +7815 -0
- data/lib/support/gateway_support.rb +58 -0
- data/lib/support/outbound_hosts.rb +25 -0
- metadata +276 -0
@@ -0,0 +1,295 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module ActiveMerchant #:nodoc:
|
4
|
+
module Billing #:nodoc:
|
5
|
+
class QbmsGateway < Gateway
|
6
|
+
API_VERSION = '4.0'
|
7
|
+
|
8
|
+
class_attribute :test_url, :live_url
|
9
|
+
|
10
|
+
self.test_url = "https://webmerchantaccount.ptc.quickbooks.com/j/AppGateway"
|
11
|
+
self.live_url = "https://webmerchantaccount.quickbooks.com/j/AppGateway"
|
12
|
+
|
13
|
+
self.homepage_url = 'http://payments.intuit.com/'
|
14
|
+
self.display_name = 'QuickBooks Merchant Services'
|
15
|
+
self.default_currency = 'USD'
|
16
|
+
self.supported_cardtypes = [ :visa, :master, :discover, :american_express, :diners_club, :jcb ]
|
17
|
+
self.supported_countries = [ 'US' ]
|
18
|
+
|
19
|
+
TYPES = {
|
20
|
+
:authorize => 'CustomerCreditCardAuth',
|
21
|
+
:capture => 'CustomerCreditCardCapture',
|
22
|
+
:purchase => 'CustomerCreditCardCharge',
|
23
|
+
:refund => 'CustomerCreditCardTxnVoidOrRefund',
|
24
|
+
:void => 'CustomerCreditCardTxnVoid',
|
25
|
+
:query => 'MerchantAccountQuery',
|
26
|
+
}
|
27
|
+
|
28
|
+
# Creates a new QbmsGateway
|
29
|
+
#
|
30
|
+
# The gateway requires that a valid app id, app login, and ticket be passed
|
31
|
+
# in the +options+ hash.
|
32
|
+
#
|
33
|
+
# ==== Options
|
34
|
+
#
|
35
|
+
# * <tt>:login</tt> -- The App Login (REQUIRED)
|
36
|
+
# * <tt>:ticket</tt> -- The Connection Ticket. (REQUIRED)
|
37
|
+
# * <tt>:pem</tt> -- The PEM-encoded SSL client key and certificate. (REQUIRED)
|
38
|
+
# * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
|
39
|
+
# Otherwise, perform transactions against the production server.
|
40
|
+
#
|
41
|
+
def initialize(options = {})
|
42
|
+
requires!(options, :login, :ticket)
|
43
|
+
test_mode = options[:test] || false
|
44
|
+
@options = options
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
# Performs an authorization, which reserves the funds on the customer's credit card, but does not
|
49
|
+
# charge the card.
|
50
|
+
#
|
51
|
+
# ==== Parameters
|
52
|
+
#
|
53
|
+
# * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
|
54
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
55
|
+
# * <tt>options</tt> -- A hash of optional parameters.
|
56
|
+
#
|
57
|
+
def authorize(money, creditcard, options = {})
|
58
|
+
commit(:authorize, money, options.merge(:credit_card => creditcard))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Perform a purchase, which is essentially an authorization and capture in a single operation.
|
62
|
+
#
|
63
|
+
# ==== Parameters
|
64
|
+
#
|
65
|
+
# * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
|
66
|
+
# * <tt>creditcard</tt> -- The CreditCard details for the transaction.
|
67
|
+
# * <tt>options</tt> -- A hash of optional parameters.
|
68
|
+
#
|
69
|
+
def purchase(money, creditcard, options = {})
|
70
|
+
commit(:purchase, money, options.merge(:credit_card => creditcard))
|
71
|
+
end
|
72
|
+
|
73
|
+
# Captures the funds from an authorized transaction.
|
74
|
+
#
|
75
|
+
# ==== Parameters
|
76
|
+
#
|
77
|
+
# * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
|
78
|
+
# * <tt>authorization</tt> -- The authorization returned from the previous authorize request.
|
79
|
+
#
|
80
|
+
def capture(money, authorization, options = {})
|
81
|
+
commit(:capture, money, options.merge(:transaction_id => authorization))
|
82
|
+
end
|
83
|
+
|
84
|
+
# Void a previous transaction
|
85
|
+
#
|
86
|
+
# ==== Parameters
|
87
|
+
#
|
88
|
+
# * <tt>authorization</tt> - The authorization returned from the previous authorize request.
|
89
|
+
#
|
90
|
+
def void(authorization, options = {})
|
91
|
+
commit(:void, nil, options.merge(:transaction_id => authorization))
|
92
|
+
end
|
93
|
+
|
94
|
+
# Credit an account.
|
95
|
+
#
|
96
|
+
# This transaction is also referred to as a Refund and indicates to the gateway that
|
97
|
+
# money should flow from the merchant to the customer.
|
98
|
+
#
|
99
|
+
# ==== Parameters
|
100
|
+
#
|
101
|
+
# * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
|
102
|
+
# * <tt>identification</tt> -- The ID of the original transaction against which the credit is being issued.
|
103
|
+
# * <tt>options</tt> -- A hash of parameters.
|
104
|
+
#
|
105
|
+
#
|
106
|
+
def credit(money, identification, options = {})
|
107
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
108
|
+
refund(money, identification, options = {})
|
109
|
+
end
|
110
|
+
|
111
|
+
def refund(money, identification, options = {})
|
112
|
+
commit(:refund, money, options.merge(:transaction_id => identification))
|
113
|
+
end
|
114
|
+
|
115
|
+
# Query the merchant account status
|
116
|
+
def query
|
117
|
+
commit(:query, nil, {})
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def hosted?
|
123
|
+
@options[:pem]
|
124
|
+
end
|
125
|
+
|
126
|
+
def commit(action, money, parameters)
|
127
|
+
url = test? ? self.test_url : self.live_url
|
128
|
+
|
129
|
+
type = TYPES[action]
|
130
|
+
parameters[:trans_request_id] ||= SecureRandom.hex(10)
|
131
|
+
|
132
|
+
req = build_request(type, money, parameters)
|
133
|
+
data = ssl_post(url, req, "Content-Type" => "application/x-qbmsxml")
|
134
|
+
response = parse(type, data)
|
135
|
+
message = (response[:status_message] || '').strip
|
136
|
+
|
137
|
+
Response.new(success?(response), message, response,
|
138
|
+
:test => test?,
|
139
|
+
:authorization => response[:credit_card_trans_id],
|
140
|
+
:fraud_review => fraud_review?(response),
|
141
|
+
:avs_result => { :code => avs_result(response) },
|
142
|
+
:cvv_result => cvv_result(response)
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
def success?(response)
|
147
|
+
response[:status_code] == 0
|
148
|
+
end
|
149
|
+
|
150
|
+
def fraud_review?(response)
|
151
|
+
[10100, 10101].member? response[:status_code]
|
152
|
+
end
|
153
|
+
|
154
|
+
def parse(type, body)
|
155
|
+
xml = REXML::Document.new(body)
|
156
|
+
|
157
|
+
signon = REXML::XPath.first(xml, "//SignonMsgsRs/#{hosted? ? 'SignonAppCertRs' : 'SignonDesktopRs'}")
|
158
|
+
status_code = signon.attributes["statusCode"].to_i
|
159
|
+
|
160
|
+
if status_code != 0
|
161
|
+
return {
|
162
|
+
:status_code => status_code,
|
163
|
+
:status_message => signon.attributes["statusMessage"],
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
response = REXML::XPath.first(xml, "//QBMSXMLMsgsRs/#{type}Rs")
|
168
|
+
|
169
|
+
results = {
|
170
|
+
:status_code => response.attributes["statusCode"].to_i,
|
171
|
+
:status_message => response.attributes["statusMessage"],
|
172
|
+
}
|
173
|
+
|
174
|
+
response.elements.each do |e|
|
175
|
+
name = e.name.underscore.to_sym
|
176
|
+
value = e.text()
|
177
|
+
|
178
|
+
if old_value = results[name]
|
179
|
+
results[name] = [old_value] if !old_value.kind_of?(Array)
|
180
|
+
results[name] << value
|
181
|
+
else
|
182
|
+
results[name] = value
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
results
|
187
|
+
end
|
188
|
+
|
189
|
+
def build_request(type, money, parameters = {})
|
190
|
+
xml = Builder::XmlMarkup.new(:indent => 0)
|
191
|
+
|
192
|
+
xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
|
193
|
+
xml.instruct!(:qbmsxml, :version => API_VERSION)
|
194
|
+
|
195
|
+
xml.tag!("QBMSXML") do
|
196
|
+
xml.tag!("SignonMsgsRq") do
|
197
|
+
xml.tag!(hosted? ? "SignonAppCertRq" : "SignonDesktopRq") do
|
198
|
+
xml.tag!("ClientDateTime", Time.now.xmlschema)
|
199
|
+
xml.tag!("ApplicationLogin", @options[:login])
|
200
|
+
xml.tag!("ConnectionTicket", @options[:ticket])
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
xml.tag!("QBMSXMLMsgsRq") do
|
205
|
+
xml.tag!("#{type}Rq") do
|
206
|
+
method("build_#{type}").call(xml, money, parameters)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
xml.target!
|
212
|
+
end
|
213
|
+
|
214
|
+
def build_CustomerCreditCardAuth(xml, money, parameters)
|
215
|
+
cc = parameters[:credit_card]
|
216
|
+
name = "#{cc.first_name} #{cc.last_name}"[0...30]
|
217
|
+
|
218
|
+
xml.tag!("TransRequestID", parameters[:trans_request_id])
|
219
|
+
xml.tag!("CreditCardNumber", cc.number)
|
220
|
+
xml.tag!("ExpirationMonth", cc.month)
|
221
|
+
xml.tag!("ExpirationYear", cc.year)
|
222
|
+
xml.tag!("IsECommerce", "true")
|
223
|
+
xml.tag!("Amount", amount(money))
|
224
|
+
xml.tag!("NameOnCard", name)
|
225
|
+
add_address(xml, parameters)
|
226
|
+
xml.tag!("CardSecurityCode", cc.verification_value) if cc.verification_value?
|
227
|
+
end
|
228
|
+
|
229
|
+
def build_CustomerCreditCardCapture(xml, money, parameters)
|
230
|
+
xml.tag!("TransRequestID", parameters[:trans_request_id])
|
231
|
+
xml.tag!("CreditCardTransID", parameters[:transaction_id])
|
232
|
+
xml.tag!("Amount", amount(money))
|
233
|
+
end
|
234
|
+
|
235
|
+
def build_CustomerCreditCardCharge(xml, money, parameters)
|
236
|
+
cc = parameters[:credit_card]
|
237
|
+
name = "#{cc.first_name} #{cc.last_name}"[0...30]
|
238
|
+
|
239
|
+
xml.tag!("TransRequestID", parameters[:trans_request_id])
|
240
|
+
xml.tag!("CreditCardNumber", cc.number)
|
241
|
+
xml.tag!("ExpirationMonth", cc.month)
|
242
|
+
xml.tag!("ExpirationYear", cc.year)
|
243
|
+
xml.tag!("IsECommerce", "true")
|
244
|
+
xml.tag!("Amount", amount(money))
|
245
|
+
xml.tag!("NameOnCard", name)
|
246
|
+
add_address(xml, parameters)
|
247
|
+
xml.tag!("CardSecurityCode", cc.verification_value) if cc.verification_value?
|
248
|
+
end
|
249
|
+
|
250
|
+
def build_CustomerCreditCardTxnVoidOrRefund(xml, money, parameters)
|
251
|
+
xml.tag!("TransRequestID", parameters[:trans_request_id])
|
252
|
+
xml.tag!("CreditCardTransID", parameters[:transaction_id])
|
253
|
+
xml.tag!("Amount", amount(money))
|
254
|
+
end
|
255
|
+
|
256
|
+
def build_CustomerCreditCardTxnVoid(xml, money, parameters)
|
257
|
+
xml.tag!("TransRequestID", parameters[:trans_request_id])
|
258
|
+
xml.tag!("CreditCardTransID", parameters[:transaction_id])
|
259
|
+
end
|
260
|
+
|
261
|
+
# Called reflectively by build_request
|
262
|
+
def build_MerchantAccountQuery(xml, money, parameters)
|
263
|
+
end
|
264
|
+
|
265
|
+
def add_address(xml, parameters)
|
266
|
+
if address = parameters[:billing_address] || parameters[:address]
|
267
|
+
xml.tag!("CreditCardAddress", address[:address1][0...30])
|
268
|
+
xml.tag!("CreditCardPostalCode", address[:zip][0...9])
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def cvv_result(response)
|
273
|
+
case response[:card_security_code_match]
|
274
|
+
when "Pass" then 'M'
|
275
|
+
when "Fail" then 'N'
|
276
|
+
when "NotAvailable" then 'P'
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def avs_result(response)
|
281
|
+
case "#{response[:avs_street]}|#{response[:avs_zip]}"
|
282
|
+
when "Pass|Pass" then "D"
|
283
|
+
when "Pass|Fail" then "A"
|
284
|
+
when "Pass|NotAvailable" then "B"
|
285
|
+
when "Fail|Pass" then "Z"
|
286
|
+
when "Fail|Fail" then "C"
|
287
|
+
when "Fail|NotAvailable" then "N"
|
288
|
+
when "NotAvailable|Pass" then "P"
|
289
|
+
when "NotAvailable|Fail" then "N"
|
290
|
+
when "NotAvailable|NotAvailable" then "U"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,282 @@
|
|
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
|
+
LIVE_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
|
+
@options = options
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
# Should run against the test servers or not?
|
45
|
+
def test?
|
46
|
+
@options[:test] || Base.gateway_mode == :test
|
47
|
+
end
|
48
|
+
|
49
|
+
# Request an authorization for an amount from CyberSource
|
50
|
+
#
|
51
|
+
def authorize(money, creditcard, options = {})
|
52
|
+
setup_address_hash(options)
|
53
|
+
commit(build_auth_request(money, creditcard, options), options )
|
54
|
+
end
|
55
|
+
|
56
|
+
# Capture an authorization that has previously been requested
|
57
|
+
def capture(money, authorization, options = {})
|
58
|
+
setup_address_hash(options)
|
59
|
+
commit(build_capture_request(money, authorization, options), options)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Purchase is an auth followed by a capture
|
63
|
+
# You must supply an order_id in the options hash
|
64
|
+
def purchase(money, creditcard, options = {})
|
65
|
+
setup_address_hash(options)
|
66
|
+
commit(build_purchase_request(money, creditcard, options), options)
|
67
|
+
end
|
68
|
+
|
69
|
+
def void(identification, options = {})
|
70
|
+
commit(build_void_request(identification, options), options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def refund(money, identification, options = {})
|
74
|
+
commit(build_credit_request(money, identification, options), options)
|
75
|
+
end
|
76
|
+
|
77
|
+
def credit(money, identification, options = {})
|
78
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
79
|
+
refund(money, identification, options)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def setup_address_hash(options)
|
85
|
+
options[:billing_address] = options[:billing_address] || options[:address] || {}
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_auth_request(money, creditcard, options)
|
89
|
+
xml = Builder::XmlMarkup.new
|
90
|
+
add_common_credit_card_info(xml,'AUTH_ONLY')
|
91
|
+
add_purchase_data(xml, money)
|
92
|
+
add_creditcard(xml, creditcard)
|
93
|
+
add_address(xml, creditcard, options[:billing_address], options)
|
94
|
+
add_invoice_details(xml, options)
|
95
|
+
add_customer_details(xml, options)
|
96
|
+
add_memo(xml, options)
|
97
|
+
add_business_rules_data(xml)
|
98
|
+
xml.target!
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_capture_request(money, authorization, options)
|
102
|
+
xml = Builder::XmlMarkup.new
|
103
|
+
add_common_credit_card_info(xml,'PREVIOUS_SALE')
|
104
|
+
transaction_id, _ = authorization_parts_from(authorization)
|
105
|
+
add_transaction_id(xml, transaction_id)
|
106
|
+
xml.target!
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_purchase_request(money, creditcard, options)
|
110
|
+
xml = Builder::XmlMarkup.new
|
111
|
+
add_common_credit_card_info(xml, @options[:ignore_avs] || @options[:ignore_cvv] ? 'SALES' : 'AUTH_CAPTURE')
|
112
|
+
add_address(xml, creditcard, options[:billing_address], options)
|
113
|
+
add_purchase_data(xml, money)
|
114
|
+
add_creditcard(xml, creditcard)
|
115
|
+
add_invoice_details(xml, options)
|
116
|
+
add_customer_details(xml, options)
|
117
|
+
add_memo(xml, options)
|
118
|
+
add_business_rules_data(xml)
|
119
|
+
xml.target!
|
120
|
+
end
|
121
|
+
|
122
|
+
def build_void_request(authorization, options)
|
123
|
+
xml = Builder::XmlMarkup.new
|
124
|
+
add_common_credit_card_info(xml,'VOID')
|
125
|
+
transaction_id, _ = authorization_parts_from(authorization)
|
126
|
+
add_transaction_id(xml, transaction_id)
|
127
|
+
xml.target!
|
128
|
+
end
|
129
|
+
|
130
|
+
def build_credit_request(money, authorization, options)
|
131
|
+
xml = Builder::XmlMarkup.new
|
132
|
+
add_common_credit_card_info(xml,'RETURN')
|
133
|
+
add_purchase_data(xml, money)
|
134
|
+
transaction_id, cc = authorization_parts_from(authorization)
|
135
|
+
add_transaction_id(xml, transaction_id)
|
136
|
+
xml.tag! 'CreditCardNumber', cc
|
137
|
+
xml.target!
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_common_credit_card_info(xml, process_type)
|
141
|
+
xml.tag! 'RequestType', 'ProcessSingleTransaction'
|
142
|
+
xml.tag! 'TransactionType', 'CREDIT'
|
143
|
+
xml.tag! 'PaymentType', 'CC'
|
144
|
+
xml.tag! 'ProcessType', process_type
|
145
|
+
end
|
146
|
+
|
147
|
+
def add_business_rules_data(xml)
|
148
|
+
xml.tag!('CustomerEmail', @options[:email_receipt] ? 'Y' : 'N')
|
149
|
+
xml.tag!('MerchantEmail', @options[:merchant_receipt] ? 'Y' : 'N')
|
150
|
+
end
|
151
|
+
|
152
|
+
def add_invoice_details(xml, options)
|
153
|
+
xml.tag! 'InvoiceNumber', options[:invoice]
|
154
|
+
xml.tag! 'InvoiceDescription', options[:merchant]
|
155
|
+
end
|
156
|
+
|
157
|
+
def add_customer_details(xml, options)
|
158
|
+
xml.tag! 'CustomerID', options[:customer]
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_transaction_id(xml, transaction_id)
|
162
|
+
xml.tag! 'TransactionID', transaction_id
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_memo(xml, options)
|
166
|
+
xml.tag! 'Memo', options[:description]
|
167
|
+
end
|
168
|
+
|
169
|
+
def add_purchase_data(xml, money = 0)
|
170
|
+
xml.tag! 'Amount', amount(money)
|
171
|
+
xml.tag! 'TransactionDate', Time.now
|
172
|
+
end
|
173
|
+
|
174
|
+
def add_address(xml, creditcard, address, options, shipTo = false)
|
175
|
+
xml.tag! 'FirstName', creditcard.first_name
|
176
|
+
xml.tag! 'LastName', creditcard.last_name
|
177
|
+
xml.tag! 'Address', address[:address1] # => there is no support for address2 in quantum
|
178
|
+
xml.tag! 'City', address[:city]
|
179
|
+
xml.tag! 'State', address[:state]
|
180
|
+
xml.tag! 'ZipCode', address[:zip]
|
181
|
+
xml.tag! 'Country', address[:country]
|
182
|
+
xml.tag! 'EmailAddress', options[:email]
|
183
|
+
xml.tag! 'IPAddress', options[:ip]
|
184
|
+
end
|
185
|
+
|
186
|
+
def add_creditcard(xml, creditcard)
|
187
|
+
xml.tag! 'PaymentType', 'CC'
|
188
|
+
xml.tag! 'CreditCardNumber', creditcard.number
|
189
|
+
xml.tag! 'ExpireMonth', format(creditcard.month, :two_digits)
|
190
|
+
xml.tag! 'ExpireYear', format(creditcard.year, :four_digits)
|
191
|
+
xml.tag!('CVV2', creditcard.verification_value) unless (@options[:ignore_cvv] || creditcard.verification_value.blank? )
|
192
|
+
end
|
193
|
+
|
194
|
+
# Where we actually build the full SOAP request using builder
|
195
|
+
def build_request(body, options)
|
196
|
+
xml = Builder::XmlMarkup.new
|
197
|
+
xml.instruct!
|
198
|
+
xml.tag! 'QGWRequest' do
|
199
|
+
xml.tag! 'Authentication' do
|
200
|
+
xml.tag! 'GatewayLogin', @options[:login]
|
201
|
+
xml.tag! 'GatewayKey', @options[:password]
|
202
|
+
end
|
203
|
+
xml.tag! 'Request' do
|
204
|
+
xml << body
|
205
|
+
end
|
206
|
+
end
|
207
|
+
xml.target!
|
208
|
+
end
|
209
|
+
|
210
|
+
# Contact CyberSource, make the SOAP request, and parse the reply into a Response object
|
211
|
+
def commit(request, options)
|
212
|
+
headers = { 'Content-Type' => 'text/xml' }
|
213
|
+
response = parse(ssl_post(LIVE_URL, build_request(request, options), headers))
|
214
|
+
|
215
|
+
success = response[:request_status] == "Success"
|
216
|
+
message = response[:request_message]
|
217
|
+
|
218
|
+
if success # => checking for connectivity success first
|
219
|
+
success = %w(APPROVED FORCED VOIDED).include?(response[:Status])
|
220
|
+
message = response[:StatusDescription]
|
221
|
+
authorization = success ? authorization_for(response) : nil
|
222
|
+
end
|
223
|
+
|
224
|
+
Response.new(success, message, response,
|
225
|
+
:test => test?,
|
226
|
+
:authorization => authorization,
|
227
|
+
:avs_result => { :code => response[:AVSResponseCode] },
|
228
|
+
:cvv_result => response[:CVV2ResponseCode]
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Parse the SOAP response
|
233
|
+
# Technique inspired by the Paypal Gateway
|
234
|
+
def parse(xml)
|
235
|
+
reply = {}
|
236
|
+
|
237
|
+
begin
|
238
|
+
xml = REXML::Document.new(xml)
|
239
|
+
|
240
|
+
root = REXML::XPath.first(xml, "//QGWRequest/ResponseSummary")
|
241
|
+
parse_element(reply, root)
|
242
|
+
reply[:request_status] = reply[:Status]
|
243
|
+
reply[:request_message] = "#{reply[:Status]}: #{reply[:StatusDescription]}"
|
244
|
+
|
245
|
+
if root = REXML::XPath.first(xml, "//QGWRequest/Result")
|
246
|
+
root.elements.to_a.each do |node|
|
247
|
+
parse_element(reply, node)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
rescue Exception => e
|
251
|
+
reply[:request_status] = 'Failure'
|
252
|
+
reply[:request_message] = "Failure: There was a problem parsing the response XML"
|
253
|
+
end
|
254
|
+
|
255
|
+
return reply
|
256
|
+
end
|
257
|
+
|
258
|
+
def parse_element(reply, node)
|
259
|
+
if node.has_elements?
|
260
|
+
node.elements.each{|e| parse_element(reply, e) }
|
261
|
+
else
|
262
|
+
if node.parent.name =~ /item/
|
263
|
+
parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
|
264
|
+
reply[(parent + '_' + node.name).to_sym] = node.text
|
265
|
+
else
|
266
|
+
reply[node.name.to_sym] = node.text
|
267
|
+
end
|
268
|
+
end
|
269
|
+
return reply
|
270
|
+
end
|
271
|
+
|
272
|
+
def authorization_for(reply)
|
273
|
+
"#{reply[:TransactionID]};#{reply[:CreditCardNumber]}"
|
274
|
+
end
|
275
|
+
|
276
|
+
def authorization_parts_from(authorization)
|
277
|
+
authorization.split(/;/)
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|