activemerchant 1.94.0 → 1.99.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +120 -1
  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 +65 -2
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +119 -34
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +11 -6
  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 +2 -1
  16. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +37 -27
  18. data/lib/active_merchant/billing/gateways/credorax.rb +69 -4
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +51 -11
  20. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  21. data/lib/active_merchant/billing/gateways/decidir.rb +245 -0
  22. data/lib/active_merchant/billing/gateways/epay.rb +13 -2
  23. data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
  24. data/lib/active_merchant/billing/gateways/fat_zebra.rb +6 -0
  25. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
  26. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  27. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/mastercard.rb +30 -5
  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 +33 -6
  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 +64 -14
  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/redsys.rb +113 -30
  44. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  45. data/lib/active_merchant/billing/gateways/stripe.rb +38 -9
  46. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +271 -0
  47. data/lib/active_merchant/billing/gateways/tns.rb +10 -5
  48. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
  49. data/lib/active_merchant/billing/gateways/trust_commerce.rb +45 -6
  50. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
  51. data/lib/active_merchant/billing/gateways/worldpay.rb +177 -39
  52. data/lib/active_merchant/country.rb +1 -0
  53. data/lib/active_merchant/version.rb +1 -1
  54. metadata +19 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a2728fa6c7116ab636feb7ba715c2e4d5305fb54898d1687d633cbfb2a4795f
4
- data.tar.gz: d63425c0a65919bc4a2a007d41b145a355a341667be616ae8fc0337ce2d8d145
3
+ metadata.gz: cbb76da56e87639a3f9129d70297b78a0edabe9faf2300e682e06e872e019419
4
+ data.tar.gz: 94cb0693e867b03071fe55a7e71b3cfbaf53a7c340d480f24108948822e9e1e7
5
5
  SHA512:
6
- metadata.gz: f577b5623169e0e7a1a12f4f2fa212517fba33a5ec40fb61a8e14c9df629939a4e31ed6a24ed9fabe783404553ff98ff8bdb5f0fabdc3c9da8fe8bc31c8ad97f
7
- data.tar.gz: 2d37c0742319a5533236acaeb9e885692b8610208d540adda49dc21147584a3dbc50ba28632d014fb7d280d4ff9202032e5fe05dde1b1741eabf4889e9a08a42
6
+ metadata.gz: aa96ed1337c57fa00d0405ff07a70b9423bac6a26ec69fde450d73bc9cabfc0ae27b75a46faddcdf3e79f2198db8214e8e6c5926468f0acb999bef43fc220a73
7
+ data.tar.gz: f672640992309d545629a41e4f89d505732d0bb88559648a96935ef01293bcff737de41c8a7b8992993dafdfdb71d29bcbf8b85133c3d9ae5311f7b720b7c9c7
data/CHANGELOG CHANGED
@@ -1,6 +1,125 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
3
  == HEAD
4
+ == Version 1.99.0 (Sep 26, 2019)
5
+ * Adyen: Add functionality to set 3DS exemptions via API [britth] #3331
6
+ * Adyen: Send "NA" instead of "N/A" [leila-alderman] #3339
7
+ * Stripe Payment Intents: Set application fee or transfer amount on capture [britth] #3340
8
+ * TNS: Support Europe endpoint [curiousepic] #3346
9
+ * Redsys: Add 3DS support to gateway [britth] #3336
10
+ * Worldpay: Allow multiple refunds per authorization [jknipp] #3349
11
+ * MercadoPago: Add remote and unit tests for Naranja card [hdeters] #3345
12
+ * CyberSource: Pass commerce indicator if present [curiousepic] #3350
13
+ * Worldpay: Add 3DS2 Support [nfarve] #3344
14
+ * Credorax: Add 3DS 2.0 [nfarve] #3342
15
+ * TNS: Update verison and support pay mode [curiousepic] #3355
16
+ * Stripe: Add supported countries [therufs] #3358
17
+ * Stripe Payment Intents: Add supported countries [therufs] #3359
18
+ * Mundipagg: Append error messages to the message response field [jasonxp] #3353
19
+ * Redsys: Add ability to pass sca_exemption and moto fields to request exemptions [britth] #3354
20
+ * Credorax: Add A Mandatory 3DS field [nfarve] #3360
21
+ * CyberSource: Support 3DS2 pass-through fields [curiousepic] #3363
22
+ * Credorax: Add support for MOTO flagging [britth] #3366
23
+ * Credorax: Enable selecting a processor [leila-alderman] #3302
24
+ * Adyen: Add Cabal card [leila-alderman] #3361
25
+ * Decidir: Add remote tests for Cabal and Naranja [leila-alderman] #3337
26
+ * Payflow: Pass correct field in Status for 3DS in Payflow [nebdil] #3362
27
+ * CyberSource: Use 3DS hash for enrolled field [curiousepic] #3371
28
+
29
+ == Version 1.98.0 (Sep 9, 2019)
30
+ * Stripe Payment Intents: Add new gateway [britth] #3290
31
+ * Stripe: Send cardholder name and address when creating sources for 3DS 1.0 [jknipp] #3300
32
+ * Checkout_v2: Support for native 3DS2.0 [nfarve] #3303
33
+ * Adds new Maestro BINs [tanyajajodia] #3305
34
+ * eWAY Rapid: If no address is available, default to the name associated with the payment method when setting the Customer fields [jasonxp] #3306
35
+ * eWAY Rapid: Fix a bug in which the email was not set in Customer fields if no address was provided [jasonxp] #3306
36
+ * eWAY Rapid: Support both `phone` and `phone_number` fields under the `shipping_address` option [jasonxp] #3306
37
+ * PayU Latam: Add support for the `merchant_buyer_id` field in the `options` and `buyer` hashes [jasonxp] #3308
38
+ * Update Braintree Gem [curiousepic] #3311
39
+ * Fat Zebra: Send metadata for purchase and authorize [montdidier] #3101
40
+ * TrustCommerce: Add support for custom fields [jasonxp] #3313
41
+ * Stripe Payment Intents: Support option fields `transfer_destination` and `transfer_amount` and remove `transfer_data` hash [britth] #3317
42
+ * Barclaycard Smartpay: Add support for `shopperStatement` gateway-specific field [jasonxp] #3319
43
+ * Stripe Payment Intents: Add support for billing_details on payment methods [britth] #3320
44
+ * BlueSnap: add standardized 3DS 2 auth fields [bayprogrammer] #3318
45
+ * Barclaycard Smartpay: Add app based 3DS requests for auth and purchase [britth] #3327
46
+ * Stripe Payment Intents, Checkout V2: Add support for `MOTO` flagging [britth] #3323
47
+ * Braintree Blue: Adding 3DS2 passthru support [molbrown] #3328
48
+ * Global Collect: Add Cabal card [leila-alderman] #3310
49
+ * WorldPay: Add Cabal card [leila-alderman] #3316
50
+ * Decidir: Add Cabal card [leila-alderman] #3322
51
+ * PayU Latam: Add Cabal card [leila-alderman] #3324
52
+ * dLocal: Add Cabal card [leila-alderman] #3325
53
+ * BlueSnap: Add Cabal card [leila-alderman] #3326
54
+ * Adyen: added 3DS support through external [rikterbeek] #3294
55
+ * Worldpay: Add support for MOTO flagging [britth] #3329
56
+ * ePay: 3DS support [AllaWLie] #3321
57
+ * Checkout.com: added options[:metadata][:manual_entry] support for MOTO transactions [filipebarcos] #3330
58
+
59
+ == Version 1.97.0 (Aug 15, 2019)
60
+ * CyberSource: Add issuer `additionalData` gateway-specific field [jasonxp] #3296
61
+ * PayU Latam: Add Naranja card type [hdeters] #3299
62
+ * Adyen: Add app based 3DS requests for auth and purchase [jeremywrowe] #3298
63
+ * MercadoPago: Add Cabal card type [leila-alderman] #3295
64
+ * MONEI: Add external MPI 3DS 1 support [jimmyn] #3292
65
+ * Bambora formerly Beanstream: Pass card owner when storing tokenized cards [alexdunae] #3006
66
+ * Realex: Prevent error calculating `refund_hash` or `credit_hash` when the secret is nil [jasonxp] #3291
67
+ * Orbital: Add external MPI support for 3DS1 [pi3r] #3261
68
+ * Paymill: Add currency and amount to store requests [jasonxp] #3289
69
+ * Realex: Re-implement credit as general credit [leila-alderman] #3280
70
+ * Braintree Blue: Support for stored credentials [hdeters] #3286
71
+ * CardConnect: Move domain from gateway specific to gateway field [hdeters] #3283
72
+
73
+ == Version 1.96.0 (Jul 26, 2019)
74
+ * Bluesnap: Omit state codes for unsupported countries [therufs] #3229
75
+ * Adyen: Pass updateShopperStatement, industryUsage [curiousepic] #3233
76
+ * TransFirst Transaction Express: Fix blank address2 values [britth] #3231
77
+ * WorldPay: Add support for store method [bayprogrammer] #3232
78
+ * Adyen: Support for additional AVS code mapping [jknipp] #3236
79
+ * Adyen: Update message for AVS result code 'A' to generically cover postal code mismatches [jknipp] #3237
80
+ * CyberSource: Update CyberSource SOAP documentation link [vince-smith] #3204
81
+ * USAePay: Handle additional error codes and add default error code [estelendur] #3167
82
+ * Braintree: Add `skip_avs` and `skip_cvv` gateway specific fields [leila-alderman] #3241
83
+ * NAB Transact: Update periodic test url [mengqing] #3177
84
+ * NMI: Add level 3 gateway-specific fields tax, shipping, and ponumber [jasonxp] #3239
85
+ * Checkout V2: Update stored card flag [curiousepic] #3247
86
+ * NMI: Add support for stored credentials [bayprogrammer] #3243
87
+ * Spreedly: Consolidate API requests and support bank accounts [lancecarlson] #3105
88
+ * BPoint: Hook up merchant_reference and CRN fields [curiousepic] #3249
89
+ * Checkout V2: Stop sending phone number to Checkout V2 integration [filipebarcos] #3248
90
+ * Barclaycard Smartpay: Add support for 3DS2 [britth] #3251
91
+ * Adyen: Add support for non-fractional currencies [molbrown] #3257
92
+ * Decidir: Add new gateway [jknipp] #3254
93
+ * Checkout V2: Reapply Update stored card flag [curiousepic]
94
+ * CyberSource: Update supported countries [molbrown] #3260
95
+ * Credorax: Update supported countries [molbrown] #3260
96
+ * Kushki: Update supported countries [molbrown] #3260
97
+ * Paypal: Update supported countries [molbrown] #3260
98
+ * BlueSnap: Send amount in capture requests [jknipp] #3262
99
+ * Mundipagg: Add Alelo card support [jasonxp] #3255
100
+ * Adyen: Remove temporary amount modification for non-fractional currencies [molbrown] #3263
101
+ * Adyen: Set blank state to N/A [therufs] #3252
102
+ * MiGS: Add tx_source gateway specific field [leila-alderman] #3264
103
+ * NMI: Correct password scrubber to scrub symbols [hdeters] #3267
104
+ * Global Collect: Only add name if present [curiousepic] #3268
105
+ * HPS: Add Apple Pay raw cryptogram support [slogsdon] #3209
106
+ * CardConnect: Fix parsing of level 3 fields [hdeters] #3273
107
+ * TrustCommerce: Support void after purchase [jknipp] #3265
108
+ * Payflow: Support arbitrary level 2 + level 3 fields [therufs] #3272
109
+ * BlueSnap: Default to not send amount on capture [molbrown] #3270
110
+ * Spreedly: extra fields, remove extraneous check [montdidier] #3102 #3281
111
+ * Cecabank: Update encryption to SHA2 [leila-alderman] #3278
112
+ * Checkout V2: Fix 3DS 1&2 integration [nicolas-maalouf-cko] #3240
113
+ * Credorax: add 3DS2 MPI auth data support [bayprogrammer] #3274
114
+ * Add Kosovo to the list of countries [AnotherJoSmith] #3226
115
+ * Realex: Adds 3DS 1&2 support through external MPI [filipebarcos] #3284
116
+ * PayPal: Adds 3DS 1 support through external MPI [nebdil] #3279
117
+
118
+ == Version 1.95.0 (May 23, 2019)
119
+ * Adyen: Constantize version to fix subdomains [curiousepic] #3228
120
+ * Qvalent: Adds support for standard stored credential framework [molbrown] #3227
121
+ * Cybersource: Send tokenization data when card is :master [pi3r] #3230
122
+
4
123
  == Version 1.94.0 (May 21, 2019)
5
124
  * Mundipagg: Fix number lengths for both VR and Sodexo [dtykocki] #3195
6
125
  * Stripe: Support show and list webhook endpoints [jknipp] #3196
@@ -16,7 +135,7 @@
16
135
  * WorldPay: Support Unknown Card Type [tanyajajodia] #3213
17
136
  * Mundipagg: Make gateway_affiliation_id an option [curiousepic] #3219
18
137
  * CyberSource: Adds Elo Card Type [tanyajajodia] #3220
19
- * CyberSource: Support standalone credit for cards [curiousepic] 3225
138
+ * CyberSource: Support standalone credit for cards [curiousepic] #3225
20
139
 
21
140
  == Version 1.93.0 (April 18, 2019)
22
141
  * Stripe: Do not consider a refund unsuccessful if only refunding the fee failed [jasonwebster] #3188
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,8 +6,10 @@ 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}$/ },
@@ -15,6 +17,7 @@ module ActiveMerchant #:nodoc:
15
17
  'forbrugsforeningen' => ->(num) { num =~ /^600722\d{10}$/ },
16
18
  'sodexo' => ->(num) { num =~ /^(606071|603389|606070|606069|606068|600818)\d{10}$/ },
17
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, :cabal]
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
 
@@ -93,7 +97,14 @@ module ActiveMerchant #:nodoc:
93
97
  add_stored_credentials(post, credit_card, options)
94
98
  add_recurring_contract(post, options)
95
99
  add_address(post, options)
96
- commit('authorise', post, options)
100
+
101
+ initial_response = commit('authorise', post, options)
102
+
103
+ if initial_response.success? && card_not_stored?(initial_response)
104
+ unsupported_failure_response(initial_response)
105
+ else
106
+ initial_response
107
+ end
97
108
  end
98
109
 
99
110
  def verify(credit_card, options={})
@@ -138,10 +149,14 @@ module ActiveMerchant #:nodoc:
138
149
  '16' => 'N', # Postal code doesn't match, address unknown
139
150
  '17' => 'U', # Postal code doesn't match, address not checked
140
151
  '18' => 'I', # Neither postal code nor address were checked
152
+ '19' => 'L', # Name and postal code matches.
141
153
  '20' => 'V', # Name, address and postal code matches.
154
+ '21' => 'O', # Name and address matches.
155
+ '22' => 'K', # Name matches.
142
156
  '23' => 'F', # Postal code matches, name doesn't match.
143
157
  '24' => 'H', # Both postal code and address matches, name doesn't match.
144
- '25' => 'T' # Address matches, name doesn't match.
158
+ '25' => 'T', # Address matches, name doesn't match.
159
+ '26' => 'N' # Neither postal code, address nor name matches.
145
160
  }
146
161
 
147
162
  CVC_MAPPING = {
@@ -177,6 +192,8 @@ module ActiveMerchant #:nodoc:
177
192
  post[:additionalData]['paymentdatasource.type'] = NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
178
193
  post[:additionalData][:authorisationType] = options[:authorisation_type] if options[:authorisation_type]
179
194
  post[:additionalData][:adjustAuthorisationData] = options[:adjust_authorisation_data] if options[:adjust_authorisation_data]
195
+ post[:additionalData][:industryUsage] = options[:industry_usage] if options[:industry_usage]
196
+ post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement]
180
197
  post[:additionalData][:RequestedTestAcquirerResponseCode] = options[:requested_test_acquirer_response_code] if options[:requested_test_acquirer_response_code] && test?
181
198
  post[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
182
199
  add_risk_data(post, options)
@@ -219,27 +236,33 @@ module ActiveMerchant #:nodoc:
219
236
  return unless post[:card]&.kind_of?(Hash)
220
237
  if (address = options[:billing_address] || options[:address]) && address[:country]
221
238
  post[:billingAddress] = {}
222
- post[:billingAddress][:street] = address[:address1] || 'N/A'
223
- post[:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
239
+ post[:billingAddress][:street] = address[:address1] || 'NA'
240
+ post[:billingAddress][:houseNumberOrName] = address[:address2] || 'NA'
224
241
  post[:billingAddress][:postalCode] = address[:zip] if address[:zip]
225
- post[:billingAddress][:city] = address[:city] || 'N/A'
226
- post[:billingAddress][:stateOrProvince] = address[:state] || 'N/A'
242
+ post[:billingAddress][:city] = address[:city] || 'NA'
243
+ post[:billingAddress][:stateOrProvince] = get_state(address)
227
244
  post[:billingAddress][:country] = address[:country] if address[:country]
228
245
  end
229
246
  end
230
247
 
248
+ def get_state(address)
249
+ address[:state] && !address[:state].blank? ? address[:state] : 'NA'
250
+ end
251
+
231
252
  def add_invoice(post, money, options)
253
+ currency = options[:currency] || currency(money)
232
254
  amount = {
233
- value: amount(money),
234
- currency: options[:currency] || currency(money)
255
+ value: localized_amount(money, currency),
256
+ currency: currency
235
257
  }
236
258
  post[:amount] = amount
237
259
  end
238
260
 
239
261
  def add_invoice_for_modification(post, money, options)
262
+ currency = options[:currency] || currency(money)
240
263
  amount = {
241
- value: amount(money),
242
- currency: options[:currency] || currency(money)
264
+ value: localized_amount(money, currency),
265
+ currency: currency
243
266
  }
244
267
  post[:modificationAmount] = amount
245
268
  end
@@ -313,24 +336,17 @@ module ActiveMerchant #:nodoc:
313
336
 
314
337
  def add_3ds(post, options)
315
338
  if three_ds_2_options = options[:three_ds_2]
316
- if browser_info = three_ds_2_options[:browser_info]
317
- post[:browserInfo] = {
318
- acceptHeader: browser_info[:accept_header],
319
- colorDepth: browser_info[:depth],
320
- javaEnabled: browser_info[:java],
321
- language: browser_info[:language],
322
- screenHeight: browser_info[:height],
323
- screenWidth: browser_info[:width],
324
- timeZoneOffset: browser_info[:timezone],
325
- userAgent: browser_info[:user_agent]
326
- }
327
-
328
- if device_channel = three_ds_2_options[:channel]
329
- post[:threeDS2RequestData] = {
330
- deviceChannel: device_channel,
331
- notificationURL: three_ds_2_options[:notification_url] || 'https://example.com/notification'
332
- }
333
- end
339
+ device_channel = three_ds_2_options[:channel]
340
+ if device_channel == 'app'
341
+ post[:threeDS2RequestData] = { deviceChannel: device_channel }
342
+ else
343
+ add_browser_info(three_ds_2_options[:browser_info], post)
344
+ post[:threeDS2RequestData] = { deviceChannel: device_channel, notificationURL: three_ds_2_options[:notification_url] }
345
+ end
346
+
347
+ if options.has_key?(:execute_threed)
348
+ post[:additionalData][:executeThreeD] = options[:execute_threed]
349
+ post[:additionalData][:scaExemption] = options[:sca_exemption] if options[:sca_exemption]
334
350
  end
335
351
  else
336
352
  return unless options[:execute_threed] || options[:threed_dynamic]
@@ -339,6 +355,44 @@ module ActiveMerchant #:nodoc:
339
355
  end
340
356
  end
341
357
 
358
+ def add_3ds_authenticated_data(post, options)
359
+ if options[:three_d_secure] && options[:three_d_secure][:eci] && options[:three_d_secure][:xid]
360
+ add_3ds1_authenticated_data(post, options)
361
+ elsif options[:three_d_secure]
362
+ add_3ds2_authenticated_data(post, options)
363
+ end
364
+ end
365
+
366
+ def add_3ds1_authenticated_data(post, options)
367
+ three_d_secure_options = options[:three_d_secure]
368
+ post[:mpiData] = {
369
+ cavv: three_d_secure_options[:cavv],
370
+ cavvAlgorithm: three_d_secure_options[:cavv_algorithm],
371
+ eci: three_d_secure_options[:eci],
372
+ xid: three_d_secure_options[:xid],
373
+ directoryResponse: three_d_secure_options[:directory_response_status],
374
+ authenticationResponse: three_d_secure_options[:authentication_response_status]
375
+ }
376
+ end
377
+
378
+ def add_3ds2_authenticated_data(post, options)
379
+ three_d_secure_options = options[:three_d_secure]
380
+ # If the transaction was authenticated in a frictionless flow, send the transStatus from the ARes.
381
+ if(three_d_secure_options[:authentication_response_status].nil?)
382
+ authentication_response = three_d_secure_options[:directory_response_status]
383
+ else
384
+ authentication_response = three_d_secure_options[:authentication_response_status]
385
+ end
386
+ post[:mpiData] = {
387
+ threeDSVersion: three_d_secure_options[:version],
388
+ eci: three_d_secure_options[:eci],
389
+ cavv: three_d_secure_options[:cavv],
390
+ dsTransID: three_d_secure_options[:ds_transaction_id],
391
+ directoryResponse: three_d_secure_options[:directory_response_status],
392
+ authenticationResponse: authentication_response
393
+ }
394
+ end
395
+
342
396
  def parse(body)
343
397
  return {} if body.blank?
344
398
  JSON.parse(body)
@@ -375,11 +429,11 @@ module ActiveMerchant #:nodoc:
375
429
 
376
430
  def url
377
431
  if test?
378
- test_url
432
+ "#{test_url}#{API_VERSION}"
379
433
  elsif @options[:subdomain]
380
- "https://#{@options[:subdomain]}-pal-live.adyenpayments.com/pal/servlet/Payment/v18"
434
+ "https://#{@options[:subdomain]}-pal-live.adyenpayments.com/pal/servlet/Payment/#{API_VERSION}"
381
435
  else
382
- live_url
436
+ "#{live_url}#{API_VERSION}"
383
437
  end
384
438
  end
385
439
 
@@ -442,6 +496,37 @@ module ActiveMerchant #:nodoc:
442
496
  def error_code_from(response)
443
497
  STANDARD_ERROR_CODE_MAPPING[response['errorCode']]
444
498
  end
499
+
500
+ def add_browser_info(browser_info, post)
501
+ return unless browser_info
502
+ post[:browserInfo] = {
503
+ acceptHeader: browser_info[:accept_header],
504
+ colorDepth: browser_info[:depth],
505
+ javaEnabled: browser_info[:java],
506
+ language: browser_info[:language],
507
+ screenHeight: browser_info[:height],
508
+ screenWidth: browser_info[:width],
509
+ timeZoneOffset: browser_info[:timezone],
510
+ userAgent: browser_info[:user_agent]
511
+ }
512
+ end
513
+
514
+ def unsupported_failure_response(initial_response)
515
+ Response.new(
516
+ false,
517
+ 'Recurring transactions are not supported for this card type.',
518
+ initial_response.params,
519
+ authorization: initial_response.authorization,
520
+ test: initial_response.test,
521
+ error_code: initial_response.error_code,
522
+ avs_result: initial_response.avs_result,
523
+ cvv_result: initial_response.cvv_result[:code]
524
+ )
525
+ end
526
+
527
+ def card_not_stored?(response)
528
+ response.authorization ? response.authorization.split('#')[2].nil? : true
529
+ end
445
530
  end
446
531
  end
447
532
  end