From 85ad44c77b837b48a582092f04b74ac568b5a638 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Thu, 8 Sep 2022 21:43:18 -0500 Subject: [PATCH] Block repeated failed attempts to verify cards Declined verifications are ultimately a kind of declined transaction, and still reflect poorly on us. --- config.ru | 55 ++++++++++++++++++++++++++++++++--------- views/credit_cards.slim | 10 ++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/config.ru b/config.ru index 6e2cbb21c0de6f6e8dee016f88dc84feba281cd5..dee3006bde3a41bf9828b2ad7fb9d45bdb13ae5d 100644 --- a/config.ru +++ b/config.ru @@ -48,9 +48,10 @@ class CreditCardGateway end end - def initialize(jid, customer_id=nil) + def initialize(jid, customer_id, antifraud) @jid = jid @customer_id = customer_id + @antifraud = antifraud @gateway = Braintree::Gateway.new( environment: BRAINTREE_CONFIG[:environment].to_s, @@ -115,18 +116,32 @@ class CreditCardGateway !@gateway.customer.find(customer_id).payment_methods.empty? end + def antifraud + REDIS.mget(@antifraud.map { |k| "jmp_antifraud-#{k}" }).find do |anti| + anti.to_i > 2 + end && + Braintree::ErrorResult.new( + @gateway, errors: {}, message: "Please contact support" + ) + end + + def incr_antifraud! + @antifraud.each do |k| + REDIS.incr("jmp_antifraud-#{k}") + REDIS.expire("jmp_antifraud-#{k}", 60 * 60 * 24) + end + end + def default_method(nonce) - result = @gateway.payment_method.create( - customer_id: customer_id, - payment_method_nonce: nonce, - options: { - verify_card: true, - make_default: true - } + result = antifraud || @gateway.payment_method.create( + customer_id: customer_id, payment_method_nonce: nonce, + options: { verify_card: true, make_default: true } ) - raise ErrorResult.for(result) unless result.success? - result + return result if result.success? + + incr_antifraud! + raise ErrorResult.for(result) end def remove_method(token) @@ -192,6 +207,7 @@ end class JmpPay < Roda SENTRY_DSN = ENV["SENTRY_DSN"] && URI(ENV["SENTRY_DSN"]) plugin :render, engine: "slim" + plugin :cookies, path: "/" plugin :common_logger, $stdout extend Forwardable @@ -230,16 +246,33 @@ class JmpPay < Roda r.on :jid do |jid| Sentry.set_user(id: params["customer_id"], jid: jid) - gateway = CreditCardGateway.new(jid, params["customer_id"]) + atfd = r.cookies["atfd"] || SecureRandom.uuid + one_year = 60 * 60 * 24 * 365 + response.set_cookie( + "atfd", + value: atfd, expires: Time.now + one_year + ) + params.delete("atfd") if params["atfd"].to_s == "" + antifrauds = [atfd, r.ip, params["atfd"]].compact.uniq + customer_id = params["customer_id"] + gateway = CreditCardGateway.new(jid, customer_id, antifrauds) topup = AutoTopUpRepo.new r.on "credit_cards" do r.get do + if gateway.antifraud + return view( + :message, + locals: { message: "Please contact support" } + ) + end + view( "credit_cards", locals: { token: gateway.client_token, customer_id: gateway.customer_id, + antifraud: atfd, auto_top_up: topup.find(gateway.customer_id) || (gateway.payment_methods? ? "" : "15") } diff --git a/views/credit_cards.slim b/views/credit_cards.slim index 2e898164fe25d5d68f00e0e18700424a32fc1a32..5856202e5210c10f64f2df02c5e116998270810f 100644 --- a/views/credit_cards.slim +++ b/views/credit_cards.slim @@ -32,12 +32,22 @@ form method="post" action="" small Leave blank for no auto top-up. input type="hidden" name="customer_id" value=customer_id + input type="hidden" name="atfd" value=antifraud input type="hidden" name="braintree_nonce" script src="https://js.braintreegateway.com/web/dropin/1.33.0/js/dropin.js" javascript: document.querySelector("#braintree").innerHTML = ""; + if(window.localStorage) { + var atfd = localStorage.getItem("atfd"); + if(!atfd) { + atfd = "#{antifraud}"; + localStorage.setItem("atfd", atfd); + } + document.querySelector("input[name=atfd]").value = atfd; + } + var button = document.createElement("button"); button.innerHTML = "Save"; document.querySelector("form").appendChild(button);