bitfluent-activemerchant 1.5.1.1 → 1.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/CHANGELOG +168 -0
  2. data/CONTRIBUTORS +96 -1
  3. data/README.rdoc +33 -5
  4. data/lib/active_merchant.rb +12 -0
  5. data/lib/active_merchant/billing/credit_card.rb +59 -46
  6. data/lib/active_merchant/billing/credit_card_methods.rb +3 -3
  7. data/lib/active_merchant/billing/gateway.rb +16 -9
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -9
  9. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +134 -12
  10. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +309 -0
  11. data/lib/active_merchant/billing/gateways/beanstream.rb +39 -2
  12. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +64 -26
  13. data/lib/active_merchant/billing/gateways/blue_pay.rb +11 -0
  14. data/lib/active_merchant/billing/gateways/bogus.rb +33 -3
  15. data/lib/active_merchant/billing/gateways/braintree.rb +11 -11
  16. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  17. data/lib/active_merchant/billing/gateways/braintree_blue.rb +293 -0
  18. data/lib/active_merchant/billing/gateways/braintree_orange.rb +17 -0
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +5 -1
  20. data/lib/active_merchant/billing/gateways/data_cash.rb +6 -2
  21. data/lib/active_merchant/billing/gateways/efsnet.rb +8 -2
  22. data/lib/active_merchant/billing/gateways/epay.rb +268 -0
  23. data/lib/active_merchant/billing/gateways/eway_managed.rb +231 -0
  24. data/lib/active_merchant/billing/gateways/federated_canada.rb +168 -0
  25. data/lib/active_merchant/billing/gateways/first_pay.rb +8 -3
  26. data/lib/active_merchant/billing/gateways/garanti.rb +132 -92
  27. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +250 -0
  28. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  29. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  30. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +55 -0
  31. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  32. data/lib/active_merchant/billing/gateways/iridium.rb +258 -0
  33. data/lib/active_merchant/billing/gateways/jetpay.rb +12 -6
  34. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -1
  35. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +10 -8
  36. data/lib/active_merchant/billing/gateways/merchant_ware.rb +7 -1
  37. data/lib/active_merchant/billing/gateways/moneris.rb +6 -2
  38. data/lib/active_merchant/billing/gateways/netaxept.rb +239 -0
  39. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  40. data/lib/active_merchant/billing/gateways/ogone.rb +16 -3
  41. data/lib/active_merchant/billing/gateways/orbital.rb +317 -0
  42. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +46 -0
  43. data/lib/active_merchant/billing/gateways/pay_junction.rb +9 -9
  44. data/lib/active_merchant/billing/gateways/paybox_direct.rb +207 -0
  45. data/lib/active_merchant/billing/gateways/payflow.rb +26 -9
  46. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +11 -11
  47. data/lib/active_merchant/billing/gateways/payflow_express.rb +122 -38
  48. data/lib/active_merchant/billing/gateways/payment_express.rb +7 -2
  49. data/lib/active_merchant/billing/gateways/paypal.rb +5 -5
  50. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +33 -8
  51. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +26 -15
  52. data/lib/active_merchant/billing/gateways/paypal_ca.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/paypal_express.rb +55 -13
  54. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +8 -3
  55. data/lib/active_merchant/billing/gateways/plugnpay.rb +9 -3
  56. data/lib/active_merchant/billing/gateways/psigate.rb +5 -0
  57. data/lib/active_merchant/billing/gateways/qbms.rb +295 -0
  58. data/lib/active_merchant/billing/gateways/quantum.rb +282 -0
  59. data/lib/active_merchant/billing/gateways/quickpay.rb +7 -2
  60. data/lib/active_merchant/billing/gateways/realex.rb +187 -76
  61. data/lib/active_merchant/billing/gateways/sage_pay.rb +16 -5
  62. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  63. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +39 -3
  64. data/lib/active_merchant/billing/gateways/smart_ps.rb +9 -3
  65. data/lib/active_merchant/billing/gateways/trust_commerce.rb +7 -2
  66. data/lib/active_merchant/billing/gateways/usa_epay.rb +1 -1
  67. data/lib/active_merchant/billing/gateways/verifi.rb +6 -1
  68. data/lib/active_merchant/billing/gateways/viaklix.rb +1 -1
  69. data/lib/active_merchant/billing/gateways/worldpay.rb +280 -0
  70. data/lib/active_merchant/billing/integrations.rb +0 -12
  71. data/lib/active_merchant/billing/integrations/action_view_helper.rb +13 -24
  72. data/lib/active_merchant/billing/integrations/bogus.rb +2 -2
  73. data/lib/active_merchant/billing/integrations/chronopay.rb +2 -2
  74. data/lib/active_merchant/billing/integrations/direc_pay.rb +41 -0
  75. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +200 -0
  76. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  77. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  78. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  79. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  80. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  81. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  82. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  83. data/lib/active_merchant/billing/integrations/gestpay.rb +2 -2
  84. data/lib/active_merchant/billing/integrations/helper.rb +14 -11
  85. data/lib/active_merchant/billing/integrations/hi_trust.rb +2 -2
  86. data/lib/active_merchant/billing/integrations/ipay88.rb +4 -4
  87. data/lib/active_merchant/billing/integrations/moneybookers.rb +26 -0
  88. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +59 -0
  89. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +129 -0
  90. data/lib/active_merchant/billing/integrations/nochex.rb +2 -2
  91. data/lib/active_merchant/billing/integrations/notification.rb +1 -1
  92. data/lib/active_merchant/billing/integrations/paypal.rb +2 -2
  93. data/lib/active_merchant/billing/integrations/quickpay.rb +6 -2
  94. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +1 -1
  95. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +2 -2
  96. data/lib/active_merchant/billing/integrations/return.rb +10 -3
  97. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  98. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  99. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +111 -0
  100. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +210 -0
  101. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +31 -0
  102. data/lib/active_merchant/billing/integrations/two_checkout.rb +2 -2
  103. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +5 -5
  104. data/lib/active_merchant/billing/integrations/valitor.rb +33 -0
  105. data/lib/active_merchant/billing/integrations/valitor/helper.rb +86 -0
  106. data/lib/active_merchant/billing/integrations/valitor/notification.rb +13 -0
  107. data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +88 -0
  108. data/lib/active_merchant/billing/integrations/valitor/return.rb +13 -0
  109. data/lib/active_merchant/billing/integrations/world_pay.rb +27 -0
  110. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  111. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  112. data/lib/active_merchant/common.rb +1 -1
  113. data/lib/active_merchant/common/connection.rb +13 -8
  114. data/lib/active_merchant/common/country.rb +15 -6
  115. data/lib/active_merchant/common/post_data.rb +1 -1
  116. data/lib/active_merchant/common/posts_data.rb +21 -5
  117. data/lib/active_merchant/common/utils.rb +4 -0
  118. data/lib/active_merchant/common/validateable.rb +20 -15
  119. data/lib/active_merchant/version.rb +3 -0
  120. data/lib/support/gateway_support.rb +1 -1
  121. metadata +65 -27
@@ -5,11 +5,11 @@ module ActiveMerchant #:nodoc:
5
5
  CARD_COMPANIES = {
6
6
  'visa' => /^4\d{12}(\d{3})?$/,
7
7
  'master' => /^(5[1-5]\d{4}|677189)\d{10}$/,
8
- 'discover' => /^(6011|65\d{2})\d{12}$/,
8
+ 'discover' => /^(6011|65\d{2}|64[4-9]\d)\d{12}|(62\d{14})$/,
9
9
  'american_express' => /^3[47]\d{13}$/,
10
10
  'diners_club' => /^3(0[0-5]|[68]\d)\d{11}$/,
11
11
  'jcb' => /^35(28|29|[3-8]\d)\d{12}$/,
12
- 'switch' => /^6759\d{12}(\d{2,3})?$/,
12
+ 'switch' => /^6759\d{12}(\d{2,3})?$/,
13
13
  'solo' => /^6767\d{12}(\d{2,3})?$/,
14
14
  'dankort' => /^5019\d{12}$/,
15
15
  'maestro' => /^(5[06-8]|6\d)\d{10,17}$/,
@@ -122,4 +122,4 @@ module ActiveMerchant #:nodoc:
122
122
  end
123
123
  end
124
124
  end
125
- end
125
+ end
@@ -62,7 +62,9 @@ module ActiveMerchant #:nodoc:
62
62
  include Utils
63
63
 
64
64
  DEBIT_CARDS = [ :switch, :solo ]
65
-
65
+ CURRENCIES_WITHOUT_FRACTIONS = [ 'JPY' ]
66
+ CREDIT_DEPRECATION_MESSAGE = "Support for using credit to refund existing transactions is deprecated and will be removed from a future release of ActiveMerchant. Please use the refund method instead."
67
+
66
68
  cattr_reader :implementations
67
69
  @@implementations = []
68
70
 
@@ -74,22 +76,22 @@ module ActiveMerchant #:nodoc:
74
76
  # The format of the amounts used by the gateway
75
77
  # :dollars => '12.50'
76
78
  # :cents => '1250'
77
- class_inheritable_accessor :money_format
79
+ class_attribute :money_format
78
80
  self.money_format = :dollars
79
81
 
80
82
  # The default currency for the transactions if no currency is provided
81
- class_inheritable_accessor :default_currency
83
+ class_attribute :default_currency
82
84
 
83
85
  # The countries of merchants the gateway supports
84
- class_inheritable_accessor :supported_countries
86
+ class_attribute :supported_countries
85
87
  self.supported_countries = []
86
88
 
87
89
  # The supported card types for the gateway
88
- class_inheritable_accessor :supported_cardtypes
90
+ class_attribute :supported_cardtypes
89
91
  self.supported_cardtypes = []
90
92
 
91
- class_inheritable_accessor :homepage_url
92
- class_inheritable_accessor :display_name
93
+ class_attribute :homepage_url
94
+ class_attribute :display_name
93
95
 
94
96
  # The application making the calls to the gateway
95
97
  # Useful for things like the PayPal build notation (BN) id fields
@@ -133,13 +135,13 @@ module ActiveMerchant #:nodoc:
133
135
  def amount(money)
134
136
  return nil if money.nil?
135
137
  cents = if money.respond_to?(:cents)
136
- warn "Support for Money objects is deprecated and will be removed from a future release of ActiveMerchant. Please use an Integer value in cents"
138
+ deprecated "Support for Money objects is deprecated and will be removed from a future release of ActiveMerchant. Please use an Integer value in cents"
137
139
  money.cents
138
140
  else
139
141
  money
140
142
  end
141
143
 
142
- if money.is_a?(String) or cents.to_i < 0
144
+ if money.is_a?(String)
143
145
  raise ArgumentError, 'money amount must be a positive Integer in cents.'
144
146
  end
145
147
 
@@ -149,6 +151,11 @@ module ActiveMerchant #:nodoc:
149
151
  sprintf("%.2f", cents.to_f / 100)
150
152
  end
151
153
  end
154
+
155
+ def localized_amount(money, currency)
156
+ amount = amount(money)
157
+ CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
158
+ end
152
159
 
153
160
  def currency(money)
154
161
  money.respond_to?(:currency) ? money.currency : self.default_currency
@@ -26,7 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
  class AuthorizeNetGateway < Gateway
27
27
  API_VERSION = '3.1'
28
28
 
29
- class_inheritable_accessor :test_url, :live_url, :arb_test_url, :arb_live_url
29
+ class_attribute :test_url, :live_url, :arb_test_url, :arb_live_url
30
30
 
31
31
  self.test_url = "https://test.authorize.net/gateway/transact.dll"
32
32
  self.live_url = "https://secure.authorize.net/gateway/transact.dll"
@@ -34,7 +34,7 @@ module ActiveMerchant #:nodoc:
34
34
  self.arb_test_url = 'https://apitest.authorize.net/xml/v1/request.api'
35
35
  self.arb_live_url = 'https://api.authorize.net/xml/v1/request.api'
36
36
 
37
- class_inheritable_accessor :duplicate_window
37
+ class_attribute :duplicate_window
38
38
 
39
39
  APPROVED, DECLINED, ERROR, FRAUD_REVIEW = 1, 2, 3, 4
40
40
 
@@ -48,6 +48,7 @@ module ActiveMerchant #:nodoc:
48
48
 
49
49
  CARD_CODE_ERRORS = %w( N S )
50
50
  AVS_ERRORS = %w( A E N R W Z )
51
+ AVS_REASON_CODES = %w(27 45)
51
52
 
52
53
  AUTHORIZE_NET_ARB_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
53
54
 
@@ -130,34 +131,41 @@ module ActiveMerchant #:nodoc:
130
131
  # * <tt>authorization</tt> - The authorization returned from the previous authorize request.
131
132
  def void(authorization, options = {})
132
133
  post = {:trans_id => authorization}
134
+ add_duplicate_window(post)
133
135
  commit('VOID', nil, post)
134
136
  end
135
137
 
136
- # Credit an account.
138
+ # Refund a transaction.
137
139
  #
138
- # This transaction is also referred to as a Refund and indicates to the gateway that
140
+ # This transaction indicates to the gateway that
139
141
  # money should flow from the merchant to the customer.
140
142
  #
141
143
  # ==== Parameters
142
144
  #
143
145
  # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
144
- # * <tt>identification</tt> -- The ID of the original transaction against which the credit is being issued.
146
+ # * <tt>identification</tt> -- The ID of the original transaction against which the refund is being issued.
145
147
  # * <tt>options</tt> -- A hash of parameters.
146
148
  #
147
149
  # ==== Options
148
150
  #
149
- # * <tt>:card_number</tt> -- The credit card number the credit is being issued to. (REQUIRED)
150
- def credit(money, identification, options = {})
151
+ # * <tt>:card_number</tt> -- The credit card number the refund is being issued to. (REQUIRED)
152
+ def refund(money, identification, options = {})
151
153
  requires!(options, :card_number)
152
154
 
153
155
  post = { :trans_id => identification,
154
156
  :card_num => options[:card_number]
155
157
  }
156
158
  add_invoice(post, options)
159
+ add_duplicate_window(post)
157
160
 
158
161
  commit('CREDIT', money, post)
159
162
  end
160
163
 
164
+ def credit(money, identification, options = {})
165
+ deprecated CREDIT_DEPRECATION_MESSAGE
166
+ refund(money, identification, options)
167
+ end
168
+
161
169
  # Create a recurring payment.
162
170
  #
163
171
  # This transaction creates a new Automated Recurring Billing (ARB) subscription. Your account must have ARB enabled.
@@ -370,10 +378,12 @@ module ActiveMerchant #:nodoc:
370
378
  def message_from(results)
371
379
  if results[:response_code] == DECLINED
372
380
  return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
373
- return AVSResult.messages[ results[:avs_result_code] ] if AVS_ERRORS.include?(results[:avs_result_code])
381
+ if AVS_REASON_CODES.include?(results[:response_reason_code]) && AVS_ERRORS.include?(results[:avs_result_code])
382
+ return AVSResult.messages[ results[:avs_result_code] ]
383
+ end
374
384
  end
375
385
 
376
- return results[:response_reason_text].nil? ? '' : results[:response_reason_text][0..-2]
386
+ (results[:response_reason_text] ? results[:response_reason_text].chomp('.') : '')
377
387
  end
378
388
 
379
389
  def expdate(creditcard)
@@ -27,7 +27,7 @@ module ActiveMerchant #:nodoc:
27
27
  # 5. Click Submit
28
28
  class AuthorizeNetCimGateway < Gateway
29
29
 
30
- class_inheritable_accessor :test_url, :live_url
30
+ class_attribute :test_url, :live_url
31
31
 
32
32
  self.test_url = 'https://apitest.authorize.net/xml/v1/request.api'
33
33
  self.live_url = 'https://api.authorize.net/xml/v1/request.api'
@@ -54,13 +54,17 @@ module ActiveMerchant #:nodoc:
54
54
  CIM_TRANSACTION_TYPES = {
55
55
  :auth_capture => 'profileTransAuthCapture',
56
56
  :auth_only => 'profileTransAuthOnly',
57
- :capture_only => 'profileTransCaptureOnly'
57
+ :capture_only => 'profileTransCaptureOnly',
58
+ :prior_auth_capture => 'profileTransPriorAuthCapture',
59
+ :refund => 'profileTransRefund',
60
+ :void => 'profileTransVoid'
58
61
  }
59
62
 
60
63
  CIM_VALIDATION_MODES = {
61
64
  :none => 'none',
62
65
  :test => 'testMode',
63
- :live => 'liveMode'
66
+ :live => 'liveMode',
67
+ :old => 'oldLiveMode'
64
68
  }
65
69
 
66
70
  BANK_ACCOUNT_TYPES = {
@@ -309,14 +313,105 @@ module ActiveMerchant #:nodoc:
309
313
  #
310
314
  # ==== Transaction
311
315
  #
312
- # * <tt>:type</tt> -- The type of transaction. Can be either <tt>:auth_only</tt>, <tt>:capture_only</tt>, or <tt>:auth_capture</tt>. (REQUIRED)
313
- # * <tt>:amount</tt> -- The amount for the tranaction. Formatted with a decimal. For example "4.95" (REQUIRED)
314
- # * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (REQUIRED)
315
- # * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction. (REQUIRED)
316
+ # * <tt>:type</tt> -- The type of transaction. Can be either <tt>:auth_only</tt>, <tt>:capture_only</tt>, <tt>:auth_capture</tt>, <tt>:prior_auth_capture</tt>, <tt>:refund</tt> or <tt>:void</tt>. (REQUIRED)
317
+ # * <tt>:amount</tt> -- The amount for the tranaction. Formatted with a decimal. For example "4.95" (CONDITIONAL)
318
+ # - :type == :void (NOT USED)
319
+ # - :type == (:refund, :auth_only, :capture_only, :auth_capture, :prior_auth_capture) (REQUIRED)
320
+ #
321
+ # * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (CONDITIONAL)
322
+ # - :type == (:void, :prior_auth_capture) (OPTIONAL)
323
+ # - :type == :refund (CONDITIONAL - required if masked information is not being submitted [see below])
324
+ # - :type == (:auth_only, :capture_only, :auth_capture) (REQUIRED)
325
+ #
326
+ # * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction. (CONDITIONAL)
327
+ # - :type == (:void, :prior_auth_capture) (OPTIONAL)
328
+ # - :type == :refund (CONDITIONAL - required if masked information is not being submitted [see below])
329
+ # - :type == (:auth_only, :capture_only, :auth_capture) (REQUIRED)
330
+ #
331
+ # * <tt>:trans_id</tt> -- The payment gateway assigned transaction ID of the original transaction (CONDITIONAL):
332
+ # - :type = (:void, :refund, :prior_auth_capture) (REQUIRED)
333
+ # - :type = (:auth_only, :capture_only, :auth_capture) (NOT USED)
334
+ #
335
+ # * <tt>customer_shipping_address_id</tt> -- Payment gateway assigned ID associated with the customer shipping address (CONDITIONAL)
336
+ # - :type = (:void, :refund) (OPTIONAL)
337
+ # - :type = (:auth_only, :capture_only, :auth_capture) (NOT USED)
338
+ # - :type = (:prior_auth_capture) (OPTIONAL)
339
+ #
340
+ # ==== For :type == :refund only
341
+ # * <tt>:credit_card_number_masked</tt> -- (CONDITIONAL - requied for credit card refunds is :customer_profile_id AND :customer_payment_profile_id are missing)
342
+ # * <tt>:bank_routing_number_masked && :bank_account_number_masked</tt> -- (CONDITIONAL - requied for electronic check refunds is :customer_profile_id AND :customer_payment_profile_id are missing) (NOT ABLE TO TEST - I keep getting "ACH transactions are not accepted by this merchant." when trying to make a payment and, until that's possible I can't refund (wiseleyb@gmail.com))
316
343
  def create_customer_profile_transaction(options)
317
344
  requires!(options, :transaction)
318
- requires!(options[:transaction], :type, :amount, :customer_profile_id, :customer_payment_profile_id)
345
+ requires!(options[:transaction], :type)
346
+ case options[:transaction][:type]
347
+ when :void
348
+ requires!(options[:transaction], :trans_id)
349
+ when :refund
350
+ requires!(options[:transaction], :trans_id) &&
351
+ (
352
+ (options[:transaction][:customer_profile_id] && options[:transaction][:customer_payment_profile_id]) ||
353
+ options[:transaction][:credit_card_number_masked] ||
354
+ (options[:transaction][:bank_routing_number_masked] && options[:transaction][:bank_account_number_masked])
355
+ )
356
+ when :prior_auth_capture
357
+ requires!(options[:transaction], :amount, :trans_id)
358
+ else
359
+ requires!(options[:transaction], :amount, :customer_profile_id, :customer_payment_profile_id)
360
+ end
361
+ request = build_request(:create_customer_profile_transaction, options)
362
+ commit(:create_customer_profile_transaction, request)
363
+ end
319
364
 
365
+ # Creates a new payment transaction for refund from an existing customer profile
366
+ #
367
+ # This is what is used to refund a transaction you have stored in a Customer Profile.
368
+ #
369
+ # Returns a Response object that contains the result of the transaction in <tt>params['direct_response']</tt>
370
+ #
371
+ # ==== Options
372
+ #
373
+ # * <tt>:transaction</tt> -- A hash containing information on the transaction that is being requested. (REQUIRED)
374
+ #
375
+ # ==== Transaction
376
+ #
377
+ # * <tt>:amount</tt> -- The total amount to be refunded (REQUIRED)
378
+ #
379
+ # * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction. (CONDITIONAL :customer_payment_profile_id must be included if used)
380
+ # * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction. (CONDITIONAL :customer_profile_id must be included if used)
381
+ #
382
+ # * <tt>:credit_card_number_masked</tt> -- Four Xs follwed by the last four digits of the credit card (CONDITIONAL - used if customer_profile_id and customer_payment_profile_id aren't given)
383
+ #
384
+ # * <tt>:bank_routing_number_masked</tt> -- The last four gidits of the routing number to be refunded (CONDITIONAL - must be used with :bank_account_number_masked)
385
+ # * <tt>:bank_account_number_masked</tt> -- The last four digis of the bank account number to be refunded, Ex. XXXX1234 (CONDITIONAL - must be used with :bank_routing_number_masked)
386
+ def create_customer_profile_transaction_for_refund(options)
387
+ requires!(options, :transaction)
388
+ options[:transaction][:type] = :refund
389
+ requires!(options[:transaction], :trans_id)
390
+ requires!(options[:transaction], :amount)
391
+ request = build_request(:create_customer_profile_transaction, options)
392
+ commit(:create_customer_profile_transaction, request)
393
+ end
394
+
395
+ # Creates a new payment transaction for void from an existing customer profile
396
+ #
397
+ # This is what is used to void a transaction you have stored in a Customer Profile.
398
+ #
399
+ # Returns a Response object that contains the result of the transaction in <tt>params['direct_response']</tt>
400
+ #
401
+ # ==== Options
402
+ #
403
+ # * <tt>:transaction</tt> -- A hash containing information on the transaction that is being requested. (REQUIRED)
404
+ #
405
+ # ==== Transaction
406
+ #
407
+ # * <tt>:trans_id</tt> -- The payment gateway assigned transaction id of the original transaction. (REQUIRED)
408
+ # * <tt>:customer_profile_id</tt> -- The Customer Profile ID of the customer to use in this transaction.
409
+ # * <tt>:customer_payment_profile_id</tt> -- The Customer Payment Profile ID of the Customer Payment Profile to use in this transaction.
410
+ # * <tt>:customer_shipping_address_id</tt> -- Payment gateway assigned ID associated with the customer shipping address.
411
+ def create_customer_profile_transaction_for_void(options)
412
+ requires!(options, :transaction)
413
+ options[:transaction][:type] = :void
414
+ requires!(options[:transaction], :trans_id)
320
415
  request = build_request(:create_customer_profile_transaction, options)
321
416
  commit(:create_customer_profile_transaction, request)
322
417
  end
@@ -443,6 +538,8 @@ module ActiveMerchant #:nodoc:
443
538
  add_payment_profile(xml, options[:payment_profile])
444
539
  end
445
540
 
541
+ xml.tag!('validationMode', CIM_VALIDATION_MODES[options[:validation_mode]]) if options[:validation_mode]
542
+
446
543
  xml.target!
447
544
  end
448
545
 
@@ -502,10 +599,31 @@ module ActiveMerchant #:nodoc:
502
599
  xml.tag!('transaction') do
503
600
  xml.tag!(CIM_TRANSACTION_TYPES[transaction[:type]]) do
504
601
  # The amount to be billed to the customer
505
- xml.tag!('amount', transaction[:amount])
506
- xml.tag!('customerProfileId', transaction[:customer_profile_id])
507
- xml.tag!('customerPaymentProfileId', transaction[:customer_payment_profile_id])
508
- xml.tag!('approvalCode', transaction[:approval_code]) if transaction[:type] == :capture_only
602
+ case transaction[:type]
603
+ when :void
604
+ tag_unless_blank(xml,'customerProfileId', transaction[:customer_profile_id])
605
+ tag_unless_blank(xml,'customerPaymentProfileId', transaction[:customer_payment_profile_id])
606
+ tag_unless_blank(xml,'customerShippingAddressId', transaction[:customer_shipping_address_id])
607
+ xml.tag!('transId', transaction[:trans_id])
608
+ when :refund
609
+ #TODO - add support for all the other options fields
610
+ xml.tag!('amount', transaction[:amount])
611
+ tag_unless_blank(xml, 'customerProfileId', transaction[:customer_profile_id])
612
+ tag_unless_blank(xml, 'customerPaymentProfileId', transaction[:customer_payment_profile_id])
613
+ tag_unless_blank(xml, 'customerShippingAddressId', transaction[:customer_shipping_address_id])
614
+ tag_unless_blank(xml, 'creditCardNumberMasked', transaction[:credit_card_number_masked])
615
+ tag_unless_blank(xml, 'bankRoutingNumberMasked', transaction[:bank_routing_number_masked])
616
+ tag_unless_blank(xml, 'bankAccountNumberMasked', transaction[:bank_account_number_masked])
617
+ xml.tag!('transId', transaction[:trans_id])
618
+ when :prior_auth_capture
619
+ xml.tag!('amount', transaction[:amount])
620
+ xml.tag!('transId', transaction[:trans_id])
621
+ else
622
+ xml.tag!('amount', transaction[:amount])
623
+ xml.tag!('customerProfileId', transaction[:customer_profile_id])
624
+ xml.tag!('customerPaymentProfileId', transaction[:customer_payment_profile_id])
625
+ xml.tag!('approvalCode', transaction[:approval_code]) if transaction[:type] == :capture_only
626
+ end
509
627
  add_order(xml, transaction[:order]) if transaction[:order]
510
628
  end
511
629
  end
@@ -648,6 +766,10 @@ module ActiveMerchant #:nodoc:
648
766
  response
649
767
  end
650
768
 
769
+ def tag_unless_blank(xml, tag_name, data)
770
+ xml.tag!(tag_name, data) unless data.blank? || data.nil?
771
+ end
772
+
651
773
  def parse_direct_response(response)
652
774
  direct_response = {'raw' => response.params['direct_response']}
653
775
  direct_response_fields = response.params['direct_response'].split(',')
@@ -0,0 +1,309 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class BarclaysEpdqGateway < Gateway
4
+ TEST_URL = 'https://secure2.mde.epdq.co.uk:11500'
5
+ LIVE_URL = 'https://secure2.epdq.co.uk:11500'
6
+
7
+ self.supported_countries = ['UK']
8
+ self.default_currency = 'GBP'
9
+ self.supported_cardtypes = [:visa, :master, :maestro, :switch ]
10
+ self.money_format = :cents
11
+ self.homepage_url = 'http://www.barclaycard.co.uk/business/accepting-payments/epdq-mpi/'
12
+ self.display_name = 'Barclays ePDQ'
13
+
14
+ def initialize(options = {})
15
+ requires!(options, :login, :password, :client_id)
16
+ @options = options
17
+ super
18
+ end
19
+
20
+ def authorize(money, creditcard, options = {})
21
+ document = Document.new(self, @options) do
22
+ add_order_form(options[:order_id]) do
23
+ add_consumer(options) do
24
+ add_creditcard(creditcard)
25
+ end
26
+ add_transaction(:PreAuth, money)
27
+ end
28
+ end
29
+
30
+ commit(document)
31
+ end
32
+
33
+ def purchase(money, creditcard, options = {})
34
+ # disable fraud checks if this is a repeat order:
35
+ if options[:payment_number] && (options[:payment_number] > 1)
36
+ no_fraud = true
37
+ else
38
+ no_fraud = options[:no_fraud]
39
+ end
40
+ document = Document.new(self, @options, :no_fraud => no_fraud) do
41
+ add_order_form(options[:order_id], options[:group_id]) do
42
+ add_consumer(options) do
43
+ add_creditcard(creditcard)
44
+ end
45
+ add_transaction(:Auth, money, options)
46
+ end
47
+ end
48
+ commit(document)
49
+ end
50
+
51
+ # authorization is your unique order ID, not the authorization
52
+ # code returned by ePDQ
53
+ def capture(money, authorization, options = {})
54
+ document = Document.new(self, @options) do
55
+ add_order_form(authorization) do
56
+ add_transaction(:PostAuth, money)
57
+ end
58
+ end
59
+
60
+ commit(document)
61
+ end
62
+
63
+ # authorization is your unique order ID, not the authorization
64
+ # code returned by ePDQ
65
+ def credit(money, creditcard_or_authorization, options = {})
66
+ if creditcard_or_authorization.is_a?(String)
67
+ deprecated CREDIT_DEPRECATION_MESSAGE
68
+ refund(money, creditcard_or_authorization, options)
69
+ else
70
+ credit_new_order(money, creditcard_or_authorization, options)
71
+ end
72
+ end
73
+
74
+ def refund(money, authorization, options = {})
75
+ credit_existing_order(money, authorization, options)
76
+ end
77
+
78
+ def void(authorization, options = {})
79
+ document = Document.new(self, @options) do
80
+ add_order_form(authorization) do
81
+ add_transaction(:Void)
82
+ end
83
+ end
84
+
85
+ commit(document)
86
+ end
87
+
88
+ private
89
+ def credit_new_order(money, creditcard, options)
90
+ document = Document.new(self, @options) do
91
+ add_order_form do
92
+ add_consumer(options) do
93
+ add_creditcard(creditcard)
94
+ end
95
+ add_transaction(:Credit, money)
96
+ end
97
+ end
98
+
99
+ commit(document)
100
+ end
101
+
102
+ def credit_existing_order(money, authorization, options)
103
+ order_id, _ = authorization.split(":")
104
+ document = Document.new(self, @options) do
105
+ add_order_form(order_id) do
106
+ add_transaction(:Credit, money)
107
+ end
108
+ end
109
+
110
+ commit(document)
111
+ end
112
+
113
+ def parse(body)
114
+ parser = Parser.new(body)
115
+ response = parser.parse
116
+ Response.new(response[:success], response[:message], response,
117
+ :test => test?,
118
+ :authorization => response[:authorization],
119
+ :avs_result => response[:avsresponse],
120
+ :cvv_result => response[:cvv_result],
121
+ :order_id => response[:order_id],
122
+ :raw_response => response[:raw_response]
123
+ )
124
+ end
125
+
126
+ def commit(document)
127
+ url = (test? ? TEST_URL : LIVE_URL)
128
+ data = ssl_post(url, document.to_xml)
129
+ parse(data)
130
+ end
131
+
132
+ class Parser
133
+ def initialize(response)
134
+ @response = response
135
+ end
136
+
137
+ def parse
138
+ doc = REXML::Document.new(@response)
139
+ auth_type = find(doc, "//Transaction/Type").to_s
140
+
141
+ message = find(doc, "//Message/Text")
142
+ if message.blank?
143
+ message = find(doc, "//Transaction/CardProcResp/CcReturnMsg")
144
+ end
145
+
146
+ case auth_type
147
+ when 'Credit', 'Void'
148
+ success = find(doc, "//CcReturnMsg") == "Approved."
149
+ else
150
+ success = find(doc, "//Transaction/AuthCode").present?
151
+ end
152
+
153
+ {
154
+ :success => success,
155
+ :message => message,
156
+ :authorization =>
157
+ [find(doc, "//OrderFormDoc/Id"), find(doc, "//Transaction/Id")].join(":"),
158
+ :avs_result => find(doc, "//Transaction/AvsRespCode"),
159
+ :cvv_result => find(doc, "//Transaction/Cvv2Resp"),
160
+ :order_id => find(doc, "//OrderFormDoc/Transaction/Id"),
161
+ :raw_response => @response
162
+ }
163
+ end
164
+
165
+ def find(doc, xpath)
166
+ REXML::XPath.first(doc, xpath).try(:text)
167
+ end
168
+ end
169
+
170
+ class Document
171
+ attr_reader :type, :xml
172
+
173
+ PAYMENT_INTERVALS = {
174
+ :days => 'D',
175
+ :months => 'M'
176
+ }
177
+
178
+ EPDQ_CARD_TYPES = {
179
+ :visa => 1,
180
+ :master => 2,
181
+ :switch => 9,
182
+ :maestro => 10,
183
+ }
184
+
185
+ def initialize(gateway, options = {}, document_options = {}, &block)
186
+ @gateway = gateway
187
+ @options = options
188
+ @document_options = document_options
189
+ @xml = Builder::XmlMarkup.new(:indent => 2)
190
+ build(&block)
191
+ end
192
+
193
+ def to_xml
194
+ @xml.target!
195
+ end
196
+
197
+ def build(&block)
198
+ xml.instruct!(:xml, :version => '1.0')
199
+ xml.EngineDocList do
200
+ xml.DocVersion "1.0"
201
+ xml.EngineDoc do
202
+ xml.ContentType "OrderFormDoc"
203
+ xml.User do
204
+ xml.Name(@options[:login])
205
+ xml.Password(@options[:password])
206
+ xml.ClientId({ :DataType => "S32" }, @options[:client_id])
207
+ end
208
+ xml.Instructions do
209
+ if @document_options[:no_fraud]
210
+ xml.Pipeline "PaymentNoFraud"
211
+ else
212
+ xml.Pipeline "Payment"
213
+ end
214
+ end
215
+ instance_eval(&block)
216
+ end
217
+ end
218
+ end
219
+
220
+ def add_order_form(order_id=nil, group_id=nil, &block)
221
+ xml.OrderFormDoc do
222
+ xml.Mode 'P'
223
+ xml.Id(order_id) if order_id
224
+ xml.GroupId(group_id) if group_id
225
+ instance_eval(&block)
226
+ end
227
+ end
228
+
229
+ def add_consumer(options=nil, &block)
230
+ xml.Consumer do
231
+ if options
232
+ xml.Email(options[:email]) if options[:email]
233
+ billing_address = options[:billing_address] || options[:address]
234
+ if billing_address
235
+ xml.BillTo do
236
+ xml.Location do
237
+ xml.Address do
238
+ xml.Street1 billing_address[:address1]
239
+ xml.Street2 billing_address[:address2]
240
+ xml.City billing_address[:city]
241
+ xml.StateProv billing_address[:state]
242
+ xml.PostalCode billing_address[:zip]
243
+ xml.Country billing_address[:country_code]
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ instance_eval(&block)
250
+ end
251
+ end
252
+
253
+ def add_creditcard(creditcard)
254
+ xml.PaymentMech do
255
+ xml.CreditCard do
256
+ xml.Type({ :DataType => 'S32' }, EPDQ_CARD_TYPES[creditcard.brand.to_sym])
257
+ xml.Number creditcard.number
258
+ xml.Expires({ :DataType => 'ExpirationDate', :Locale => 826 }, format_expiry_date(creditcard))
259
+ if creditcard.verification_value.present?
260
+ xml.Cvv2Indicator 1
261
+ xml.Cvv2Val creditcard.verification_value
262
+ else
263
+ xml.Cvv2Indicator 5
264
+ end
265
+ xml.IssueNum(creditcard.issue_number) if creditcard.issue_number.present?
266
+ end
267
+ end
268
+ end
269
+
270
+ def add_transaction(auth_type, amount = nil, options = {})
271
+ @auth_type = auth_type
272
+ xml.Transaction do
273
+ xml.Type @auth_type.to_s
274
+ if options[:payment_number] && options[:payment_number] > 1
275
+ xml.CardholderPresentCode({ :DataType => 'S32' }, 8)
276
+ else
277
+ xml.CardholderPresentCode({ :DataType => 'S32' }, 7)
278
+ end
279
+ if options[:payment_number]
280
+ xml.PaymentNumber({ :DataType => 'S32' }, options[:payment_number])
281
+ end
282
+ if options[:total_payments]
283
+ xml.TotalNumberPayments({ :DataType => 'S32' }, options[:total_payments])
284
+ end
285
+ if amount
286
+ xml.CurrentTotals do
287
+ xml.Totals do
288
+ xml.Total({ :DataType => 'Money', :Currency => 826 }, amount)
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ # date must be formatted MM/YY
296
+ def format_expiry_date(creditcard)
297
+ month_str = "%02d" % creditcard.month
298
+ if match = creditcard.year.to_s.match(/^\d{2}(\d{2})$/)
299
+ year_str = "%02d" % match[1].to_i
300
+ else
301
+ year_str = "%02d" % creditcard.year
302
+ end
303
+ "#{month_str}/#{year_str}"
304
+ end
305
+ end
306
+ end
307
+ end
308
+ end
309
+