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,194 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class OpenpayGateway < Gateway
4
+ self.live_url = 'https://api.openpay.mx/v1/'
5
+ self.test_url = 'https://sandbox-api.openpay.mx/v1/'
6
+
7
+ self.supported_countries = ['MX']
8
+ self.supported_cardtypes = [:visa, :master, :american_express]
9
+ self.homepage_url = 'http://www.openpay.mx/'
10
+ self.display_name = 'Openpay'
11
+ self.default_currency = 'MXN'
12
+
13
+ # Instantiate a instance of OpenpayGateway by passing through your
14
+ # merchant id and private api key.
15
+ #
16
+ # === To obtain your own credentials
17
+ # 1. Visit http://openpay.mx
18
+ # 2. Sign up
19
+ # 3. Activate your account clicking on the email confirmation
20
+ def initialize(options = {})
21
+ requires!(options, :key, :merchant_id)
22
+ @api_key = options[:key]
23
+ @merchant_id = options[:merchant_id]
24
+ super
25
+ end
26
+
27
+ def purchase(money, creditcard, options = {})
28
+ post = create_post_for_auth_or_purchase(money, creditcard, options)
29
+ commit(:post, 'charges', post, options)
30
+ end
31
+
32
+ def authorize(money, creditcard, options = {})
33
+ post = create_post_for_auth_or_purchase(money, creditcard, options)
34
+ post[:capture] = false
35
+ commit(:post, 'charges', post, options)
36
+ end
37
+
38
+ def capture(money, authorization, options = {})
39
+ post = {}
40
+ post[:amount] = amount(money) if money
41
+ commit(:post, "charges/#{CGI.escape(authorization)}/capture", post, options)
42
+ end
43
+
44
+ def void(identification, options = {})
45
+ commit(:post, "charges/#{CGI.escape(identification)}/refund", nil, options)
46
+ end
47
+
48
+ def refund(money, identification, options = {})
49
+ post = {}
50
+ post[:description] = options[:description]
51
+ post[:amount] = amount(money)
52
+ commit(:post, "charges/#{CGI.escape(identification)}/refund", post, options)
53
+ end
54
+
55
+ def store(creditcard, options = {})
56
+ card_params = {}
57
+ add_creditcard(card_params, creditcard, options)
58
+ card = card_params[:card]
59
+
60
+ if options[:customer].present?
61
+ commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", card, options)
62
+ else
63
+ requires!(options, :email, :name)
64
+ post = {}
65
+ post[:name] = options[:name]
66
+ post[:email] = options[:email]
67
+ MultiResponse.run(:first) do |r|
68
+ r.process { commit(:post, 'customers', post, options) }
69
+
70
+ if(r.success? && !r.params['id'].blank?)
71
+ customer_id = r.params['id']
72
+ r.process { commit(:post, "customers/#{customer_id}/cards", card, options) }
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def unstore(customer_id, card_id = nil, options = {})
79
+ if card_id.nil?
80
+ commit(:delete, "customers/#{CGI.escape(customer_id)}", nil, options)
81
+ else
82
+ commit(:delete, "customers/#{CGI.escape(customer_id)}/cards/#{CGI.escape(card_id)}", nil, options)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def create_post_for_auth_or_purchase(money, creditcard, options)
89
+ post = {}
90
+ post[:amount] = amount(money)
91
+ post[:method] = 'card'
92
+ post[:description] = options[:description]
93
+ post[:order_id] = options[:order_id]
94
+ post[:device_session_id] = options[:device_session_id]
95
+ add_creditcard(post, creditcard, options)
96
+ post
97
+ end
98
+
99
+ def add_creditcard(post, creditcard, options)
100
+ if creditcard.kind_of?(String)
101
+ post[:source_id] = creditcard
102
+ elsif creditcard.respond_to?(:number)
103
+ card = {
104
+ card_number: creditcard.number,
105
+ expiration_month: "#{sprintf("%02d", creditcard.month)}",
106
+ expiration_year: "#{"#{creditcard.year}"[-2, 2]}",
107
+ cvv2: creditcard.verification_value,
108
+ holder_name: creditcard.name
109
+ }
110
+ add_address(card, options)
111
+ post[:card] = card
112
+ end
113
+ end
114
+
115
+ def add_address(card, options)
116
+ return unless card.kind_of?(Hash)
117
+ if address = (options[:billing_address] || options[:address])
118
+ card[:address] = {
119
+ line1: address[:address1],
120
+ line2: address[:address2],
121
+ line3: address[:company],
122
+ city: address[:city],
123
+ postal_code: address[:zip],
124
+ state: address[:state],
125
+ country_code: address[:country]
126
+ }
127
+ end
128
+ end
129
+
130
+ def headers(options = {})
131
+ {
132
+ "Content-Type" => "application/json",
133
+ "Authorization" => "Basic " + Base64.encode64(@api_key.to_s + ":").strip,
134
+ "User-Agent" => "Openpay/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
135
+ "X-Openpay-Client-User-Agent" => user_agent
136
+ }
137
+ end
138
+
139
+ def parse(body)
140
+ return {} unless body
141
+ JSON.parse(body)
142
+ end
143
+
144
+ def commit(method, resource, parameters, options = {})
145
+ response = http_request(method, resource, parameters, options)
146
+ success = !error?(response)
147
+
148
+ Response.new(success,
149
+ (success ? response['error_code'] : response['description']),
150
+ response,
151
+ :test => test?,
152
+ :authorization => response['id']
153
+ )
154
+ end
155
+
156
+ def http_request(method, resource, parameters={}, options={})
157
+ url = (test? ? self.test_url : self.live_url) + @merchant_id + '/' + resource
158
+ raw_response = nil
159
+ begin
160
+ raw_response = ssl_request(method, url, (parameters ? parameters.to_json : nil), headers(options))
161
+ parse(raw_response)
162
+ rescue ResponseError => e
163
+ raw_response = e.response.body
164
+ response_error(raw_response)
165
+ rescue JSON::ParserError
166
+ json_error(raw_response)
167
+ end
168
+ end
169
+
170
+ def error?(response)
171
+ response.key?('error_code')
172
+ end
173
+
174
+ def response_error(raw_response)
175
+ begin
176
+ parse(raw_response)
177
+ rescue JSON::ParserError
178
+ json_error(raw_response)
179
+ end
180
+ end
181
+
182
+ def json_error(raw_response)
183
+ msg = 'Invalid response received from the Openpay API. Please contact soporte@openpay.mx if you continue to receive this message.'
184
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
185
+ {
186
+ 'category' => 'request',
187
+ 'error_code' => '9999',
188
+ 'description' => msg
189
+ }
190
+ end
191
+ end
192
+ end
193
+ end
194
+
@@ -0,0 +1,313 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class OptimalPaymentGateway < Gateway
4
+ self.test_url = 'https://webservices.test.optimalpayments.com/creditcardWS/CreditCardServlet/v1'
5
+ self.live_url = 'https://webservices.optimalpayments.com/creditcardWS/CreditCardServlet/v1'
6
+
7
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
8
+ self.supported_countries = ['CA', 'US', 'GB']
9
+
10
+ # The card types supported by the payment gateway
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :solo] # :switch?
12
+
13
+ # The homepage URL of the gateway
14
+ self.homepage_url = 'http://www.optimalpayments.com/'
15
+
16
+ # The name of the gateway
17
+ self.display_name = 'Optimal Payments'
18
+
19
+ def initialize(options = {})
20
+
21
+ if(options[:login])
22
+ ActiveMerchant.deprecated("The 'login' option is deprecated in favor of 'store_id' and will be removed in a future version.")
23
+ options[:store_id] = options[:login]
24
+ end
25
+
26
+ if(options[:account])
27
+ ActiveMerchant.deprecated("The 'account' option is deprecated in favor of 'account_number' and will be removed in a future version.")
28
+ options[:account_number] = options[:account]
29
+ end
30
+
31
+ requires!(options, :account_number, :store_id, :password)
32
+ super
33
+ end
34
+
35
+ def authorize(money, card_or_auth, options = {})
36
+ parse_card_or_auth(card_or_auth, options)
37
+ commit("cc#{@stored_data}Authorize", money, options)
38
+ end
39
+ alias stored_authorize authorize # back-compat
40
+
41
+ def purchase(money, card_or_auth, options = {})
42
+ parse_card_or_auth(card_or_auth, options)
43
+ commit("cc#{@stored_data}Purchase", money, options)
44
+ end
45
+ alias stored_purchase purchase # back-compat
46
+
47
+ def refund(money, authorization, options = {})
48
+ options[:confirmationNumber] = authorization
49
+ commit('ccCredit', money, options)
50
+ end
51
+
52
+ def void(authorization, options = {})
53
+ options[:confirmationNumber] = authorization
54
+ commit('ccAuthorizeReversal', nil, options)
55
+ end
56
+
57
+ def capture(money, authorization, options = {})
58
+ options[:confirmationNumber] = authorization
59
+ commit('ccSettlement', money, options)
60
+ end
61
+
62
+ private
63
+
64
+ def parse_card_or_auth(card_or_auth, options)
65
+ if card_or_auth.respond_to?(:number)
66
+ @credit_card = card_or_auth
67
+ @stored_data = ""
68
+ else
69
+ options[:confirmationNumber] = card_or_auth
70
+ @stored_data = "StoredData"
71
+ end
72
+ end
73
+
74
+ def parse(body)
75
+ REXML::Document.new(body || '')
76
+ end
77
+
78
+ def commit(action, money, post)
79
+ post[:order_id] ||= 'order_id'
80
+
81
+ xml = case action
82
+ when 'ccAuthorize', 'ccPurchase', 'ccVerification'
83
+ cc_auth_request(money, post)
84
+ when 'ccCredit', 'ccSettlement'
85
+ cc_post_auth_request(money, post)
86
+ when 'ccStoredDataAuthorize', 'ccStoredDataPurchase'
87
+ cc_stored_data_request(money, post)
88
+ when 'ccAuthorizeReversal'
89
+ cc_auth_reversal_request(post)
90
+ #when 'ccCancelSettle', 'ccCancelCredit', 'ccCancelPayment'
91
+ # cc_cancel_request(money, post)
92
+ #when 'ccPayment'
93
+ # cc_payment_request(money, post)
94
+ #when 'ccAuthenticate'
95
+ # cc_authenticate_request(money, post)
96
+ else
97
+ raise 'Unknown Action'
98
+ end
99
+ txnRequest = escape_uri(xml)
100
+ response = parse(ssl_post(test? ? self.test_url : self.live_url, "txnMode=#{action}&txnRequest=#{txnRequest}"))
101
+
102
+ Response.new(successful?(response), message_from(response), hash_from_xml(response),
103
+ :test => test?,
104
+ :authorization => authorization_from(response),
105
+ :avs_result => { :code => avs_result_from(response) },
106
+ :cvv_result => cvv_result_from(response)
107
+ )
108
+ end
109
+
110
+ # The upstream is picky and so we can't use CGI.escape like we want to
111
+ def escape_uri(uri)
112
+ URI::DEFAULT_PARSER.escape(uri)
113
+ end
114
+
115
+ def successful?(response)
116
+ REXML::XPath.first(response, '//decision').text == 'ACCEPTED' rescue false
117
+ end
118
+
119
+ def message_from(response)
120
+ REXML::XPath.each(response, '//detail') do |detail|
121
+ if detail.is_a?(REXML::Element) && detail.elements['tag'].text == 'InternalResponseDescription'
122
+ return detail.elements['value'].text
123
+ end
124
+ end
125
+ nil
126
+ end
127
+
128
+ def authorization_from(response)
129
+ get_text_from_document(response, '//confirmationNumber')
130
+ end
131
+
132
+ def avs_result_from(response)
133
+ get_text_from_document(response, '//avsResponse')
134
+ end
135
+
136
+ def cvv_result_from(response)
137
+ get_text_from_document(response, '//cvdResponse')
138
+ end
139
+
140
+ def hash_from_xml(response)
141
+ hsh = {}
142
+ %w(confirmationNumber authCode
143
+ decision code description
144
+ actionCode avsResponse cvdResponse
145
+ txnTime duplicateFound
146
+ ).each do |tag|
147
+ node = REXML::XPath.first(response, "//#{tag}")
148
+ hsh[tag] = node.text if node
149
+ end
150
+ REXML::XPath.each(response, '//detail') do |detail|
151
+ next unless detail.is_a?(REXML::Element)
152
+ tag = detail.elements['tag'].text
153
+ value = detail.elements['value'].text
154
+ hsh[tag] = value
155
+ end
156
+ hsh
157
+ end
158
+
159
+ def xml_document(root_tag)
160
+ xml = Builder::XmlMarkup.new :indent => 2
161
+ xml.tag!(root_tag, schema) do
162
+ yield xml
163
+ end
164
+ xml.target!
165
+ end
166
+
167
+ def get_text_from_document(document, node)
168
+ node = REXML::XPath.first(document, node)
169
+ node && node.text
170
+ end
171
+
172
+ def cc_auth_request(money, opts)
173
+ xml_document('ccAuthRequestV1') do |xml|
174
+ build_merchant_account(xml)
175
+ xml.merchantRefNum opts[:order_id]
176
+ xml.amount(money/100.0)
177
+ build_card(xml, opts)
178
+ build_billing_details(xml, opts)
179
+ build_shipping_details(xml, opts)
180
+ xml.customerIP opts[:ip] if opts[:ip]
181
+ end
182
+ end
183
+
184
+ def cc_auth_reversal_request(opts)
185
+ xml_document('ccAuthReversalRequestV1') do |xml|
186
+ build_merchant_account(xml)
187
+ xml.confirmationNumber opts[:confirmationNumber]
188
+ xml.merchantRefNum opts[:order_id]
189
+ end
190
+ end
191
+
192
+ def cc_post_auth_request(money, opts)
193
+ xml_document('ccPostAuthRequestV1') do |xml|
194
+ build_merchant_account(xml)
195
+ xml.confirmationNumber opts[:confirmationNumber]
196
+ xml.merchantRefNum opts[:order_id]
197
+ xml.amount(money/100.0)
198
+ end
199
+ end
200
+
201
+ def cc_stored_data_request(money, opts)
202
+ xml_document('ccStoredDataRequestV1') do |xml|
203
+ build_merchant_account(xml)
204
+ xml.merchantRefNum opts[:order_id]
205
+ xml.confirmationNumber opts[:confirmationNumber]
206
+ xml.amount(money/100.0)
207
+ end
208
+ end
209
+
210
+ # untested
211
+ #
212
+ # def cc_cancel_request(opts)
213
+ # xml_document('ccCancelRequestV1') do |xml|
214
+ # build_merchant_account(xml)
215
+ # xml.confirmationNumber opts[:confirmationNumber]
216
+ # end
217
+ # end
218
+ #
219
+ # def cc_payment_request(money, opts)
220
+ # xml_document('ccPaymentRequestV1') do |xml|
221
+ # build_merchant_account(xml)
222
+ # xml.merchantRefNum opts[:order_id]
223
+ # xml.amount(money/100.0)
224
+ # build_card(xml, opts)
225
+ # build_billing_details(xml, opts)
226
+ # end
227
+ # end
228
+ #
229
+ # def cc_authenticate_request(opts)
230
+ # xml_document('ccAuthenticateRequestV1') do |xml|
231
+ # build_merchant_account(xml)
232
+ # xml.confirmationNumber opts[:confirmationNumber]
233
+ # xml.paymentResponse 'myPaymentResponse'
234
+ # end
235
+ # end
236
+
237
+ def schema
238
+ { 'xmlns' => 'http://www.optimalpayments.com/creditcard/xmlschema/v1',
239
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
240
+ 'xsi:schemaLocation' => 'http://www.optimalpayments.com/creditcard/xmlschema/v1'
241
+ }
242
+ end
243
+
244
+ def build_merchant_account(xml)
245
+ xml.tag! 'merchantAccount' do
246
+ xml.tag! 'accountNum' , @options[:account_number]
247
+ xml.tag! 'storeID' , @options[:store_id]
248
+ xml.tag! 'storePwd' , @options[:password]
249
+ end
250
+ end
251
+
252
+ def build_card(xml, opts)
253
+ xml.tag! 'card' do
254
+ xml.tag! 'cardNum' , @credit_card.number
255
+ xml.tag! 'cardExpiry' do
256
+ xml.tag! 'month' , @credit_card.month
257
+ xml.tag! 'year' , @credit_card.year
258
+ end
259
+ if brand = card_type(@credit_card.brand)
260
+ xml.tag! 'cardType' , brand
261
+ end
262
+ if @credit_card.verification_value
263
+ xml.tag! 'cvdIndicator' , '1' # Value Provided
264
+ xml.tag! 'cvd' , @credit_card.verification_value
265
+ end
266
+ end
267
+ end
268
+
269
+ def build_billing_details(xml, opts)
270
+ xml.tag! 'billingDetails' do
271
+ xml.tag! 'cardPayMethod', 'WEB'
272
+ build_address(xml, opts[:billing_address], opts[:email])
273
+ end
274
+ end
275
+
276
+ def build_shipping_details(xml, opts)
277
+ xml.tag! 'shippingDetails' do
278
+ build_address(xml, opts[:shipping_address], opts[:email])
279
+ end if opts[:shipping_address].present?
280
+ end
281
+
282
+ def build_address(xml, addr, email=nil)
283
+ if addr[:name]
284
+ xml.tag! 'firstName', addr[:name].split(' ').first
285
+ xml.tag! 'lastName' , addr[:name].split(' ').last
286
+ end
287
+ xml.tag! 'street' , addr[:address1] if addr[:address1].present?
288
+ xml.tag! 'street2', addr[:address2] if addr[:address2].present?
289
+ xml.tag! 'city' , addr[:city] if addr[:city].present?
290
+ if addr[:state].present?
291
+ state_tag = %w(US CA).include?(addr[:country]) ? 'state' : 'region'
292
+ xml.tag! state_tag, addr[:state]
293
+ end
294
+ xml.tag! 'country', addr[:country] if addr[:country].present?
295
+ xml.tag! 'zip' , addr[:zip] if addr[:zip].present?
296
+ xml.tag! 'phone' , addr[:phone] if addr[:phone].present?
297
+ xml.tag! 'email', email if email
298
+ end
299
+
300
+ def card_type(key)
301
+ { 'visa' => 'VI',
302
+ 'master' => 'MC',
303
+ 'american_express'=> 'AM',
304
+ 'discover' => 'DI',
305
+ 'diners_club' => 'DC',
306
+ #'switch' => '',
307
+ 'solo' => 'SO'
308
+ }[key]
309
+ end
310
+
311
+ end
312
+ end
313
+ end