Using `solidus_auth_devise` we would like to requi...
# support
t
Using
solidus_auth_devise
we would like to require users to confirm their email address if the order total is below a certain amount before they can check out as a fraud prevention measure (lots of fraudulent orders on the cheapest items in the store). We have this part working with some monkey patching, but noticed if the user registers an account during check out, once they confirm and log in, their cart is not associated, so they login to an empty cart. Found the setting
allow_unconfirmed_access_for
which does let the cart associate but then allows the user to check out without confirming their email first. Is there any way to associate the cart immediately upon account creation without letting a user check out while unconfirmed? Or maybe block checkout from fully completing if the user hasn't confirmed their email yet? My initial thought is that maybe the latter might be easier to implement. I found some of the logic in the
warden
and
devise
gem, but wondering if anyone has worked this out already or if there's a different setting I'm missing. Edit: Maybe this actually isn't working. I can't reproduce it in the normal checkout flow, but after deploying this yesterday, somehow over 12 guest orders have still come through that should have been forced to register. Not sure if there's some other way these might be coming in? Hoping anyone has any insight. Edit 2 (sorry): Looks like the method I took is only blocking the frontend form from appearing, so hitting the
/checkout/registration
endpoint with a valid auth token and the email field filled out still allows a user to guest checkout. Not sure if we did this wrong or if this is a bug.
j
Would love if anyone's able to provide any input/ideas on this. Willing to pay for help too if necessary. We have a client that's getting 40+ $10 fraud orders a day right now because there's no built in way to force any sort of account validation, these orders seem to be avoiding the frontend checkout process some how. Rackattack/Cloudflare aren't able to block them since they're switching IPs
k
What about requiring manual capture by an admin on the order if it’s below a certain amount?
Also, which payment provider are you using? Maybe they have some sort of fraud protection in place already that you can use to understand which order will require manual capturing?
t
Looks like for all these they're using the Braintree payment method, supplied by the
solidus_paypal_braintree
gem
Looking at a handful of orders, sometimes it's just one card, sometimes I see multiple credit cards were tried and failed or marked invalid state, before they get one that comes through as completed.
j
Requiring manual capture wouldn't be ideal (since that would probably annoy too many regular customers), the route we were hoping to go would be to add some sort of backend check that prevents a user from placing a low $$ order if they're not signed in with a validated account
since all of the fraud orders are using bogus emails that bounce
k
to add some sort of backend check that prevents a user from placing a low $$ order if they’re not signed in with a validated account
I think you can do that with a new before_transition in the order state machine: https://github.com/solidusio/solidus/blob/c09ead361b6b705897bf6a05aa38cef24d7d3c0d/core/lib/spree/core/state_machines/order.rb#LL114C60-L114C60
t
Okay cool that's where I was at with my last attempt. Was able to conditionally add one of these, and it will do it's check and run, but couldn't seem to figure how to block the transition from occurring? Tried a few things in
require_registration
but haven't found anything that actually works (think I grabbed this from another method).
k
Are you sure this is executed, seems correct to me
t
If I throw a `binding.pry` in there it does seem to throw me to a console.
t
Just added a binding.pry in that method now, and tried it out. It does trigger, and I can see the errors get added to
errors
but when I exit the pry, it just moves on to the address step anyways. Are
errors
the condition that should prevent it from moving forward in the state machine?
k
I think it’s more the return false that blocks the transition
t
Okay that's what I thought and then the UI can just show the errors.
👍 1
k
let me check the documentation on the state machine gem
might be that
&& (return false)
? Did you try to move it on a new line?
t
I'll try that.
Looks like the same. To clarify the flow: 1. add item to cart with total below the threshold 2. hit checkout 3. solidus redirects to login/register page as expected 4. on the login page if I enter an email in the guest checkout box, it lets me continue to address, but I would expect that the transition to address should be blocked? For step 3 the conditional seems to work because if I add a product that makes the total exceed the threshold, this login/register page is skipped and user goes straight to address.
k
can you try to move the transition to another step?
maybe the login/registration has some different logic that do not pass through the state machine?
oh, wait… what’s the state of the order when you reach that page?
t
Interesting...
k
maybe it’s still in cart?
t
I moved it to
base.state_machine.before_transition to: :confirm
and it threw me back to the delivery step when I tried to continue.
and threw the Registration required error. o.O
k
yeah, because the order is still in the previous state before calling
order.next
t
I see. So how would be the best way to handle this do you think? Definitely don't want the user to have to enter a bunch of information before finding out they need to register. Should I conditionally add a checkout step before address?
k
one thing you could try is to manually move the order in the address step before rendering, in the checkout controller, like
order.go_to_state(:address)
.
that has some implications: 1. you are writing on the database on a GET request (checkout#edit), not ideal 2. if user is in the payment step and visit the address (changing the URL), the order will be moved to the address step, instead of staying at payment, this will require them to do the steps again, not the end of the world, depends on your UX as well Maybe you can force this state transition only in certain cases (after registration, when not confirmed, etc)
t
Okay this gives me some ideas to start from and at least I understand a little better what part is not working to reassess. I think the other problem and part of the reason I am avoiding letting the user enter anything (address etc) before making them register, is that unless we allow unconfirmed access for some period (which defeats the whole purpose I think since they can check out), they lose their cart when they register and sign in. May not be a huge deal since looks like most customers are not doing this, but trying not to inconvenience more than necessary.
Thank you for the support so far. I'll report back with my findings.
k
Let me know of this goes!
t
Wound up moving the logic to the checkout controller and tweaked a little bit and it just works. Wound up just leaving the "allow_unconfirmed_access_for" setting of devise off for now, since it was causing a view/route error when users clicked the email confirmation link for some reason. Works fine with it off.
j
So this didn't help much because then they started to use burner emails they could validate with. We just noticed a ton of these are using the same address, which leads me to believe Braintree's AVS is not happening, even though it's all turned on in the Braintree account web portal. Could there be something that needs to change on the gems end?
actually some of the transactions in Braintree show as failing AVS, but still getting through
c
Looks like you can configure rules in Braintree for these failures - https://developer.paypal.com/braintree/articles/guides/fraud-tools/basic/avs-cvv-rules. Lots of cards will fail AVS for valid reasons, so you probably don’t want to block all transactions.
j
Thanks. Opened a ticket with Braintree. Hopefully they have some ideas to help. This stinks, there has to be a way of dealing with these without affecting regular customers so much, we've never ran into this with other shopping cart platforms so this is a first.
k
To be honest, I don’t think it depends on the platform, but if we discover we can do anything better, we are open to improve Solidus to be more safe.
☝🏼 1
c
Agreed with @kennyadsl, fraud is something most large online stores have to deal with. You’re dealing with a sophisticated attack that’s adapting to the blocks you’re putting in place. It’s a cat and mouse chase and you just have to make it not worth the attackers time to go through the hurdles you’ve put in place and they will move on to another target. The store I work on gets a lot of attacks, from distributed credential stuffing attacks, to card testing and so on. Some inevitably succeed, but we manage to block most of them. I would recommend adding some rudimentary fraud checking rules in your store that prevent an order from going to fulfillment and requires a manual review step if anything gets flagged. You can start by adding a few basic rules, like orders from different customers going to the same address, multiple failed payments, small amounts, etc. Alternatively you can integrate a 3rd party solution for this like Signifyd, but they tend to work on a percentage of the order total for all orders, so can get pretty expensive.
j
Do you know why we might be seeing this now with every transaction attempt that's not using an already stored card?
Does the braintree gem store logs anywhere?
Are we 100% sure the braintree solidus gem sends address info? Because if we turn these settings on, then that's the error we get with every transaction. I'd love to see api logs of what gets sent somehow