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,117 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class AlfabankGateway < Gateway
4
+
5
+ self.display_name = 'Alfabank'
6
+ self.homepage_url = 'http://www.alfabank.ru'
7
+ self.live_url = 'https://engine.paymentgate.ru/payment/rest'
8
+ self.money_format = :cents
9
+ self.ssl_strict = false
10
+ self.supported_cardtypes = [:visa, :master]
11
+ self.supported_countries = ['RU']
12
+ self.test_url = 'https://test.paymentgate.ru/testpayment/rest'
13
+
14
+ STATUSES_HASH = {
15
+ '0' => 'Order registered but not paid',
16
+ '1' => 'Waits when order will be completed',
17
+ '2' => 'Run the full authorization amount of the order',
18
+ '3' => 'Authorization revoked',
19
+ '4' => 'The transaction had an operation return',
20
+ '5' => 'Initiated ACS authorization through the issuing bank',
21
+ '6' => 'Authorization rejected'
22
+ }
23
+
24
+ # Creates a new AlfabankGateway
25
+ #
26
+ # The gateway requires that valid credentials be passed in the +options+
27
+ # hash.
28
+ #
29
+ # ==== Options
30
+ #
31
+ # * <tt>:account</tt> -- The Alfabank API account (REQUIRED)
32
+ # * <tt>:secret</tt> -- The Alfabank API secret (REQUIRED)
33
+ def initialize(options = {})
34
+ requires!(options, :account, :secret)
35
+ super
36
+ end
37
+
38
+ def make_order(options = {})
39
+ post = {}
40
+ add_order_number(post, options)
41
+ add_return_url(post, options)
42
+ add_amount(post, options)
43
+ add_description(post, options)
44
+
45
+ commit('register', options[:amount], post)
46
+ end
47
+
48
+ def get_order_status(options = {})
49
+ post = {}
50
+ add_order_number(post, options)
51
+ add_order_id(post, options)
52
+
53
+ commit('getOrderStatusExtended', nil, post)
54
+ end
55
+
56
+ private
57
+
58
+ def add_order_number(post, options)
59
+ post[:orderNumber] = options[:order_number]
60
+ end
61
+
62
+ def add_order_id(post, options)
63
+ post[:orderId] = options[:order_id]
64
+ end
65
+
66
+ def add_return_url(post, options)
67
+ post[:returnUrl] = options[:return_url]
68
+ end
69
+
70
+ def add_amount(post, options)
71
+ post[:amount] = amount(options[:amount])
72
+ end
73
+
74
+ def add_description(post, options)
75
+ post[:description] = options[:description]
76
+ end
77
+
78
+ def commit(action, money, parameters)
79
+ base_url = (test? ? self.test_url : self.live_url)
80
+ response = parse(ssl_post("#{base_url}/#{action}.do", post_data(action, parameters)))
81
+
82
+ success = !has_error?(response)
83
+ message = raw_message(response) || parse_status(response) || response['orderId']
84
+ params = convert_hash(response)
85
+
86
+ Response.new(success, message, params)
87
+ end
88
+
89
+ def post_data(action, parameters = {})
90
+ parameters.merge!({
91
+ :userName => @options[:account],
92
+ :password => @options[:secret]
93
+ }).to_query
94
+ end
95
+
96
+ def parse(response)
97
+ ActiveSupport::JSON.decode(response)
98
+ end
99
+
100
+ def has_error?(response)
101
+ response.blank? || !response['errorCode'].to_i.zero?
102
+ end
103
+
104
+ def raw_message(response)
105
+ response['errorMessage']
106
+ end
107
+
108
+ def convert_hash(hash)
109
+ Hash[hash.map { |key, value| [key.underscore, value] }]
110
+ end
111
+
112
+ def parse_status(response)
113
+ STATUSES_HASH[response['orderStatus'].to_s]
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,176 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class App55Gateway < Gateway
4
+ self.test_url = 'https://sandbox.app55.com/v1/'
5
+ self.live_url = 'https://api.app55.com/v1/'
6
+
7
+ self.supported_countries = ['AU', 'BR', 'CA', 'CH', 'CL', 'CN', 'CO', 'CZ', 'DK', 'GB', 'HK', 'HU', 'ID', 'IS', 'JP', 'KE', 'KR', 'MX', 'MY', 'NO', 'NZ', 'PH', 'PL', 'TH', 'TW', 'US', 'VN', 'ZA']
8
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :maestro, :solo]
9
+ self.default_currency = 'UKP'
10
+ self.money_format = :dollars
11
+ self.homepage_url = 'https://www.app55.com/'
12
+ self.display_name = 'App55'
13
+
14
+ # Create gateway
15
+ #
16
+ # options:
17
+ # :api_key - merchants App55 API Key
18
+ # :api_secret - merchants App55 Secret Key
19
+ def initialize(options = {})
20
+ requires!(options, :api_key, :api_secret)
21
+ @api_key = options[:api_key]
22
+ @api_secret = options[:api_secret]
23
+ super
24
+ end
25
+
26
+ # Make a purchase (authorize and commit)
27
+ #
28
+ # money - The monetary amount of the transaction in cents.
29
+ # payment_method - The CreditCard or the App55 card token.
30
+ # options - A standard ActiveMerchant options hash
31
+ def purchase(money, payment_method, options = {})
32
+ authorize(money, payment_method, options.merge(commit: true))
33
+ end
34
+
35
+ # Authorize a transaction.
36
+ #
37
+ # money - The monetary amount of the transaction in cents.
38
+ # payment_method - The CreditCard or the App55 card token.
39
+ # options - A standard ActiveMerchant options hash
40
+ def authorize(money, payment_method, options = {})
41
+ post = {}
42
+ add_creditcard(post, payment_method, options)
43
+ add_transaction(post, money, options)
44
+
45
+ commit(:post, 'transaction', post)
46
+ end
47
+
48
+ # Commit a pre-authorized transaction.
49
+ #
50
+ # money - The monetary amount of the transaction in cents.
51
+ # authorization - The App55 transaction id string.
52
+ # options - A standard ActiveMerchant options hash
53
+ def capture(money, authorization, options = {})
54
+ commit(:post, "transaction/#{authorization}")
55
+ end
56
+
57
+ private
58
+
59
+ def add_customer_data(post, options)
60
+ metadata_options = [:description, :browser_ip, :user_agent, :referrer]
61
+ post.update(options.slice(*metadata_options))
62
+ end
63
+
64
+ def add_creditcard(post, creditcard, options)
65
+ card = {}
66
+ card[:number] = creditcard.number
67
+ card[:expiry] = ("%02d". % creditcard.month) + '/' + creditcard.year.to_s
68
+ card[:security_code] = creditcard.verification_value if creditcard.verification_value?
69
+ card[:holder_name] = creditcard.name if creditcard.name
70
+ add_address(card, options)
71
+ post[:card] = card
72
+ end
73
+
74
+ def add_address(card, options)
75
+ return unless card && card.kind_of?(Hash)
76
+ address_hash = {}
77
+ if address = (options[:billing_address] || options[:address])
78
+ address_hash[:street] = address[:address1] if address[:address1]
79
+ address_hash[:street2] = address[:address2] if address[:address2]
80
+ address_hash[:country] = address[:country] if address[:country]
81
+ address_hash[:postal_code] = address[:zip] if address[:zip]
82
+ address_hash[:city] = address[:city] if address[:city]
83
+ card[:address] = address_hash
84
+ end
85
+ end
86
+
87
+ def add_transaction(post, money, options)
88
+ transaction = {}
89
+ add_amount(transaction, money, options)
90
+ transaction[:description] = (options[:description] || options[:email])
91
+ transaction[:commit] = options[:commit]
92
+ post[:transaction] = transaction
93
+ end
94
+
95
+ def add_amount(obj, money, options)
96
+ obj[:amount] = amount(money)
97
+ obj[:currency] = (options[:currency] || currency(money))
98
+ end
99
+
100
+ def parse(body)
101
+ JSON.parse(body)
102
+ rescue JSON::ParserError
103
+ json_error(raw_response)
104
+ end
105
+
106
+ def commit(method, resource, parameters=nil, meta={})
107
+ success = false
108
+ begin
109
+ raw_response = ssl_request(
110
+ method,
111
+ url(resource),
112
+ post_data(parameters),
113
+ headers
114
+ )
115
+ response = parse(raw_response)
116
+ success = response.key?("sig")
117
+ rescue ResponseError => e
118
+ response = parse(e.response.body)
119
+ end
120
+
121
+ Response.new(
122
+ success,
123
+ (success ? "OK" : response["error"]["message"]),
124
+ response,
125
+ test: test?,
126
+ authorization: authorization_from(response)
127
+ )
128
+ end
129
+
130
+ def authorization_from(response)
131
+ if response.key?("transaction")
132
+ response["transaction"]["id"]
133
+ elsif response.key?("card")
134
+ response["card"]["token"]
135
+ end
136
+ end
137
+
138
+ def json_error(raw_response)
139
+ msg = "Invalid response from app55 server: Received: #{raw_response.inspect})"
140
+ {
141
+ "error" => {
142
+ "message" => msg
143
+ }
144
+ }
145
+ end
146
+
147
+ def url(resource)
148
+ (test? ? self.test_url : self.live_url) + resource
149
+ end
150
+
151
+ def post_data(params)
152
+ return nil unless params
153
+
154
+ params.map do |key, value|
155
+ next if value.blank?
156
+ if value.is_a?(Hash)
157
+ h = {}
158
+ value.each do |k, v|
159
+ h["#{key}.#{k}"] = v unless v.blank?
160
+ end
161
+ post_data(h)
162
+ else
163
+ "#{key}=#{CGI.escape(value.to_s)}"
164
+ end
165
+ end.compact.join("&")
166
+ end
167
+
168
+ def headers
169
+ {
170
+ "Authorization" => "Basic " + Base64.strict_encode64(@options[:api_key].to_s + ":" + @options[:api_secret].to_s),
171
+ "User-Agent" => user_agent,
172
+ }
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,419 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class AuthorizeNetGateway < Gateway
6
+ include Empty
7
+
8
+ self.test_url = 'https://apitest.authorize.net/xml/v1/request.api'
9
+ self.live_url = 'https://api.authorize.net/xml/v1/request.api'
10
+
11
+ self.supported_countries = %w(AD AT AU BE BG CA CH CY CZ DE DK ES FI FR GB GB GI GR HU IE IT LI LU MC MT NL NO PL PT RO SE SI SK SM TR US VA)
12
+ self.default_currency = 'USD'
13
+ self.money_format = :dollars
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :maestro]
15
+
16
+ self.homepage_url = 'http://www.authorize.net/'
17
+ self.display_name = 'Authorize.Net'
18
+
19
+ class_attribute :duplicate_window
20
+
21
+ APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
22
+ TRANSACTION_ALREADY_ACTIONED = %w(310 311)
23
+
24
+ CARD_CODE_ERRORS = %w(N S)
25
+ AVS_ERRORS = %w(A E N R W Z)
26
+ AVS_REASON_CODES = %w(27 45)
27
+
28
+ TRACKS = {
29
+ 1 => /^%(?<format_code>.)(?<pan>[\d]{1,19}+)\^(?<name>.{2,26})\^(?<expiration>[\d]{0,4}|\^)(?<service_code>[\d]{0,3}|\^)(?<discretionary_data>.*)\?\Z/,
30
+ 2 => /\A;(?<pan>[\d]{1,19}+)=(?<expiration>[\d]{0,4}|=)(?<service_code>[\d]{0,3}|=)(?<discretionary_data>.*)\?\Z/
31
+ }.freeze
32
+
33
+ APPLE_PAY_DATA_DESCRIPTOR = "COMMON.APPLE.INAPP.PAYMENT"
34
+
35
+ def initialize(options={})
36
+ requires!(options, :login, :password)
37
+ super
38
+ end
39
+
40
+ def purchase(amount, payment, options = {})
41
+ commit("AUTH_CAPTURE") do |xml|
42
+ add_order_id(xml, options)
43
+ xml.transactionRequest do
44
+ xml.transactionType 'authCaptureTransaction'
45
+ xml.amount amount(amount)
46
+ add_payment_source(xml, payment)
47
+ add_invoice(xml, options)
48
+ add_customer_data(xml, payment, options)
49
+ add_retail_data(xml, payment)
50
+ add_settings(xml, payment, options)
51
+ add_user_fields(xml, amount, options)
52
+ end
53
+ end
54
+ end
55
+
56
+ def authorize(amount, payment, options={})
57
+ commit("AUTH_ONLY") do |xml|
58
+ add_order_id(xml, options)
59
+ xml.transactionRequest do
60
+ xml.transactionType 'authOnlyTransaction'
61
+ xml.amount amount(amount)
62
+ add_payment_source(xml, payment)
63
+ add_invoice(xml, options)
64
+ add_customer_data(xml, payment, options)
65
+ add_settings(xml, payment, options)
66
+ add_user_fields(xml, amount, options)
67
+ end
68
+ end
69
+ end
70
+
71
+ def capture(amount, authorization, options={})
72
+ commit("PRIOR_AUTH_CAPTURE") do |xml|
73
+ add_order_id(xml, options)
74
+ xml.transactionRequest do
75
+ xml.transactionType 'priorAuthCaptureTransaction'
76
+ xml.amount amount(amount)
77
+ xml.refTransId split_authorization(authorization)[0]
78
+ add_invoice(xml, options)
79
+ add_user_fields(xml, amount, options)
80
+ end
81
+ end
82
+ end
83
+
84
+ def refund(amount, authorization, options={})
85
+ transaction_id, card_number = split_authorization(authorization)
86
+ commit("CREDIT") do |xml|
87
+ xml.transactionRequest do
88
+ xml.transactionType 'refundTransaction'
89
+ xml.amount (amount.nil? ? 0 : amount(amount))
90
+ xml.payment do
91
+ xml.creditCard do
92
+ xml.cardNumber(card_number || options[:card_number])
93
+ xml.expirationDate 'XXXX'
94
+ end
95
+ end
96
+ xml.refTransId transaction_id
97
+ add_customer_data(xml, nil, options)
98
+ add_user_fields(xml, amount, options)
99
+ end
100
+ end
101
+ end
102
+
103
+ def void(authorization, options={})
104
+ commit("VOID") do |xml|
105
+ add_order_id(xml, options)
106
+ xml.transactionRequest do
107
+ xml.transactionType 'voidTransaction'
108
+ xml.refTransId split_authorization(authorization)[0]
109
+ add_user_fields(xml, nil, options)
110
+ end
111
+ end
112
+ end
113
+
114
+ def verify(credit_card, options = {})
115
+ MultiResponse.run(:use_first_response) do |r|
116
+ r.process { authorize(100, credit_card, options) }
117
+ r.process(:ignore_result) { void(r.authorization, options) }
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ def add_payment_source(xml, source)
124
+ return unless source
125
+ if card_brand(source) == 'check'
126
+ add_check(xml, source)
127
+ elsif card_brand(source) == 'apple_pay'
128
+ add_apple_pay_payment_token(xml, source)
129
+ else
130
+ add_credit_card(xml, source)
131
+ end
132
+ end
133
+
134
+ def add_settings(xml, source, options)
135
+ xml.transactionSettings do
136
+ if(card_brand(source) == "check" && options[:recurring])
137
+ xml.setting do
138
+ xml.settingName "recurringBilling"
139
+ xml.settingValue "true"
140
+ end
141
+ end
142
+ if(self.class.duplicate_window)
143
+ xml.setting do
144
+ xml.settingName "duplicateWindow"
145
+ xml.settingValue self.class.duplicate_window
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def add_user_fields(xml, amount, options)
152
+ xml.userFields do
153
+ if(currency = (options[:currency] || currency(amount)))
154
+ xml.userField do
155
+ xml.name "x_currency_code"
156
+ xml.value currency
157
+ end
158
+ end
159
+ if(application_id.present? && application_id != "ActiveMerchant")
160
+ xml.userField do
161
+ xml.name "x_solution_id"
162
+ xml.value application_id
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ def add_credit_card(xml, credit_card)
169
+ if credit_card.track_data
170
+ add_swipe_data(xml, credit_card)
171
+ else
172
+ xml.payment do
173
+ xml.creditCard do
174
+ xml.cardNumber credit_card.number
175
+ xml.expirationDate (format(credit_card.month, :two_digits) + '/' + format(credit_card.year, :four_digits))
176
+ unless empty?(credit_card.verification_value)
177
+ xml.cardCode credit_card.verification_value
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ def add_swipe_data(xml, credit_card)
185
+ TRACKS.each do |key, regex|
186
+ if regex.match(credit_card.track_data)
187
+ @valid_track_data = true
188
+ xml.payment do
189
+ xml.trackData do
190
+ xml.public_send(:"track#{key}", credit_card.track_data)
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ # http://developer.authorize.net/api/reference/#apple-pay-transactions
198
+ def add_apple_pay_payment_token(xml, apple_pay_payment_token)
199
+ xml.payment do
200
+ xml.opaqueData do
201
+ xml.dataDescriptor APPLE_PAY_DATA_DESCRIPTOR
202
+ xml.dataValue Base64.strict_encode64(apple_pay_payment_token.payment_data.to_json)
203
+ end
204
+ end
205
+ end
206
+
207
+ def add_retail_data(xml, payment)
208
+ return unless valid_track_data
209
+ xml.retail do
210
+ # As per http://www.authorize.net/support/CP_guide.pdf, '2' is for Retail, the only current market_type
211
+ xml.marketType 2
212
+ end
213
+ end
214
+
215
+ def valid_track_data
216
+ @valid_track_data ||= false
217
+ end
218
+
219
+ def add_check(xml, check)
220
+ xml.payment do
221
+ xml.bankAccount do
222
+ xml.routingNumber check.routing_number
223
+ xml.accountNumber check.account_number
224
+ xml.nameOnAccount check.name
225
+ xml.echeckType "WEB"
226
+ xml.bankName check.bank_name
227
+ xml.checkNumber check.number
228
+ end
229
+ end
230
+ end
231
+
232
+ def add_customer_data(xml, payment_source, options)
233
+ billing_address = options[:billing_address] || options[:address] || {}
234
+ shipping_address = options[:shipping_address] || options[:address] || {}
235
+
236
+ xml.customer do
237
+ xml.id(options[:customer]) unless empty?(options[:customer]) || options[:customer] !~ /^\d+$/
238
+ xml.email(options[:email]) unless empty?(options[:email])
239
+ end
240
+
241
+ xml.billTo do
242
+ first_name, last_name = names_from(payment_source, billing_address, options)
243
+ xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
244
+ xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
245
+
246
+ xml.company(truncate(billing_address[:company], 50)) unless empty?(billing_address[:company])
247
+ xml.address(truncate(billing_address[:address1], 60))
248
+ xml.city(truncate(billing_address[:city], 40))
249
+ xml.state(empty?(billing_address[:state]) ? 'n/a' : truncate(billing_address[:state], 40))
250
+ xml.zip(truncate((billing_address[:zip] || options[:zip]), 20))
251
+ xml.country(truncate(billing_address[:country], 60))
252
+ xml.phoneNumber(truncate(billing_address[:phone], 25)) unless empty?(billing_address[:phone])
253
+ xml.faxNumber(truncate(billing_address[:fax], 25)) unless empty?(billing_address[:fax])
254
+ end
255
+
256
+ unless shipping_address.blank?
257
+ xml.shipTo do
258
+ first_name, last_name = names_from(payment_source, shipping_address, options)
259
+ xml.firstName(truncate(first_name, 50)) unless empty?(first_name)
260
+ xml.lastName(truncate(last_name, 50)) unless empty?(last_name)
261
+
262
+ xml.company(truncate(shipping_address[:company], 50)) unless empty?(shipping_address[:company])
263
+ xml.address(truncate(shipping_address[:address1], 60))
264
+ xml.city(truncate(shipping_address[:city], 40))
265
+ xml.state(truncate(shipping_address[:state], 40))
266
+ xml.zip(truncate(shipping_address[:zip], 20))
267
+ xml.country(truncate(shipping_address[:country], 60))
268
+ end
269
+ end
270
+
271
+ xml.customerIP(options[:ip]) unless empty?(options[:ip])
272
+
273
+ xml.cardholderAuthentication do
274
+ xml.authenticationIndicator options[:authentication_indicator]
275
+ xml.cardholderAuthenticationValue options[:cardholder_authentication_value]
276
+ end
277
+ end
278
+
279
+ def add_order_id(xml, options)
280
+ xml.refId truncate(options[:order_id], 20)
281
+ end
282
+
283
+ def add_invoice(xml, options)
284
+ xml.order do
285
+ xml.invoiceNumber truncate(options[:order_id], 20)
286
+ xml.description truncate(options[:description], 255)
287
+ end
288
+ end
289
+
290
+ def names_from(payment_source, address, options)
291
+ if payment_source && !payment_source.is_a?(PaymentToken)
292
+ first_name, last_name = (address[:name] || "").split
293
+ [(payment_source.first_name || first_name), (payment_source.last_name || last_name)]
294
+ else
295
+ [options[:first_name], options[:last_name]]
296
+ end
297
+ end
298
+
299
+ def commit(action, &payload)
300
+ url = (test? ? test_url : live_url)
301
+ response = parse(action, ssl_post(url, post_data(&payload), 'Content-Type' => 'text/xml'))
302
+
303
+ avs_result = AVSResult.new(code: response[:avs_result_code])
304
+ cvv_result = CVVResult.new(response[:card_code])
305
+ Response.new(
306
+ success_from(response),
307
+ message_from(response, avs_result, cvv_result),
308
+ response,
309
+ authorization: authorization_from(response),
310
+ test: test?,
311
+ avs_result: avs_result,
312
+ cvv_result: cvv_result,
313
+ fraud_review: fraud_review?(response)
314
+ )
315
+ end
316
+
317
+ def post_data
318
+ Nokogiri::XML::Builder.new do |xml|
319
+ xml.createTransactionRequest('xmlns' => 'AnetApi/xml/v1/schema/AnetApiSchema.xsd') do
320
+ xml.merchantAuthentication do
321
+ xml.name @options[:login]
322
+ xml.transactionKey @options[:password]
323
+ end
324
+ yield(xml)
325
+ end
326
+ end.to_xml(indent: 0)
327
+ end
328
+
329
+ def parse(action, body)
330
+ doc = Nokogiri::XML(body)
331
+ doc.remove_namespaces!
332
+
333
+ response = {action: action}
334
+
335
+ response[:response_code] = if(element = doc.at_xpath("//transactionResponse/responseCode"))
336
+ (empty?(element.content) ? nil : element.content.to_i)
337
+ end
338
+
339
+ if(element = doc.at_xpath("//errors/error"))
340
+ response[:response_reason_code] = element.at_xpath("errorCode").content[/0*(\d+)$/, 1]
341
+ response[:response_reason_text] = element.at_xpath("errorText").content.chomp('.')
342
+ elsif(element = doc.at_xpath("//transactionResponse/messages/message"))
343
+ response[:response_reason_code] = element.at_xpath("code").content[/0*(\d+)$/, 1]
344
+ response[:response_reason_text] = element.at_xpath("description").content.chomp('.')
345
+ elsif(element = doc.at_xpath("//messages/message"))
346
+ response[:response_reason_code] = element.at_xpath("code").content[/0*(\d+)$/, 1]
347
+ response[:response_reason_text] = element.at_xpath("text").content.chomp('.')
348
+ else
349
+ response[:response_reason_code] = nil
350
+ response[:response_reason_text] = ""
351
+ end
352
+
353
+ response[:avs_result_code] = if(element = doc.at_xpath("//avsResultCode"))
354
+ (empty?(element.content) ? nil : element.content)
355
+ end
356
+
357
+ response[:transaction_id] = if(element = doc.at_xpath("//transId"))
358
+ (empty?(element.content) ? nil : element.content)
359
+ end
360
+
361
+ response[:card_code] = if(element = doc.at_xpath("//cvvResultCode"))
362
+ (empty?(element.content) ? nil : element.content)
363
+ end
364
+
365
+ response[:authorization_code] = if(element = doc.at_xpath("//authCode"))
366
+ (empty?(element.content) ? nil : element.content)
367
+ end
368
+
369
+ response[:cardholder_authentication_code] = if(element = doc.at_xpath("//cavvResultCode"))
370
+ (empty?(element.content) ? nil : element.content)
371
+ end
372
+
373
+ response[:account_number] = if(element = doc.at_xpath("//accountNumber"))
374
+ (empty?(element.content) ? nil : element.content[-4..-1])
375
+ end
376
+
377
+ response
378
+ end
379
+
380
+ def success_from(response)
381
+ (
382
+ response[:response_code] == APPROVED &&
383
+ TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code])
384
+ )
385
+ end
386
+
387
+ def message_from(response, avs_result, cvv_result)
388
+ if response[:response_code] == DECLINED
389
+ if CARD_CODE_ERRORS.include?(cvv_result.code)
390
+ return cvv_result.message
391
+ elsif(AVS_REASON_CODES.include?(response[:response_reason_code]) && AVS_ERRORS.include?(avs_result.code))
392
+ return avs_result.message
393
+ end
394
+ end
395
+
396
+ response[:response_reason_text]
397
+ end
398
+
399
+ def authorization_from(response)
400
+ [response[:transaction_id], response[:account_number]].join("#")
401
+ end
402
+
403
+ def split_authorization(authorization)
404
+ transaction_id, card_number = authorization.split("#")
405
+ [transaction_id, card_number]
406
+ end
407
+
408
+ def fraud_review?(response)
409
+ (response[:response_code] == FRAUD_REVIEW)
410
+ end
411
+
412
+ def truncate(value, max_size)
413
+ return nil unless value
414
+ value.to_s[0, max_size]
415
+ end
416
+
417
+ end
418
+ end
419
+ end