jelaniharris-activemerchant 1.24.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. data/CHANGELOG +893 -0
  2. data/CONTRIBUTORS +307 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +196 -0
  5. data/gem-public_cert.pem +20 -0
  6. data/lib/active_merchant.rb +63 -0
  7. data/lib/active_merchant/billing.rb +9 -0
  8. data/lib/active_merchant/billing/avs_result.rb +98 -0
  9. data/lib/active_merchant/billing/base.rb +57 -0
  10. data/lib/active_merchant/billing/check.rb +68 -0
  11. data/lib/active_merchant/billing/credit_card.rb +274 -0
  12. data/lib/active_merchant/billing/credit_card_formatting.rb +21 -0
  13. data/lib/active_merchant/billing/credit_card_methods.rb +143 -0
  14. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  15. data/lib/active_merchant/billing/expiry_date.rb +34 -0
  16. data/lib/active_merchant/billing/gateway.rb +170 -0
  17. data/lib/active_merchant/billing/gateways.rb +18 -0
  18. data/lib/active_merchant/billing/gateways/authorize_net.rb +694 -0
  19. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +946 -0
  20. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +308 -0
  21. data/lib/active_merchant/billing/gateways/beanstream.rb +167 -0
  22. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +388 -0
  23. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  24. data/lib/active_merchant/billing/gateways/blue_pay.rb +492 -0
  25. data/lib/active_merchant/billing/gateways/bogus.rb +142 -0
  26. data/lib/active_merchant/billing/gateways/braintree.rb +17 -0
  27. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  28. data/lib/active_merchant/billing/gateways/braintree_blue.rb +335 -0
  29. data/lib/active_merchant/billing/gateways/braintree_orange.rb +21 -0
  30. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  31. data/lib/active_merchant/billing/gateways/card_stream.rb +230 -0
  32. data/lib/active_merchant/billing/gateways/certo_direct.rb +279 -0
  33. data/lib/active_merchant/billing/gateways/cyber_source.rb +430 -0
  34. data/lib/active_merchant/billing/gateways/data_cash.rb +597 -0
  35. data/lib/active_merchant/billing/gateways/efsnet.rb +235 -0
  36. data/lib/active_merchant/billing/gateways/elavon.rb +137 -0
  37. data/lib/active_merchant/billing/gateways/epay.rb +276 -0
  38. data/lib/active_merchant/billing/gateways/eway.rb +277 -0
  39. data/lib/active_merchant/billing/gateways/eway_managed.rb +265 -0
  40. data/lib/active_merchant/billing/gateways/exact.rb +227 -0
  41. data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
  42. data/lib/active_merchant/billing/gateways/first_pay.rb +177 -0
  43. data/lib/active_merchant/billing/gateways/garanti.rb +262 -0
  44. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -0
  45. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  46. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  47. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
  48. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  49. data/lib/active_merchant/billing/gateways/instapay.rb +164 -0
  50. data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
  51. data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
  52. data/lib/active_merchant/billing/gateways/jetpay.rb +276 -0
  53. data/lib/active_merchant/billing/gateways/linkpoint.rb +454 -0
  54. data/lib/active_merchant/billing/gateways/litle.rb +275 -0
  55. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +157 -0
  56. data/lib/active_merchant/billing/gateways/merchant_ware.rb +289 -0
  57. data/lib/active_merchant/billing/gateways/migs.rb +259 -0
  58. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  59. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  60. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +220 -0
  61. data/lib/active_merchant/billing/gateways/moneris.rb +250 -0
  62. data/lib/active_merchant/billing/gateways/moneris_us.rb +211 -0
  63. data/lib/active_merchant/billing/gateways/nab_transact.rb +255 -0
  64. data/lib/active_merchant/billing/gateways/net_registry.rb +189 -0
  65. data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
  66. data/lib/active_merchant/billing/gateways/netbilling.rb +168 -0
  67. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  68. data/lib/active_merchant/billing/gateways/ogone.rb +422 -0
  69. data/lib/active_merchant/billing/gateways/optimal_payment.rb +277 -0
  70. data/lib/active_merchant/billing/gateways/orbital.rb +353 -0
  71. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  72. data/lib/active_merchant/billing/gateways/pay_junction.rb +397 -0
  73. data/lib/active_merchant/billing/gateways/pay_secure.rb +120 -0
  74. data/lib/active_merchant/billing/gateways/paybox_direct.rb +199 -0
  75. data/lib/active_merchant/billing/gateways/payflow.rb +266 -0
  76. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +211 -0
  77. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  78. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  79. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  80. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  81. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  82. data/lib/active_merchant/billing/gateways/payment_express.rb +282 -0
  83. data/lib/active_merchant/billing/gateways/paypal.rb +106 -0
  84. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +640 -0
  85. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +49 -0
  86. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +245 -0
  87. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  88. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +43 -0
  89. data/lib/active_merchant/billing/gateways/paypal_express.rb +178 -0
  90. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  91. data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
  92. data/lib/active_merchant/billing/gateways/plugnpay.rb +298 -0
  93. data/lib/active_merchant/billing/gateways/psigate.rb +219 -0
  94. data/lib/active_merchant/billing/gateways/psl_card.rb +304 -0
  95. data/lib/active_merchant/billing/gateways/qbms.rb +297 -0
  96. data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
  97. data/lib/active_merchant/billing/gateways/quickpay.rb +298 -0
  98. data/lib/active_merchant/billing/gateways/realex.rb +313 -0
  99. data/lib/active_merchant/billing/gateways/sage.rb +146 -0
  100. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  101. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +116 -0
  102. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  103. data/lib/active_merchant/billing/gateways/sage_pay.rb +320 -0
  104. data/lib/active_merchant/billing/gateways/sallie_mae.rb +144 -0
  105. data/lib/active_merchant/billing/gateways/samurai.rb +121 -0
  106. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  107. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  108. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  109. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +113 -0
  110. data/lib/active_merchant/billing/gateways/skip_jack.rb +458 -0
  111. data/lib/active_merchant/billing/gateways/smart_ps.rb +271 -0
  112. data/lib/active_merchant/billing/gateways/stripe.rb +236 -0
  113. data/lib/active_merchant/billing/gateways/trans_first.rb +127 -0
  114. data/lib/active_merchant/billing/gateways/transax.rb +25 -0
  115. data/lib/active_merchant/billing/gateways/trust_commerce.rb +437 -0
  116. data/lib/active_merchant/billing/gateways/usa_epay.rb +23 -0
  117. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1496 -0
  118. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +202 -0
  119. data/lib/active_merchant/billing/gateways/verifi.rb +233 -0
  120. data/lib/active_merchant/billing/gateways/viaklix.rb +194 -0
  121. data/lib/active_merchant/billing/gateways/vindicia.rb +359 -0
  122. data/lib/active_merchant/billing/gateways/wirecard.rb +318 -0
  123. data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
  124. data/lib/active_merchant/billing/integrations.rb +17 -0
  125. data/lib/active_merchant/billing/integrations/action_view_helper.rb +72 -0
  126. data/lib/active_merchant/billing/integrations/authorize_net_sim.rb +38 -0
  127. data/lib/active_merchant/billing/integrations/authorize_net_sim/helper.rb +228 -0
  128. data/lib/active_merchant/billing/integrations/authorize_net_sim/notification.rb +340 -0
  129. data/lib/active_merchant/billing/integrations/bogus.rb +23 -0
  130. data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
  131. data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
  132. data/lib/active_merchant/billing/integrations/bogus/return.rb +10 -0
  133. data/lib/active_merchant/billing/integrations/chronopay.rb +23 -0
  134. data/lib/active_merchant/billing/integrations/chronopay/helper.rb +120 -0
  135. data/lib/active_merchant/billing/integrations/chronopay/notification.rb +158 -0
  136. data/lib/active_merchant/billing/integrations/chronopay/return.rb +10 -0
  137. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  138. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
  139. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  140. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  141. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  142. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  143. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  144. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  145. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  146. data/lib/active_merchant/billing/integrations/dotpay.rb +22 -0
  147. data/lib/active_merchant/billing/integrations/dotpay/helper.rb +77 -0
  148. data/lib/active_merchant/billing/integrations/dotpay/notification.rb +86 -0
  149. data/lib/active_merchant/billing/integrations/dotpay/return.rb +11 -0
  150. data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
  151. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +31 -0
  152. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +55 -0
  153. data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
  154. data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
  155. data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
  156. data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
  157. data/lib/active_merchant/billing/integrations/epay.rb +21 -0
  158. data/lib/active_merchant/billing/integrations/epay/helper.rb +55 -0
  159. data/lib/active_merchant/billing/integrations/epay/notification.rb +110 -0
  160. data/lib/active_merchant/billing/integrations/gestpay.rb +25 -0
  161. data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
  162. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +70 -0
  163. data/lib/active_merchant/billing/integrations/gestpay/notification.rb +85 -0
  164. data/lib/active_merchant/billing/integrations/gestpay/return.rb +10 -0
  165. data/lib/active_merchant/billing/integrations/helper.rb +113 -0
  166. data/lib/active_merchant/billing/integrations/hi_trust.rb +27 -0
  167. data/lib/active_merchant/billing/integrations/hi_trust/helper.rb +58 -0
  168. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +59 -0
  169. data/lib/active_merchant/billing/integrations/hi_trust/return.rb +67 -0
  170. data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
  171. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
  172. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
  173. data/lib/active_merchant/billing/integrations/nochex.rb +88 -0
  174. data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
  175. data/lib/active_merchant/billing/integrations/nochex/notification.rb +94 -0
  176. data/lib/active_merchant/billing/integrations/nochex/return.rb +10 -0
  177. data/lib/active_merchant/billing/integrations/notification.rb +62 -0
  178. data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
  179. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +100 -0
  180. data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
  181. data/lib/active_merchant/billing/integrations/paypal.rb +39 -0
  182. data/lib/active_merchant/billing/integrations/paypal/helper.rb +119 -0
  183. data/lib/active_merchant/billing/integrations/paypal/notification.rb +155 -0
  184. data/lib/active_merchant/billing/integrations/paypal/return.rb +10 -0
  185. data/lib/active_merchant/billing/integrations/quickpay.rb +21 -0
  186. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +71 -0
  187. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +74 -0
  188. data/lib/active_merchant/billing/integrations/return.rb +42 -0
  189. data/lib/active_merchant/billing/integrations/robokassa.rb +49 -0
  190. data/lib/active_merchant/billing/integrations/robokassa/common.rb +19 -0
  191. data/lib/active_merchant/billing/integrations/robokassa/helper.rb +50 -0
  192. data/lib/active_merchant/billing/integrations/robokassa/notification.rb +55 -0
  193. data/lib/active_merchant/billing/integrations/robokassa/return.rb +17 -0
  194. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  195. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  196. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +129 -0
  197. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
  198. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
  199. data/lib/active_merchant/billing/integrations/two_checkout.rb +44 -0
  200. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +91 -0
  201. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +139 -0
  202. data/lib/active_merchant/billing/integrations/two_checkout/return.rb +17 -0
  203. data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
  204. data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
  205. data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
  206. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +97 -0
  207. data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
  208. data/lib/active_merchant/billing/integrations/verkkomaksut.rb +20 -0
  209. data/lib/active_merchant/billing/integrations/verkkomaksut/helper.rb +87 -0
  210. data/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +59 -0
  211. data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
  212. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  213. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  214. data/lib/active_merchant/billing/response.rb +32 -0
  215. data/lib/active_merchant/version.rb +3 -0
  216. data/lib/activemerchant.rb +1 -0
  217. data/lib/support/gateway_support.rb +58 -0
  218. data/lib/support/outbound_hosts.rb +25 -0
  219. metadata +447 -0
@@ -0,0 +1,359 @@
1
+ begin
2
+ require "vindicia-api"
3
+ rescue LoadError
4
+ raise "Could not load the vindicia-api gem. Use `gem install vindicia-api` to install it."
5
+ end
6
+
7
+ module ActiveMerchant #:nodoc:
8
+ module Billing #:nodoc:
9
+
10
+ # For more information on the Vindicia Gateway please visit their {website}[http://vindicia.com/]
11
+ #
12
+ # The login and password are not the username and password you use to
13
+ # login to the Vindicia Merchant Portal.
14
+ #
15
+ # ==== Recurring Billing
16
+ #
17
+ # AutoBills are an feature of Vindicia's API that allows for creating and managing subscriptions.
18
+ #
19
+ # For more information about Vindicia's API and various other services visit their {Resource Center}[http://www.vindicia.com/resources/index.html]
20
+ class VindiciaGateway < Gateway
21
+ self.supported_countries = %w{US CA GB AU MX BR DE KR CN HK}
22
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
23
+ self.homepage_url = 'http://www.vindicia.com/'
24
+ self.display_name = 'Vindicia'
25
+
26
+ class_attribute :test_url, :live_url
27
+
28
+ self.test_url = "https://soap.prodtest.sj.vindicia.com/soap.pl"
29
+ self.live_url = "http://soap.vindicia.com/soap.pl"
30
+
31
+ # Creates a new VindiciaGateway
32
+ #
33
+ # The gateway requires that a valid login and password be passed
34
+ # in the +options+ hash.
35
+ #
36
+ # ==== Options
37
+ #
38
+ # * <tt>:login</tt> -- Vindicia SOAP login (REQUIRED)
39
+ # * <tt>:password</tt> -- Vindicia SOAP password (REQUIRED)
40
+ # * <tt>:api_version</tt> -- Vindicia API Version - defaults to 3.6 (OPTIONAL)
41
+ # * <tt>:account_id</tt> -- Account Id which all transactions will be run against. (REQUIRED)
42
+ # * <tt>:transaction_prefix</tt> -- Prefix to order id for one-time transactions - defaults to 'X' (OPTIONAL
43
+ # * <tt>:min_chargeback_probability</tt> -- Minimum score for chargebacks - defaults to 65 (OPTIONAL)
44
+ # * <tt>:cvn_success</tt> -- Array of valid CVN Check return values - defaults to [M, P] (OPTIONAL)
45
+ # * <tt>:avs_success</tt> -- Array of valid AVS Check return values - defaults to [X, Y, A, W, Z] (OPTIONAL)
46
+ def initialize(options = {})
47
+ requires!(options, :login, :password)
48
+
49
+ config = lambda do |config|
50
+ config.login = options[:login]
51
+ config.password = options[:password]
52
+ config.api_version = options[:api_version] || "3.6"
53
+ config.endpoint = test? ? self.test_url : self.live_url
54
+ config.namespace = "http://soap.vindicia.com"
55
+ end
56
+
57
+ if Vindicia.config.is_configured?
58
+ config.call(Vindicia.config)
59
+ else
60
+ Vindicia.configure(&config)
61
+ end
62
+
63
+ requires!(options, :account_id)
64
+ @account_id = options[:account_id]
65
+
66
+ @transaction_prefix = options[:transaction_prefix] || "X"
67
+
68
+ @min_chargeback_probability = options[:min_chargeback_probability] || 65
69
+ @cvn_success = options[:cvn_success] || %w{M P}
70
+ @avs_success = options[:avs_success] || %w{X Y A W Z}
71
+
72
+ @options = options
73
+ @allowed_authorization_statuses = %w{Authorized}
74
+ end
75
+
76
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
77
+ #
78
+ # ==== Parameters
79
+ #
80
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
81
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
82
+ # * <tt>options</tt> -- A hash of optional parameters.
83
+ def purchase(money, creditcard, options = {})
84
+ response = authorize(money, creditcard, options)
85
+ return response if !response.success? || response.fraud_review?
86
+
87
+ capture(money, response.authorization, options)
88
+ end
89
+
90
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
91
+ # charge the card.
92
+ #
93
+ # ==== Parameters
94
+ #
95
+ # * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
96
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
97
+ # * <tt>options</tt> -- A hash of optional parameters.
98
+ def authorize(money, creditcard, options = {})
99
+ vindicia_transaction = authorize_transaction(money, creditcard, options)
100
+ response = check_transaction(vindicia_transaction)
101
+
102
+ # if this response is under fraud review because of our AVS/CVV checks void the transaction
103
+ if !response.success? && response.fraud_review? && !response.authorization.blank?
104
+ void_response = void([vindicia_transaction[:transaction][:merchantTransactionId]], options)
105
+ if void_response.success?
106
+ return response
107
+ else
108
+ return void_response
109
+ end
110
+ end
111
+
112
+ response
113
+ end
114
+
115
+ # Captures the funds from an authorized transaction.
116
+ #
117
+ # ==== Parameters
118
+ #
119
+ # * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
120
+ # * <tt>identification</tt> -- The authorization returned from the previous authorize request.
121
+ def capture(money, identification, options = {})
122
+ response = post(Vindicia::Transaction.capture({
123
+ :transactions => [{ :merchantTransactionId => identification }]
124
+ }))
125
+
126
+ if response[:return][:returnCode] != '200' || response[:qtyFail].to_i > 0
127
+ return fail(response)
128
+ end
129
+
130
+ success(response, identification)
131
+ end
132
+
133
+ # Void a previous transaction
134
+ #
135
+ # ==== Parameters
136
+ #
137
+ # * <tt>identification</tt> - The authorization returned from the previous authorize request.
138
+ # * <tt>options</tt> - Extra options (currently only :ip used)
139
+ def void(identification, options = {})
140
+ response = post(Vindicia::Transaction.cancel({
141
+ :transactions => [{
142
+ :account => { :merchantAccountId => @account_id },
143
+ :merchantTransactionId => identification,
144
+ :sourceIp => options[:ip]
145
+ }]
146
+ }))
147
+
148
+ if response[:return][:returnCode] == '200' && response[:qtyFail].to_i == 0
149
+ success(response, identification)
150
+ else
151
+ fail(response)
152
+ end
153
+ end
154
+
155
+ # Perform a recurring billing, which is essentially a purchase and autobill setup in a single operation.
156
+ #
157
+ # ==== Parameters
158
+ #
159
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
160
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
161
+ # * <tt>options</tt> -- A hash of parameters.
162
+ #
163
+ # ==== Options
164
+ #
165
+ # * <tt>:product_sku</tt> -- The subscription product's sku
166
+ # * <tt>:autobill_prefix</tt> -- Prefix to order id for subscriptions - defaults to 'A' (OPTIONAL)
167
+ def recurring(money, creditcard, options={})
168
+ options[:recurring] = true
169
+ @autobill_prefix = options[:autobill_prefix] || "A"
170
+
171
+ response = authorize(money, creditcard, options)
172
+ return response if !response.success? || response.fraud_review?
173
+
174
+ capture_resp = capture(money, response.authorization, options)
175
+ return capture_resp if !response.success?
176
+
177
+ # Setting up a recurring AutoBill requires an associated product
178
+ requires!(options, :product_sku)
179
+ autobill_response = check_subscription(authorize_subscription(options.merge(:product_sku => options[:product_sku])))
180
+
181
+ if autobill_response.success?
182
+ autobill_response
183
+ else
184
+ # If the AutoBill fails to set-up, void the transaction and return it as the response
185
+ void_response = void(capture_resp.authorization, options)
186
+ if void_response.success?
187
+ return autobill_response
188
+ else
189
+ return void_response
190
+ end
191
+ end
192
+ end
193
+
194
+ protected
195
+
196
+ def post(body)
197
+ parse(ssl_post(Vindicia.config.endpoint, body, "Content-Type" => "text/xml"))
198
+ end
199
+
200
+ def parse(response)
201
+ # Vindicia always returns in the form of request_type_response => { actual_response }
202
+ Hash.from_xml(response)["Envelope"]["Body"].values.first.with_indifferent_access
203
+ end
204
+
205
+ def check_transaction(vindicia_transaction)
206
+ if vindicia_transaction[:return][:returnCode] == '200'
207
+ status_log = vindicia_transaction[:transaction][:statusLog].first
208
+ if status_log[:creditCardStatus]
209
+ avs = status_log[:creditCardStatus][:avsCode]
210
+ cvn = status_log[:creditCardStatus][:cvnCode]
211
+ end
212
+
213
+ if @allowed_authorization_statuses.include?(status_log[:status]) &&
214
+ check_cvn(cvn) && check_avs(avs)
215
+
216
+ success(vindicia_transaction,
217
+ vindicia_transaction[:transaction][:merchantTransactionId],
218
+ avs, cvn)
219
+ else
220
+ # If the transaction is authorized, but it didn't pass our AVS/CVV checks send the authorization along so
221
+ # that is gets voided. Otherwise, send no authorization.
222
+ fail(vindicia_transaction, avs, cvn, false,
223
+ @allowed_authorization_statuses.include?(status_log[:status]) ? vindicia_transaction[:transaction][:merchantTransactionId] : "")
224
+ end
225
+ else
226
+ # 406 = Chargeback risk score is higher than minChargebackProbability, transaction not authorized.
227
+ fail(vindicia_transaction, nil, nil, vindicia_transaction[:return][:return_code] == '406')
228
+ end
229
+ end
230
+
231
+ def authorize_transaction(money, creditcard, options)
232
+ parameters = {
233
+ :amount => amount(money),
234
+ :currency => options[:currency] || currency(money)
235
+ }
236
+
237
+ add_account_data(parameters, options)
238
+ add_customer_data(parameters, options)
239
+ add_payment_source(parameters, creditcard, options)
240
+
241
+ post(Vindicia::Transaction.auth({
242
+ :transaction => parameters,
243
+ :minChargebackProbability => @min_chargeback_probability
244
+ }))
245
+ end
246
+
247
+ def add_account_data(parameters, options)
248
+ parameters[:account] = { :merchantAccountId => @account_id }
249
+ parameters[:sourceIp] = options[:ip] if options[:ip]
250
+ end
251
+
252
+ def add_customer_data(parameters, options)
253
+ parameters[:merchantTransactionId] = transaction_id(options[:order_id])
254
+ parameters[:shippingAddress] = convert_am_address_to_vindicia(options[:shipping_address])
255
+
256
+ # Transaction items must be provided for tax purposes
257
+ requires!(options, :line_items)
258
+ parameters[:transactionItems] = options[:line_items]
259
+
260
+ if options[:recurring]
261
+ parameters[:nameValues] = [{:name => 'merchantAutoBillIdentifier', :value => autobill_id(options[:order_id])}]
262
+ end
263
+ end
264
+
265
+ def add_payment_source(parameters, creditcard, options)
266
+ parameters[:sourcePaymentMethod] = {
267
+ :type => 'CreditCard',
268
+ :creditCard => { :account => creditcard.number, :expirationDate => "%4d%02d" % [creditcard.year, creditcard.month] },
269
+ :accountHolderName => creditcard.name,
270
+ :nameValues => [{ :name => 'CVN', :value => creditcard.verification_value }],
271
+ :billingAddress => convert_am_address_to_vindicia(options[:billing_address] || options[:address]),
272
+ :customerSpecifiedType => creditcard.brand.capitalize,
273
+ :active => !!options[:recurring]
274
+ }
275
+ end
276
+
277
+ def authorize_subscription(options)
278
+ parameters = {}
279
+
280
+ add_account_data(parameters, options)
281
+ add_subscription_information(parameters, options)
282
+
283
+ post(Vindicia::AutoBill.update({
284
+ :autobill => parameters,
285
+ :validatePaymentMethod => false,
286
+ :minChargebackProbability => 100
287
+ }))
288
+ end
289
+
290
+ def check_subscription(vindicia_transaction)
291
+ if vindicia_transaction[:return][:returnCode] == '200'
292
+ if vindicia_transaction[:autobill] && vindicia_transaction[:autobill][:status] == "Active"
293
+ success(vindicia_transaction,
294
+ vindicia_transaction[:autobill][:merchantAutoBillId])
295
+ else
296
+ fail(vindicia_transaction)
297
+ end
298
+ else
299
+ fail(vindicia_transaction)
300
+ end
301
+ end
302
+
303
+ def add_subscription_information(parameters, options)
304
+ requires!(options, :product_sku)
305
+
306
+ if options[:shipping_address]
307
+ parameters[:account][:shipping_address] = options[:shipping_address]
308
+ end
309
+
310
+ parameters[:merchantAutoBillId] = autobill_id(options[:order_id])
311
+ parameters[:product] = { :merchantProductId => options[:product_sku] }
312
+ end
313
+
314
+ def check_avs(avs)
315
+ avs.blank? || @avs_success.include?(avs)
316
+ end
317
+
318
+ def check_cvn(cvn)
319
+ cvn.blank? || @cvn_success.include?(cvn)
320
+ end
321
+
322
+ def success(response, authorization, avs_code = nil, cvn_code = nil)
323
+ ActiveMerchant::Billing::Response.new(true, response[:return][:returnString], response,
324
+ { :fraud_review => false, :authorization => authorization, :test => test?,
325
+ :avs_result => { :code => avs_code }, :cvv_result => cvn_code })
326
+ end
327
+
328
+ def fail(response, avs_code = nil, cvn_code = nil, fraud_review = false, authorization = "")
329
+ ActiveMerchant::Billing::Response.new(false, response[:return][:returnString], response,
330
+ { :fraud_review => fraud_review || !authorization.blank?,
331
+ :authorization => authorization, :test => test?,
332
+ :avs_result => { :code => avs_code }, :cvv_result => cvn_code })
333
+
334
+ end
335
+
336
+ def autobill_id(order_id)
337
+ "#{@autobill_prefix}#{order_id}"
338
+ end
339
+
340
+ def transaction_id(order_id)
341
+ "#{@transaction_prefix}#{order_id}"
342
+ end
343
+
344
+ # Converts valid ActiveMerchant address hash to proper Vindicia format
345
+ def convert_am_address_to_vindicia(address)
346
+ return if address.nil?
347
+
348
+ convs = { :address1 => :addr1, :address2 => :addr2,
349
+ :state => :district, :zip => :postalCode }
350
+
351
+ vindicia_address = {}
352
+ address.each do |key, val|
353
+ vindicia_address[convs[key] || key] = val
354
+ end
355
+ vindicia_address
356
+ end
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,318 @@
1
+ require 'base64'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class WirecardGateway < Gateway
6
+ # Test server location
7
+ TEST_URL = 'https://c3-test.wirecard.com/secure/ssl-gateway'
8
+
9
+ # Live server location
10
+ LIVE_URL = 'https://c3.wirecard.com/secure/ssl-gateway'
11
+
12
+ # The Namespaces are not really needed, because it just tells the System, that there's actually no namespace used.
13
+ # It's just specified here for completeness.
14
+ ENVELOPE_NAMESPACES = {
15
+ 'xmlns:xsi' => 'http://www.w3.org/1999/XMLSchema-instance',
16
+ 'xsi:noNamespaceSchemaLocation' => 'wirecard.xsd'
17
+ }
18
+
19
+ PERMITTED_TRANSACTIONS = %w[ AUTHORIZATION CAPTURE_AUTHORIZATION PURCHASE ]
20
+
21
+ RETURN_CODES = %w[ ACK NOK ]
22
+
23
+ # Wirecard only allows phone numbers with a format like this: +xxx(yyy)zzz-zzzz-ppp, where:
24
+ # xxx = Country code
25
+ # yyy = Area or city code
26
+ # zzz-zzzz = Local number
27
+ # ppp = PBX extension
28
+ # For example, a typical U.S. or Canadian number would be "+1(202)555-1234-739" indicating PBX extension 739 at phone
29
+ # number 5551234 within area code 202 (country code 1).
30
+ VALID_PHONE_FORMAT = /\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/
31
+
32
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
33
+ # TODO: Check supported countries
34
+ self.supported_countries = ['DE']
35
+
36
+ # Wirecard supports all major credit and debit cards:
37
+ # Visa, Mastercard, American Express, Diners Club,
38
+ # JCB, Switch, VISA Carte Bancaire, Visa Electron and UATP cards.
39
+ # They also support the latest anti-fraud systems such as Verified by Visa or Master Secure Code.
40
+ self.supported_cardtypes = [
41
+ :visa, :master, :american_express, :diners_club, :jcb, :switch
42
+ ]
43
+
44
+ # The homepage URL of the gateway
45
+ self.homepage_url = 'http://www.wirecard.com'
46
+
47
+ # The name of the gateway
48
+ self.display_name = 'Wirecard'
49
+
50
+ # The currency should normally be EUROs
51
+ self.default_currency = 'EUR'
52
+
53
+ # 100 is 1.00 Euro
54
+ self.money_format = :cents
55
+
56
+ def initialize(options = {})
57
+ # verify that username and password are supplied
58
+ requires!(options, :login, :password)
59
+ # unfortunately Wirecard also requires a BusinessCaseSignature in the XML request
60
+ requires!(options, :signature)
61
+ @options = options
62
+ super
63
+ end
64
+
65
+ # Should run against the test servers or not?
66
+ def test?
67
+ @options[:test] || super
68
+ end
69
+
70
+ # Authorization
71
+ def authorize(money, creditcard, options = {})
72
+ prepare_options_hash(options)
73
+ @options[:credit_card] = creditcard
74
+ request = build_request(:authorization, money, @options)
75
+ commit(request)
76
+ end
77
+
78
+
79
+ # Capture Authorization
80
+ def capture(money, authorization, options = {})
81
+ prepare_options_hash(options)
82
+ @options[:authorization] = authorization
83
+ request = build_request(:capture_authorization, money, @options)
84
+ commit(request)
85
+ end
86
+
87
+
88
+ # Purchase
89
+ def purchase(money, creditcard, options = {})
90
+ prepare_options_hash(options)
91
+ @options[:credit_card] = creditcard
92
+ request = build_request(:purchase, money, @options)
93
+ commit(request)
94
+ end
95
+
96
+ private
97
+
98
+ def prepare_options_hash(options)
99
+ @options.update(options)
100
+ setup_address_hash!(options)
101
+ end
102
+
103
+ # Create all address hash key value pairs so that
104
+ # it still works if only provided with one or two of them
105
+ def setup_address_hash!(options)
106
+ options[:billing_address] = options[:billing_address] || options[:address] || {}
107
+ options[:shipping_address] = options[:shipping_address] || {}
108
+ # Include Email in address-hash from options-hash
109
+ options[:billing_address][:email] = options[:email] if options[:email]
110
+ end
111
+
112
+ # Contact WireCard, make the XML request, and parse the
113
+ # reply into a Response object
114
+ def commit(request)
115
+ headers = { 'Content-Type' => 'text/xml',
116
+ 'Authorization' => encoded_credentials }
117
+
118
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, request, headers))
119
+ # Pending Status also means Acknowledged (as stated in their specification)
120
+ success = response[:FunctionResult] == "ACK" || response[:FunctionResult] == "PENDING"
121
+ message = response[:Message]
122
+ authorization = (success && @options[:action] == :authorization) ? response[:GuWID] : nil
123
+
124
+ Response.new(success, message, response,
125
+ :test => test?,
126
+ :authorization => authorization,
127
+ :avs_result => { :code => response[:avsCode] },
128
+ :cvv_result => response[:cvCode]
129
+ )
130
+ end
131
+
132
+ # Generates the complete xml-message, that gets sent to the gateway
133
+ def build_request(action, money, options = {})
134
+ xml = Builder::XmlMarkup.new :indent => 2
135
+ xml.instruct!
136
+ xml.tag! 'WIRECARD_BXML' do
137
+ xml.tag! 'W_REQUEST' do
138
+ xml.tag! 'W_JOB' do
139
+ # TODO: OPTIONAL, check what value needs to be insert here
140
+ xml.tag! 'JobID', 'test dummy data'
141
+ # UserID for this transaction
142
+ xml.tag! 'BusinessCaseSignature', options[:signature] || options[:login]
143
+ # Create the whole rest of the message
144
+ add_transaction_data(xml, action, money, options)
145
+ end
146
+ end
147
+ end
148
+ xml.target!
149
+ end
150
+
151
+ # Includes the whole transaction data (payment, creditcard, address)
152
+ def add_transaction_data(xml, action, money, options = {})
153
+ options[:action] = action
154
+ # TODO: require order_id instead of auto-generating it if not supplied
155
+ options[:order_id] ||= generate_unique_id
156
+ transaction_type = action.to_s.upcase
157
+
158
+ xml.tag! "FNC_CC_#{transaction_type}" do
159
+ # TODO: OPTIONAL, check which param should be used here
160
+ xml.tag! 'FunctionID', options[:description] || 'Test dummy FunctionID'
161
+
162
+ xml.tag! 'CC_TRANSACTION' do
163
+ xml.tag! 'TransactionID', options[:order_id]
164
+ if [:authorization, :purchase].include?(action)
165
+ add_invoice(xml, money, options)
166
+ add_creditcard(xml, options[:credit_card])
167
+ add_address(xml, options[:billing_address])
168
+ elsif action == :capture_authorization
169
+ xml.tag! 'GuWID', options[:authorization] if options[:authorization]
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ # Includes the payment (amount, currency, country) to the transaction-xml
176
+ def add_invoice(xml, money, options)
177
+ xml.tag! 'Amount', amount(money)
178
+ xml.tag! 'Currency', options[:currency] || currency(money)
179
+ xml.tag! 'CountryCode', options[:billing_address][:country]
180
+ xml.tag! 'RECURRING_TRANSACTION' do
181
+ xml.tag! 'Type', options[:recurring] || 'Single'
182
+ end
183
+ end
184
+
185
+ # Includes the credit-card data to the transaction-xml
186
+ def add_creditcard(xml, creditcard)
187
+ raise "Creditcard must be supplied!" if creditcard.nil?
188
+ xml.tag! 'CREDIT_CARD_DATA' do
189
+ xml.tag! 'CreditCardNumber', creditcard.number
190
+ xml.tag! 'CVC2', creditcard.verification_value
191
+ xml.tag! 'ExpirationYear', creditcard.year
192
+ xml.tag! 'ExpirationMonth', format(creditcard.month, :two_digits)
193
+ xml.tag! 'CardHolderName', [creditcard.first_name, creditcard.last_name].join(' ')
194
+ end
195
+ end
196
+
197
+ # Includes the IP address of the customer to the transaction-xml
198
+ def add_customer_data(xml, options)
199
+ return unless options[:ip]
200
+ xml.tag! 'CONTACT_DATA' do
201
+ xml.tag! 'IPAddress', options[:ip]
202
+ end
203
+ end
204
+
205
+ # Includes the address to the transaction-xml
206
+ def add_address(xml, address)
207
+ return if address.nil?
208
+ xml.tag! 'CORPTRUSTCENTER_DATA' do
209
+ xml.tag! 'ADDRESS' do
210
+ xml.tag! 'Address1', address[:address1]
211
+ xml.tag! 'Address2', address[:address2] if address[:address2]
212
+ xml.tag! 'City', address[:city]
213
+ xml.tag! 'ZipCode', address[:zip]
214
+
215
+ if address[:state] =~ /[A-Za-z]{2}/ && address[:country] =~ /^(us|ca)$/i
216
+ xml.tag! 'State', address[:state].upcase
217
+ end
218
+
219
+ xml.tag! 'Country', address[:country]
220
+ xml.tag! 'Phone', address[:phone] if address[:phone] =~ VALID_PHONE_FORMAT
221
+ xml.tag! 'Email', address[:email]
222
+ end
223
+ end
224
+ end
225
+
226
+
227
+ # Read the XML message from the gateway and check if it was successful,
228
+ # and also extract required return values from the response.
229
+ def parse(xml)
230
+ basepath = '/WIRECARD_BXML/W_RESPONSE'
231
+ response = {}
232
+
233
+ xml = REXML::Document.new(xml)
234
+ if root = REXML::XPath.first(xml, "#{basepath}/W_JOB")
235
+ parse_response(response, root)
236
+ elsif root = REXML::XPath.first(xml, "//ERROR")
237
+ parse_error(response, root)
238
+ else
239
+ response[:Message] = "No valid XML response message received. \
240
+ Propably wrong credentials supplied with HTTP header."
241
+ end
242
+
243
+ response
244
+ end
245
+
246
+ # Parse the <ProcessingStatus> Element which containts all important information
247
+ def parse_response(response, root)
248
+ status = nil
249
+ # get the root element for this Transaction
250
+ root.elements.to_a.each do |node|
251
+ if node.name =~ /FNC_CC_/
252
+ status = REXML::XPath.first(node, "CC_TRANSACTION/PROCESSING_STATUS")
253
+ end
254
+ end
255
+ message = ""
256
+ if status
257
+ if info = status.elements['Info']
258
+ message << info.text
259
+ end
260
+ # Get basic response information
261
+ status.elements.to_a.each do |node|
262
+ response[node.name.to_sym] = (node.text || '').strip
263
+ end
264
+ end
265
+ parse_error(root, message)
266
+ response[:Message] = message
267
+ end
268
+
269
+ # Parse a generic error response from the gateway
270
+ def parse_error(root, message = "")
271
+ # Get errors if available and append them to the message
272
+ errors = errors_to_string(root)
273
+ unless errors.strip.blank?
274
+ message << ' - ' unless message.strip.blank?
275
+ message << errors
276
+ end
277
+ message
278
+ end
279
+
280
+ # Parses all <ERROR> elements in the response and converts the information
281
+ # to a single string
282
+ def errors_to_string(root)
283
+ # Get context error messages (can be 0..*)
284
+ errors = []
285
+ REXML::XPath.each(root, "//ERROR") do |error_elem|
286
+ error = {}
287
+ error[:Advice] = []
288
+ error[:Message] = error_elem.elements['Message'].text
289
+ error_elem.elements.each('Advice') do |advice|
290
+ error[:Advice] << advice.text
291
+ end
292
+ errors << error
293
+ end
294
+ # Convert all messages to a single string
295
+ string = ''
296
+ errors.each do |error|
297
+ string << error[:Message]
298
+ error[:Advice].each_with_index do |advice, index|
299
+ string << ' (' if index == 0
300
+ string << "#{index+1}. #{advice}"
301
+ string << ' and ' if index < error[:Advice].size - 1
302
+ string << ')' if index == error[:Advice].size - 1
303
+ end
304
+ end
305
+ string
306
+ end
307
+
308
+ # Encode login and password in Base64 to supply as HTTP header
309
+ # (for http basic authentication)
310
+ def encoded_credentials
311
+ credentials = [@options[:login], @options[:password]].join(':')
312
+ "Basic " << Base64.encode64(credentials).strip
313
+ end
314
+
315
+ end
316
+ end
317
+ end
318
+