diff --git a/config.ru b/config.ru index 6e2cbb21c0de6f6e8dee016f88dc84feba281cd5..2fdb5e25c15fd44f621d1cc098eed14979d52641 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,34 @@ class CreditCardGateway !@gateway.customer.find(customer_id).payment_methods.empty? end + def antifraud + return if REDIS.exists?("jmp_antifraud_bypass-#{customer_id}") + + 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 +209,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 +248,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 05ca9e6ad48f222334da2be17374b9a4e7979627..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); @@ -53,7 +63,10 @@ javascript: chooseAnotherWayToPay: "Add a different payment source" } }, function (createErr, instance) { - if(createErr) console.log(createErr); + if(createErr) { + console.log(createErr); + Sentry.captureException(createErr); + } document.querySelector("form").addEventListener("submit", function(e) { e.preventDefault(); @@ -82,17 +95,24 @@ javascript: return Promise.reject(response); } }).catch(function(err) { - console.log(err); - err.text().then(function(msg) { - instance._mainView.hideLoadingIndicator(); - instance.clearSelectedPaymentMethod(); - instance._mainView.showSheetError(msg); - }); + if(!(err instanceof Response)) return Promise.reject(err); + + return err.text().then(function(msg) { + console.log(msg); + instance._mainView.hideLoadingIndicator(); + instance.clearSelectedPaymentMethod(); + instance._mainView.showSheetError(msg); + var errEl = instance._mainView.sheetErrorText; + if(errEl.innerHTML === instance._mainView.strings.genericError) { + errEl.innerHTML = "Card Issuer Says: " + msg; + } + }); }).catch(function(err) { console.log(err); instance._mainView.hideLoadingIndicator(); instance.clearSelectedPaymentMethod(); instance._mainView.showSheetError(); + Sentry.captureException(err); }); } });