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,256 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class NmiGateway < Gateway
4
+ API_VERSION = '3.1'
5
+
6
+ self.test_url = 'https://secure.networkmerchants.com/gateway/transact.dll'
7
+ self.live_url = 'https://secure.networkmerchants.com/gateway/transact.dll'
8
+
9
+ class_attribute :duplicate_window
10
+
11
+ APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
12
+
13
+ RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT, AUTHORIZATION_CODE = 0, 2, 3, 4
14
+ AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE, CARDHOLDER_AUTH_CODE = 5, 6, 38, 39
15
+
16
+ self.default_currency = 'USD'
17
+
18
+ self.supported_countries = ['US']
19
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
20
+ self.homepage_url = 'http://nmi.com/'
21
+ self.display_name = 'NMI'
22
+
23
+ CARD_CODE_ERRORS = %w( N S )
24
+ AVS_ERRORS = %w( A E N R W Z )
25
+ AVS_REASON_CODES = %w(27 45)
26
+ TRANSACTION_ALREADY_ACTIONED = %w(310 311)
27
+
28
+ def initialize(options = {})
29
+ requires!(options, :login, :password)
30
+ super
31
+ end
32
+
33
+ def authorize(money, paysource, options = {})
34
+ post = {}
35
+ add_currency_code(post, money, options)
36
+ add_invoice(post, options)
37
+ add_payment_source(post, paysource, options)
38
+ add_address(post, options)
39
+ add_customer_data(post, options)
40
+ add_duplicate_window(post)
41
+
42
+ commit('AUTH_ONLY', money, post)
43
+ end
44
+
45
+ def purchase(money, paysource, options = {})
46
+ post = {}
47
+ add_currency_code(post, money, options)
48
+ add_invoice(post, options)
49
+ add_payment_source(post, paysource, options)
50
+ add_address(post, options)
51
+ add_customer_data(post, options)
52
+ add_duplicate_window(post)
53
+
54
+ commit('AUTH_CAPTURE', money, post)
55
+ end
56
+
57
+ def capture(money, authorization, options = {})
58
+ post = {:trans_id => authorization}
59
+ add_customer_data(post, options)
60
+ add_invoice(post, options)
61
+ commit('PRIOR_AUTH_CAPTURE', money, post)
62
+ end
63
+
64
+ def void(authorization, options = {})
65
+ post = {:trans_id => authorization}
66
+ add_duplicate_window(post)
67
+ commit('VOID', nil, post)
68
+ end
69
+
70
+ def refund(money, identification, options = {})
71
+ requires!(options, :card_number)
72
+
73
+ post = { :trans_id => identification,
74
+ :card_num => options[:card_number]
75
+ }
76
+
77
+ post[:first_name] = options[:first_name] if options[:first_name]
78
+ post[:last_name] = options[:last_name] if options[:last_name]
79
+ post[:zip] = options[:zip] if options[:zip]
80
+
81
+ add_invoice(post, options)
82
+ add_duplicate_window(post)
83
+
84
+ commit('CREDIT', money, post)
85
+ end
86
+
87
+ def credit(money, identification, options = {})
88
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
89
+ refund(money, identification, options)
90
+ end
91
+
92
+ def verify(credit_card, options = {})
93
+ MultiResponse.run(:use_first_response) do |r|
94
+ r.process { authorize(100, credit_card, options) }
95
+ r.process(:ignore_result) { void(r.authorization, options) }
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def commit(action, money, parameters)
102
+ parameters[:amount] = amount(money) unless action == 'VOID'
103
+
104
+ url = test? ? self.test_url : self.live_url
105
+ data = ssl_post(url, post_data(action, parameters))
106
+
107
+ response = parse(data)
108
+ response[:action] = action
109
+
110
+ message = message_from(response)
111
+
112
+ Response.new(success?(response), message, response,
113
+ :test => test?,
114
+ :authorization => response[:transaction_id],
115
+ :fraud_review => fraud_review?(response),
116
+ :avs_result => { :code => response[:avs_result_code] },
117
+ :cvv_result => response[:card_code]
118
+ )
119
+ end
120
+
121
+ def success?(response)
122
+ response[:response_code] == APPROVED && TRANSACTION_ALREADY_ACTIONED.exclude?(response[:response_reason_code])
123
+ end
124
+
125
+ def fraud_review?(response)
126
+ response[:response_code] == FRAUD_REVIEW
127
+ end
128
+
129
+ def parse(body)
130
+ fields = split(body)
131
+
132
+ results = {
133
+ :response_code => fields[RESPONSE_CODE].to_i,
134
+ :response_reason_code => fields[RESPONSE_REASON_CODE],
135
+ :response_reason_text => fields[RESPONSE_REASON_TEXT],
136
+ :avs_result_code => fields[AVS_RESULT_CODE],
137
+ :transaction_id => fields[TRANSACTION_ID],
138
+ :card_code => fields[CARD_CODE_RESPONSE_CODE],
139
+ :authorization_code => fields[AUTHORIZATION_CODE],
140
+ :cardholder_authentication_code => fields[CARDHOLDER_AUTH_CODE]
141
+ }
142
+ results
143
+ end
144
+
145
+ def post_data(action, parameters = {})
146
+ post = {}
147
+
148
+ post[:version] = API_VERSION
149
+ post[:login] = @options[:login]
150
+ post[:tran_key] = @options[:password]
151
+ post[:relay_response] = "FALSE"
152
+ post[:type] = action
153
+ post[:delim_data] = "TRUE"
154
+ post[:delim_char] = ","
155
+ post[:encap_char] = "$"
156
+ post[:solution_ID] = application_id if application_id.present? && application_id != "ActiveMerchant"
157
+
158
+ request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
159
+ request
160
+ end
161
+
162
+ def add_currency_code(post, money, options)
163
+ post[:currency_code] = options[:currency] || currency(money)
164
+ end
165
+
166
+ def add_invoice(post, options)
167
+ post[:invoice_num] = options[:order_id]
168
+ post[:description] = options[:description]
169
+ end
170
+
171
+ def add_creditcard(post, creditcard, options={})
172
+ post[:card_num] = creditcard.number
173
+ post[:card_code] = creditcard.verification_value if creditcard.verification_value?
174
+ post[:exp_date] = expdate(creditcard)
175
+ post[:first_name] = creditcard.first_name
176
+ post[:last_name] = creditcard.last_name
177
+
178
+ post[:recurring_billing] = "TRUE" if options[:recurring]
179
+ end
180
+
181
+ def add_payment_source(params, source, options={})
182
+ add_creditcard(params, source, options)
183
+ end
184
+
185
+ def add_customer_data(post, options)
186
+ if options.has_key? :email
187
+ post[:email] = options[:email]
188
+ post[:email_customer] = false
189
+ end
190
+
191
+ if options.has_key? :customer
192
+ post[:cust_id] = options[:customer] if Float(options[:customer]) rescue nil
193
+ end
194
+
195
+ if options.has_key? :ip
196
+ post[:customer_ip] = options[:ip]
197
+ end
198
+
199
+ if options.has_key? :cardholder_authentication_value
200
+ post[:cardholder_authentication_value] = options[:cardholder_authentication_value]
201
+ end
202
+
203
+ if options.has_key? :authentication_indicator
204
+ post[:authentication_indicator] = options[:authentication_indicator]
205
+ end
206
+
207
+ end
208
+
209
+ def add_duplicate_window(post)
210
+ unless duplicate_window.nil?
211
+ post[:duplicate_window] = duplicate_window
212
+ end
213
+ end
214
+
215
+ def add_address(post, options)
216
+ if address = options[:billing_address] || options[:address]
217
+ post[:address] = address[:address1].to_s
218
+ post[:company] = address[:company].to_s
219
+ post[:phone] = address[:phone].to_s
220
+ post[:zip] = address[:zip].to_s
221
+ post[:city] = address[:city].to_s
222
+ post[:country] = address[:country].to_s
223
+ post[:state] = address[:state].blank? ? 'n/a' : address[:state]
224
+ end
225
+
226
+ if address = options[:shipping_address]
227
+ post[:ship_to_first_name] = address[:first_name].to_s
228
+ post[:ship_to_last_name] = address[:last_name].to_s
229
+ post[:ship_to_address] = address[:address1].to_s
230
+ post[:ship_to_company] = address[:company].to_s
231
+ post[:ship_to_phone] = address[:phone].to_s
232
+ post[:ship_to_zip] = address[:zip].to_s
233
+ post[:ship_to_city] = address[:city].to_s
234
+ post[:ship_to_country] = address[:country].to_s
235
+ post[:ship_to_state] = address[:state].blank? ? 'n/a' : address[:state]
236
+ end
237
+ end
238
+
239
+ def message_from(results)
240
+ if results[:response_code] == DECLINED
241
+ return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
242
+ if AVS_REASON_CODES.include?(results[:response_reason_code]) && AVS_ERRORS.include?(results[:avs_result_code])
243
+ return AVSResult.messages[ results[:avs_result_code] ]
244
+ end
245
+ end
246
+
247
+ (results[:response_reason_text] ? results[:response_reason_text].chomp('.') : '')
248
+ end
249
+
250
+ def split(response)
251
+ response[1..-2].split(/\$,\$/)
252
+ end
253
+ end
254
+ end
255
+ end
256
+
@@ -0,0 +1,435 @@
1
+ # coding: utf-8
2
+ require 'rexml/document'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ # = Ogone DirectLink Gateway
7
+ #
8
+ # DirectLink is the API version of the Ogone Payment Platform. It allows server to server
9
+ # communication between Ogone systems and your e-commerce website.
10
+ #
11
+ # This implementation follows the specification provided in the DirectLink integration
12
+ # guide version 4.3.0 (25 April 2012), available here:
13
+ # https://secure.ogone.com/ncol/Ogone_DirectLink_EN.pdf
14
+ #
15
+ # It also features aliases, which allow to store/unstore credit cards, as specified in
16
+ # the Alias Manager Option guide version 3.2.1 (25 April 2012) available here:
17
+ # https://secure.ogone.com/ncol/Ogone_Alias_EN.pdf
18
+ #
19
+ # It also implements the 3-D Secure feature, as specified in the DirectLink with
20
+ # 3-D Secure guide version 3.0 (25 April 2012) available here:
21
+ # https://secure.ogone.com/ncol/Ogone_DirectLink-3-D_EN.pdf
22
+ #
23
+ # It was last tested on Release 4.92 of Ogone DirectLink + AliasManager + Direct Link 3D
24
+ # (25 April 2012).
25
+ #
26
+ # For any questions or comments, please contact one of the following:
27
+ # - Joel Cogen (joel.cogen@belighted.com)
28
+ # - Nicolas Jacobeus (nicolas.jacobeus@belighted.com),
29
+ # - Sébastien Grosjean (public@zencocoon.com),
30
+ # - Rémy Coutable (remy@jilion.com).
31
+ #
32
+ # == Usage
33
+ #
34
+ # gateway = ActiveMerchant::Billing::OgoneGateway.new(
35
+ # :login => "my_ogone_psp_id",
36
+ # :user => "my_ogone_user_id",
37
+ # :password => "my_ogone_pswd",
38
+ # :signature => "my_ogone_sha_signature", # Only if you configured your Ogone environment so.
39
+ # :signature_encryptor => "sha512" # Can be "none" (default), "sha1", "sha256" or "sha512".
40
+ # # Must be the same as the one configured in your Ogone account.
41
+ # )
42
+ #
43
+ # # set up credit card object as in main ActiveMerchant example
44
+ # creditcard = ActiveMerchant::Billing::CreditCard.new(
45
+ # :type => 'visa',
46
+ # :number => '4242424242424242',
47
+ # :month => 8,
48
+ # :year => 2009,
49
+ # :first_name => 'Bob',
50
+ # :last_name => 'Bobsen'
51
+ # )
52
+ #
53
+ # # run request
54
+ # response = gateway.purchase(1000, creditcard, :order_id => "1") # charge 10 EUR
55
+ #
56
+ # If you don't provide an :order_id, the gateway will generate a random one for you.
57
+ #
58
+ # puts response.success? # Check whether the transaction was successful
59
+ # puts response.message # Retrieve the message returned by Ogone
60
+ # puts response.authorization # Retrieve the unique transaction ID returned by Ogone
61
+ # puts response.order_id # Retrieve the order ID
62
+ #
63
+ # == Alias feature
64
+ #
65
+ # To use the alias feature, simply add :billing_id in the options hash:
66
+ #
67
+ # # Associate the alias to that credit card
68
+ # gateway.purchase(1000, creditcard, :order_id => "1", :billing_id => "myawesomecustomer")
69
+ #
70
+ # # You can use the alias instead of the credit card for subsequent orders
71
+ # gateway.purchase(2000, "myawesomecustomer", :order_id => "2")
72
+ #
73
+ # # You can also create an alias without making a purchase using store
74
+ # gateway.store(creditcard, :billing_id => "myawesomecustomer")
75
+ #
76
+ # # When using store, you can also let Ogone generate the alias for you
77
+ # response = gateway.store(creditcard)
78
+ # puts response.billing_id # Retrieve the generated alias
79
+ #
80
+ # # By default, Ogone tries to authorize 0.01 EUR but you can change this
81
+ # # amount using the :store_amount option when creating the gateway object:
82
+ # gateway = ActiveMerchant::Billing::OgoneGateway.new(
83
+ # :login => "my_ogone_psp_id",
84
+ # :user => "my_ogone_user_id",
85
+ # :password => "my_ogone_pswd",
86
+ # :signature => "my_ogone_sha_signature",
87
+ # :signature_encryptor => "sha512",
88
+ # :store_amount => 100 # The store method will try to authorize 1 EUR instead of 0.01 EUR
89
+ # )
90
+ # response = gateway.store(creditcard) # authorize 1 EUR and void the authorization right away
91
+ #
92
+ # == 3-D Secure feature
93
+ #
94
+ # To use the 3-D Secure feature, simply add :d3d => true in the options hash:
95
+ # gateway.purchase(2000, "myawesomecustomer", :order_id => "2", :d3d => true)
96
+ #
97
+ # Specific 3-D Secure request options are (please refer to the documentation for more infos about these options):
98
+ # :win_3ds => :main_window (default), :pop_up or :pop_ix.
99
+ # :http_accept => "*/*" (default), or any other HTTP_ACCEPT header value.
100
+ # :http_user_agent => The cardholder's User-Agent string
101
+ # :accept_url => URL of the web page to show the customer when the payment is authorized.
102
+ # (or waiting to be authorized).
103
+ # :decline_url => URL of the web page to show the customer when the acquirer rejects the authorization
104
+ # more than the maximum permitted number of authorization attempts (10 by default, but can
105
+ # be changed in the "Global transaction parameters" tab, "Payment retry" section of the
106
+ # Technical Information page).
107
+ # :exception_url => URL of the web page to show the customer when the payment result is uncertain.
108
+ # :paramplus => Field to submit the miscellaneous parameters and their values that you wish to be
109
+ # returned in the post sale request or final redirection.
110
+ # :complus => Field to submit a value you wish to be returned in the post sale request or output.
111
+ # :language => Customer's language, for example: "en_EN"
112
+ #
113
+ class OgoneGateway < Gateway
114
+ CVV_MAPPING = { 'OK' => 'M',
115
+ 'KO' => 'N',
116
+ 'NO' => 'P' }
117
+
118
+ AVS_MAPPING = { 'OK' => 'M',
119
+ 'KO' => 'N',
120
+ 'NO' => 'R' }
121
+
122
+ SUCCESS_MESSAGE = "The transaction was successful"
123
+
124
+ THREE_D_SECURE_DISPLAY_WAYS = { :main_window => 'MAINW', # display the identification page in the main window
125
+ # (default value).
126
+ :pop_up => 'POPUP', # display the identification page in a pop-up window
127
+ # and return to the main window at the end.
128
+ :pop_ix => 'POPIX' } # display the identification page in a pop-up window
129
+ # and remain in the pop-up window.
130
+
131
+ OGONE_NO_SIGNATURE_DEPRECATION_MESSAGE = "Signature usage will be the default for a future release of ActiveMerchant. You should either begin using it, or update your configuration to explicitly disable it (signature_encryptor: none)"
132
+ OGONE_STORE_OPTION_DEPRECATION_MESSAGE = "The 'store' option has been renamed to 'billing_id', and its usage is deprecated."
133
+
134
+ self.test_url = "https://secure.ogone.com/ncol/test/"
135
+ self.live_url = "https://secure.ogone.com/ncol/prod/"
136
+
137
+ self.supported_countries = ['BE', 'DE', 'FR', 'NL', 'AT', 'CH']
138
+ # also supports Airplus and UATP
139
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover, :jcb, :maestro]
140
+ self.homepage_url = 'http://www.ogone.com/'
141
+ self.display_name = 'Ogone'
142
+ self.default_currency = 'EUR'
143
+ self.money_format = :cents
144
+ self.ssl_version = :TLSv1
145
+
146
+ def initialize(options = {})
147
+ requires!(options, :login, :user, :password)
148
+ super
149
+ end
150
+
151
+ # Verify and reserve the specified amount on the account, without actually doing the transaction.
152
+ def authorize(money, payment_source, options = {})
153
+ post = {}
154
+ add_invoice(post, options)
155
+ add_payment_source(post, payment_source, options)
156
+ add_address(post, payment_source, options)
157
+ add_customer_data(post, options)
158
+ add_money(post, money, options)
159
+ commit('RES', post)
160
+ end
161
+
162
+ # Verify and transfer the specified amount.
163
+ def purchase(money, payment_source, options = {})
164
+ post = {}
165
+ action = options[:action] || 'SAL'
166
+ add_invoice(post, options)
167
+ add_payment_source(post, payment_source, options)
168
+ add_address(post, payment_source, options)
169
+ add_customer_data(post, options)
170
+ add_money(post, money, options)
171
+ commit(action, post)
172
+ end
173
+
174
+ # Complete a previously authorized transaction.
175
+ def capture(money, authorization, options = {})
176
+ post = {}
177
+ action = options[:action] || 'SAL'
178
+ add_authorization(post, reference_from(authorization))
179
+ add_invoice(post, options)
180
+ add_customer_data(post, options)
181
+ add_money(post, money, options)
182
+ commit(action, post)
183
+ end
184
+
185
+ # Cancels a previously authorized transaction.
186
+ def void(identification, options = {})
187
+ post = {}
188
+ add_authorization(post, reference_from(identification))
189
+ commit('DES', post)
190
+ end
191
+
192
+ # Credit the specified account by a specific amount.
193
+ def credit(money, identification_or_credit_card, options = {})
194
+ if reference_transaction?(identification_or_credit_card)
195
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
196
+ # Referenced credit: refund of a settled transaction
197
+ refund(money, identification_or_credit_card, options)
198
+ else # must be a credit card or card reference
199
+ perform_non_referenced_credit(money, identification_or_credit_card, options)
200
+ end
201
+ end
202
+
203
+ # Refund of a settled transaction
204
+ def refund(money, reference, options = {})
205
+ perform_reference_credit(money, reference, options)
206
+ end
207
+
208
+ # Store a credit card by creating an Ogone Alias
209
+ def store(payment_source, options = {})
210
+ options.merge!(:alias_operation => 'BYPSP') unless(options.has_key?(:billing_id) || options.has_key?(:store))
211
+ response = authorize(@options[:store_amount] || 1, payment_source, options)
212
+ void(response.authorization) if response.success?
213
+ response
214
+ end
215
+
216
+ private
217
+
218
+ def reference_from(authorization)
219
+ authorization.split(";").first
220
+ end
221
+
222
+ def reference_transaction?(identifier)
223
+ return false unless identifier.is_a?(String)
224
+ _, action = identifier.split(";")
225
+ !action.nil?
226
+ end
227
+
228
+ def perform_reference_credit(money, payment_target, options = {})
229
+ post = {}
230
+ add_authorization(post, reference_from(payment_target))
231
+ add_money(post, money, options)
232
+ commit('RFD', post)
233
+ end
234
+
235
+ def perform_non_referenced_credit(money, payment_target, options = {})
236
+ # Non-referenced credit: acts like a reverse purchase
237
+ post = {}
238
+ add_invoice(post, options)
239
+ add_payment_source(post, payment_target, options)
240
+ add_address(post, payment_target, options)
241
+ add_customer_data(post, options)
242
+ add_money(post, money, options)
243
+ commit('RFD', post)
244
+ end
245
+
246
+ def add_payment_source(post, payment_source, options)
247
+ add_d3d(post, options) if options[:d3d]
248
+
249
+ if payment_source.is_a?(String)
250
+ add_alias(post, payment_source, options[:alias_operation])
251
+ add_eci(post, options[:eci] || '9')
252
+ else
253
+ if options.has_key?(:store)
254
+ ActiveMerchant.deprecated OGONE_STORE_OPTION_DEPRECATION_MESSAGE
255
+ options[:billing_id] ||= options[:store]
256
+ end
257
+ add_alias(post, options[:billing_id], options[:alias_operation])
258
+ add_eci(post, options[:eci] || '7')
259
+ add_creditcard(post, payment_source)
260
+ end
261
+ end
262
+
263
+ def add_d3d(post, options)
264
+ add_pair post, 'FLAG3D', 'Y'
265
+ win_3ds = THREE_D_SECURE_DISPLAY_WAYS.key?(options[:win_3ds]) ?
266
+ THREE_D_SECURE_DISPLAY_WAYS[options[:win_3ds]] :
267
+ THREE_D_SECURE_DISPLAY_WAYS[:main_window]
268
+ add_pair post, 'WIN3DS', win_3ds
269
+
270
+ add_pair post, 'HTTP_ACCEPT', options[:http_accept] || "*/*"
271
+ add_pair post, 'HTTP_USER_AGENT', options[:http_user_agent] if options[:http_user_agent]
272
+ add_pair post, 'ACCEPTURL', options[:accept_url] if options[:accept_url]
273
+ add_pair post, 'DECLINEURL', options[:decline_url] if options[:decline_url]
274
+ add_pair post, 'EXCEPTIONURL', options[:exception_url] if options[:exception_url]
275
+ add_pair post, 'PARAMPLUS', options[:paramplus] if options[:paramplus]
276
+ add_pair post, 'COMPLUS', options[:complus] if options[:complus]
277
+ add_pair post, 'LANGUAGE', options[:language] if options[:language]
278
+ end
279
+
280
+ def add_eci(post, eci)
281
+ add_pair post, 'ECI', eci.to_s
282
+ end
283
+
284
+ def add_alias(post, _alias, alias_operation = nil)
285
+ add_pair post, 'ALIAS', _alias
286
+ add_pair post, 'ALIASOPERATION', alias_operation unless alias_operation.nil?
287
+ end
288
+
289
+ def add_authorization(post, authorization)
290
+ add_pair post, 'PAYID', authorization
291
+ end
292
+
293
+ def add_money(post, money, options)
294
+ add_pair post, 'currency', options[:currency] || @options[:currency] || currency(money)
295
+ add_pair post, 'amount', amount(money)
296
+ end
297
+
298
+ def add_customer_data(post, options)
299
+ add_pair post, 'EMAIL', options[:email]
300
+ add_pair post, 'REMOTE_ADDR', options[:ip]
301
+ end
302
+
303
+ def add_address(post, creditcard, options)
304
+ return unless options[:billing_address]
305
+ add_pair post, 'Owneraddress', options[:billing_address][:address1]
306
+ add_pair post, 'OwnerZip', options[:billing_address][:zip]
307
+ add_pair post, 'ownertown', options[:billing_address][:city]
308
+ add_pair post, 'ownercty', options[:billing_address][:country]
309
+ add_pair post, 'ownertelno', options[:billing_address][:phone]
310
+ end
311
+
312
+ def add_invoice(post, options)
313
+ add_pair post, 'orderID', options[:order_id] || generate_unique_id[0...30]
314
+ add_pair post, 'COM', options[:description]
315
+ end
316
+
317
+ def add_creditcard(post, creditcard)
318
+ add_pair post, 'CN', creditcard.name
319
+ add_pair post, 'CARDNO', creditcard.number
320
+ add_pair post, 'ED', "%02d%02s" % [creditcard.month, creditcard.year.to_s[-2..-1]]
321
+ add_pair post, 'CVC', creditcard.verification_value
322
+ end
323
+
324
+ def parse(body)
325
+ xml_root = REXML::Document.new(body).root
326
+ response = convert_attributes_to_hash(xml_root.attributes)
327
+
328
+ # Add HTML_ANSWER element (3-D Secure specific to the response's params)
329
+ # Note: HTML_ANSWER is not an attribute so we add it "by hand" to the response
330
+ if html_answer = REXML::XPath.first(xml_root, "//HTML_ANSWER")
331
+ response["HTML_ANSWER"] = html_answer.text
332
+ end
333
+
334
+ response
335
+ end
336
+
337
+ def commit(action, parameters)
338
+ add_pair parameters, 'PSPID', @options[:login]
339
+ add_pair parameters, 'USERID', @options[:user]
340
+ add_pair parameters, 'PSWD', @options[:password]
341
+
342
+ response = parse(ssl_post(url(parameters['PAYID']), post_data(action, parameters)))
343
+
344
+ options = {
345
+ :authorization => [response["PAYID"], action].join(";"),
346
+ :test => test?,
347
+ :avs_result => { :code => AVS_MAPPING[response["AAVCheck"]] },
348
+ :cvv_result => CVV_MAPPING[response["CVCCheck"]]
349
+ }
350
+ OgoneResponse.new(successful?(response), message_from(response), response, options)
351
+ end
352
+
353
+ def url(payid)
354
+ (test? ? test_url : live_url) + (payid ? "maintenancedirect.asp" : "orderdirect.asp")
355
+ end
356
+
357
+ def successful?(response)
358
+ response["NCERROR"] == "0"
359
+ end
360
+
361
+ def message_from(response)
362
+ if successful?(response)
363
+ SUCCESS_MESSAGE
364
+ else
365
+ format_error_message(response["NCERRORPLUS"])
366
+ end
367
+ end
368
+
369
+ def format_error_message(message)
370
+ raw_message = message.to_s.strip
371
+ case raw_message
372
+ when /\|/
373
+ raw_message.split("|").join(", ").capitalize
374
+ when /\//
375
+ raw_message.split("/").first.to_s.capitalize
376
+ else
377
+ raw_message.to_s.capitalize
378
+ end
379
+ end
380
+
381
+ def post_data(action, parameters = {})
382
+ add_pair parameters, 'Operation', action
383
+ add_signature(parameters)
384
+ parameters.to_query
385
+ end
386
+
387
+ def add_signature(parameters)
388
+ if @options[:signature].blank?
389
+ ActiveMerchant.deprecated(OGONE_NO_SIGNATURE_DEPRECATION_MESSAGE) unless(@options[:signature_encryptor] == "none")
390
+ return
391
+ end
392
+
393
+ sha_encryptor = case @options[:signature_encryptor]
394
+ when 'sha256'
395
+ Digest::SHA256
396
+ when 'sha512'
397
+ Digest::SHA512
398
+ else
399
+ Digest::SHA1
400
+ end
401
+
402
+ string_to_digest = if @options[:signature_encryptor]
403
+ parameters.sort { |a, b| a[0].upcase <=> b[0].upcase }.map { |k, v| "#{k.upcase}=#{v}" }.join(@options[:signature])
404
+ else
405
+ %w[orderID amount currency CARDNO PSPID Operation ALIAS].map { |key| parameters[key] }.join
406
+ end
407
+ string_to_digest << @options[:signature]
408
+
409
+ add_pair parameters, 'SHASign', sha_encryptor.hexdigest(string_to_digest).upcase
410
+ end
411
+
412
+ def add_pair(post, key, value)
413
+ post[key] = value if !value.blank?
414
+ end
415
+
416
+ def convert_attributes_to_hash(rexml_attributes)
417
+ response_hash = {}
418
+ rexml_attributes.each do |key, value|
419
+ response_hash[key] = value
420
+ end
421
+ response_hash
422
+ end
423
+ end
424
+
425
+ class OgoneResponse < Response
426
+ def order_id
427
+ @params['orderID']
428
+ end
429
+
430
+ def billing_id
431
+ @params['ALIAS']
432
+ end
433
+ end
434
+ end
435
+ end