activemerchant 1.93.0 → 1.98.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +111 -0
  3. data/README.md +3 -0
  4. data/lib/active_merchant/billing/avs_result.rb +4 -5
  5. data/lib/active_merchant/billing/credit_card.rb +6 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +67 -4
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +106 -22
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +2 -0
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -2
  13. data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +56 -9
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +3 -1
  16. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +98 -61
  18. data/lib/active_merchant/billing/gateways/credorax.rb +29 -3
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +30 -13
  20. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  21. data/lib/active_merchant/billing/gateways/decidir.rb +233 -0
  22. data/lib/active_merchant/billing/gateways/elavon.rb +9 -0
  23. data/lib/active_merchant/billing/gateways/epay.rb +13 -2
  24. data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
  25. data/lib/active_merchant/billing/gateways/fat_zebra.rb +6 -0
  26. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
  27. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  28. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/migs.rb +8 -0
  31. data/lib/active_merchant/billing/gateways/monei.rb +31 -0
  32. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -2
  33. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  34. data/lib/active_merchant/billing/gateways/nmi.rb +39 -1
  35. data/lib/active_merchant/billing/gateways/opp.rb +20 -1
  36. data/lib/active_merchant/billing/gateways/orbital.rb +60 -10
  37. data/lib/active_merchant/billing/gateways/payflow.rb +40 -2
  38. data/lib/active_merchant/billing/gateways/paymill.rb +5 -0
  39. data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
  40. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -2
  41. data/lib/active_merchant/billing/gateways/qvalent.rb +43 -1
  42. data/lib/active_merchant/billing/gateways/realex.rb +32 -9
  43. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  44. data/lib/active_merchant/billing/gateways/stripe.rb +54 -9
  45. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +267 -0
  46. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
  47. data/lib/active_merchant/billing/gateways/trust_commerce.rb +45 -6
  48. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
  49. data/lib/active_merchant/billing/gateways/worldpay.rb +171 -39
  50. data/lib/active_merchant/country.rb +1 -0
  51. data/lib/active_merchant/version.rb +1 -1
  52. metadata +19 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 077fd706902e893fc2824d409a604bc8512849f142e8503c6c1d2081f6fd64b4
4
- data.tar.gz: 34a4a0385f319d09e2000ad4f150814f9b86a331b75941f594387ba7cb30c5b5
3
+ metadata.gz: acfbb780d39dd416b823a9abef4cdcf4de902ec75210ed78e6771314d00b069b
4
+ data.tar.gz: 95e9bf94b3610b33d843fd82af8e4f204380e7c53557d24141b654c553160d9f
5
5
  SHA512:
6
- metadata.gz: 0bc0752fc32fb159b99aa2d0fe36fe3b7d2f1f3059945c76b30f80dad9a8d236f29e5ea5b7105636bb4de16398ae44b2dec13fac230dbc6380ff3496f0476a3c
7
- data.tar.gz: 479edb7ec96840e1b05f89955f11fe2b89914dda0f8ebcd42428c40de80e84e93901b79d4549858aba3c95c308052b68412d9d6b984a154aaa3f636315b6e404
6
+ metadata.gz: 6222cdad43482972e2a799981936dc1d8114ccbede88604880d350650f7b81422b6551a07375dd1a7e06ef562d4f128c332dabc2fc16ebb7b635f774de18a54e
7
+ data.tar.gz: 2f9fe3fe9dfe17fc045f0a3e97940ae4b38a461c48f0416a26ed4b5a4e2bb549464911fbe7527392c8dfbcbbee16bfa82266e3ad644060199ee9552b5b82659b
data/CHANGELOG CHANGED
@@ -1,6 +1,117 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
3
  == HEAD
4
+ == Version 1.98.0 (Sep 9, 2019)
5
+ * Stripe Payment Intents: Add new gateway [britth] #3290
6
+ * Stripe: Send cardholder name and address when creating sources for 3DS 1.0 [jknipp] #3300
7
+ * Checkout_v2: Support for native 3DS2.0 [nfarve] #3303
8
+ * Adds new Maestro BINs [tanyajajodia] #3305
9
+ * eWAY Rapid: If no address is available, default to the name associated with the payment method when setting the Customer fields [jasonxp] #3306
10
+ * eWAY Rapid: Fix a bug in which the email was not set in Customer fields if no address was provided [jasonxp] #3306
11
+ * eWAY Rapid: Support both `phone` and `phone_number` fields under the `shipping_address` option [jasonxp] #3306
12
+ * PayU Latam: Add support for the `merchant_buyer_id` field in the `options` and `buyer` hashes [jasonxp] #3308
13
+ * Update Braintree Gem [curiousepic] #3311
14
+ * Fat Zebra: Send metadata for purchase and authorize [montdidier] #3101
15
+ * TrustCommerce: Add support for custom fields [jasonxp] #3313
16
+ * Stripe Payment Intents: Support option fields `transfer_destination` and `transfer_amount` and remove `transfer_data` hash [britth] #3317
17
+ * Barclaycard Smartpay: Add support for `shopperStatement` gateway-specific field [jasonxp] #3319
18
+ * Stripe Payment Intents: Add support for billing_details on payment methods [britth] #3320
19
+ * BlueSnap: add standardized 3DS 2 auth fields [bayprogrammer] #3318
20
+ * Barclaycard Smartpay: Add app based 3DS requests for auth and purchase [britth] #3327
21
+ * Stripe Payment Intents, Checkout V2: Add support for `MOTO` flagging [britth] #3323
22
+ * Braintree Blue: Adding 3DS2 passthru support [molbrown] #3328
23
+ * Global Collect: Add Cabal card [leila-alderman] #3310
24
+ * WorldPay: Add Cabal card [leila-alderman] #3316
25
+ * Decidir: Add Cabal card [leila-alderman] #3322
26
+ * PayU Latam: Add Cabal card [leila-alderman] #3324
27
+ * dLocal: Add Cabal card [leila-alderman] #3325
28
+ * BlueSnap: Add Cabal card [leila-alderman] #3326
29
+ * Adyen: added 3DS support through external [rikterbeek] #3294
30
+ * Worldpay: Add support for MOTO flagging [britth] #3329
31
+ * ePay: 3DS support [AllaWLie] #3321
32
+ * Checkout.com: added options[:metadata][:manual_entry] support for MOTO transactions [filipebarcos] #3330
33
+
34
+ == Version 1.97.0 (Aug 15, 2019)
35
+ * CyberSource: Add issuer `additionalData` gateway-specific field [jasonxp] #3296
36
+ * PayU Latam: Add Naranja card type [hdeters] #3299
37
+ * Adyen: Add app based 3DS requests for auth and purchase [jeremywrowe] #3298
38
+ * MercadoPago: Add Cabal card type [leila-alderman] #3295
39
+ * MONEI: Add external MPI 3DS 1 support [jimmyn] #3292
40
+ * Bambora formerly Beanstream: Pass card owner when storing tokenized cards [alexdunae] #3006
41
+ * Realex: Prevent error calculating `refund_hash` or `credit_hash` when the secret is nil [jasonxp] #3291
42
+ * Orbital: Add external MPI support for 3DS1 [pi3r] #3261
43
+ * Paymill: Add currency and amount to store requests [jasonxp] #3289
44
+ * Realex: Re-implement credit as general credit [leila-alderman] #3280
45
+ * Braintree Blue: Support for stored credentials [hdeters] #3286
46
+ * CardConnect: Move domain from gateway specific to gateway field [hdeters] #3283
47
+
48
+ == Version 1.96.0 (Jul 26, 2019)
49
+ * Bluesnap: Omit state codes for unsupported countries [therufs] #3229
50
+ * Adyen: Pass updateShopperStatement, industryUsage [curiousepic] #3233
51
+ * TransFirst Transaction Express: Fix blank address2 values [britth] #3231
52
+ * WorldPay: Add support for store method [bayprogrammer] #3232
53
+ * Adyen: Support for additional AVS code mapping [jknipp] #3236
54
+ * Adyen: Update message for AVS result code 'A' to generically cover postal code mismatches [jknipp] #3237
55
+ * CyberSource: Update CyberSource SOAP documentation link [vince-smith] #3204
56
+ * USAePay: Handle additional error codes and add default error code [estelendur] #3167
57
+ * Braintree: Add `skip_avs` and `skip_cvv` gateway specific fields [leila-alderman] #3241
58
+ * NAB Transact: Update periodic test url [mengqing] #3177
59
+ * NMI: Add level 3 gateway-specific fields tax, shipping, and ponumber [jasonxp] #3239
60
+ * Checkout V2: Update stored card flag [curiousepic] #3247
61
+ * NMI: Add support for stored credentials [bayprogrammer] #3243
62
+ * Spreedly: Consolidate API requests and support bank accounts [lancecarlson] #3105
63
+ * BPoint: Hook up merchant_reference and CRN fields [curiousepic] #3249
64
+ * Checkout V2: Stop sending phone number to Checkout V2 integration [filipebarcos] #3248
65
+ * Barclaycard Smartpay: Add support for 3DS2 [britth] #3251
66
+ * Adyen: Add support for non-fractional currencies [molbrown] #3257
67
+ * Decidir: Add new gateway [jknipp] #3254
68
+ * Checkout V2: Reapply Update stored card flag [curiousepic]
69
+ * CyberSource: Update supported countries [molbrown] #3260
70
+ * Credorax: Update supported countries [molbrown] #3260
71
+ * Kushki: Update supported countries [molbrown] #3260
72
+ * Paypal: Update supported countries [molbrown] #3260
73
+ * BlueSnap: Send amount in capture requests [jknipp] #3262
74
+ * Mundipagg: Add Alelo card support [jasonxp] #3255
75
+ * Adyen: Remove temporary amount modification for non-fractional currencies [molbrown] #3263
76
+ * Adyen: Set blank state to N/A [therufs] #3252
77
+ * MiGS: Add tx_source gateway specific field [leila-alderman] #3264
78
+ * NMI: Correct password scrubber to scrub symbols [hdeters] #3267
79
+ * Global Collect: Only add name if present [curiousepic] #3268
80
+ * HPS: Add Apple Pay raw cryptogram support [slogsdon] #3209
81
+ * CardConnect: Fix parsing of level 3 fields [hdeters] #3273
82
+ * TrustCommerce: Support void after purchase [jknipp] #3265
83
+ * Payflow: Support arbitrary level 2 + level 3 fields [therufs] #3272
84
+ * BlueSnap: Default to not send amount on capture [molbrown] #3270
85
+ * Spreedly: extra fields, remove extraneous check [montdidier] #3102 #3281
86
+ * Cecabank: Update encryption to SHA2 [leila-alderman] #3278
87
+ * Checkout V2: Fix 3DS 1&2 integration [nicolas-maalouf-cko] #3240
88
+ * Credorax: add 3DS2 MPI auth data support [bayprogrammer] #3274
89
+ * Add Kosovo to the list of countries [AnotherJoSmith] #3226
90
+ * Realex: Adds 3DS 1&2 support through external MPI [filipebarcos] #3284
91
+ * PayPal: Adds 3DS 1 support through external MPI [nebdil] #3279
92
+
93
+ == Version 1.95.0 (May 23, 2019)
94
+ * Adyen: Constantize version to fix subdomains [curiousepic] #3228
95
+ * Qvalent: Adds support for standard stored credential framework [molbrown] #3227
96
+ * Cybersource: Send tokenization data when card is :master [pi3r] #3230
97
+
98
+ == Version 1.94.0 (May 21, 2019)
99
+ * Mundipagg: Fix number lengths for both VR and Sodexo [dtykocki] #3195
100
+ * Stripe: Support show and list webhook endpoints [jknipp] #3196
101
+ * CardConnect: Add frontendid parameter to requests [gcatlin] #3198
102
+ * Adyen: Correct formatting of Billing Address [nfarve] #3200
103
+ * Stripe: Stripe: Show payment source [jknipp] #3202
104
+ * Checkout V2: Checkout V2: Correct success criteria [curiousepic] #3205
105
+ * Adyen: Add normalized hash of 3DS 2.0 data fields from web browsers [davidsantoso] #3207
106
+ * Stripe: Do not attempt application fee refund if refund was not successful [jasonwebster] #3206
107
+ * Elavon: Send transaction_currency if currency is provided [gcatlin] #3201
108
+ * Elavon: Multi-currency support [jknipp] #3210
109
+ * Adyen: Support preAuths and Synchronous Adjusts [curiousepic] #3212
110
+ * WorldPay: Support Unknown Card Type [tanyajajodia] #3213
111
+ * Mundipagg: Make gateway_affiliation_id an option [curiousepic] #3219
112
+ * CyberSource: Adds Elo Card Type [tanyajajodia] #3220
113
+ * CyberSource: Support standalone credit for cards [curiousepic] #3225
114
+
4
115
  == Version 1.93.0 (April 18, 2019)
5
116
  * Stripe: Do not consider a refund unsuccessful if only refunding the fee failed [jasonwebster] #3188
6
117
  * Stripe: Fix webhook creation for connected account [jknipp] #3193
data/README.md CHANGED
@@ -84,6 +84,9 @@ end
84
84
  For more in-depth documentation and tutorials, see [GettingStarted.md](GettingStarted.md) and the
85
85
  [API documentation](http://www.rubydoc.info/github/activemerchant/active_merchant/).
86
86
 
87
+ Emerging ActiveMerchant 3DS conventions are documented in the [Contributing](https://github.com/activemerchant/active_merchant/wiki/Contributing#3ds-options)
88
+ guide and [Standardized 3DS Fields](https://github.com/activemerchant/active_merchant/wiki/Standardized-3DS-Fields) guide of the wiki.
89
+
87
90
  ## Supported Payment Gateways
88
91
 
89
92
  The [ActiveMerchant Wiki](https://github.com/activemerchant/active_merchant/wikis) contains a [table of features supported by each gateway](https://github.com/activemerchant/active_merchant/wiki/Gateway-Feature-Matrix).
@@ -3,14 +3,13 @@
3
3
  module ActiveMerchant
4
4
  module Billing
5
5
  # Implements the Address Verification System
6
- # https://www.wellsfargo.com/downloads/pdf/biz/merchant/visa_avs.pdf
6
+ # https://www.cybersource.com/developers/other_resources/quick_references/avs_results/.
7
7
  # http://en.wikipedia.org/wiki/Address_Verification_System
8
- # http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_IG/html/app_avs_cvn_codes.htm#app_AVS_CVN_codes_7891_48375
9
- # http://imgserver.skipjack.com/imgServer/5293710/AVS%20and%20CVV2.pdf
10
8
  # http://www.emsecommerce.net/avs_cvv2_response_codes.htm
9
+ # https://www.cardfellow.com/blog/address-verification-service-avs/
11
10
  class AVSResult
12
11
  MESSAGES = {
13
- 'A' => 'Street address matches, but 5-digit and 9-digit postal code do not match.',
12
+ 'A' => 'Street address matches, but postal code does not match.',
14
13
  'B' => 'Street address matches, but postal code not verified.',
15
14
  'C' => 'Street address and postal code do not match.',
16
15
  'D' => 'Street address and postal code match.',
@@ -23,7 +22,7 @@ module ActiveMerchant
23
22
  'K' => 'Card member\'s name matches but billing address and billing postal code do not match.',
24
23
  'L' => 'Card member\'s name and billing postal code match, but billing address does not match.',
25
24
  'M' => 'Street address and postal code match.',
26
- 'N' => 'Street address and postal code do not match.',
25
+ 'N' => 'Street address and postal code do not match. For American Express: Card member\'s name, street address and postal code do not match.',
27
26
  'O' => 'Card member\'s name and billing address match, but billing postal code does not match.',
28
27
  'P' => 'Postal code matches, but street address not verified.',
29
28
  'Q' => 'Card member\'s name, billing address, and postal code match. Shipping information verified but chargeback protection not guaranteed.',
@@ -19,6 +19,9 @@ module ActiveMerchant #:nodoc:
19
19
  # * Maestro
20
20
  # * Forbrugsforeningen
21
21
  # * Elo
22
+ # * Alelo
23
+ # * Cabal
24
+ # * Naranja
22
25
  #
23
26
  # For testing purposes, use the 'bogus' credit card brand. This skips the vast majority of
24
27
  # validations, allowing you to focus on your core concerns until you're ready to be more concerned
@@ -90,6 +93,9 @@ module ActiveMerchant #:nodoc:
90
93
  # * +'maestro'+
91
94
  # * +'forbrugsforeningen'+
92
95
  # * +'elo'+
96
+ # * +'alelo'+
97
+ # * +'cabal'+
98
+ # * +'naranja'+
93
99
  #
94
100
  # Or, if you wish to test your implementation, +'bogus'+.
95
101
  #
@@ -6,15 +6,18 @@ module ActiveMerchant #:nodoc:
6
6
  'visa' => ->(num) { num =~ /^4\d{12}(\d{3})?(\d{3})?$/ },
7
7
  'master' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), MASTERCARD_RANGES) },
8
8
  'elo' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), ELO_RANGES) },
9
+ 'alelo' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), ALELO_RANGES) },
9
10
  'discover' => ->(num) { num =~ /^(6011|65\d{2}|64[4-9]\d)\d{12,15}|(62\d{14,17})$/ },
10
11
  'american_express' => ->(num) { num =~ /^3[47]\d{13}$/ },
12
+ 'naranja' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), NARANJA_RANGES) },
11
13
  'diners_club' => ->(num) { num =~ /^3(0[0-5]|[68]\d)\d{11}$/ },
12
14
  'jcb' => ->(num) { num =~ /^35(28|29|[3-8]\d)\d{12}$/ },
13
15
  'dankort' => ->(num) { num =~ /^5019\d{12}$/ },
14
16
  'maestro' => ->(num) { (12..19).cover?(num&.size) && in_bin_range?(num.slice(0, 6), MAESTRO_RANGES) },
15
17
  'forbrugsforeningen' => ->(num) { num =~ /^600722\d{10}$/ },
16
- 'sodexo' => ->(num) { num =~ /^(606071|603389|606070|606069|606068|600818)\d{8}$/ },
17
- 'vr' => ->(num) { num =~ /^(627416|637036)\d{8}$/ },
18
+ 'sodexo' => ->(num) { num =~ /^(606071|603389|606070|606069|606068|600818)\d{10}$/ },
19
+ 'vr' => ->(num) { num =~ /^(627416|637036)\d{10}$/ },
20
+ 'cabal' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 8), CABAL_RANGES) },
18
21
  'carnet' => lambda { |num|
19
22
  num&.size == 16 && (
20
23
  in_bin_range?(num.slice(0, 6), CARNET_RANGES) ||
@@ -63,6 +66,24 @@ module ActiveMerchant #:nodoc:
63
66
 
64
67
  # https://www.mastercard.us/content/dam/mccom/global/documents/mastercard-rules.pdf, page 73
65
68
  MAESTRO_RANGES = [
69
+ (561200..561269),
70
+ (561271..561299),
71
+ (561320..561356),
72
+ (581700..581751),
73
+ (581753..581800),
74
+ (589998..591259),
75
+ (591261..596770),
76
+ (596772..598744),
77
+ (598746..599999),
78
+ (600297..600314),
79
+ (600316..600335),
80
+ (600337..600362),
81
+ (600364..600382),
82
+ (601232..601254),
83
+ (601256..601276),
84
+ (601640..601652),
85
+ (601689..601700),
86
+ (602011..602050),
66
87
  (639000..639099),
67
88
  (670000..679999),
68
89
  ]
@@ -79,6 +100,29 @@ module ActiveMerchant #:nodoc:
79
100
  651652..651667, 651675..651678, 655000..655010, 655012..655015, 655051..655052, 655056..655057
80
101
  ]
81
102
 
103
+ # Alelo provides BIN ranges by e-mailing them out periodically.
104
+ # The BINs beginning with the digit 4 overlap with Visa's range of valid card numbers.
105
+ # By placing the 'alelo' entry in CARD_COMPANY_DETECTORS below the 'visa' entry, we
106
+ # identify these cards as Visa. This works because transactions with such cards will
107
+ # run on Visa rails.
108
+ ALELO_RANGES = [
109
+ 402588..402588, 404347..404347, 405876..405876, 405882..405882, 405884..405884,
110
+ 405886..405886, 430471..430471, 438061..438061, 438064..438064, 470063..470066,
111
+ 496067..496067, 506699..506704, 506706..506706, 506713..506714, 506716..506716,
112
+ 506749..506750, 506752..506752, 506754..506756, 506758..506762, 506764..506767,
113
+ 506770..506771, 509015..509019, 509880..509882, 509884..509885, 509987..509988
114
+ ]
115
+
116
+ CABAL_RANGES = [
117
+ 60420100..60440099,
118
+ 58965700..58965799,
119
+ 60352200..60352299
120
+ ]
121
+
122
+ NARANJA_RANGES = [
123
+ 589562..589562
124
+ ]
125
+
82
126
  def self.included(base)
83
127
  base.extend(ClassMethods)
84
128
  end
@@ -154,7 +198,7 @@ module ActiveMerchant #:nodoc:
154
198
  valid_test_mode_card_number?(number) ||
155
199
  valid_card_number_length?(number) &&
156
200
  valid_card_number_characters?(number) &&
157
- valid_checksum?(number)
201
+ valid_by_algorithm?(brand?(number), number)
158
202
  end
159
203
 
160
204
  def card_companies
@@ -228,6 +272,15 @@ module ActiveMerchant #:nodoc:
228
272
  %w[1 2 3 success failure error].include?(number)
229
273
  end
230
274
 
275
+ def valid_by_algorithm?(brand, numbers) #:nodoc:
276
+ case brand
277
+ when 'naranja'
278
+ valid_naranja_algo?(numbers)
279
+ else
280
+ valid_luhn?(numbers)
281
+ end
282
+ end
283
+
231
284
  ODD_LUHN_VALUE = {
232
285
  48 => 0,
233
286
  49 => 1,
@@ -258,7 +311,7 @@ module ActiveMerchant #:nodoc:
258
311
  # Checks the validity of a card number by use of the Luhn Algorithm.
259
312
  # Please see http://en.wikipedia.org/wiki/Luhn_algorithm for details.
260
313
  # This implementation is from the luhn_checksum gem, https://github.com/zendesk/luhn_checksum.
261
- def valid_checksum?(numbers) #:nodoc:
314
+ def valid_luhn?(numbers) #:nodoc:
262
315
  sum = 0
263
316
 
264
317
  odd = true
@@ -274,6 +327,16 @@ module ActiveMerchant #:nodoc:
274
327
 
275
328
  sum % 10 == 0
276
329
  end
330
+
331
+ # Checks the validity of a card number by use of Naranja's specific algorithm.
332
+ def valid_naranja_algo?(numbers) #:nodoc:
333
+ num_array = numbers.to_s.chars.map(&:to_i)
334
+ multipliers = [4, 3, 2, 7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2]
335
+ num_sum = num_array[0..14].zip(multipliers).map { |a, b| a*b }.reduce(:+)
336
+ intermediate = 11 - (num_sum % 11)
337
+ final_num = intermediate > 9 ? 0 : intermediate
338
+ final_num == num_array[15]
339
+ end
277
340
  end
278
341
  end
279
342
  end
@@ -200,6 +200,16 @@ module ActiveMerchant #:nodoc:
200
200
  false
201
201
  end
202
202
 
203
+ def add_fields_to_post_if_present(post, options, fields)
204
+ fields.each do |field|
205
+ add_field_to_post_if_present(post, options, field)
206
+ end
207
+ end
208
+
209
+ def add_field_to_post_if_present(post, options, field)
210
+ post[field] = options[field] if options[field]
211
+ end
212
+
203
213
  protected # :nodoc: all
204
214
 
205
215
  def normalize(field)
@@ -4,18 +4,21 @@ module ActiveMerchant #:nodoc:
4
4
 
5
5
  # we recommend setting up merchant-specific endpoints.
6
6
  # https://docs.adyen.com/developers/api-manual#apiendpoints
7
- self.test_url = 'https://pal-test.adyen.com/pal/servlet/Payment/v40'
8
- self.live_url = 'https://pal-live.adyen.com/pal/servlet/Payment/v40'
7
+ self.test_url = 'https://pal-test.adyen.com/pal/servlet/Payment/'
8
+ self.live_url = 'https://pal-live.adyen.com/pal/servlet/Payment/'
9
9
 
10
10
  self.supported_countries = ['AT', 'AU', 'BE', 'BG', 'BR', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GI', 'GR', 'HK', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'MX', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SG', 'SK', 'SI', 'US']
11
11
  self.default_currency = 'USD'
12
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover, :elo]
12
+ self.currencies_without_fractions = %w(CVE DJF GNF IDR JPY KMF KRW PYG RWF UGX VND VUV XAF XOF XPF)
13
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover, :elo, :naranja]
13
14
 
14
15
  self.money_format = :cents
15
16
 
16
17
  self.homepage_url = 'https://www.adyen.com/'
17
18
  self.display_name = 'Adyen'
18
19
 
20
+ API_VERSION = 'v40'
21
+
19
22
  STANDARD_ERROR_CODE_MAPPING = {
20
23
  '101' => STANDARD_ERROR_CODE[:incorrect_number],
21
24
  '103' => STANDARD_ERROR_CODE[:invalid_cvc],
@@ -53,6 +56,7 @@ module ActiveMerchant #:nodoc:
53
56
  add_address(post, options)
54
57
  add_installments(post, options) if options[:installments]
55
58
  add_3ds(post, options)
59
+ add_3ds_authenticated_data(post, options)
56
60
  commit('authorise', post, options)
57
61
  end
58
62
 
@@ -80,6 +84,7 @@ module ActiveMerchant #:nodoc:
80
84
  post = init_post(options)
81
85
  add_invoice_for_modification(post, money, options)
82
86
  add_reference(post, authorization, options)
87
+ add_extra_data(post, nil, options)
83
88
  commit('adjustAuthorisation', post, options)
84
89
  end
85
90
 
@@ -137,10 +142,14 @@ module ActiveMerchant #:nodoc:
137
142
  '16' => 'N', # Postal code doesn't match, address unknown
138
143
  '17' => 'U', # Postal code doesn't match, address not checked
139
144
  '18' => 'I', # Neither postal code nor address were checked
145
+ '19' => 'L', # Name and postal code matches.
140
146
  '20' => 'V', # Name, address and postal code matches.
147
+ '21' => 'O', # Name and address matches.
148
+ '22' => 'K', # Name matches.
141
149
  '23' => 'F', # Postal code matches, name doesn't match.
142
150
  '24' => 'H', # Both postal code and address matches, name doesn't match.
143
- '25' => 'T' # Address matches, name doesn't match.
151
+ '25' => 'T', # Address matches, name doesn't match.
152
+ '26' => 'N' # Neither postal code, address nor name matches.
144
153
  }
145
154
 
146
155
  CVC_MAPPING = {
@@ -174,6 +183,11 @@ module ActiveMerchant #:nodoc:
174
183
  post[:additionalData][:overwriteBrand] = normalize(options[:overwrite_brand]) if options[:overwrite_brand]
175
184
  post[:additionalData][:customRoutingFlag] = options[:custom_routing_flag] if options[:custom_routing_flag]
176
185
  post[:additionalData]['paymentdatasource.type'] = NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
186
+ post[:additionalData][:authorisationType] = options[:authorisation_type] if options[:authorisation_type]
187
+ post[:additionalData][:adjustAuthorisationData] = options[:adjust_authorisation_data] if options[:adjust_authorisation_data]
188
+ post[:additionalData][:industryUsage] = options[:industry_usage] if options[:industry_usage]
189
+ post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement]
190
+ post[:additionalData][:RequestedTestAcquirerResponseCode] = options[:requested_test_acquirer_response_code] if options[:requested_test_acquirer_response_code] && test?
177
191
  post[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
178
192
  add_risk_data(post, options)
179
193
  end
@@ -214,28 +228,34 @@ module ActiveMerchant #:nodoc:
214
228
  def add_address(post, options)
215
229
  return unless post[:card]&.kind_of?(Hash)
216
230
  if (address = options[:billing_address] || options[:address]) && address[:country]
217
- post[:card][:billingAddress] = {}
218
- post[:card][:billingAddress][:street] = address[:address1] || 'N/A'
219
- post[:card][:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
220
- post[:card][:billingAddress][:postalCode] = address[:zip] if address[:zip]
221
- post[:card][:billingAddress][:city] = address[:city] || 'N/A'
222
- post[:card][:billingAddress][:stateOrProvince] = address[:state] || 'N/A'
223
- post[:card][:billingAddress][:country] = address[:country] if address[:country]
231
+ post[:billingAddress] = {}
232
+ post[:billingAddress][:street] = address[:address1] || 'N/A'
233
+ post[:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
234
+ post[:billingAddress][:postalCode] = address[:zip] if address[:zip]
235
+ post[:billingAddress][:city] = address[:city] || 'N/A'
236
+ post[:billingAddress][:stateOrProvince] = get_state(address)
237
+ post[:billingAddress][:country] = address[:country] if address[:country]
224
238
  end
225
239
  end
226
240
 
241
+ def get_state(address)
242
+ address[:state] && !address[:state].blank? ? address[:state] : 'N/A'
243
+ end
244
+
227
245
  def add_invoice(post, money, options)
246
+ currency = options[:currency] || currency(money)
228
247
  amount = {
229
- value: amount(money),
230
- currency: options[:currency] || currency(money)
248
+ value: localized_amount(money, currency),
249
+ currency: currency
231
250
  }
232
251
  post[:amount] = amount
233
252
  end
234
253
 
235
254
  def add_invoice_for_modification(post, money, options)
255
+ currency = options[:currency] || currency(money)
236
256
  amount = {
237
- value: amount(money),
238
- currency: options[:currency] || currency(money)
257
+ value: localized_amount(money, currency),
258
+ currency: currency
239
259
  }
240
260
  post[:modificationAmount] = amount
241
261
  end
@@ -308,9 +328,57 @@ module ActiveMerchant #:nodoc:
308
328
  end
309
329
 
310
330
  def add_3ds(post, options)
311
- return unless options[:execute_threed] || options[:threed_dynamic]
312
- post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
313
- post[:additionalData] = { executeThreeD: 'true' } if options[:execute_threed]
331
+ if three_ds_2_options = options[:three_ds_2]
332
+ device_channel = three_ds_2_options[:channel]
333
+ if device_channel == 'app'
334
+ post[:threeDS2RequestData] = { deviceChannel: device_channel }
335
+ else
336
+ add_browser_info(three_ds_2_options[:browser_info], post)
337
+ post[:threeDS2RequestData] = { deviceChannel: device_channel, notificationURL: three_ds_2_options[:notification_url] }
338
+ end
339
+ else
340
+ return unless options[:execute_threed] || options[:threed_dynamic]
341
+ post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
342
+ post[:additionalData] = { executeThreeD: 'true' } if options[:execute_threed]
343
+ end
344
+ end
345
+
346
+ def add_3ds_authenticated_data(post, options)
347
+ if options[:three_d_secure] && options[:three_d_secure][:eci] && options[:three_d_secure][:xid]
348
+ add_3ds1_authenticated_data(post, options)
349
+ elsif options[:three_d_secure]
350
+ add_3ds2_authenticated_data(post, options)
351
+ end
352
+ end
353
+
354
+ def add_3ds1_authenticated_data(post, options)
355
+ three_d_secure_options = options[:three_d_secure]
356
+ post[:mpiData] = {
357
+ cavv: three_d_secure_options[:cavv],
358
+ cavvAlgorithm: three_d_secure_options[:cavv_algorithm],
359
+ eci: three_d_secure_options[:eci],
360
+ xid: three_d_secure_options[:xid],
361
+ directoryResponse: three_d_secure_options[:directory_response_status],
362
+ authenticationResponse: three_d_secure_options[:authentication_response_status]
363
+ }
364
+ end
365
+
366
+ def add_3ds2_authenticated_data(post, options)
367
+ three_d_secure_options = options[:three_d_secure]
368
+ # If the transaction was authenticated in a frictionless flow, send the transStatus from the ARes.
369
+ if(three_d_secure_options[:authentication_response_status].nil?)
370
+ authentication_response = three_d_secure_options[:directory_response_status]
371
+ else
372
+ authentication_response = three_d_secure_options[:authentication_response_status]
373
+ end
374
+ post[:mpiData] = {
375
+ threeDSVersion: three_d_secure_options[:version],
376
+ eci: three_d_secure_options[:eci],
377
+ cavv: three_d_secure_options[:cavv],
378
+ dsTransID: three_d_secure_options[:ds_transaction_id],
379
+ directoryResponse: three_d_secure_options[:directory_response_status],
380
+ authenticationResponse: authentication_response
381
+ }
314
382
  end
315
383
 
316
384
  def parse(body)
@@ -349,11 +417,11 @@ module ActiveMerchant #:nodoc:
349
417
 
350
418
  def url
351
419
  if test?
352
- test_url
420
+ "#{test_url}#{API_VERSION}"
353
421
  elsif @options[:subdomain]
354
- "https://#{@options[:subdomain]}-pal-live.adyenpayments.com/pal/servlet/Payment/v18"
422
+ "https://#{@options[:subdomain]}-pal-live.adyenpayments.com/pal/servlet/Payment/#{API_VERSION}"
355
423
  else
356
- live_url
424
+ "#{live_url}#{API_VERSION}"
357
425
  end
358
426
  end
359
427
 
@@ -374,8 +442,10 @@ module ActiveMerchant #:nodoc:
374
442
  case action.to_s
375
443
  when 'authorise', 'authorise3d'
376
444
  ['Authorised', 'Received', 'RedirectShopper'].include?(response['resultCode'])
377
- when 'capture', 'refund', 'cancel', 'adjustAuthorisation'
445
+ when 'capture', 'refund', 'cancel'
378
446
  response['response'] == "[#{action}-received]"
447
+ when 'adjustAuthorisation'
448
+ response['response'] == 'Authorised' || response['response'] == '[adjustAuthorisation-received]'
379
449
  else
380
450
  false
381
451
  end
@@ -414,6 +484,20 @@ module ActiveMerchant #:nodoc:
414
484
  def error_code_from(response)
415
485
  STANDARD_ERROR_CODE_MAPPING[response['errorCode']]
416
486
  end
487
+
488
+ def add_browser_info(browser_info, post)
489
+ return unless browser_info
490
+ post[:browserInfo] = {
491
+ acceptHeader: browser_info[:accept_header],
492
+ colorDepth: browser_info[:depth],
493
+ javaEnabled: browser_info[:java],
494
+ language: browser_info[:language],
495
+ screenHeight: browser_info[:height],
496
+ screenWidth: browser_info[:width],
497
+ timeZoneOffset: browser_info[:timezone],
498
+ userAgent: browser_info[:user_agent]
499
+ }
500
+ end
417
501
  end
418
502
  end
419
503
  end