activemerchant 1.94.0 → 1.99.0

Sign up to get free protection for your applications and to get access to all the features.
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