activemerchant 1.56.0 → 1.66.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +331 -0
  3. data/README.md +9 -9
  4. data/lib/active_merchant/billing/check.rb +3 -0
  5. data/lib/active_merchant/billing/credit_card.rb +8 -3
  6. data/lib/active_merchant/billing/credit_card_methods.rb +41 -1
  7. data/lib/active_merchant/billing/gateway.rb +14 -6
  8. data/lib/active_merchant/billing/gateways/adyen.rb +228 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +157 -44
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +7 -4
  11. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +283 -0
  12. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +68 -2
  13. data/lib/active_merchant/billing/gateways/blue_pay.rb +2 -2
  14. data/lib/active_merchant/billing/gateways/blue_snap.rb +348 -0
  15. data/lib/active_merchant/billing/gateways/bpoint.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -20
  17. data/lib/active_merchant/billing/gateways/bridge_pay.rb +37 -8
  18. data/lib/active_merchant/billing/gateways/card_stream.rb +161 -40
  19. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -0
  20. data/lib/active_merchant/billing/gateways/checkout_v2.rb +5 -2
  21. data/lib/active_merchant/billing/gateways/citrus_pay.rb +24 -0
  22. data/lib/active_merchant/billing/gateways/clearhaus.rb +24 -40
  23. data/lib/active_merchant/billing/gateways/conekta.rb +6 -1
  24. data/lib/active_merchant/billing/gateways/creditcall.rb +1 -1
  25. data/lib/active_merchant/billing/gateways/credorax.rb +310 -0
  26. data/lib/active_merchant/billing/gateways/culqi.rb +279 -0
  27. data/lib/active_merchant/billing/gateways/cyber_source.rb +80 -64
  28. data/lib/active_merchant/billing/gateways/data_cash.rb +10 -304
  29. data/lib/active_merchant/billing/gateways/digitzs.rb +292 -0
  30. data/lib/active_merchant/billing/gateways/elavon.rb +40 -26
  31. data/lib/active_merchant/billing/gateways/element.rb +356 -0
  32. data/lib/active_merchant/billing/gateways/fat_zebra.rb +16 -2
  33. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -1
  34. data/lib/active_merchant/billing/gateways/forte.rb +10 -2
  35. data/lib/active_merchant/billing/gateways/global_collect.rb +311 -0
  36. data/lib/active_merchant/billing/gateways/global_transport.rb +1 -0
  37. data/lib/active_merchant/billing/gateways/iats_payments.rb +13 -0
  38. data/lib/active_merchant/billing/gateways/in_context_paypal_express.rb +15 -0
  39. data/lib/active_merchant/billing/gateways/iveri.rb +251 -0
  40. data/lib/active_merchant/billing/gateways/jetpay.rb +33 -19
  41. data/lib/active_merchant/billing/gateways/kushki.rb +217 -0
  42. data/lib/active_merchant/billing/gateways/latitude19.rb +416 -0
  43. data/lib/active_merchant/billing/gateways/linkpoint.rb +2 -0
  44. data/lib/active_merchant/billing/gateways/litle.rb +29 -13
  45. data/lib/active_merchant/billing/gateways/mastercard.rb +268 -0
  46. data/lib/active_merchant/billing/gateways/maxipago.rb +145 -122
  47. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +15 -1
  48. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +10 -7
  49. data/lib/active_merchant/billing/gateways/mercury.rb +13 -5
  50. data/lib/active_merchant/billing/gateways/metrics_global.rb +1 -1
  51. data/lib/active_merchant/billing/gateways/migs.rb +23 -1
  52. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/moneris.rb +21 -1
  54. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -0
  55. data/lib/active_merchant/billing/gateways/ncr_secure_pay.rb +165 -0
  56. data/lib/active_merchant/billing/gateways/netbanx.rb +245 -0
  57. data/lib/active_merchant/billing/gateways/nmi.rb +30 -9
  58. data/lib/active_merchant/billing/gateways/omise.rb +9 -5
  59. data/lib/active_merchant/billing/gateways/openpay.rb +10 -1
  60. data/lib/active_merchant/billing/gateways/opp.rb +362 -0
  61. data/lib/active_merchant/billing/gateways/orbital.rb +28 -7
  62. data/lib/active_merchant/billing/gateways/pagarme.rb +248 -0
  63. data/lib/active_merchant/billing/gateways/pay_junction_v2.rb +190 -0
  64. data/lib/active_merchant/billing/gateways/payeezy.rb +61 -12
  65. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  66. data/lib/active_merchant/billing/gateways/payflow.rb +6 -0
  67. data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
  68. data/lib/active_merchant/billing/gateways/paymill.rb +29 -11
  69. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  70. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -6
  71. data/lib/active_merchant/billing/gateways/payu_in.rb +3 -2
  72. data/lib/active_merchant/billing/gateways/payu_latam.rb +402 -0
  73. data/lib/active_merchant/billing/gateways/pin.rb +6 -3
  74. data/lib/active_merchant/billing/gateways/pro_pay.rb +326 -0
  75. data/lib/active_merchant/billing/gateways/psl_card.rb +3 -3
  76. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +1 -1
  77. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +0 -2
  78. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +1 -1
  79. data/lib/active_merchant/billing/gateways/quickpay.rb +3 -3
  80. data/lib/active_merchant/billing/gateways/qvalent.rb +44 -1
  81. data/lib/active_merchant/billing/gateways/redsys.rb +3 -0
  82. data/lib/active_merchant/billing/gateways/s5.rb +8 -5
  83. data/lib/active_merchant/billing/gateways/safe_charge.rb +220 -0
  84. data/lib/active_merchant/billing/gateways/sage.rb +397 -128
  85. data/lib/active_merchant/billing/gateways/sage_pay.rb +45 -20
  86. data/lib/active_merchant/billing/gateways/secure_net.rb +0 -5
  87. data/lib/active_merchant/billing/gateways/secure_pay.rb +1 -1
  88. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -0
  89. data/lib/active_merchant/billing/gateways/securion_pay.rb +46 -17
  90. data/lib/active_merchant/billing/gateways/stripe.rb +125 -29
  91. data/lib/active_merchant/billing/gateways/telr.rb +275 -0
  92. data/lib/active_merchant/billing/gateways/tns.rb +13 -222
  93. data/lib/active_merchant/billing/gateways/trans_first.rb +40 -16
  94. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +606 -0
  95. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +114 -9
  96. data/lib/active_merchant/billing/gateways/vanco.rb +14 -10
  97. data/lib/active_merchant/billing/gateways/visanet_peru.rb +209 -0
  98. data/lib/active_merchant/billing/gateways/wepay.rb +73 -38
  99. data/lib/active_merchant/billing/gateways/wirecard.rb +1 -0
  100. data/lib/active_merchant/billing/gateways/world_net.rb +344 -0
  101. data/lib/active_merchant/billing/gateways/worldpay.rb +48 -16
  102. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +15 -0
  103. data/lib/active_merchant/country.rb +6 -4
  104. data/lib/active_merchant/posts_data.rb +1 -1
  105. data/lib/active_merchant/version.rb +1 -1
  106. metadata +32 -13
  107. data/lib/active_merchant/billing/gateways/app55.rb +0 -176
  108. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +0 -314
  109. data/lib/active_merchant/billing/gateways/certo_direct.rb +0 -278
  110. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +0 -89
  111. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +0 -115
  112. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +0 -149
  113. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +0 -97
@@ -20,7 +20,8 @@ module ActiveMerchant #:nodoc:
20
20
  :void => 'VOID',
21
21
  :abort => 'ABORT',
22
22
  :store => 'TOKEN',
23
- :unstore => 'REMOVETOKEN'
23
+ :unstore => 'REMOVETOKEN',
24
+ :repeat => 'REPEAT'
24
25
  }
25
26
 
26
27
  CREDIT_CARDS = {
@@ -36,8 +37,6 @@ module ActiveMerchant #:nodoc:
36
37
  :jcb => "JCB"
37
38
  }
38
39
 
39
- ELECTRON = /^(424519|42496[23]|450875|48440[6-8]|4844[1-5][1-5]|4917[3-5][0-9]|491880)\d{10}(\d{3})?$/
40
-
41
40
  AVS_CVV_CODE = {
42
41
  "NOTPROVIDED" => nil,
43
42
  "NOTCHECKED" => 'X',
@@ -45,6 +44,26 @@ module ActiveMerchant #:nodoc:
45
44
  "NOTMATCHED" => 'N'
46
45
  }
47
46
 
47
+ OPTIONAL_REQUEST_FIELDS = {
48
+ paypal_callback_url: :PayPalCallbackURL,
49
+ basket: :Basket,
50
+ gift_aid_payment: :GiftAidPayment ,
51
+ apply_avscv2: :ApplyAVSCV2 ,
52
+ apply_3d_secure: :Apply3DSecure,
53
+ account_type: :AccountType,
54
+ billing_agreement: :BillingAgreement,
55
+ basket_xml: :BasketXML,
56
+ customer_xml: :CustomerXML,
57
+ surcharge_xml: :SurchargeXML,
58
+ vendor_data: :VendorData,
59
+ language: :Language,
60
+ website: :Website,
61
+ recipient_account_number: :FIRecipientAcctNumber ,
62
+ recipient_surname: :FIRecipientSurname ,
63
+ recipient_postcode: :FIRecipientPostcode ,
64
+ recipient_dob: :FIRecipientDoB
65
+ }
66
+
48
67
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :switch, :solo, :maestro, :diners_club]
49
68
  self.supported_countries = ['GB', 'IE']
50
69
  self.default_currency = 'GBP'
@@ -69,7 +88,7 @@ module ActiveMerchant #:nodoc:
69
88
  add_customer_data(post, options)
70
89
  add_optional_data(post, options)
71
90
 
72
- commit(:purchase, post)
91
+ commit((options[:repeat] ? :repeat : :purchase), post)
73
92
  end
74
93
 
75
94
  def authorize(money, payment_method, options = {})
@@ -112,7 +131,7 @@ module ActiveMerchant #:nodoc:
112
131
 
113
132
  post = {}
114
133
 
115
- add_credit_reference(post, identification)
134
+ add_related_reference(post, identification)
116
135
  add_amount(post, money, options)
117
136
  add_invoice(post, options)
118
137
 
@@ -157,6 +176,17 @@ module ActiveMerchant #:nodoc:
157
176
  end
158
177
 
159
178
  private
179
+ def truncate(value, max_size)
180
+ return nil unless value
181
+ return value.to_s if CGI.escape(value.to_s).length <= max_size
182
+
183
+ if value.size > max_size
184
+ truncate(super(value, max_size), max_size)
185
+ else
186
+ truncate(value.to_s.chop, max_size)
187
+ end
188
+ end
189
+
160
190
  def add_reference(post, identification)
161
191
  order_id, transaction_id, authorization, security_key = identification.split(';')
162
192
 
@@ -166,7 +196,7 @@ module ActiveMerchant #:nodoc:
166
196
  add_pair(post, :SecurityKey, security_key)
167
197
  end
168
198
 
169
- def add_credit_reference(post, identification)
199
+ def add_related_reference(post, identification)
170
200
  order_id, transaction_id, authorization, security_key = identification.split(';')
171
201
 
172
202
  add_pair(post, :RelatedVendorTxCode, order_id)
@@ -197,14 +227,11 @@ module ActiveMerchant #:nodoc:
197
227
  end
198
228
 
199
229
  def add_optional_data(post, options)
200
- add_pair(post, :GiftAidPayment, options[:gift_aid_payment]) unless options[:gift_aid_payment].blank?
201
- add_pair(post, :ApplyAVSCV2, options[:apply_avscv2]) unless options[:apply_avscv2].blank?
202
- add_pair(post, :Apply3DSecure, options[:apply_3d_secure]) unless options[:apply_3d_secure].blank?
203
230
  add_pair(post, :CreateToken, 1) unless options[:store].blank?
204
- add_pair(post, :FIRecipientAcctNumber, options[:recipient_account_number])
205
- add_pair(post, :FIRecipientSurname, options[:recipient_surname])
206
- add_pair(post, :FIRecipientPostcode, options[:recipient_postcode])
207
- add_pair(post, :FIRecipientDoB, options[:recipient_dob])
231
+
232
+ OPTIONAL_REQUEST_FIELDS.each do |gateway_option, sagepay_field|
233
+ add_pair(post, sagepay_field, options[gateway_option])
234
+ end
208
235
  end
209
236
 
210
237
  def add_address(post, options)
@@ -241,7 +268,9 @@ module ActiveMerchant #:nodoc:
241
268
  end
242
269
 
243
270
  def add_payment_method(post, payment_method, options)
244
- if payment_method.respond_to?(:number)
271
+ if options[:repeat]
272
+ add_related_reference(post, payment_method)
273
+ elsif payment_method.respond_to?(:number)
245
274
  add_credit_card(post, payment_method)
246
275
  else
247
276
  add_token_details(post, payment_method, options)
@@ -266,6 +295,7 @@ module ActiveMerchant #:nodoc:
266
295
  def add_token_details(post, token, options)
267
296
  add_token(post, token)
268
297
  add_pair(post, :StoreToken, options[:customer])
298
+ add_pair(post, :CV2, options[:verification_value])
269
299
  end
270
300
 
271
301
  def add_token(post, token)
@@ -292,8 +322,7 @@ module ActiveMerchant #:nodoc:
292
322
 
293
323
  card_type = card_brand(credit_card).to_sym
294
324
 
295
- # Check if it is an electron card
296
- if card_type == :visa && credit_card.number =~ ELECTRON
325
+ if card_type == :visa && credit_card.electron?
297
326
  CREDIT_CARDS[:electron]
298
327
  else
299
328
  CREDIT_CARDS[card_type]
@@ -393,10 +422,6 @@ module ActiveMerchant #:nodoc:
393
422
  post[key] = value if !value.blank? || options[:required]
394
423
  end
395
424
 
396
- def localized_amount(money, currency)
397
- amount = amount(money)
398
- CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s) ? amount.split('.').first : amount
399
- end
400
425
  end
401
426
 
402
427
  end
@@ -219,11 +219,6 @@ module ActiveMerchant #:nodoc:
219
219
  end
220
220
 
221
221
  def message_from(response)
222
- if response[:response_code].to_i == DECLINED
223
- return CVVResult.messages[ response[:card_code_response_code] ] if CARD_CODE_ERRORS.include?(response[:card_code_response_code])
224
- return AVSResult.messages[ response[:avs_result_code] ] if AVS_ERRORS.include?(response[:avs_result_code])
225
- end
226
-
227
222
  return response[:response_reason_text].nil? ? '' : response[:response_reason_text][0..-1]
228
223
  end
229
224
 
@@ -100,7 +100,7 @@ module ActiveMerchant #:nodoc:
100
100
  post[:delim_data] = "TRUE"
101
101
  post[:delim_char] = ","
102
102
  post[:encap_char] = "$"
103
- post[:solution_ID] = application_id if application_id.present? && application_id != "ActiveMerchant"
103
+ post[:solution_ID] = application_id if application_id
104
104
 
105
105
  request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
106
106
  request
@@ -103,6 +103,18 @@ module ActiveMerchant #:nodoc:
103
103
  commit_periodic(build_periodic_item(:remove_triggered, options[:amount], nil, options))
104
104
  end
105
105
 
106
+ def supports_scrubbing?
107
+ true
108
+ end
109
+
110
+ def scrub(transcript)
111
+ transcript.
112
+ gsub(%r((<merchantID>).+(</merchantID>)), '\1[FILTERED]\2').
113
+ gsub(%r((<password>).+(</password>)), '\1[FILTERED]\2').
114
+ gsub(%r((<cardNumber>).+(</cardNumber>)), '\1[FILTERED]\2').
115
+ gsub(%r((<cvv>).+(</cvv>)), '\1[FILTERED]\2')
116
+ end
117
+
106
118
  private
107
119
 
108
120
  def build_purchase_request(money, credit_card, options)
@@ -4,18 +4,10 @@ module ActiveMerchant #:nodoc:
4
4
  self.test_url = 'https://api.securionpay.com/'
5
5
  self.live_url = 'https://api.securionpay.com/'
6
6
 
7
- self.supported_countries = ["AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD",
8
- "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV",
9
- "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ",
10
- "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE",
11
- "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID",
12
- "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KV", "KW", "KG", "LA", "LV", "LB",
13
- "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM",
14
- "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP",
15
- "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH",
16
- "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "SI", "SB", "SO", "ZA", "GS", "ES",
17
- "LK", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC",
18
- "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", "AX"]
7
+
8
+ self.supported_countries = %w(AL AD AT BY BE BG HR CY CZ RE DK EE IS FI FR DE GI GR HU IS IE IT IL LV LI LT LU
9
+ MK MT MD MC NL NO PL PT RO RU MA RS SK SI ES SE CH UA GB KI CI ME)
10
+
19
11
  self.default_currency = 'USD'
20
12
  self.money_format = :cents
21
13
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
@@ -79,6 +71,29 @@ module ActiveMerchant #:nodoc:
79
71
  end
80
72
  end
81
73
 
74
+ def store(credit_card, options = {})
75
+ if options[:customer_id].blank?
76
+ MultiResponse.run() do |r|
77
+ #create charge object
78
+ r.process { authorize(100, credit_card, options) }
79
+ #create customer and save card
80
+ r.process { create_customer_add_card(r.authorization, options) }
81
+ #void the charge
82
+ r.process(:ignore_result) { void(r.params["metadata"]["chargeId"], options) }
83
+ end
84
+ else
85
+ verify(credit_card, options)
86
+ end
87
+ end
88
+
89
+ def customer(options = {})
90
+ if options[:customer_id].blank?
91
+ return nil
92
+ else
93
+ commit("customers/#{CGI.escape(options[:customer_id])}", nil, options, :get)
94
+ end
95
+ end
96
+
82
97
  def supports_scrubbing?
83
98
  true
84
99
  end
@@ -92,8 +107,18 @@ module ActiveMerchant #:nodoc:
92
107
 
93
108
  private
94
109
 
110
+ def create_customer_add_card(authorization, options)
111
+ post = {}
112
+ post[:email] = options[:email]
113
+ post[:description] = options[:description]
114
+ post[:card] = authorization
115
+ post[:metadata] = {}
116
+ post[:metadata][:chargeId] = authorization
117
+ commit('customers', post, options)
118
+ end
119
+
95
120
  def add_customer(post, payment, options)
96
- post[:customer] = options[:customer] if options[:customer]
121
+ post[:customerId] = options[:customer_id] if options[:customer_id]
97
122
  end
98
123
 
99
124
  def add_customer_data(post, options)
@@ -156,8 +181,8 @@ module ActiveMerchant #:nodoc:
156
181
  JSON.parse(body)
157
182
  end
158
183
 
159
- def commit(url, parameters = nil, options = {})
160
- response = api_request(url, parameters, options)
184
+ def commit(url, parameters = nil, options = {}, method = nil)
185
+ response = api_request(url, parameters, options, method)
161
186
  success = !response.key?("error")
162
187
 
163
188
  Response.new(success,
@@ -206,10 +231,14 @@ module ActiveMerchant #:nodoc:
206
231
  end.compact.join("&")
207
232
  end
208
233
 
209
- def api_request(endpoint, parameters = nil, options = {})
234
+ def api_request(endpoint, parameters = nil, options = {}, method = nil)
210
235
  raw_response = response = nil
211
236
  begin
212
- raw_response = ssl_post(self.live_url + endpoint, post_data(parameters), headers(options))
237
+ if method.blank?
238
+ raw_response = ssl_post(self.live_url + endpoint, post_data(parameters), headers(options))
239
+ else
240
+ raw_response = ssl_request(method, self.live_url + endpoint, post_data(parameters), headers(options))
241
+ end
213
242
  response = parse(raw_response)
214
243
  rescue ResponseError => e
215
244
  raw_response = e.response.body
@@ -21,12 +21,11 @@ module ActiveMerchant #:nodoc:
21
21
  'unchecked' => 'P'
22
22
  }
23
23
 
24
- CURRENCIES_WITHOUT_FRACTIONS = %w(BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF)
25
-
26
- self.supported_countries = %w(AT AU BE CA CH DE DK ES FI FR GB IE IT LU NL NO SE US)
24
+ self.supported_countries = %w(AT AU BE BR CA CH DE DK ES FI FR GB HK IE IT JP LU MX NL NO NZ PT SE SG US)
27
25
  self.default_currency = 'USD'
28
26
  self.money_format = :cents
29
27
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
28
+ self.currencies_without_fractions = %w(BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF)
30
29
 
31
30
  self.homepage_url = 'https://stripe.com/'
32
31
  self.display_name = 'Stripe'
@@ -43,14 +42,35 @@ module ActiveMerchant #:nodoc:
43
42
  'card_declined' => STANDARD_ERROR_CODE[:card_declined],
44
43
  'call_issuer' => STANDARD_ERROR_CODE[:call_issuer],
45
44
  'processing_error' => STANDARD_ERROR_CODE[:processing_error],
46
- 'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin]
45
+ 'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin],
46
+ 'test_mode_live_card' => STANDARD_ERROR_CODE[:test_mode_live_card]
47
+ }
48
+
49
+ BANK_ACCOUNT_HOLDER_TYPE_MAPPING = {
50
+ "personal" => "individual",
51
+ "business" => "company",
52
+ }
53
+
54
+ MINIMUM_AUTHORIZE_AMOUNTS = {
55
+ "USD" => 100,
56
+ "CAD" => 100,
57
+ "GBP" => 60,
58
+ "EUR" => 100,
59
+ "DKK" => 500,
60
+ "NOK" => 600,
61
+ "SEK" => 600,
62
+ "CHF" => 100,
63
+ "AUD" => 100,
64
+ "JPY" => 100,
65
+ "MXN" => 2000,
66
+ "SGD" => 100,
67
+ "HKD" => 800
47
68
  }
48
69
 
49
70
  def initialize(options = {})
50
71
  requires!(options, :login)
51
72
  @api_key = options[:login]
52
73
  @fee_refund_api_key = options[:fee_refund_login]
53
-
54
74
  super
55
75
  end
56
76
 
@@ -80,6 +100,11 @@ module ActiveMerchant #:nodoc:
80
100
  #
81
101
  # purchase(money, nil, { :customer => id, ... })
82
102
  def purchase(money, payment, options = {})
103
+ if ach?(payment)
104
+ direct_bank_error = "Direct bank account transactions are not supported. Bank accounts must be stored and verified before use."
105
+ return Response.new(false, direct_bank_error)
106
+ end
107
+
83
108
  MultiResponse.run do |r|
84
109
  if payment.is_a?(ApplePayPaymentToken)
85
110
  r.process { tokenize_apple_pay_token(payment) }
@@ -107,6 +132,7 @@ module ActiveMerchant #:nodoc:
107
132
 
108
133
  def void(identification, options = {})
109
134
  post = {}
135
+ post[:metadata] = options[:metadata] if options[:metadata]
110
136
  post[:expand] = [:charge]
111
137
  commit(:post, "charges/#{CGI.escape(identification)}/refunds", post, options)
112
138
  end
@@ -131,7 +157,8 @@ module ActiveMerchant #:nodoc:
131
157
 
132
158
  def verify(payment, options = {})
133
159
  MultiResponse.run(:use_first_response) do |r|
134
- r.process { authorize(50, payment, options) }
160
+ r.process { authorize(auth_minimum_amount(options), payment, options) }
161
+ options[:idempotency_key] = nil
135
162
  r.process(:ignore_result) { void(r.authorization, options) }
136
163
  end
137
164
  end
@@ -155,24 +182,33 @@ module ActiveMerchant #:nodoc:
155
182
 
156
183
  # Note: creating a new credit card will not change the customer's existing default credit card (use :set_default => true)
157
184
  def store(payment, options = {})
158
- card_params = {}
185
+ params = {}
159
186
  post = {}
160
187
 
161
188
  if payment.is_a?(ApplePayPaymentToken)
162
189
  token_exchange_response = tokenize_apple_pay_token(payment)
163
- card_params = { card: token_exchange_response.params["token"]["id"] } if token_exchange_response.success?
190
+ params = { card: token_exchange_response.params["token"]["id"] } if token_exchange_response.success?
191
+ elsif payment.is_a?(StripePaymentToken)
192
+ add_payment_token(params, payment, options)
193
+ elsif payment.is_a?(Check)
194
+ bank_token_response = tokenize_bank_account(payment)
195
+ return bank_token_response unless bank_token_response.success?
196
+ params = { source: bank_token_response.params["token"]["id"] }
164
197
  else
165
- add_creditcard(card_params, payment, options)
198
+ add_creditcard(params, payment, options)
166
199
  end
167
200
 
168
201
  post[:validate] = options[:validate] unless options[:validate].nil?
169
202
  post[:description] = options[:description] if options[:description]
170
203
  post[:email] = options[:email] if options[:email]
171
204
 
172
- if options[:customer]
205
+ if options[:account]
206
+ add_external_account(post, params, payment)
207
+ commit(:post, "accounts/#{CGI.escape(options[:account])}/external_accounts", post, options)
208
+ elsif options[:customer]
173
209
  MultiResponse.run(:first) do |r|
174
210
  # The /cards endpoint does not update other customer parameters.
175
- r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", card_params, options) }
211
+ r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", params, options) }
176
212
 
177
213
  if options[:set_default] and r.success? and !r.params['id'].blank?
178
214
  post[:default_card] = r.params['id']
@@ -183,7 +219,7 @@ module ActiveMerchant #:nodoc:
183
219
  end
184
220
  end
185
221
  else
186
- commit(:post, 'customers', post.merge(card_params), options)
222
+ commit(:post, 'customers', post.merge(params), options)
187
223
  end
188
224
  end
189
225
 
@@ -218,6 +254,16 @@ module ActiveMerchant #:nodoc:
218
254
  end
219
255
  end
220
256
 
257
+ def verify_credentials
258
+ begin
259
+ ssl_get(live_url + "charges/nonexistent", headers)
260
+ rescue ResponseError => e
261
+ return false if e.response.code.to_i == 401
262
+ end
263
+
264
+ true
265
+ end
266
+
221
267
  def supports_scrubbing?
222
268
  true
223
269
  end
@@ -225,13 +271,15 @@ module ActiveMerchant #:nodoc:
225
271
  def scrub(transcript)
226
272
  transcript.
227
273
  gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
228
- gsub(%r((card\[number\]=)\d+), '\1[FILTERED]').
229
- gsub(%r((card\[cvc\]=)\d+), '\1[FILTERED]').
230
274
  gsub(%r((&?three_d_secure\[cryptogram\]=)[\w=]*(&?)), '\1[FILTERED]\2').
231
- gsub(%r((card\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\2').
275
+ gsub(%r((card\[cryptogram\]=)[^&]+(&?)), '\1[FILTERED]\2').
276
+ gsub(%r((card\[cvc\]=)\d+), '\1[FILTERED]').
277
+ gsub(%r((card\[emv_approval_data\]=)[^&]+(&?)), '\1[FILTERED]\2').
278
+ gsub(%r((card\[emv_auth_data\]=)[^&]+(&?)), '\1[FILTERED]\2').
232
279
  gsub(%r((card\[encrypted_pin\]=)[^&]+(&?)), '\1[FILTERED]\2').
233
280
  gsub(%r((card\[encrypted_pin_key_id\]=)[\w=]+(&?)), '\1[FILTERED]\2').
234
- gsub(%r((card\[emv_auth_data\]=)[^&]+(&?)), '\1[FILTERED]\2')
281
+ gsub(%r((card\[number\]=)\d+), '\1[FILTERED]').
282
+ gsub(%r((card\[swipe_data\]=)[^&]+(&?)), '\1[FILTERED]\2')
235
283
  end
236
284
 
237
285
  def supports_network_tokenization?
@@ -258,7 +306,6 @@ module ActiveMerchant #:nodoc:
258
306
  unless emv_payment?(payment)
259
307
  add_amount(post, money, options, true)
260
308
  add_customer_data(post, options)
261
- add_metadata(post, options)
262
309
  post[:description] = options[:description]
263
310
  post[:statement_descriptor] = options[:statement_description]
264
311
  post[:receipt_email] = options[:receipt_email] if options[:receipt_email]
@@ -266,6 +313,7 @@ module ActiveMerchant #:nodoc:
266
313
  add_flags(post, options)
267
314
  end
268
315
 
316
+ add_metadata(post, options)
269
317
  add_application_fee(post, options)
270
318
  add_destination(post, options)
271
319
  post
@@ -290,6 +338,13 @@ module ActiveMerchant #:nodoc:
290
338
  post[:expand].concat(Array.wrap(options[:expand]).map(&:to_sym)).uniq!
291
339
  end
292
340
 
341
+ def add_external_account(post, card_params, payment)
342
+ external_account = {}
343
+ external_account[:object] ="card"
344
+ external_account[:currency] = (options[:currency] || currency(payment)).downcase
345
+ post[:external_account] = external_account.merge(card_params[:card])
346
+ end
347
+
293
348
  def add_customer_data(post, options)
294
349
  metadata_options = [:description, :ip, :user_agent, :referrer]
295
350
  post.update(options.slice(*metadata_options))
@@ -314,7 +369,8 @@ module ActiveMerchant #:nodoc:
314
369
  card = {}
315
370
  if emv_payment?(creditcard)
316
371
  add_emv_creditcard(post, creditcard.icc_data)
317
- post[:card][:read_method] = "contactless" if creditcard.contactless
372
+ post[:card][:read_method] = "contactless" if creditcard.contactless_emv
373
+ post[:card][:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
318
374
  if creditcard.encrypted_pin_cryptogram.present? && creditcard.encrypted_pin_ksn.present?
319
375
  post[:card][:encrypted_pin] = creditcard.encrypted_pin_cryptogram
320
376
  post[:card][:encrypted_pin_key_id] = creditcard.encrypted_pin_ksn
@@ -323,7 +379,8 @@ module ActiveMerchant #:nodoc:
323
379
  if creditcard.respond_to?(:track_data) && creditcard.track_data.present?
324
380
  card[:swipe_data] = creditcard.track_data
325
381
  card[:fallback_reason] = creditcard.fallback_reason if creditcard.fallback_reason
326
- card[:read_method] = "contactless" if creditcard.contactless
382
+ card[:read_method] = "contactless" if creditcard.contactless_emv
383
+ card[:read_method] = "contactless_magstripe_mode" if creditcard.contactless_magstripe
327
384
  else
328
385
  card[:number] = creditcard.number
329
386
  card[:exp_month] = creditcard.month
@@ -331,14 +388,13 @@ module ActiveMerchant #:nodoc:
331
388
  card[:cvc] = creditcard.verification_value if creditcard.verification_value?
332
389
  card[:name] = creditcard.name if creditcard.name
333
390
  end
334
- post[:card] = card
335
391
 
336
392
  if creditcard.is_a?(NetworkTokenizationCreditCard)
337
- post[:three_d_secure] = {
338
- apple_pay: true,
339
- cryptogram: creditcard.payment_cryptogram
340
- }
393
+ card[:cryptogram] = creditcard.payment_cryptogram
394
+ card[:eci] = creditcard.eci.rjust(2, '0') if creditcard.eci =~ /^[0-9]+$/
395
+ card[:tokenization_method] = creditcard.source.to_s
341
396
  end
397
+ post[:card] = card
342
398
 
343
399
  add_address(post, options)
344
400
  elsif creditcard.kind_of?(String)
@@ -419,13 +475,19 @@ module ActiveMerchant #:nodoc:
419
475
  "Authorization" => "Basic " + Base64.encode64(key.to_s + ":").strip,
420
476
  "User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
421
477
  "Stripe-Version" => api_version(options),
422
- "X-Stripe-Client-User-Agent" => user_agent,
478
+ "X-Stripe-Client-User-Agent" => stripe_client_user_agent(options),
423
479
  "X-Stripe-Client-User-Metadata" => {:ip => options[:ip]}.to_json
424
480
  }
425
481
  headers.merge!("Idempotency-Key" => idempotency_key) if idempotency_key
482
+ headers.merge!("Stripe-Account" => options[:stripe_account]) if options[:stripe_account]
426
483
  headers
427
484
  end
428
485
 
486
+ def stripe_client_user_agent(options)
487
+ return user_agent unless options[:application]
488
+ JSON.dump(JSON.parse(user_agent).merge!({application: options[:application]}))
489
+ end
490
+
429
491
  def api_version(options)
430
492
  options[:version] || @options[:version] || "2015-04-07"
431
493
  end
@@ -506,10 +568,6 @@ module ActiveMerchant #:nodoc:
506
568
  end
507
569
  end
508
570
 
509
- def non_fractional_currency?(currency)
510
- CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s)
511
- end
512
-
513
571
  def emv_payment?(payment)
514
572
  payment.respond_to?(:emv?) && payment.emv?
515
573
  end
@@ -532,6 +590,44 @@ module ActiveMerchant #:nodoc:
532
590
  error_code ||= STANDARD_ERROR_CODE_MAPPING[code]
533
591
  error_code
534
592
  end
593
+
594
+ def tokenize_bank_account(bank_account, options = {})
595
+ account_holder_type = BANK_ACCOUNT_HOLDER_TYPE_MAPPING[bank_account.account_holder_type]
596
+
597
+ post = {
598
+ bank_account: {
599
+ account_number: bank_account.account_number,
600
+ country: 'US',
601
+ currency: 'usd',
602
+ routing_number: bank_account.routing_number,
603
+ name: bank_account.name,
604
+ account_holder_type: account_holder_type,
605
+ }
606
+ }
607
+
608
+ token_response = api_request(:post, "tokens?#{post_data(post)}")
609
+ success = token_response["error"].nil?
610
+
611
+ if success && token_response["id"]
612
+ Response.new(success, nil, token: token_response)
613
+ else
614
+ Response.new(success, token_response["error"]["message"])
615
+ end
616
+ end
617
+
618
+ def ach?(payment_method)
619
+ case payment_method
620
+ when String, nil
621
+ false
622
+ else
623
+ card_brand(payment_method) == "check"
624
+ end
625
+ end
626
+
627
+ def auth_minimum_amount(options)
628
+ return 100 unless options[:currency]
629
+ return MINIMUM_AUTHORIZE_AMOUNTS[options[:currency].upcase] || 100
630
+ end
535
631
  end
536
632
  end
537
633
  end