From 384490ecc5ccb8f081e98403d72125325e989ba4 Mon Sep 17 00:00:00 2001 From: Ryan Woods Date: Fri, 10 Dec 2021 11:00:56 +0100 Subject: [PATCH] Add email to Braintree customer Issue: https://github.com/solidusio/solidus_paypal_braintree/issues/309 Braintree comes with free basic fraud protection. One of the tools is Risk Threshold Rules (velocity checks) [1]. One of the checks is checking amount of transactions per customer email. However, the email was not being attached to the customer when creating it before the transaction. This fixes that so developers can utilize this check. [1] https://developer.paypal.com/braintree/articles/guides/fraud-tools/basic/risk-threshold-rules --- .../solidus_paypal_braintree/gateway.rb | 2 + spec/fixtures/cassettes/gateway/customer.yml | 79 +++++++++++++++++++ .../solidus_paypal_braintree/gateway_spec.rb | 50 ++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 spec/fixtures/cassettes/gateway/customer.yml diff --git a/app/models/solidus_paypal_braintree/gateway.rb b/app/models/solidus_paypal_braintree/gateway.rb index 9c7023fc..d3047759 100644 --- a/app/models/solidus_paypal_braintree/gateway.rb +++ b/app/models/solidus_paypal_braintree/gateway.rb @@ -398,6 +398,8 @@ def paypal_payee_email_for(source, options) def customer_profile_params(payment) params = {} + params[:email] = payment&.order&.email + if store_in_vault && payment.source.try(:nonce) params[:payment_method_nonce] = payment.source.nonce end diff --git a/spec/fixtures/cassettes/gateway/customer.yml b/spec/fixtures/cassettes/gateway/customer.yml new file mode 100644 index 00000000..2940c9aa --- /dev/null +++ b/spec/fixtures/cassettes/gateway/customer.yml @@ -0,0 +1,79 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/vn32pmzn67tzbcd9/customers + body: + encoding: UTF-8 + string: | + + + braintree@customers.com + fake-valid-nonce + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 3.4.0 + X-Apiversion: + - '6' + Content-Type: + - application/xml + Authorization: + - Basic cTM3ZnF0eXc0cDJzN2Zidzo2M2RhYmNjOWFmMTcyNDhlZjQyMTdjZmRlYWEwM2UwMQ== + response: + status: + code: 201 + message: '' + headers: + Date: + - Fri, 10 Dec 2021 10:53:18 GMT + Content-Type: + - application/xml; charset=utf-8 + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Authentication: + - basic_auth + X-User: + - mp9fm99xgbv5d7nc + Vary: + - Accept-Encoding, Origin + Content-Encoding: + - gzip + Etag: + - W/"435fd70a86beeb87bca285ff652a7010" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.366158' + X-Request-Id: + - e7e1e4f3-b4cb-4f66-83d8-68956318e262 + Content-Security-Policy: + - frame-ancestors 'self' + X-Broxyid: + - e7e1e4f3-b4cb-4f66-83d8-68956318e262 + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + Paypal-Debug-Id: + - ed5d938d152c4 + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: !binary |- + H4sIAJ4xs2EAA6xW23KjOBB9z1e4eCeAsR07hcnW1GxStVU7+7DjzE5epgRqG8VCIpLw7etXwtx8wXEq84ZOn241rdNSBw+blPZWICThbGp5t67VAxZzTNhias2+P9pj6yG8CeJcKp6CCG96vYDgcDwc+kN35LmBo1cG1MY4QUzZer1ifj9Ld2x0p3ZRjCeB07Ya9pwIqWyGUugxQqeWEjlYTmGiqMsS8zRDbHuCQ4oIDSOBCFMC4I8qWXmrPQJnbzbELOHsNOwcbU6wNUSSqDMpCEAKsI1UT20zmFpYLxVJwQr7bt+zvb7tud89937o33t3L4HTOBT+eYY/5t847Pcvfs2eE6BY7lNaUB4hagr703/e4aeJevnvr/k/X2fe37vZ+tvrn+vAaTjlT2Ci7BgJLMs0kBBoaxnroX2PaCwilGpN2AhjAVJWeKmGu0oGJVadgH0olTbckLu1UBI6FVHtdl4XpVUaTagq8Q4SbBQwbOp8kUZ5jChRXVsJWOg26jBmXCp9Brq3IJwMPFfXrA21fyfXOt4WsI1olqB+548fM/1rmCzXZ0Did6gXCl70kQkTgeiK8rlGKaN8sl2KGE6HdLWkWThwvf54bDisxo3ubbNd+Ewk0pnV6zYj4RRrJXdVyYjSXHoE0XDGloyvmY7UYA1tX20+t4mUOWIxtPmnxtrx8wX+QJs21GuumzPkkzimlZTulnD2b8uhRis+hoiopiL7ZWOco5xWBYg4p4CYFZqTMNTC2JBzoU/Z1j2aU1OIVtBjS+UCm4yIIh875UwlodfXz8kxeIa9BSTMMRzSC/SADfg49zmiEkqvViZNIeOnx2H04znHTx7FT8nq5XHy+tMbD7H/5S3+4W6PLvvCOwFEVaJV29JWC6toJEULsHNBw0SpTN47DpISlLytn9aFLs8abc276mRomwJTv1JQCce/KF9wZ6Ub5jZjiwdgKyI4M4SpRAxHfKOfiDp+vaNWtWnNCLFlk9oBWlGLR2AQeuOxFzjlorLpVASnrUargJogIEO6IN+4tpXfjY3jPC4mk8a/wSqazCMZC5KZkzx8NZueV3wJLJyss9e3VB/9flkZc0be8urW1JF1aYh+x4V+DEYe8gfjyO9P4iGejEcjNMSAwJ/7Aw3oW63LtY79G+7JFbCU2xIvOyRZ21seQqexb9iukhBpM1BrLpZ2UQ6y69T8OWo5kThHI8kBUNzoQXm7w9mJ5vjqP5lZPnIVXp5XLk8rl2aVKyaVq+aUi1PKhRnlygnl2vnk2unk6tnk3cnk3bnktzyan+61wGnJsV6AXjZ6C2/+BwAA//8DAM4vgB+aDQAA + recorded_at: Fri, 10 Dec 2021 10:53:18 GMT +recorded_with: VCR 6.0.0 diff --git a/spec/models/solidus_paypal_braintree/gateway_spec.rb b/spec/models/solidus_paypal_braintree/gateway_spec.rb index 3808b8a6..4668cc4b 100644 --- a/spec/models/solidus_paypal_braintree/gateway_spec.rb +++ b/spec/models/solidus_paypal_braintree/gateway_spec.rb @@ -544,6 +544,56 @@ end end + describe '#customer_profile_params' do + subject(:params) { gateway.send(:customer_profile_params, payment) } + + let(:payment) do + build(:payment, { + payment_method: gateway, + source: source + }) + end + + context 'when payment does not belong to an order' do + before { allow(payment).to receive(:order).and_return(nil) } + + it 'has the email param as nil' do + expect(subject[:email]).to be_nil + end + end + + context 'when payment belongs to an order' do + it 'has no email param' do + expect(subject[:email]).to eq(payment.order.email) + end + end + end + + describe "Braintree Customer" do + subject(:customer) { braintree.customer.create(params).customer } + + let(:params) { gateway.send(:customer_profile_params, payment) } + + let(:payment) do + build(:payment, { + payment_method: gateway, + source: source + }) + end + + cassette_options = { + cassette_name: 'gateway/customer', + match_requests_on: [:braintree_uri] + } + + context "with customer", vcr: cassette_options do + it 'saves the customer email correctly' do + allow(payment.order).to receive(:email).and_return('braintree@customers.com') + expect(subject.email).to eq(payment.order.email) + end + end + end + shared_examples "sources_by_order" do let(:order) { FactoryBot.create :order, user: user, state: "complete", completed_at: Time.current } let(:gateway) { new_gateway.tap(&:save!) }