activemerchant 1.93.0 → 1.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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