From 486fb6fa930ee1880750c06f5c9c965ceccfc8c3 Mon Sep 17 00:00:00 2001 From: rikt Date: Tue, 10 Dec 2019 10:41:23 +0100 Subject: [PATCH 1/8] add codeowners file --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..14fca11d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @crrood @hbkwong @KadoBOT @Aleffio @rikterbeek \ No newline at end of file From 556780688317c2cf924b3787be46036ea5d64545 Mon Sep 17 00:00:00 2001 From: crrood Date: Wed, 8 Jan 2020 13:30:42 -0800 Subject: [PATCH 2/8] Mask PCI sensitive data in error logging --- lib/adyen/errors.rb | 23 +++++++++++++++++++++-- spec/errors_spec.rb | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/lib/adyen/errors.rb b/lib/adyen/errors.rb index 22c38c40..082b8cca 100644 --- a/lib/adyen/errors.rb +++ b/lib/adyen/errors.rb @@ -3,14 +3,33 @@ class AdyenError < StandardError attr_reader :code, :response, :request, :msg def initialize(request = nil, response = nil, msg = nil, code = nil) + # mask PCI data in request + if not request.nil? + request = request.is_a?(Hash) ? request : JSON.parse(request) + request.each do |k, v| + if request[k].is_a?(Hash) + request[k].each do |k2, v2| + if k2 == :number + v2.replace("#{v2[0,6]}******#{v2[12,16]}") + elsif k2 == :cvc + v2.replace("*" * v2.length) + end + end + end + end + end + + # components of formatted error message attributes = { code: code, + msg: msg, request: request, - response: response, - msg: msg + response: response }.select { |_k, v| v }.map { |k, v| "#{k}:#{v}" }.join(', ') message = "#{self.class.name} #{attributes}" super(message) + + # internal variables @code = code @response = response @request = request diff --git a/spec/errors_spec.rb b/spec/errors_spec.rb index e820f2ff..f8f73f16 100644 --- a/spec/errors_spec.rb +++ b/spec/errors_spec.rb @@ -3,15 +3,45 @@ require 'spec_helper' RSpec.describe Adyen::AdyenError do + before(:all) do + @shared_values = { + request: { + amount: { + currency: "USD", + value: 1000 + }, + reference: "Your order number", + paymentMethod: { + type: "scheme", + number: "4111111111111111", + expiryMonth: "10", + expiryYear: "2020", + holderName: "John Smith", + cvc: "737" + }, + returnUrl: "https://your-company.com/", + merchantAccount: "YOUR_MERCHANT_ACCOUNT" + } + } + end + describe '#to_s' do it 'describes using the error properties' do - expect(Adyen::AdyenError.new('request', 'response', 'message', 'code').to_s).to eq('Adyen::AdyenError code:code, request:request, response:response, msg:message') + expect(Adyen::AdyenError.new(@shared_values[:request], 'response', 'message', 'code').to_s).to eq("Adyen::AdyenError code:code, msg:message, request:#{@shared_values[:request]}, response:response") end it 'skips the null properties' do - expect(Adyen::AdyenError.new('request', nil, nil, 'code').to_s).to eq('Adyen::AdyenError code:code, request:request') + expect(Adyen::AdyenError.new(@shared_values[:request], nil, nil, 'code').to_s).to eq("Adyen::AdyenError code:code, request:#{@shared_values[:request]}") end it 'uses the proper error class name' do - expect(Adyen::PermissionError.new('a message', 'a request').to_s).to eq('Adyen::PermissionError code:403, request:a request, msg:a message') + expect(Adyen::PermissionError.new('message', @shared_values[:request]).to_s).to eq("Adyen::PermissionError code:403, msg:message, request:#{@shared_values[:request]}") + end + end + describe '#masking' do + it 'masks card number when logging request in errors' do + expect(Adyen::AdyenError.new(@shared_values[:request], 'response', 'message', 'code').request[:paymentMethod][:number]).to eq('411111******1111') + end + it 'masks CVC when logging request in errors' do + expect(Adyen::AdyenError.new(@shared_values[:request], 'response', 'message', 'code').request[:paymentMethod][:cvc]).to eq('***') end end end From 7c63d3c4ae8e1a92c28d16d5d601b99d0ce25eb0 Mon Sep 17 00:00:00 2001 From: crrood Date: Wed, 8 Jan 2020 15:05:19 -0800 Subject: [PATCH 3/8] fix ruby >= 2.3 errors --- Gemfile | 4 ++-- lib/adyen/errors.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index f584db2a..cceca64f 100644 --- a/Gemfile +++ b/Gemfile @@ -2,11 +2,11 @@ source "https://rubygems.org" -ruby "> 2.1.0" +ruby ">= 2.1.0" gem "faraday" gem "bundler", :group => :development gem "webmock", :group => :development gem "rspec", :group => :development -gem "activesupport", :group => :development \ No newline at end of file +gem "activesupport", :group => :development diff --git a/lib/adyen/errors.rb b/lib/adyen/errors.rb index 082b8cca..02f68cc2 100644 --- a/lib/adyen/errors.rb +++ b/lib/adyen/errors.rb @@ -10,9 +10,9 @@ def initialize(request = nil, response = nil, msg = nil, code = nil) if request[k].is_a?(Hash) request[k].each do |k2, v2| if k2 == :number - v2.replace("#{v2[0,6]}******#{v2[12,16]}") + request[k][k2] = "#{v2[0,6]}******#{v2[12,16]}" elsif k2 == :cvc - v2.replace("*" * v2.length) + request[k][k2] = "*" * v2.length end end end From d82699498d642942319efaec3e4d98acabb00d13 Mon Sep 17 00:00:00 2001 From: Rik ter Beek Date: Thu, 9 Jan 2020 12:14:22 +0100 Subject: [PATCH 4/8] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..17a0592c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at support@adyen.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From 12e5ae74b3a9e7a5873d1407e007fca75ae4b655 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 13 Jan 2020 09:50:28 +0100 Subject: [PATCH 5/8] Add renovate.json (#52) --- renovate.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..f45d8f11 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} From 69740189719abc81a27a432b9d62b8fdcac37596 Mon Sep 17 00:00:00 2001 From: crrood Date: Wed, 15 Jan 2020 10:29:28 -0800 Subject: [PATCH 6/8] add extra fields to mask, move to separate method --- lib/adyen/errors.rb | 53 ++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/lib/adyen/errors.rb b/lib/adyen/errors.rb index 02f68cc2..ce019bcd 100644 --- a/lib/adyen/errors.rb +++ b/lib/adyen/errors.rb @@ -3,21 +3,7 @@ class AdyenError < StandardError attr_reader :code, :response, :request, :msg def initialize(request = nil, response = nil, msg = nil, code = nil) - # mask PCI data in request - if not request.nil? - request = request.is_a?(Hash) ? request : JSON.parse(request) - request.each do |k, v| - if request[k].is_a?(Hash) - request[k].each do |k2, v2| - if k2 == :number - request[k][k2] = "#{v2[0,6]}******#{v2[12,16]}" - elsif k2 == :cvc - request[k][k2] = "*" * v2.length - end - end - end - end - end + mask_fields(request) # components of formatted error message attributes = { @@ -35,6 +21,43 @@ def initialize(request = nil, response = nil, msg = nil, code = nil) @request = request @msg = msg end + + # mask PCI data in request + def mask_fields(request) + return if request.nil? + + # sensitive fields + fields_to_mask = [ + :expiryMonth, + :expiryYear, + :encryptedCardNumber, + :encryptedExpiryMonth, + :encryptedExpiryYear, + :encryptedSecurityCode + ] + + # convert to hash if necessary + request = request.is_a?(Hash) ? request : JSON.parse(request) + + # iterate through request to find fields to mask + request.each do |k, v| + if request[k].is_a?(Hash) + # recursively traverse multi-level hashes + mask_fields(request[k]) + else + if k == :number + # show first 6 and last 4 for cards + request[k] = "#{v[0,6]}******#{v[12,16]}" + elsif k == :cvc + # show length of cvc for debugging + request[k] = "*" * v.length + elsif fields_to_mask.include? k + # generic mask for other fields + request[k] = "***" + end + end + end + end end class AuthenticationError < AdyenError From e12bf1a418361f47fa8cae58436b29b2d6b585ae Mon Sep 17 00:00:00 2001 From: VvanGemert Date: Thu, 6 Feb 2020 12:05:09 +0100 Subject: [PATCH 7/8] Added checkout.payment_links call, adding Pay by Link functionality --- README.md | 1 + docs/checkout.html | 17 +++++++++++++++++ lib/adyen/services/checkout.rb | 3 ++- spec/checkout_spec.rb | 1 + spec/mocks/requests/Checkout/payment_links.json | 9 +++++++++ .../mocks/responses/Checkout/payment_links.json | 9 +++++++++ spec/service_spec.rb | 1 + 7 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 spec/mocks/requests/Checkout/payment_links.json create mode 100644 spec/mocks/responses/Checkout/payment_links.json diff --git a/README.md b/README.md index cad5e83d..4b3b924a 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ adyen.checkout.version = 50 - payment_methods - payments - payments.details +- payment_links **checkout utility:** - origin_keys diff --git a/docs/checkout.html b/docs/checkout.html index eb7ae450..f0572a2f 100755 --- a/docs/checkout.html +++ b/docs/checkout.html @@ -41,6 +41,8 @@

Available methods

  • payments
  • payments.details
  • + +
  • payment_links
  • Authentication

    @@ -99,7 +101,22 @@

    Usage - Checkout API

    A successful call to payment_methods will return a list of supported payment methods along with redirect URL's so that you can send your shoppers directly to the issuer's site without losing control of front-end styling / logic.

    + You can also create a link to Adyen's hosted payment form: + +
    response = adyen.checkout.payment_links('{
    +  "amount": {
    +    "value": 1500,
    +    "currency": "EUR"
    +  },
    +  "countryCode": "US",
    +  "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
    +  "reference": "YOUR_REFERENCE"
    +}')
    + +

    A successful call to payment_links will return a url, which directs a user to Adyen's hosted payment form.

    + +