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,97 @@
1
+ require 'json'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class WebpayGateway < StripeGateway
6
+ self.live_url = 'https://api.webpay.jp/v1/'
7
+
8
+ self.supported_countries = ['JP']
9
+ self.default_currency = 'JPY'
10
+ self.money_format = :cents
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :diners_club]
12
+
13
+ self.homepage_url = 'https://webpay.jp/'
14
+ self.display_name = 'WebPay'
15
+
16
+ def capture(money, authorization, options = {})
17
+ post = {}
18
+ add_amount(post, money, options)
19
+ add_application_fee(post, options)
20
+ commit(:post, "charges/#{CGI.escape(authorization)}/capture", post)
21
+ end
22
+
23
+ def refund(money, identification, options = {})
24
+ post = {}
25
+ add_amount(post, money, options)
26
+ MultiResponse.run do |r|
27
+ r.process { commit(:post, "charges/#{CGI.escape(identification)}/refund", post, options) }
28
+
29
+ return r unless options[:refund_fee_amount]
30
+
31
+ r.process { fetch_application_fees(identification, options) }
32
+ r.process { refund_application_fee(options[:refund_fee_amount], application_fee_from_response(r), options) }
33
+ end
34
+ end
35
+
36
+ def refund_fee(identification, options, meta)
37
+ raise NotImplementedError.new
38
+ end
39
+
40
+ def add_customer(post, creditcard, options)
41
+ post[:customer] = options[:customer] if options[:customer] && !creditcard.respond_to?(:number)
42
+ end
43
+
44
+ def store(creditcard, options = {})
45
+ post = {}
46
+ add_creditcard(post, creditcard, options)
47
+ post[:description] = options[:description]
48
+ post[:email] = options[:email]
49
+
50
+ if options[:customer]
51
+ MultiResponse.run(:first) do |r|
52
+ r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/", post, options) }
53
+
54
+ return r unless options[:set_default] and r.success? and !r.params["id"].blank?
55
+
56
+ r.process { update_customer(options[:customer], :default_card => r.params["id"]) }
57
+ end
58
+ else
59
+ commit(:post, 'customers', post, options)
60
+ end
61
+ end
62
+
63
+ def update(customer_id, creditcard, options = {})
64
+ post = {}
65
+ add_creditcard(post, creditcard, options)
66
+ commit(:post, "customers/#{CGI.escape(customer_id)}", post, options)
67
+ end
68
+
69
+ private
70
+
71
+ def create_post_for_auth_or_purchase(money, creditcard, options)
72
+ stripe_post = super
73
+ stripe_post[:description] ||= stripe_post.delete(:metadata).try(:[], :email)
74
+ stripe_post
75
+ end
76
+
77
+ def json_error(raw_response)
78
+ msg = 'Invalid response received from the WebPay API. Please contact support@webpay.jp if you continue to receive this message.'
79
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
80
+ {
81
+ "error" => {
82
+ "message" => msg
83
+ }
84
+ }
85
+ end
86
+
87
+ def headers(options = {})
88
+ {
89
+ "Authorization" => "Basic " + Base64.encode64(@api_key.to_s + ":").strip,
90
+ "User-Agent" => "Webpay/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
91
+ "X-Webpay-Client-User-Agent" => user_agent,
92
+ "X-Webpay-Client-User-Metadata" => {:ip => options[:ip]}.to_json
93
+ }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,189 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class WepayGateway < Gateway
4
+ self.test_url = 'https://stage.wepayapi.com/v2'
5
+ self.live_url = 'https://wepayapi.com/v2'
6
+
7
+ self.supported_countries = ['US']
8
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
9
+ self.homepage_url = 'https://www.wepay.com/'
10
+ self.default_currency = 'USD'
11
+ self.display_name = 'WePay'
12
+
13
+ def initialize(options = {})
14
+ requires!(options, :client_id, :account_id, :access_token)
15
+ super(options)
16
+ end
17
+
18
+ def purchase(money, payment_method, options = {})
19
+ post = {}
20
+ if payment_method.is_a?(String)
21
+ purchase_with_token(post, money, payment_method, options)
22
+ else
23
+ MultiResponse.run do |r|
24
+ r.process { store(payment_method, options) }
25
+ r.process { purchase_with_token(post, money, split_authorization(r.authorization).first, options) }
26
+ end
27
+ end
28
+ end
29
+
30
+ def authorize(money, payment_method, options = {})
31
+ post = {auto_capture: 0}
32
+ if payment_method.is_a?(String)
33
+ purchase_with_token(post, money, payment_method, options)
34
+ else
35
+ MultiResponse.run do |r|
36
+ r.process { store(payment_method, options) }
37
+ r.process { purchase_with_token(post, money, split_authorization(r.authorization).first, options) }
38
+ end
39
+ end
40
+ end
41
+
42
+ def capture(money, identifier, options = {})
43
+ post = {}
44
+ post[:checkout_id] = split_authorization(identifier).first
45
+ commit('/checkout/capture', post)
46
+ end
47
+
48
+ def void(identifier, options = {})
49
+ post = {}
50
+ post[:checkout_id] = split_authorization(identifier).first
51
+ post[:cancel_reason] = (options[:description] || "Void")
52
+ commit('/checkout/cancel', post)
53
+ end
54
+
55
+ def refund(money, identifier, options = {})
56
+ checkout_id, original_amount = split_authorization(identifier)
57
+
58
+ post = {}
59
+ post[:checkout_id] = checkout_id
60
+ if(money && (original_amount != amount(money)))
61
+ post[:amount] = amount(money)
62
+ end
63
+ post[:refund_reason] = (options[:description] || "Refund")
64
+ post[:app_fee] = options[:application_fee] if options[:application_fee]
65
+ post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message]
66
+ post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message]
67
+ commit("/checkout/refund", post)
68
+ end
69
+
70
+ def store(creditcard, options = {})
71
+ requires!(options, :email)
72
+
73
+ post = {}
74
+ post[:client_id] = @options[:client_id]
75
+ post[:user_name] = "#{creditcard.first_name} #{creditcard.last_name}"
76
+ post[:email] = options[:email]
77
+ post[:cc_number] = creditcard.number
78
+ post[:cvv] = creditcard.verification_value
79
+ post[:expiration_month] = creditcard.month
80
+ post[:expiration_year] = creditcard.year
81
+ post[:original_ip] = options[:ip] if options[:ip]
82
+ post[:original_device] = options[:device_fingerprint] if options[:device_fingerprint]
83
+ if(billing_address = (options[:billing_address] || options[:address]))
84
+ post[:address] = {
85
+ "address1" => billing_address[:address1],
86
+ "city" => billing_address[:city],
87
+ "state" => billing_address[:state],
88
+ "country" => billing_address[:country]
89
+ }
90
+ if(post[:country] == "US")
91
+ post[:address]["zip"] = billing_address[:zip]
92
+ else
93
+ post[:address]["postcode"] = billing_address[:zip]
94
+ end
95
+ end
96
+ commit('/credit_card/create', post)
97
+ end
98
+
99
+ private
100
+
101
+ def purchase_with_token(post, money, token, options)
102
+ add_token(post, token)
103
+ add_product_data(post, money, options)
104
+ commit('/checkout/create', post)
105
+ end
106
+
107
+ def add_product_data(post, money, options)
108
+ post[:account_id] = @options[:account_id]
109
+ post[:amount] = amount(money)
110
+ post[:short_description] = (options[:description] || "Purchase")
111
+ post[:type] = (options[:type] || "GOODS")
112
+ post[:currency] = (options[:currency] || currency(money))
113
+ post[:long_description] = options[:long_description] if options[:long_description]
114
+ post[:payer_email_message] = options[:payer_email_message] if options[:payer_email_message]
115
+ post[:payee_email_message] = options[:payee_email_message] if options[:payee_email_message]
116
+ post[:reference_id] = options[:order_id] if options[:order_id]
117
+ post[:app_fee] = options[:application_fee] if options[:application_fee]
118
+ post[:fee_payer] = options[:fee_payer] if options[:fee_payer]
119
+ post[:redirect_uri] = options[:redirect_uri] if options[:redirect_uri]
120
+ post[:callback_uri] = options[:callback_uri] if options[:callback_uri]
121
+ post[:fallback_uri] = options[:fallback_uri] if options[:fallback_uri]
122
+ post[:require_shipping] = options[:require_shipping] if options[:require_shipping]
123
+ post[:shipping_fee] = options[:shipping_fee] if options[:shipping_fee]
124
+ post[:charge_tax] = options[:charge_tax] if options[:charge_tax]
125
+ post[:mode] = options[:mode] if options[:mode]
126
+ post[:preapproval_id] = options[:preapproval_id] if options[:preapproval_id]
127
+ post[:prefill_info] = options[:prefill_info] if options[:prefill_info]
128
+ post[:funding_sources] = options[:funding_sources] if options[:funding_sources]
129
+ end
130
+
131
+ def add_token(post, token)
132
+ post[:payment_method_id] = token
133
+ post[:payment_method_type] = "credit_card"
134
+ end
135
+
136
+ def parse(response)
137
+ JSON.parse(response)
138
+ end
139
+
140
+ def commit(action, params, options={})
141
+ begin
142
+ response = parse(ssl_post(
143
+ ((test? ? test_url : live_url) + action),
144
+ params.to_json,
145
+ headers
146
+ ))
147
+ rescue ResponseError => e
148
+ response = parse(e.response.body)
149
+ end
150
+
151
+ Response.new(
152
+ success_from(response),
153
+ message_from(response),
154
+ response,
155
+ authorization: authorization_from(response, params),
156
+ test: test?
157
+ )
158
+ end
159
+
160
+ def success_from(response)
161
+ (!response["error"])
162
+ end
163
+
164
+ def message_from(response)
165
+ (response["error"] ? response["error_description"] : "Success")
166
+ end
167
+
168
+ def authorization_from(response, params)
169
+ return response["credit_card_id"].to_s if response["credit_card_id"]
170
+
171
+ [response["checkout_id"], params[:amount]].join('|')
172
+ end
173
+
174
+ def split_authorization(authorization)
175
+ auth, original_amount = authorization.to_s.split("|")
176
+ [auth, original_amount]
177
+ end
178
+
179
+ def headers
180
+ {
181
+ "Content-Type" => "application/json",
182
+ "User-Agent" => "ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
183
+ "Authorization" => "Bearer #{@options[:access_token]}"
184
+ }
185
+ end
186
+ end
187
+ end
188
+ end
189
+
@@ -0,0 +1,421 @@
1
+ require 'base64'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class WirecardGateway < Gateway
6
+ self.test_url = 'https://c3-test.wirecard.com/secure/ssl-gateway'
7
+ self.live_url = 'https://c3.wirecard.com/secure/ssl-gateway'
8
+
9
+ # The Namespaces are not really needed, because it just tells the System, that there's actually no namespace used.
10
+ # It's just specified here for completeness.
11
+ ENVELOPE_NAMESPACES = {
12
+ 'xmlns:xsi' => 'http://www.w3.org/1999/XMLSchema-instance',
13
+ 'xsi:noNamespaceSchemaLocation' => 'wirecard.xsd'
14
+ }
15
+
16
+ PERMITTED_TRANSACTIONS = %w[ PREAUTHORIZATION CAPTURE PURCHASE ]
17
+
18
+ RETURN_CODES = %w[ ACK NOK ]
19
+
20
+ # Wirecard only allows phone numbers with a format like this: +xxx(yyy)zzz-zzzz-ppp, where:
21
+ # xxx = Country code
22
+ # yyy = Area or city code
23
+ # zzz-zzzz = Local number
24
+ # ppp = PBX extension
25
+ # For example, a typical U.S. or Canadian number would be "+1(202)555-1234-739" indicating PBX extension 739 at phone
26
+ # number 5551234 within area code 202 (country code 1).
27
+ VALID_PHONE_FORMAT = /\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/
28
+
29
+ self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb, :switch ]
30
+ self.supported_countries = %w(AD CY GI IM MT RO CH AT DK GR IT MC SM TR BE EE HU LV NL SK GB BG FI IS LI NO SI VA FR IL LT PL ES CZ DE IE LU PT SE)
31
+ self.homepage_url = 'http://www.wirecard.com'
32
+ self.display_name = 'Wirecard'
33
+ self.default_currency = 'EUR'
34
+ self.money_format = :cents
35
+
36
+ # Public: Create a new Wirecard gateway.
37
+ #
38
+ # options - A hash of options:
39
+ # :login - The username
40
+ # :password - The password
41
+ # :signature - The BusinessCaseSignature
42
+ def initialize(options = {})
43
+ requires!(options, :login, :password, :signature)
44
+ super
45
+ end
46
+
47
+ # Authorization - the second parameter may be a CreditCard or
48
+ # a String which represents a GuWID reference to an earlier
49
+ # transaction. If a GuWID is given, rather than a CreditCard,
50
+ # then then the :recurring option will be forced to "Repeated"
51
+ def authorize(money, payment_method, options = {})
52
+ if payment_method.respond_to?(:number)
53
+ options[:credit_card] = payment_method
54
+ else
55
+ options[:preauthorization] = payment_method
56
+ end
57
+ commit(:preauthorization, money, options)
58
+ end
59
+
60
+ def capture(money, authorization, options = {})
61
+ options[:preauthorization] = authorization
62
+ commit(:capture, money, options)
63
+ end
64
+
65
+ # Purchase - the second parameter may be a CreditCard or
66
+ # a String which represents a GuWID reference to an earlier
67
+ # transaction. If a GuWID is given, rather than a CreditCard,
68
+ # then then the :recurring option will be forced to "Repeated"
69
+ def purchase(money, payment_method, options = {})
70
+ if payment_method.respond_to?(:number)
71
+ options[:credit_card] = payment_method
72
+ else
73
+ options[:preauthorization] = payment_method
74
+ end
75
+ commit(:purchase, money, options)
76
+ end
77
+
78
+ def void(identification, options = {})
79
+ options[:preauthorization] = identification
80
+ commit(:reversal, nil, options)
81
+ end
82
+
83
+ def refund(money, identification, options = {})
84
+ options[:preauthorization] = identification
85
+ commit(:bookback, money, options)
86
+ end
87
+
88
+ # Store card - Wirecard supports the notion of "Recurring
89
+ # Transactions" by allowing the merchant to provide a reference
90
+ # to an earlier transaction (the GuWID) rather than a credit
91
+ # card. A reusable reference (GuWID) can be obtained by sending
92
+ # a purchase or authorization transaction with the element
93
+ # "RECURRING_TRANSACTION/Type" set to "Initial". Subsequent
94
+ # transactions can then use the GuWID in place of a credit
95
+ # card by setting "RECURRING_TRANSACTION/Type" to "Repeated".
96
+ #
97
+ # This implementation of card store utilizes a Wirecard
98
+ # "Authorization Check" (a Preauthorization that is automatically
99
+ # reversed). It defaults to a check amount of "100" (i.e.
100
+ # $1.00) but this can be overriden (see below).
101
+ #
102
+ # IMPORTANT: In order to reuse the stored reference, the
103
+ # +authorization+ from the response should be saved by
104
+ # your application code.
105
+ #
106
+ # ==== Options specific to +store+
107
+ #
108
+ # * <tt>:amount</tt> -- The amount, in cents, that should be
109
+ # "validated" by the Authorization Check. This amount will
110
+ # be reserved and then reversed. Default is 100.
111
+ #
112
+ # Note: This is not the only way to achieve a card store
113
+ # operation at Wirecard. Any +purchase+ or +authorize+
114
+ # can be sent with +options[:recurring] = 'Initial'+ to make
115
+ # the returned authorization/GuWID usable in later transactions
116
+ # with +options[:recurring] = 'Repeated'+.
117
+ def store(creditcard, options = {})
118
+ options[:credit_card] = creditcard
119
+ options[:recurring] = 'Initial'
120
+ money = options.delete(:amount) || 100
121
+ # Amex does not support authorization_check
122
+ if creditcard.brand == 'american_express'
123
+ commit(:preauthorization, money, options)
124
+ else
125
+ commit(:authorization_check, money, options)
126
+ end
127
+ end
128
+
129
+ private
130
+ def clean_description(description)
131
+ description.to_s.slice(0,32).encode("US-ASCII", invalid: :replace, undef: :replace, replace: '?')
132
+ end
133
+
134
+ def prepare_options_hash(options)
135
+ result = @options.merge(options)
136
+ setup_address_hash!(result)
137
+ result
138
+ end
139
+
140
+ # Create all address hash key value pairs so that
141
+ # it still works if only provided with one or two of them
142
+ def setup_address_hash!(options)
143
+ options[:billing_address] = options[:billing_address] || options[:address] || {}
144
+ options[:shipping_address] = options[:shipping_address] || {}
145
+ # Include Email in address-hash from options-hash
146
+ options[:billing_address][:email] = options[:email] if options[:email]
147
+ end
148
+
149
+ # If a GuWID (string-based reference) is passed rather than a
150
+ # credit card, then the :recurring type needs to be forced to
151
+ # "Repeated"
152
+ def setup_recurring_flag(options)
153
+ options[:recurring] = 'Repeated' if options[:preauthorization].present?
154
+ end
155
+
156
+ # Contact WireCard, make the XML request, and parse the
157
+ # reply into a Response object
158
+ def commit(action, money, options)
159
+ request = build_request(action, money, options)
160
+
161
+ headers = { 'Content-Type' => 'text/xml',
162
+ 'Authorization' => encoded_credentials }
163
+
164
+ response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers))
165
+ # Pending Status also means Acknowledged (as stated in their specification)
166
+ success = response[:FunctionResult] == "ACK" || response[:FunctionResult] == "PENDING"
167
+ message = response[:Message]
168
+ authorization = response[:GuWID]
169
+
170
+ Response.new(success, message, response,
171
+ :test => test?,
172
+ :authorization => authorization,
173
+ :avs_result => { :code => avs_code(response, options) },
174
+ :cvv_result => response[:CVCResponseCode]
175
+ )
176
+ rescue ResponseError => e
177
+ if e.response.code == "401"
178
+ return Response.new(false, "Invalid Login")
179
+ else
180
+ raise
181
+ end
182
+ end
183
+
184
+ # Generates the complete xml-message, that gets sent to the gateway
185
+ def build_request(action, money, options)
186
+ options = prepare_options_hash(options)
187
+ options[:action] = action
188
+ xml = Builder::XmlMarkup.new :indent => 2
189
+ xml.instruct!
190
+ xml.tag! 'WIRECARD_BXML' do
191
+ xml.tag! 'W_REQUEST' do
192
+ xml.tag! 'W_JOB' do
193
+ xml.tag! 'JobID', ''
194
+ # UserID for this transaction
195
+ xml.tag! 'BusinessCaseSignature', options[:signature] || options[:login]
196
+ # Create the whole rest of the message
197
+ add_transaction_data(xml, money, options)
198
+ end
199
+ end
200
+ end
201
+ xml.target!
202
+ end
203
+
204
+ # Includes the whole transaction data (payment, creditcard, address)
205
+ def add_transaction_data(xml, money, options)
206
+ options[:order_id] ||= generate_unique_id
207
+
208
+ xml.tag! "FNC_CC_#{options[:action].to_s.upcase}" do
209
+ xml.tag! 'FunctionID', clean_description(options[:description])
210
+ xml.tag! 'CC_TRANSACTION' do
211
+ xml.tag! 'TransactionID', options[:order_id]
212
+ xml.tag! 'CommerceType', options[:commerce_type] if options[:commerce_type]
213
+ case options[:action]
214
+ when :preauthorization, :purchase, :authorization_check
215
+ setup_recurring_flag(options)
216
+ add_invoice(xml, money, options)
217
+
218
+ if options[:credit_card]
219
+ add_creditcard(xml, options[:credit_card])
220
+ else
221
+ xml.tag! 'GuWID', options[:preauthorization]
222
+ end
223
+
224
+ add_address(xml, options[:billing_address])
225
+ when :capture, :bookback
226
+ xml.tag! 'GuWID', options[:preauthorization]
227
+ add_amount(xml, money)
228
+ when :reversal
229
+ xml.tag! 'GuWID', options[:preauthorization]
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ # Includes the payment (amount, currency, country) to the transaction-xml
236
+ def add_invoice(xml, money, options)
237
+ add_amount(xml, money)
238
+ xml.tag! 'Currency', options[:currency] || currency(money)
239
+ xml.tag! 'CountryCode', options[:billing_address][:country]
240
+ xml.tag! 'RECURRING_TRANSACTION' do
241
+ xml.tag! 'Type', options[:recurring] || 'Single'
242
+ end
243
+ end
244
+
245
+ # Include the amount in the transaction-xml
246
+ def add_amount(xml, money)
247
+ xml.tag! 'Amount', amount(money)
248
+ end
249
+
250
+ # Includes the credit-card data to the transaction-xml
251
+ def add_creditcard(xml, creditcard)
252
+ raise "Creditcard must be supplied!" if creditcard.nil?
253
+ xml.tag! 'CREDIT_CARD_DATA' do
254
+ xml.tag! 'CreditCardNumber', creditcard.number
255
+ xml.tag! 'CVC2', creditcard.verification_value
256
+ xml.tag! 'ExpirationYear', creditcard.year
257
+ xml.tag! 'ExpirationMonth', format(creditcard.month, :two_digits)
258
+ xml.tag! 'CardHolderName', [creditcard.first_name, creditcard.last_name].join(' ')
259
+ end
260
+ end
261
+
262
+ # Includes the IP address of the customer to the transaction-xml
263
+ def add_customer_data(xml, options)
264
+ return unless options[:ip]
265
+ xml.tag! 'CONTACT_DATA' do
266
+ xml.tag! 'IPAddress', options[:ip]
267
+ end
268
+ end
269
+
270
+ # Includes the address to the transaction-xml
271
+ def add_address(xml, address)
272
+ return if address.nil?
273
+ xml.tag! 'CORPTRUSTCENTER_DATA' do
274
+ xml.tag! 'ADDRESS' do
275
+ xml.tag! 'Address1', address[:address1]
276
+ xml.tag! 'Address2', address[:address2] if address[:address2]
277
+ xml.tag! 'City', address[:city]
278
+ xml.tag! 'ZipCode', address[:zip]
279
+
280
+ if address[:state] =~ /[A-Za-z]{2}/ && address[:country] =~ /^(us|ca)$/i
281
+ xml.tag! 'State', address[:state].upcase
282
+ end
283
+
284
+ xml.tag! 'Country', address[:country]
285
+ xml.tag! 'Phone', address[:phone] if address[:phone] =~ VALID_PHONE_FORMAT
286
+ xml.tag! 'Email', address[:email]
287
+ end
288
+ end
289
+ end
290
+
291
+ # Read the XML message from the gateway and check if it was successful,
292
+ # and also extract required return values from the response.
293
+ def parse(xml)
294
+ basepath = '/WIRECARD_BXML/W_RESPONSE'
295
+ response = {}
296
+
297
+ xml = REXML::Document.new(xml)
298
+ if root = REXML::XPath.first(xml, "#{basepath}/W_JOB")
299
+ parse_response(response, root)
300
+ elsif root = REXML::XPath.first(xml, "//ERROR")
301
+ parse_error_only_response(response, root)
302
+ else
303
+ response[:Message] = "No valid XML response message received. \
304
+ Propably wrong credentials supplied with HTTP header."
305
+ end
306
+
307
+ response
308
+ end
309
+
310
+ def parse_error_only_response(response, root)
311
+ error_code = REXML::XPath.first(root, "Number")
312
+ response[:ErrorCode] = error_code.text if error_code
313
+ response[:Message] = parse_error(root)
314
+ end
315
+
316
+ # Parse the <ProcessingStatus> Element which contains all important information
317
+ def parse_response(response, root)
318
+ status = nil
319
+
320
+ root.elements.to_a.each do |node|
321
+ if node.name =~ /FNC_CC_/
322
+ status = REXML::XPath.first(node, "CC_TRANSACTION/PROCESSING_STATUS")
323
+ end
324
+ end
325
+
326
+ message = ""
327
+ if status
328
+ if info = status.elements['Info']
329
+ message << info.text
330
+ end
331
+
332
+ status.elements.to_a.each do |node|
333
+ if (node.elements.size == 0)
334
+ response[node.name.to_sym] = (node.text || '').strip
335
+ else
336
+ node.elements.each do |childnode|
337
+ name = "#{node.name}_#{childnode.name}"
338
+ response[name.to_sym] = (childnode.text || '').strip
339
+ end
340
+ end
341
+ end
342
+
343
+ error_code = REXML::XPath.first(status, "ERROR/Number")
344
+ response['ErrorCode'] = error_code.text if error_code
345
+ end
346
+
347
+ parse_error(root, message)
348
+ response[:Message] = message
349
+ end
350
+
351
+ # Parse a generic error response from the gateway
352
+ def parse_error(root, message = "")
353
+ # Get errors if available and append them to the message
354
+ errors = errors_to_string(root)
355
+ unless errors.strip.blank?
356
+ message << ' - ' unless message.strip.blank?
357
+ message << errors
358
+ end
359
+ message
360
+ end
361
+
362
+ # Parses all <ERROR> elements in the response and converts the information
363
+ # to a single string
364
+ def errors_to_string(root)
365
+ # Get context error messages (can be 0..*)
366
+ errors = []
367
+ REXML::XPath.each(root, "//ERROR") do |error_elem|
368
+ error = {}
369
+ error[:Advice] = []
370
+ error[:Message] = error_elem.elements['Message'].text
371
+ error_elem.elements.each('Advice') do |advice|
372
+ error[:Advice] << advice.text
373
+ end
374
+ errors << error
375
+ end
376
+ # Convert all messages to a single string
377
+ string = ''
378
+ errors.each do |error|
379
+ string << error[:Message] if error[:Message]
380
+ error[:Advice].each_with_index do |advice, index|
381
+ string << ' (' if index == 0
382
+ string << "#{index+1}. #{advice}"
383
+ string << ' and ' if index < error[:Advice].size - 1
384
+ string << ')' if index == error[:Advice].size - 1
385
+ end
386
+ end
387
+ string
388
+ end
389
+
390
+ # Amex have different AVS response codes
391
+ AMEX_TRANSLATED_AVS_CODES = {
392
+ "A" => "B", # CSC and Address Matched
393
+ "F" => "D", # All Data Matched
394
+ "N" => "I", # CSC Match
395
+ "U" => "U", # Data Not Checked
396
+ "Y" => "D", # All Data Matched
397
+ "Z" => "P", # CSC and Postcode Matched
398
+ "F" => "D" # Street address and zip code match
399
+ }
400
+
401
+ # Amex have different AVS response codes to visa etc
402
+ def avs_code(response, options)
403
+ if response.has_key?(:AVS_ProviderResultCode)
404
+ if options[:credit_card].present? && ActiveMerchant::Billing::CreditCard.brand?(options[:credit_card].number) == "american_express"
405
+ AMEX_TRANSLATED_AVS_CODES[response[:AVS_ProviderResultCode]]
406
+ else
407
+ response[:AVS_ProviderResultCode]
408
+ end
409
+ end
410
+ end
411
+
412
+ # Encode login and password in Base64 to supply as HTTP header
413
+ # (for http basic authentication)
414
+ def encoded_credentials
415
+ credentials = [@options[:login], @options[:password]].join(':')
416
+ "Basic " << Base64.encode64(credentials).strip
417
+ end
418
+ end
419
+ end
420
+ end
421
+