Skip to content

Commit 59b4be3

Browse files
authored
Consolidate partnerDuplicatePayoutMethod into partnerDuplicateAccount (dubinc#3782)
1 parent b50def4 commit 59b4be3

11 files changed

Lines changed: 60 additions & 33 deletions

File tree

apps/web/app/(ee)/api/cron/partners/merge-accounts/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ export async function POST(req: Request) {
346346
where: {
347347
partnerId: sourcePartnerId,
348348
fraudEventGroup: {
349-
type: FraudRuleType.partnerDuplicatePayoutMethod,
349+
type: FraudRuleType.partnerDuplicateAccount,
350350
},
351351
},
352352
include: {
@@ -395,7 +395,7 @@ export async function POST(req: Request) {
395395
]
396396
: []),
397397
],
398-
type: FraudRuleType.partnerDuplicatePayoutMethod,
398+
type: FraudRuleType.partnerDuplicateAccount,
399399
},
400400
resolutionReason:
401401
"Automatically resolved because partners with duplicate payout methods were merged. No other partners share this payout method.",

apps/web/lib/api/fraud/constants.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,14 @@ export const FRAUD_RULES: FraudRuleInfo[] = [
4848
configurable: true,
4949
},
5050
{
51-
type: "partnerDuplicatePayoutMethod",
51+
type: "partnerDuplicateAccount",
5252
name: "Duplicate account detected",
5353
description:
5454
"This partner was flagged by our system for having 2 or more Dub accounts. Please review to prevent abuse of program restrictions, caps, or bonuses.",
5555
scope: "partner",
5656
severity: "high",
5757
configurable: true,
5858
},
59-
// Not visible in the UI
60-
{
61-
type: "partnerDuplicateAccount",
62-
name: "Duplicate account detected",
63-
description:
64-
"This partner was flagged by our system for having 2 or more Dub accounts. Please review to prevent abuse of program restrictions, caps, or bonuses.",
65-
scope: "partner",
66-
severity: "low",
67-
configurable: false,
68-
},
6959
{
7060
type: "partnerEmailDomainMismatch",
7161
name: "Email domain mismatch with website",

apps/web/lib/api/fraud/detect-duplicate-identity-fraud.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export async function detectDuplicateIdentityFraud({
7272
programEnrollments = programEnrollments.filter((enrollment) =>
7373
isFraudRuleEnabled({
7474
fraudRules: enrollment.program.fraudRules,
75-
ruleType: FraudRuleType.partnerDuplicatePayoutMethod, // TODO: Change to partnerDuplicateAccount
75+
ruleType: FraudRuleType.partnerDuplicateAccount,
7676
}),
7777
);
7878

apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ export async function detectDuplicatePayoutMethodFraud({
4444
return;
4545
}
4646

47-
// Filter out program enrollments where the partnerDuplicatePayoutMethod rule is disabled
47+
// Filter out program enrollments where the partnerDuplicateAccount rule is disabled
4848
programEnrollments = programEnrollments.filter((enrollment) =>
4949
isFraudRuleEnabled({
5050
fraudRules: enrollment.program.fraudRules,
51-
ruleType: FraudRuleType.partnerDuplicatePayoutMethod,
51+
ruleType: FraudRuleType.partnerDuplicateAccount,
5252
}),
5353
);
5454

@@ -89,7 +89,7 @@ export async function detectDuplicatePayoutMethodFraud({
8989
fraudEvents.push({
9090
programId,
9191
partnerId: sourcePartner.partnerId,
92-
type: FraudRuleType.partnerDuplicatePayoutMethod,
92+
type: FraudRuleType.partnerDuplicateAccount,
9393
metadata: {
9494
...(payoutMethodHash ? { payoutMethodHash } : {}),
9595
...(cryptoWalletAddress ? { cryptoWalletAddress } : {}),

apps/web/lib/api/fraud/detect-record-fraud-application.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export async function detectAndRecordFraudApplication({
3333
if (
3434
isFraudRuleEnabled({
3535
fraudRules,
36-
ruleType: FraudRuleType.partnerDuplicatePayoutMethod,
36+
ruleType: FraudRuleType.partnerDuplicateAccount,
3737
})
3838
) {
3939
const { payoutMethodHash, cryptoWalletAddress } = partner;
@@ -81,7 +81,7 @@ export async function detectAndRecordFraudApplication({
8181
fraudEvents.push({
8282
programId: program.id,
8383
partnerId: sourcePartner.id,
84-
type: FraudRuleType.partnerDuplicatePayoutMethod,
84+
type: FraudRuleType.partnerDuplicateAccount,
8585
metadata: {
8686
...(payoutMethodHash ? { payoutMethodHash } : {}),
8787
...(cryptoWalletAddress ? { cryptoWalletAddress } : {}),

apps/web/lib/api/fraud/get-partner-application-risks.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export async function getPartnerApplicationRisks({
2121
partnerId: partner.id,
2222
status: "pending",
2323
type: {
24-
in: ["partnerCrossProgramBan", "partnerDuplicatePayoutMethod"],
24+
in: ["partnerCrossProgramBan", "partnerDuplicateAccount"],
2525
},
2626
},
2727
});
@@ -30,13 +30,13 @@ export async function getPartnerApplicationRisks({
3030
(group) => group.type === "partnerCrossProgramBan",
3131
);
3232

33-
const hasDuplicatePayoutMethod = fraudGroups.some(
34-
(group) => group.type === "partnerDuplicatePayoutMethod",
33+
const hasDuplicateAccounts = fraudGroups.some(
34+
(group) => group.type === "partnerDuplicateAccount",
3535
);
3636

3737
const risksDetected: Partial<Record<ExtendedFraudRuleType, boolean>> = {
3838
partnerCrossProgramBan: hasCrossProgramBan,
39-
partnerDuplicatePayoutMethod: hasDuplicatePayoutMethod,
39+
partnerDuplicateAccount: hasDuplicateAccounts,
4040
partnerEmailDomainMismatch: checkPartnerEmailDomainMismatch(partner),
4141
partnerEmailMasked: checkPartnerEmailMasked(partner),
4242
partnerNoSocialLinks: checkPartnerNoSocialLinks(partner),

apps/web/lib/api/fraud/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,15 @@ function getIdentityFieldsForFraudEvent({
8989
customerId,
9090
};
9191

92-
case "partnerDuplicatePayoutMethod":
9392
case "partnerDuplicateAccount":
9493
return {
9594
duplicatePartnerId: eventMetadata?.duplicatePartnerId,
9695
};
9796

97+
// Note: This will be removed in the next PR
98+
case "partnerDuplicatePayoutMethod":
99+
throw new Error("Fraud rule is no longer supported.");
100+
98101
case "partnerCrossProgramBan":
99102
if (!sourceProgramId) {
100103
throw new Error(`sourceProgramId is required for ${type} fraud rule.`);
@@ -136,10 +139,7 @@ export function getPartnerIdForFraudEvent(
136139
) {
137140
const metadata = event.metadata as Record<string, string> | undefined;
138141

139-
if (
140-
event.type === "partnerDuplicatePayoutMethod" ||
141-
event.type === "partnerDuplicateAccount"
142-
) {
142+
if (event.type === "partnerDuplicateAccount") {
143143
return metadata?.duplicatePartnerId ?? event.partnerId;
144144
}
145145

apps/web/lib/zod/schemas/fraud.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ export const updateFraudRuleSettingsSchema = z.object({
251251
customerEmailMatch: toggleOnlyFraudRuleSchema,
252252
customerEmailSuspiciousDomain: toggleOnlyFraudRuleSchema,
253253
partnerCrossProgramBan: toggleOnlyFraudRuleSchema,
254-
partnerDuplicatePayoutMethod: toggleOnlyFraudRuleSchema,
255254
partnerDuplicateAccount: toggleOnlyFraudRuleSchema,
256255
});
257256

@@ -326,8 +325,6 @@ export const fraudEventSchemas = {
326325
}),
327326
}),
328327

329-
partnerDuplicatePayoutMethod: baseFraudEventSchema,
330-
331328
partnerDuplicateAccount: baseFraudEventSchema,
332329
};
333330

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { prisma } from "@dub/prisma";
2+
import "dotenv-flow/config";
3+
4+
async function main() {
5+
// Migrate FraudRule
6+
const { count } = await prisma.fraudRule.updateMany({
7+
where: {
8+
type: "partnerDuplicatePayoutMethod",
9+
},
10+
data: {
11+
type: "partnerDuplicateAccount",
12+
},
13+
});
14+
15+
console.log(
16+
`Updated ${count} fraud rules from partnerDuplicatePayoutMethod to partnerDuplicateAccount`,
17+
);
18+
19+
// Migrate FraudEventGroup
20+
while (true) {
21+
const { count } = await prisma.fraudEventGroup.updateMany({
22+
where: {
23+
type: "partnerDuplicatePayoutMethod",
24+
},
25+
data: {
26+
type: "partnerDuplicateAccount",
27+
},
28+
limit: 100,
29+
});
30+
31+
console.log(
32+
`Updated ${count} fraud event groups from partnerDuplicatePayoutMethod to partnerDuplicateAccount`,
33+
);
34+
35+
if (count === 0) {
36+
break;
37+
}
38+
}
39+
}
40+
41+
main();

apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-partner-info-table.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Link from "next/link";
1010
import * as z from "zod/v4";
1111

1212
type EventDataProps = z.infer<
13-
(typeof fraudEventSchemas)["partnerDuplicatePayoutMethod"]
13+
(typeof fraudEventSchemas)["partnerDuplicateAccount"]
1414
>;
1515

1616
export function FraudPartnerInfoTable() {

0 commit comments

Comments
 (0)