From 4dfb575050d68fbb5344fab3db41e3a62f19832a Mon Sep 17 00:00:00 2001 From: edow <285729101@qq.com> Date: Wed, 18 Feb 2026 13:15:00 +0800 Subject: [PATCH] Probate related accounts on TOS violation instead of suspending for fraud --- app/models/user.rb | 3 +- app/modules/user/risk.rb | 4 + app/sidekiq/check_payment_address_worker.rb | 62 ++++--- ...te_accounts_with_payment_address_worker.rb | 21 +++ ...nd_accounts_with_payment_address_worker.rb | 47 ++---- spec/models/user_spec.rb | 4 +- spec/modules/user/risk_spec.rb | 2 +- .../check_payment_address_worker_spec.rb | 159 +++++------------- ...counts_with_payment_address_worker_spec.rb | 51 ++++++ ...counts_with_payment_address_worker_spec.rb | 143 ++++------------ 10 files changed, 205 insertions(+), 291 deletions(-) create mode 100644 app/sidekiq/probate_accounts_with_payment_address_worker.rb create mode 100644 spec/sidekiq/probate_accounts_with_payment_address_worker_spec.rb diff --git a/app/models/user.rb b/app/models/user.rb index ebeea470ae..4d5738c898 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -323,7 +323,8 @@ class User < ApplicationRecord :do => :add_user_comment after_transition any => [:flagged_for_tos_violation], :do => :add_product_comment - after_transition any => %i[suspended_for_fraud suspended_for_tos_violation], :do => :suspend_sellers_other_accounts + after_transition any => :suspended_for_fraud, :do => :suspend_sellers_other_accounts + after_transition any => :suspended_for_tos_violation, :do => :probate_sellers_other_accounts after_transition any => %i[suspended_for_fraud suspended_for_tos_violation], :do => :block_seller_ip! after_transition any => %i[suspended_for_fraud suspended_for_tos_violation], :do => :delete_custom_domain! after_transition any => %i[suspended_for_fraud suspended_for_tos_violation], :do => :log_suspension_time_to_mongo diff --git a/app/modules/user/risk.rb b/app/modules/user/risk.rb index 7766dc3a59..4f6e168344 100644 --- a/app/modules/user/risk.rb +++ b/app/modules/user/risk.rb @@ -127,6 +127,10 @@ def suspend_sellers_other_accounts(transition) SuspendAccountsWithPaymentAddressWorker.perform_in(5.seconds, id) end + def probate_sellers_other_accounts(_transition) + ProbateAccountsWithPaymentAddressWorker.perform_in(5.seconds, id) + end + def block_seller_ip! BlockSuspendedAccountIpWorker.perform_in(5.seconds, id) end diff --git a/app/sidekiq/check_payment_address_worker.rb b/app/sidekiq/check_payment_address_worker.rb index a7e30c7dcc..e8d2f71a0b 100644 --- a/app/sidekiq/check_payment_address_worker.rb +++ b/app/sidekiq/check_payment_address_worker.rb @@ -4,42 +4,50 @@ class CheckPaymentAddressWorker include Sidekiq::Job sidekiq_options retry: 0, queue: :default + CHECK_PAYMENT_ADDRESS_AUTHOR_NAME = "CheckPaymentAddress" + def perform(user_id) user = User.find_by(id: user_id) - return unless user&.can_flag_for_fraud? + return if !user.can_flag_for_fraud? || user.payment_address.blank? - should_flag = payment_address_matches_suspended_account?(user) || - stripe_fingerprint_matches_suspended_account?(user) + banned_accounts_with_same_payment_address = User.where( + payment_address: user.payment_address, + user_risk_state: ["suspended_for_tos_violation", "suspended_for_fraud"] + ).order(updated_at: :desc) - user.flag_for_fraud!(author_name: "CheckPaymentAddress") if should_flag - end + blocked_email = BlockedObject.find_active_object(user.payment_address) - private - def payment_address_matches_suspended_account?(user) - return false if user.payment_address.blank? + suspended_for_fraud_uids = banned_accounts_with_same_payment_address.with_user_risk_state(:suspended_for_fraud).pluck(:external_id) + suspended_for_tos_violation_uids = banned_accounts_with_same_payment_address.with_user_risk_state(:suspended_for_tos_violation).pluck(:external_id) + has_fraudulent_activity_on_other_accounts = suspended_for_fraud_uids.present? || blocked_email.present? - banned_accounts_with_same_payment_address = User.where( - payment_address: user.payment_address, - user_risk_state: User::Risk::SUSPENDED_STATES + if suspended_for_tos_violation_uids.present? && (user.compliant? || user.not_reviewed?) && !has_fraudulent_activity_on_other_accounts + user.put_on_probation!( + author_name: CHECK_PAYMENT_ADDRESS_AUTHOR_NAME, + content: "Probated (payouts suspended) automatically on #{self.class.formatted_date} because of usage of payment address #{user.payment_address} (from suspended for TOS violation #{self.class.pluralize_user_label(suspended_for_tos_violation_uids.count)} #{self.class.format_uids(suspended_for_tos_violation_uids)})" + ) + elsif has_fraudulent_activity_on_other_accounts + source = if suspended_for_fraud_uids.present? + "from suspended for fraud #{self.class.pluralize_user_label(suspended_for_fraud_uids.count)} #{self.class.format_uids(suspended_for_fraud_uids)}" + else + "from a fraudulent purchase" + end + user.flag_for_fraud!( + author_name: CHECK_PAYMENT_ADDRESS_AUTHOR_NAME, + content: "Flagged for fraud automatically on #{self.class.formatted_date} because of usage of payment address #{user.payment_address} (#{source})" ) - - blocked_email = BlockedObject.find_active_object(user.payment_address) - - banned_accounts_with_same_payment_address.exists? || blocked_email.present? end + end - def stripe_fingerprint_matches_suspended_account?(user) - fingerprints = user.alive_bank_accounts.where.not(stripe_fingerprint: [nil, ""]).distinct.pluck(:stripe_fingerprint) - return false if fingerprints.empty? - - suspended_accounts_with_same_fingerprint = BankAccount - .joins(:user) - .where(stripe_fingerprint: fingerprints) - .where.not(user_id: user.id) - .where(users: { user_risk_state: User::Risk::SUSPENDED_STATES }) + def self.format_uids(uids) + uids.map { "##{_1}" }.to_sentence + end - return true if suspended_accounts_with_same_fingerprint.exists? + def self.formatted_date + Time.current.to_fs(:formatted_date_full_month) + end - BlockedObject.find_active_objects(fingerprints).present? - end + def self.pluralize_user_label(count) + count == 1 ? "User" : "Users" + end end diff --git a/app/sidekiq/probate_accounts_with_payment_address_worker.rb b/app/sidekiq/probate_accounts_with_payment_address_worker.rb new file mode 100644 index 0000000000..a861c9cc7a --- /dev/null +++ b/app/sidekiq/probate_accounts_with_payment_address_worker.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class ProbateAccountsWithPaymentAddressWorker + include Sidekiq::Job + sidekiq_options retry: 5, queue: :default + + PROBATE_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME = "probate_sellers_other_accounts" + + def perform(user_id) + suspended_user = User.with_user_risk_state(:suspended_for_tos_violation).find(user_id) + + return if suspended_user.payment_address.blank? + + User.where(payment_address: suspended_user.payment_address).with_user_risk_state(:not_reviewed, :compliant).where.not(id: suspended_user.id).find_each(batch_size: 100) do |user| + user.put_on_probation!( + author_name: PROBATE_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME, + content: "Probated (payouts suspended) automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{suspended_user.payment_address} (from suspended for TOS violation User##{suspended_user.id})" + ) + end + end +end diff --git a/app/sidekiq/suspend_accounts_with_payment_address_worker.rb b/app/sidekiq/suspend_accounts_with_payment_address_worker.rb index 35a9ee4107..23e05de736 100644 --- a/app/sidekiq/suspend_accounts_with_payment_address_worker.rb +++ b/app/sidekiq/suspend_accounts_with_payment_address_worker.rb @@ -4,50 +4,23 @@ class SuspendAccountsWithPaymentAddressWorker include Sidekiq::Job sidekiq_options retry: 5, queue: :default - def perform(user_id) - suspended_user = User.find(user_id) - - suspend_users_with_same_payment_address(suspended_user) - suspend_users_with_same_stripe_fingerprint(suspended_user) - end - - private - def suspend_users_with_same_payment_address(suspended_user) - return if suspended_user.payment_address.blank? - - User.not_suspended - .where(payment_address: suspended_user.payment_address) - .where.not(id: suspended_user.id) - .find_each do |user| - flag_and_suspend_user(user, suspended_user, "payment address", suspended_user.payment_address) - end - end + SUSPEND_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME = "suspend_sellers_other_accounts" - def suspend_users_with_same_stripe_fingerprint(suspended_user) - fingerprints = suspended_user.bank_accounts.where.not(stripe_fingerprint: [nil, ""]).distinct.pluck(:stripe_fingerprint) - return if fingerprints.empty? + def perform(user_id) + suspended_user = User.with_user_risk_state(:suspended_for_fraud).find(user_id) - user_ids_with_same_fingerprint = BankAccount.alive - .where(stripe_fingerprint: fingerprints) - .where.not(user_id: suspended_user.id) - .distinct - .pluck(:user_id) + return if suspended_user.payment_address.blank? - User.not_suspended.where(id: user_ids_with_same_fingerprint).find_each do |user| - matching_fingerprint = (fingerprints & user.alive_bank_accounts.pluck(:stripe_fingerprint)).first - flag_and_suspend_user(user, suspended_user, "bank account fingerprint", matching_fingerprint) - end - end - - def flag_and_suspend_user(user, suspended_user, identifier_type, identifier_value) + User.where(payment_address: suspended_user.payment_address).where.not(id: suspended_user.id).not_suspended.find_each do |user| user.flag_for_fraud( - author_name: "suspend_sellers_other_accounts", - content: "Flagged for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of #{identifier_type} #{identifier_value} (from User##{suspended_user.id})" + author_name: SUSPEND_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME, + content: "Flagged for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{suspended_user.payment_address} (from User##{suspended_user.id})" ) user.suspend_for_fraud( - author_name: "suspend_sellers_other_accounts", - content: "Suspended for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of #{identifier_type} #{identifier_value} (from User##{suspended_user.id})", + author_name: SUSPEND_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME, + content: "Suspended for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{suspended_user.payment_address} (from User##{suspended_user.id})", skip_transition_callback: :suspend_sellers_other_accounts ) end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index e7d4c63938..afb4fccea3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1882,9 +1882,11 @@ def sample_string_of_length(n) expect(@user.reload.comments.count).to eq(2) end - it "does not suspend all the others sellers accounts if suspended for tos violation" do + it "probates all the others sellers accounts if suspended for tos violation", :sidekiq_inline do + expect(@user).to receive(:probate_sellers_other_accounts).and_call_original @user.flag_for_tos_violation(author_id: @admin_user.id, product_id: @product_1.id) @user.suspend_for_tos_violation(author_id: @admin_user.id) + expect(@user_2.reload.on_probation?).to be(true) expect(@user_2.reload.suspended?).to be(false) end diff --git a/spec/modules/user/risk_spec.rb b/spec/modules/user/risk_spec.rb index 92dc1f1f02..d5ff2b27fa 100644 --- a/spec/modules/user/risk_spec.rb +++ b/spec/modules/user/risk_spec.rb @@ -48,7 +48,7 @@ context "when user has PayPal as payout processor" do it "calls SuspendAccountsWithPaymentAddressWorker only once for all related accounts" do - user = create(:user, payment_address: "test@example.com") + user = create(:user, payment_address: "test@example.com", user_risk_state: "suspended_for_fraud") create(:user, payment_address: "test@example.com") expect do diff --git a/spec/sidekiq/check_payment_address_worker_spec.rb b/spec/sidekiq/check_payment_address_worker_spec.rb index 40a986e17b..739bd5ab33 100644 --- a/spec/sidekiq/check_payment_address_worker_spec.rb +++ b/spec/sidekiq/check_payment_address_worker_spec.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true describe CheckPaymentAddressWorker do - describe "payment address checks" do - before do - @previously_banned_user = create(:user, user_risk_state: "suspended_for_fraud", payment_address: "tuhins@gmail.com") - @blocked_email_object = BlockedObject.block!(BLOCKED_OBJECT_TYPES[:email], "fraudulent_email@zombo.com", nil) - end + before do + @previously_banned_user = create(:user, user_risk_state: "suspended_for_fraud", payment_address: "tuhins@gmail.com", updated_at: 1.day.ago) + @blocked_email_object = BlockedObject.block!(BLOCKED_OBJECT_TYPES[:email], "fraudulent_email@zombo.com", nil) + end + describe "fraud flagging" do it "does not flag the user for fraud if there are no other banned users with the same payment address" do @user = create(:user, payment_address: "cleanuser@gmail.com") @@ -16,11 +16,16 @@ end it "flags the user for fraud if there are other banned users with the same payment address" do - @user = create(:user, payment_address: "tuhins@gmail.com") + suspended_user_2 = create(:user, user_risk_state: "suspended_for_fraud", payment_address: "tuhins@gmail.com") + user = create(:user, payment_address: "tuhins@gmail.com") - CheckPaymentAddressWorker.new.perform(@user.id) + CheckPaymentAddressWorker.new.perform(user.id) - expect(@user.reload.flagged?).to be(true) + expect(user.reload.flagged?).to be(true) + comment = user.comments.last + suspended_for_fraud_uids = [suspended_user_2.external_id, @previously_banned_user.external_id] + expect(comment.author_name).to eq(CheckPaymentAddressWorker::CHECK_PAYMENT_ADDRESS_AUTHOR_NAME) + expect(comment.content).to eq("Flagged for fraud automatically on #{CheckPaymentAddressWorker.formatted_date} because of usage of payment address tuhins@gmail.com (from suspended for fraud Users #{CheckPaymentAddressWorker.format_uids(suspended_for_fraud_uids)})") end it "flags the user for fraud if a blocked email object exists for their payment address" do @@ -29,131 +34,57 @@ CheckPaymentAddressWorker.new.perform(@user.id) expect(@user.reload.flagged?).to be(true) - end - end - - describe "stripe fingerprint checks" do - it "does not flag the user for fraud if there are no suspended users with the same stripe fingerprint" do - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "clean_fingerprint") - - other_user = create(:user) - create(:ach_account, user: other_user, stripe_fingerprint: "different_fingerprint") - - CheckPaymentAddressWorker.new.perform(user.id) - - expect(user.reload.flagged?).to be(false) - end - - it "flags the user for fraud if a suspended user has the same stripe fingerprint" do - suspended_user = create(:user, user_risk_state: "suspended_for_fraud") - create(:ach_account, user: suspended_user, stripe_fingerprint: "same_fingerprint_123") - - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "same_fingerprint_123") - - CheckPaymentAddressWorker.new.perform(user.id) - - expect(user.reload.flagged?).to be(true) - end - - it "flags the user for fraud if a suspended_for_tos_violation user has the same stripe fingerprint" do - suspended_user = create(:user, user_risk_state: "suspended_for_tos_violation") - create(:ach_account, user: suspended_user, stripe_fingerprint: "same_fingerprint_456") - - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "same_fingerprint_456") - - CheckPaymentAddressWorker.new.perform(user.id) - - expect(user.reload.flagged?).to be(true) + comment = @user.comments.last + expect(comment.author_name).to eq(CheckPaymentAddressWorker::CHECK_PAYMENT_ADDRESS_AUTHOR_NAME) + expect(comment.content).to eq("Flagged for fraud automatically on #{CheckPaymentAddressWorker.formatted_date} because of usage of payment address fraudulent_email@zombo.com (from a fraudulent purchase)") end - it "flags the user for fraud if a blocked fingerprint object exists" do - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "blocked_fingerprint") - BlockedObject.block!(BLOCKED_OBJECT_TYPES[:charge_processor_fingerprint], "blocked_fingerprint", nil) + it "flags the user for fraud when both fraud and TOS violations exist" do + _suspended_tos_user = create(:user, user_risk_state: "suspended_for_tos_violation", payment_address: "tuhins@gmail.com") + user = create(:user, payment_address: "tuhins@gmail.com", verified: false) CheckPaymentAddressWorker.new.perform(user.id) expect(user.reload.flagged?).to be(true) + expect(user.on_probation?).to be(false) + comment = user.comments.last + expect(comment.author_name).to eq(CheckPaymentAddressWorker::CHECK_PAYMENT_ADDRESS_AUTHOR_NAME) + expect(comment.content).to eq("Flagged for fraud automatically on #{CheckPaymentAddressWorker.formatted_date} because of usage of payment address tuhins@gmail.com (from suspended for fraud User #{CheckPaymentAddressWorker.format_uids([@previously_banned_user.external_id])})") end - it "flags the user even if the suspended user's bank account is deleted (fraud history is preserved)" do - suspended_user = create(:user, user_risk_state: "suspended_for_fraud") - bank_account = create(:ach_account, user: suspended_user, stripe_fingerprint: "same_fingerprint_789") - bank_account.mark_deleted! + it "raises error when attempting to flag verified user for fraud" do + @user = create(:user, payment_address: "tuhins@gmail.com", verified: true) - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "same_fingerprint_789") - - CheckPaymentAddressWorker.new.perform(user.id) - - expect(user.reload.flagged?).to be(true) - end - - it "does not flag if the new user's bank account is deleted" do - suspended_user = create(:user, user_risk_state: "suspended_for_fraud") - create(:ach_account, user: suspended_user, stripe_fingerprint: "same_fingerprint_abc") - - user = create(:user) - bank_account = create(:ach_account, user:, stripe_fingerprint: "same_fingerprint_abc") - bank_account.mark_deleted! - - CheckPaymentAddressWorker.new.perform(user.id) - - expect(user.reload.flagged?).to be(false) + expect { CheckPaymentAddressWorker.new.perform(@user.id) } + .to raise_error(StateMachines::InvalidTransition) end + end - it "does not flag if stripe fingerprint is blank" do - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: nil) - - CheckPaymentAddressWorker.new.perform(user.id) - - expect(user.reload.flagged?).to be(false) - end - - it "checks all fingerprints when new user has multiple bank accounts" do - suspended_user = create(:user, user_risk_state: "suspended_for_fraud") - create(:ach_account, user: suspended_user, stripe_fingerprint: "fraud_fingerprint") - - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "clean_fingerprint") - create(:ach_account, user:, stripe_fingerprint: "fraud_fingerprint") + describe "probation" do + it "puts user on probation for TOS violations when no fraud violations exist" do + suspended_tos_user = create(:user, user_risk_state: "suspended_for_tos_violation", payment_address: "tos@example.com") + user = create(:user, payment_address: "tos@example.com", user_risk_state: "not_reviewed") CheckPaymentAddressWorker.new.perform(user.id) - expect(user.reload.flagged?).to be(true) + expect(user.reload.on_probation?).to be(true) + comment = user.comments.last + expect(comment.author_name).to eq(CheckPaymentAddressWorker::CHECK_PAYMENT_ADDRESS_AUTHOR_NAME) + expect(comment.content).to eq("Probated (payouts suspended) automatically on #{CheckPaymentAddressWorker.formatted_date} because of usage of payment address tos@example.com (from suspended for TOS violation User #{CheckPaymentAddressWorker.format_uids([suspended_tos_user.external_id])})") end - it "flags if any of the user's fingerprints is blocked" do - user = create(:user) - create(:ach_account, user:, stripe_fingerprint: "clean_fingerprint") - create(:ach_account, user:, stripe_fingerprint: "blocked_fingerprint_xyz") - BlockedObject.block!(BLOCKED_OBJECT_TYPES[:charge_processor_fingerprint], "blocked_fingerprint_xyz", nil) + it "puts verified user on probation for TOS violations when no fraud violations exist" do + suspended_tos_user_1 = create(:user, user_risk_state: "suspended_for_tos_violation", payment_address: "tos@example.com", updated_at: 1.day.ago) + suspended_tos_user_2 = create(:user, user_risk_state: "suspended_for_tos_violation", payment_address: "tos@example.com") + user = create(:user, payment_address: "tos@example.com", verified: true, user_risk_state: "compliant") CheckPaymentAddressWorker.new.perform(user.id) - expect(user.reload.flagged?).to be(true) - end - end - - describe "edge cases" do - it "does not flag the user if they cannot be flagged for fraud" do - suspended_user = create(:user, user_risk_state: "suspended_for_fraud") - create(:ach_account, user: suspended_user, stripe_fingerprint: "same_fingerprint") - - already_flagged_user = create(:user, user_risk_state: "flagged_for_fraud") - create(:ach_account, user: already_flagged_user, stripe_fingerprint: "same_fingerprint") - - CheckPaymentAddressWorker.new.perform(already_flagged_user.id) - - expect(already_flagged_user.reload.user_risk_state).to eq("flagged_for_fraud") - end - - it "does not raise error if user is not found" do - expect { CheckPaymentAddressWorker.new.perform(999999999) }.not_to raise_error + expect(user.reload.on_probation?).to be(true) + comment = user.comments.last + expect(comment.author_name).to eq(CheckPaymentAddressWorker::CHECK_PAYMENT_ADDRESS_AUTHOR_NAME) + suspended_for_tos_violation_uids = [suspended_tos_user_2.external_id, suspended_tos_user_1.external_id] + expect(comment.content).to eq("Probated (payouts suspended) automatically on #{CheckPaymentAddressWorker.formatted_date} because of usage of payment address tos@example.com (from suspended for TOS violation Users #{CheckPaymentAddressWorker.format_uids(suspended_for_tos_violation_uids)})") end end end diff --git a/spec/sidekiq/probate_accounts_with_payment_address_worker_spec.rb b/spec/sidekiq/probate_accounts_with_payment_address_worker_spec.rb new file mode 100644 index 0000000000..28c82fba47 --- /dev/null +++ b/spec/sidekiq/probate_accounts_with_payment_address_worker_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +describe ProbateAccountsWithPaymentAddressWorker do + describe "#perform" do + before do + @suspended_user = create(:user, user_risk_state: "suspended_for_tos_violation", payment_address: "tos@example.com") + end + + it "probates compliant, not_reviewed, and verified users with the same payment address" do + compliant_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "compliant") + not_reviewed_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "not_reviewed") + verified_seller = create(:user, payment_address: "tos@example.com", verified: true, user_risk_state: "compliant") + + described_class.new.perform(@suspended_user.id) + + expect(compliant_seller.reload.on_probation?).to be(true) + expect(not_reviewed_seller.reload.on_probation?).to be(true) + expect(verified_seller.reload.on_probation?).to be(true) + + comment = compliant_seller.comments.last + expect(comment.author_name).to eq(ProbateAccountsWithPaymentAddressWorker::PROBATE_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME) + expect(comment.content).to eq("Probated (payouts suspended) automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address tos@example.com (from suspended for TOS violation User##{@suspended_user.id})") + end + + it "does not probate users in on_probation, flagged_for_fraud, flagged_for_tos_violation, suspended_for_fraud, or suspended_for_tos_violation states" do + on_probation_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "on_probation") + flagged_fraud_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "flagged_for_fraud") + flagged_tos_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "flagged_for_tos_violation") + suspended_fraud_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "suspended_for_fraud") + suspended_tos_seller = create(:user, payment_address: "tos@example.com", user_risk_state: "suspended_for_tos_violation") + + described_class.new.perform(@suspended_user.id) + + expect(on_probation_seller.comments.count).to eq(0) + expect(on_probation_seller.reload.on_probation?).to be(true) + expect(flagged_fraud_seller.reload.on_probation?).to be(false) + expect(flagged_tos_seller.reload.on_probation?).to be(false) + expect(suspended_fraud_seller.reload.on_probation?).to be(false) + expect(suspended_tos_seller.reload.on_probation?).to be(false) + end + + it "does nothing if payment_address is blank" do + suspended_user_no_payment = create(:user, user_risk_state: "suspended_for_tos_violation", payment_address: nil) + user = create(:user, payment_address: "tos@example.com", user_risk_state: "compliant") + + described_class.new.perform(suspended_user_no_payment.id) + + expect(user.reload.on_probation?).to be(false) + end + end +end diff --git a/spec/sidekiq/suspend_accounts_with_payment_address_worker_spec.rb b/spec/sidekiq/suspend_accounts_with_payment_address_worker_spec.rb index b6b97100a8..88e2b21115 100644 --- a/spec/sidekiq/suspend_accounts_with_payment_address_worker_spec.rb +++ b/spec/sidekiq/suspend_accounts_with_payment_address_worker_spec.rb @@ -2,130 +2,53 @@ describe SuspendAccountsWithPaymentAddressWorker do describe "#perform" do - context "with payment address" do - before do - @user = create(:user, payment_address: "sameuser@paypal.com") - @user_2 = create(:user, payment_address: "sameuser@paypal.com") - create(:user) # admin user - end - - it "suspends other accounts with the same payment address" do - described_class.new.perform(@user.id) - - expect(@user_2.reload.suspended?).to be(true) - expect(@user_2.comments.first.content).to eq("Flagged for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{@user.payment_address} (from User##{@user.id})") - expect(@user_2.comments.last.content).to eq("Suspended for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{@user.payment_address} (from User##{@user.id})") - end - - it "does not suspend already suspended users with same payment address" do - @user_2.flag_for_fraud!(author_name: "test") - @user_2.suspend_for_fraud!(author_name: "test") - initial_comment_count = @user_2.comments.count - - described_class.new.perform(@user.id) - - expect(@user_2.reload.comments.count).to eq(initial_comment_count) - end + before do + @suspended_user = create(:user, user_risk_state: "suspended_for_fraud", payment_address: "sameuser@paypal.com") end - context "with stripe fingerprint" do - before do - @user = create(:user) - @user_2 = create(:user) - @user_3 = create(:user) - - @bank_account_1 = create(:ach_account, user: @user, stripe_fingerprint: "same_fingerprint_123") - @bank_account_2 = create(:ach_account, user: @user_2, stripe_fingerprint: "same_fingerprint_123") - @bank_account_3 = create(:ach_account, user: @user_3, stripe_fingerprint: "different_fingerprint") - end - - it "suspends other accounts with the same stripe fingerprint" do - described_class.new.perform(@user.id) - - expect(@user_2.reload.suspended?).to be(true) - expect(@user_3.reload.suspended?).to be(false) - end - - it "creates both flagged and suspended comments with fingerprint details" do - described_class.new.perform(@user.id) - - expect(@user_2.reload.comments.first.content).to eq("Flagged for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of bank account fingerprint same_fingerprint_123 (from User##{@user.id})") - expect(@user_2.comments.last.content).to eq("Suspended for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of bank account fingerprint same_fingerprint_123 (from User##{@user.id})") - end - - it "does not suspend if fingerprint is blank" do - @bank_account_1.update!(stripe_fingerprint: nil) - - described_class.new.perform(@user.id) + it "suspends other accounts with the same payment address" do + @user_2 = create(:user, payment_address: "sameuser@paypal.com") - expect(@user_2.reload.suspended?).to be(false) - end + described_class.new.perform(@suspended_user.id) - it "does not suspend users whose bank accounts are deleted" do - @bank_account_2.mark_deleted! - - described_class.new.perform(@user.id) - - expect(@user_2.reload.suspended?).to be(false) - end - - it "does not suspend already suspended users" do - @user_2.flag_for_fraud!(author_name: "test") - @user_2.suspend_for_fraud!(author_name: "test") - initial_comment_count = @user_2.comments.count - - described_class.new.perform(@user.id) - - expect(@user_2.reload.comments.count).to eq(initial_comment_count) - end - - it "still suspends related accounts even if suspended user's bank account is deleted" do - @bank_account_1.mark_deleted! - - described_class.new.perform(@user.id) + expect(@user_2.reload.suspended?).to be(true) + expect(@user_2.comments.first.content).to eq("Flagged for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{@suspended_user.payment_address} (from User##{@suspended_user.id})") + expect(@user_2.comments.last.content).to eq("Suspended for fraud automatically on #{Time.current.to_fs(:formatted_date_full_month)} because of usage of payment address #{@suspended_user.payment_address} (from User##{@suspended_user.id})") + expect(@user_2.comments.first.author_name).to eq(SuspendAccountsWithPaymentAddressWorker::SUSPEND_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME) + expect(@user_2.comments.last.author_name).to eq(SuspendAccountsWithPaymentAddressWorker::SUSPEND_ACCOUNTS_WITH_PAYMENT_ADDRESS_AUTHOR_NAME) + end - expect(@user_2.reload.suspended?).to be(true) - expect(@user_2.comments.first.content).to include("bank account fingerprint same_fingerprint_123") - end + it "does not suspend already suspended users or verified users" do + suspended_fraud_seller = create(:user, payment_address: "sameuser@paypal.com", user_risk_state: "suspended_for_fraud") + suspended_tos_seller = create(:user, payment_address: "sameuser@paypal.com", user_risk_state: "suspended_for_tos_violation") + verified_seller = create(:user, payment_address: "sameuser@paypal.com", verified: true, user_risk_state: "compliant") - it "checks all fingerprints when suspended user has multiple bank accounts" do - create(:ach_account, user: @user, stripe_fingerprint: "another_fingerprint") - user_with_another_fingerprint = create(:user) - create(:ach_account, user: user_with_another_fingerprint, stripe_fingerprint: "another_fingerprint") + described_class.new.perform(@suspended_user.id) - described_class.new.perform(@user.id) + expect(suspended_fraud_seller.reload.suspended_for_fraud?).to be(true) + expect(suspended_tos_seller.reload.suspended_for_tos_violation?).to be(true) + expect(verified_seller.reload.compliant?).to be(true) - expect(@user_2.reload.suspended?).to be(true) - expect(user_with_another_fingerprint.reload.suspended?).to be(true) - end + expect(suspended_fraud_seller.comments.count).to eq(0) + expect(suspended_tos_seller.comments.count).to eq(0) + expect(verified_seller.comments.count).to eq(0) + end - it "uses the matching fingerprint in the comment when user has multiple fingerprints" do - create(:ach_account, user: @user, stripe_fingerprint: "another_fingerprint") - user_with_another_fingerprint = create(:user) - create(:ach_account, user: user_with_another_fingerprint, stripe_fingerprint: "another_fingerprint") + it "does nothing if payment_address is blank" do + suspended_seller_no_payment = create(:user, user_risk_state: "suspended_for_fraud", payment_address: nil) + compliant_seller = create(:user, payment_address: "sameuser@paypal.com", user_risk_state: "compliant") - described_class.new.perform(@user.id) + described_class.new.perform(suspended_seller_no_payment.id) - expect(user_with_another_fingerprint.reload.comments.first.content).to include("another_fingerprint") - end + expect(compliant_seller.reload.suspended?).to be(false) end - context "with both payment address and stripe fingerprint" do - before do - @user = create(:user, payment_address: "sameuser@paypal.com") - @user_paypal_match = create(:user, payment_address: "sameuser@paypal.com") - @user_fingerprint_match = create(:user) - - @bank_account_1 = create(:ach_account, user: @user, stripe_fingerprint: "same_fingerprint_123") - @bank_account_2 = create(:ach_account, user: @user_fingerprint_match, stripe_fingerprint: "same_fingerprint_123") - end - - it "suspends accounts matching either payment address or stripe fingerprint" do - described_class.new.perform(@user.id) + it "raises error if user is not in suspended_for_fraud state" do + compliant_seller = create(:user, user_risk_state: "compliant", payment_address: "sameuser@paypal.com") - expect(@user_paypal_match.reload.suspended?).to be(true) - expect(@user_fingerprint_match.reload.suspended?).to be(true) - end + expect do + described_class.new.perform(compliant_seller.id) + end.to raise_error(ActiveRecord::RecordNotFound) end end end