@@ -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")
}
@@ -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);
});
}
});