@openora/core 0.4.1-canary.39 → 0.4.1-canary.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/admin-console/contract/index.d.ts +2 -0
- package/dist/admin-console/contract/index.d.ts.map +1 -1
- package/dist/admin-console/router/index.d.ts +1 -0
- package/dist/admin-console/router/index.d.ts.map +1 -1
- package/dist/admin-console/service/backoffice.service.d.ts +1 -1
- package/dist/audit/plugin.d.ts.map +1 -1
- package/dist/audit/plugin.js +48 -1
- package/dist/audit/plugin.js.map +1 -1
- package/dist/common/errors/errors.classes.js +3 -3
- package/dist/common/errors/errors.classes.js.map +1 -1
- package/dist/common/errors/errors.mapper.js +1 -1
- package/dist/common/errors/errors.mapper.js.map +1 -1
- package/dist/compliance/__tests__/hmac-kyc-webhook-verifier.test.js +66 -12
- package/dist/compliance/__tests__/hmac-kyc-webhook-verifier.test.js.map +1 -1
- package/dist/compliance/__tests__/kyc-admin.router.test.d.ts +2 -0
- package/dist/compliance/__tests__/kyc-admin.router.test.d.ts.map +1 -0
- package/dist/compliance/__tests__/kyc-admin.router.test.js +191 -0
- package/dist/compliance/__tests__/kyc-admin.router.test.js.map +1 -0
- package/dist/compliance/__tests__/kyc-webhook.router.test.d.ts +2 -0
- package/dist/compliance/__tests__/kyc-webhook.router.test.d.ts.map +1 -0
- package/dist/compliance/__tests__/kyc-webhook.router.test.js +148 -0
- package/dist/compliance/__tests__/kyc-webhook.router.test.js.map +1 -0
- package/dist/compliance/__tests__/kyc.service.test.js +16 -10
- package/dist/compliance/__tests__/kyc.service.test.js.map +1 -1
- package/dist/compliance/__tests__/rg.router.test.js +5 -1
- package/dist/compliance/__tests__/rg.router.test.js.map +1 -1
- package/dist/compliance/adapters/hmac-kyc-webhook-verifier.d.ts +16 -3
- package/dist/compliance/adapters/hmac-kyc-webhook-verifier.d.ts.map +1 -1
- package/dist/compliance/adapters/hmac-kyc-webhook-verifier.js +47 -5
- package/dist/compliance/adapters/hmac-kyc-webhook-verifier.js.map +1 -1
- package/dist/compliance/contract/index.d.ts +312 -0
- package/dist/compliance/contract/index.d.ts.map +1 -1
- package/dist/compliance/contract/index.js +50 -1
- package/dist/compliance/contract/index.js.map +1 -1
- package/dist/compliance/drizzle/migrations/0002_condemned_monster_badoon.sql +3 -0
- package/dist/compliance/drizzle/migrations/0003_public_spirit.sql +1 -0
- package/dist/compliance/drizzle/migrations/meta/0002_snapshot.json +673 -0
- package/dist/compliance/drizzle/migrations/meta/0003_snapshot.json +679 -0
- package/dist/compliance/drizzle/migrations/meta/_journal.json +14 -0
- package/dist/compliance/plugin.d.ts.map +1 -1
- package/dist/compliance/plugin.js +23 -3
- package/dist/compliance/plugin.js.map +1 -1
- package/dist/compliance/router/index.d.ts +186 -2
- package/dist/compliance/router/index.d.ts.map +1 -1
- package/dist/compliance/router/index.js +52 -3
- package/dist/compliance/router/index.js.map +1 -1
- package/dist/compliance/schema/index.d.ts +65 -3
- package/dist/compliance/schema/index.d.ts.map +1 -1
- package/dist/compliance/schema/index.js +8 -0
- package/dist/compliance/schema/index.js.map +1 -1
- package/dist/compliance/service/kyc.service.d.ts +153 -8
- package/dist/compliance/service/kyc.service.d.ts.map +1 -1
- package/dist/compliance/service/kyc.service.js +264 -32
- package/dist/compliance/service/kyc.service.js.map +1 -1
- package/dist/contracts/adapters/audit.d.ts +1 -1
- package/dist/contracts/adapters/audit.d.ts.map +1 -1
- package/dist/contracts/adapters/audit.js.map +1 -1
- package/dist/contracts/adapters/identity-reader.d.ts +9 -3
- package/dist/contracts/adapters/identity-reader.d.ts.map +1 -1
- package/dist/contracts/adapters/identity-reader.js.map +1 -1
- package/dist/contracts/adapters/index.d.ts +4 -2
- package/dist/contracts/adapters/index.d.ts.map +1 -1
- package/dist/contracts/adapters/index.js +2 -1
- package/dist/contracts/adapters/index.js.map +1 -1
- package/dist/contracts/adapters/kyc.d.ts +90 -5
- package/dist/contracts/adapters/kyc.d.ts.map +1 -1
- package/dist/contracts/adapters/kyc.js +23 -0
- package/dist/contracts/adapters/kyc.js.map +1 -1
- package/dist/contracts/adapters/tag-evaluation-commands.d.ts +10 -0
- package/dist/contracts/adapters/tag-evaluation-commands.d.ts.map +1 -0
- package/dist/contracts/adapters/tag-evaluation-commands.js +11 -0
- package/dist/contracts/adapters/tag-evaluation-commands.js.map +1 -0
- package/dist/contracts/adapters/wallet-reader.d.ts +8 -0
- package/dist/contracts/adapters/wallet-reader.d.ts.map +1 -1
- package/dist/contracts/adapters/wallet-reader.js.map +1 -1
- package/dist/contracts/schemas/__tests__/events.test.js +1 -1
- package/dist/contracts/schemas/__tests__/player-schemas.test.d.ts +2 -0
- package/dist/contracts/schemas/__tests__/player-schemas.test.d.ts.map +1 -0
- package/dist/contracts/schemas/__tests__/player-schemas.test.js +30 -0
- package/dist/contracts/schemas/__tests__/player-schemas.test.js.map +1 -0
- package/dist/contracts/schemas/events.d.ts +84 -0
- package/dist/contracts/schemas/events.d.ts.map +1 -1
- package/dist/contracts/schemas/events.js +24 -2
- package/dist/contracts/schemas/events.js.map +1 -1
- package/dist/contracts/schemas/platform-config.d.ts +13 -1
- package/dist/contracts/schemas/platform-config.d.ts.map +1 -1
- package/dist/contracts/schemas/player.d.ts +36 -3
- package/dist/contracts/schemas/player.d.ts.map +1 -1
- package/dist/contracts/schemas/player.js +14 -0
- package/dist/contracts/schemas/player.js.map +1 -1
- package/dist/contracts/schemas/tag.d.ts +85 -1
- package/dist/contracts/schemas/tag.d.ts.map +1 -1
- package/dist/contracts/schemas/tag.js +12 -0
- package/dist/contracts/schemas/tag.js.map +1 -1
- package/dist/engagement/notifications/contract/index.d.ts +5 -1
- package/dist/engagement/notifications/contract/index.d.ts.map +1 -1
- package/dist/engagement/notifications/contract/index.js +5 -1
- package/dist/engagement/notifications/contract/index.js.map +1 -1
- package/dist/engagement/notifications/plugin.d.ts.map +1 -1
- package/dist/engagement/notifications/plugin.js +43 -3
- package/dist/engagement/notifications/plugin.js.map +1 -1
- package/dist/engagement/notifications/router/index.d.ts +1 -0
- package/dist/engagement/notifications/router/index.d.ts.map +1 -1
- package/dist/iam/service/iam.service.d.ts +1 -1
- package/dist/pam/identity/__tests__/admin-user-directory.test.js +1 -1
- package/dist/pam/identity/adapters/identity-reader.service.d.ts +6 -4
- package/dist/pam/identity/adapters/identity-reader.service.d.ts.map +1 -1
- package/dist/pam/identity/adapters/identity-reader.service.js +19 -1
- package/dist/pam/identity/adapters/identity-reader.service.js.map +1 -1
- package/dist/pam/identity/admin-user-directory.d.ts +1 -1
- package/dist/pam/identity/admin-user-directory.d.ts.map +1 -1
- package/dist/pam/identity/admin-user-directory.js +6 -3
- package/dist/pam/identity/admin-user-directory.js.map +1 -1
- package/dist/pam/identity/service/identity.service.d.ts.map +1 -1
- package/dist/pam/identity/service/identity.service.js +4 -0
- package/dist/pam/identity/service/identity.service.js.map +1 -1
- package/dist/pam/player-management/__tests__/kyc-status-writer.test.js +5 -2
- package/dist/pam/player-management/__tests__/kyc-status-writer.test.js.map +1 -1
- package/dist/pam/player-management/__tests__/player.router.test.js +27 -20
- package/dist/pam/player-management/__tests__/player.router.test.js.map +1 -1
- package/dist/pam/player-management/__tests__/player.service.test.js +30 -31
- package/dist/pam/player-management/__tests__/player.service.test.js.map +1 -1
- package/dist/pam/player-management/contract/index.d.ts +37 -9
- package/dist/pam/player-management/contract/index.d.ts.map +1 -1
- package/dist/pam/player-management/contract/index.js +0 -1
- package/dist/pam/player-management/contract/index.js.map +1 -1
- package/dist/pam/player-management/plugin.d.ts.map +1 -1
- package/dist/pam/player-management/plugin.js +3 -2
- package/dist/pam/player-management/plugin.js.map +1 -1
- package/dist/pam/player-management/router/index.d.ts +32 -10
- package/dist/pam/player-management/router/index.d.ts.map +1 -1
- package/dist/pam/player-management/router/index.js +48 -16
- package/dist/pam/player-management/router/index.js.map +1 -1
- package/dist/pam/player-management/service/kyc-status-writer.d.ts +3 -2
- package/dist/pam/player-management/service/kyc-status-writer.d.ts.map +1 -1
- package/dist/pam/player-management/service/kyc-status-writer.js +24 -10
- package/dist/pam/player-management/service/kyc-status-writer.js.map +1 -1
- package/dist/pam/player-management/service/player.service.d.ts +10 -9
- package/dist/pam/player-management/service/player.service.d.ts.map +1 -1
- package/dist/pam/player-management/service/player.service.js +24 -12
- package/dist/pam/player-management/service/player.service.js.map +1 -1
- package/dist/pam/profile/contract/index.d.ts +2 -0
- package/dist/pam/profile/contract/index.d.ts.map +1 -1
- package/dist/pam/profile/drizzle/migrations/0001_powerful_tiger_shark.sql +1 -0
- package/dist/pam/profile/drizzle/migrations/meta/0001_snapshot.json +191 -0
- package/dist/pam/profile/drizzle/migrations/meta/_journal.json +7 -0
- package/dist/pam/profile/react/profile.d.ts +2 -2
- package/dist/pam/profile/router/index.d.ts +2 -0
- package/dist/pam/profile/router/index.d.ts.map +1 -1
- package/dist/pam/profile/schema/index.d.ts +3 -3
- package/dist/pam/profile/schema/index.d.ts.map +1 -1
- package/dist/pam/profile/service/profile.service.d.ts +2 -2
- package/dist/pam/shared/player-mapper.d.ts +1 -1
- package/dist/pam/tag/__tests__/tag-evaluation.service.test.js +619 -19
- package/dist/pam/tag/__tests__/tag-evaluation.service.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag-mappers.test.js +25 -0
- package/dist/pam/tag/__tests__/tag-mappers.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag.router.test.js +71 -1
- package/dist/pam/tag/__tests__/tag.router.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag.service.test.js +342 -15
- package/dist/pam/tag/__tests__/tag.service.test.js.map +1 -1
- package/dist/pam/tag/contract/index.d.ts +123 -0
- package/dist/pam/tag/contract/index.d.ts.map +1 -1
- package/dist/pam/tag/contract/index.js +3 -0
- package/dist/pam/tag/contract/index.js.map +1 -1
- package/dist/pam/tag/contract/player-tag-assign-metadata.d.ts +37 -0
- package/dist/pam/tag/contract/player-tag-assign-metadata.d.ts.map +1 -0
- package/dist/pam/tag/contract/player-tag-assign-metadata.js +20 -0
- package/dist/pam/tag/contract/player-tag-assign-metadata.js.map +1 -0
- package/dist/pam/tag/drizzle/migrations/0002_silent_skaar.sql +4 -0
- package/dist/pam/tag/drizzle/migrations/0003_overconfident_guardian.sql +3 -0
- package/dist/pam/tag/drizzle/migrations/0004_curvy_wither.sql +2 -0
- package/dist/pam/tag/drizzle/migrations/meta/0002_snapshot.json +335 -0
- package/dist/pam/tag/drizzle/migrations/meta/0003_snapshot.json +343 -0
- package/dist/pam/tag/drizzle/migrations/meta/0004_snapshot.json +345 -0
- package/dist/pam/tag/drizzle/migrations/meta/_journal.json +21 -0
- package/dist/pam/tag/migrate.d.ts.map +1 -1
- package/dist/pam/tag/migrate.js +34 -0
- package/dist/pam/tag/migrate.js.map +1 -1
- package/dist/pam/tag/plugin.d.ts.map +1 -1
- package/dist/pam/tag/plugin.js +43 -16
- package/dist/pam/tag/plugin.js.map +1 -1
- package/dist/pam/tag/router/index.d.ts +105 -0
- package/dist/pam/tag/router/index.d.ts.map +1 -1
- package/dist/pam/tag/router/index.js +34 -18
- package/dist/pam/tag/router/index.js.map +1 -1
- package/dist/pam/tag/schema/index.d.ts +42 -3
- package/dist/pam/tag/schema/index.d.ts.map +1 -1
- package/dist/pam/tag/schema/index.js +13 -2
- package/dist/pam/tag/schema/index.js.map +1 -1
- package/dist/pam/tag/seed/data/default-tag-rules.d.ts.map +1 -1
- package/dist/pam/tag/seed/data/default-tag-rules.js +42 -0
- package/dist/pam/tag/seed/data/default-tag-rules.js.map +1 -1
- package/dist/pam/tag/seed/data/default-tags.d.ts.map +1 -1
- package/dist/pam/tag/seed/data/default-tags.js +6 -0
- package/dist/pam/tag/seed/data/default-tags.js.map +1 -1
- package/dist/pam/tag/service/tag-evaluation.service.d.ts +141 -16
- package/dist/pam/tag/service/tag-evaluation.service.d.ts.map +1 -1
- package/dist/pam/tag/service/tag-evaluation.service.js +472 -47
- package/dist/pam/tag/service/tag-evaluation.service.js.map +1 -1
- package/dist/pam/tag/service/tag-mappers.d.ts +1 -1
- package/dist/pam/tag/service/tag-mappers.d.ts.map +1 -1
- package/dist/pam/tag/service/tag-mappers.js +1 -0
- package/dist/pam/tag/service/tag-mappers.js.map +1 -1
- package/dist/pam/tag/service/tag-rule.service.d.ts +3 -3
- package/dist/pam/tag/service/tag.service.d.ts +141 -11
- package/dist/pam/tag/service/tag.service.d.ts.map +1 -1
- package/dist/pam/tag/service/tag.service.js +242 -23
- package/dist/pam/tag/service/tag.service.js.map +1 -1
- package/dist/server/auth/permissions.d.ts +12 -0
- package/dist/server/auth/permissions.d.ts.map +1 -1
- package/dist/server/auth/permissions.js +2 -0
- package/dist/server/auth/permissions.js.map +1 -1
- package/dist/server/db/__tests__/migrate.test.js +79 -2
- package/dist/server/db/__tests__/migrate.test.js.map +1 -1
- package/dist/server/db/migrate.d.ts +11 -0
- package/dist/server/db/migrate.d.ts.map +1 -1
- package/dist/server/db/migrate.js +3 -0
- package/dist/server/db/migrate.js.map +1 -1
- package/dist/server/kernel/bullmq-job-queue.d.ts.map +1 -1
- package/dist/server/kernel/bullmq-job-queue.js +17 -0
- package/dist/server/kernel/bullmq-job-queue.js.map +1 -1
- package/dist/server/plugin-host/__tests__/core-plugins.test.js +1 -1
- package/dist/server/plugin-host/__tests__/core-plugins.test.js.map +1 -1
- package/dist/server/plugin-host/define-plugin.d.ts +2 -2
- package/dist/server/plugin-host/define-plugin.d.ts.map +1 -1
- package/dist/server/plugin-host/define-plugin.js.map +1 -1
- package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js +60 -0
- package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.service.test.js +59 -0
- package/dist/wallet/__tests__/wallet.service.test.js.map +1 -1
- package/dist/wallet/adapters/wallet-reader.service.d.ts +1 -0
- package/dist/wallet/adapters/wallet-reader.service.d.ts.map +1 -1
- package/dist/wallet/adapters/wallet-reader.service.js +18 -1
- package/dist/wallet/adapters/wallet-reader.service.js.map +1 -1
- package/dist/wallet/contract/index.d.ts +4 -0
- package/dist/wallet/contract/index.d.ts.map +1 -1
- package/dist/wallet/plugin.d.ts.map +1 -1
- package/dist/wallet/plugin.js +7 -4
- package/dist/wallet/plugin.js.map +1 -1
- package/dist/wallet/router/index.d.ts +2 -0
- package/dist/wallet/router/index.d.ts.map +1 -1
- package/dist/wallet/service/wallet.service.d.ts +5 -4
- package/dist/wallet/service/wallet.service.d.ts.map +1 -1
- package/dist/wallet/service/wallet.service.js +28 -8
- package/dist/wallet/service/wallet.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/compliance/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EACL,OAAO,EACP,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,OAAO,EACP,OAAO,EACP,KAAK,EACL,SAAS,EACT,WAAW,EACX,KAAK,GACN,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/compliance/schema/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DrizzleService, type EventBus } from '@openora/core/server';
|
|
2
|
-
import type
|
|
3
|
-
import type { SubmitKycInput, PlayerKycView } from '../contract/index.js';
|
|
2
|
+
import { type KycAdapter, type KycCheckResult, type KycDocument, type KycRiskSignals, type KycStatusWriter, type KycVendorStatus, type PlatformConfig, type User, ClientMeta } from '@openora/core/contracts';
|
|
3
|
+
import type { SubmitKycInput, PlayerKycView, KycOverrideStatus, BulkApproveKycResult } from '../contract/index.js';
|
|
4
4
|
import { type ReKycTrigger } from './re-kyc-trigger.js';
|
|
5
|
+
export declare const PlayerNotFoundError: new (id: string) => Error;
|
|
5
6
|
export declare const KycVerificationNotFoundError: new (id: string) => Error;
|
|
6
7
|
/**
|
|
7
8
|
* Owns the append-only `kyc_verification` history and the submit/reconcile/re-KYC
|
|
@@ -27,15 +28,21 @@ export declare class KycVerificationService {
|
|
|
27
28
|
private get provider();
|
|
28
29
|
submit(userId: User['id'], input: SubmitKycInput, meta?: ClientMeta): Promise<{
|
|
29
30
|
verificationUrl: string | undefined;
|
|
30
|
-
status: "pending" | "not_started" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
|
|
31
|
+
status: "pending" | "not_started" | "approved" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
|
|
31
32
|
id: string;
|
|
32
33
|
userId: string;
|
|
34
|
+
checks: {
|
|
35
|
+
step: string;
|
|
36
|
+
status: "expired" | "unknown" | "not_started" | "approved" | "in_progress" | "in_review" | "declined";
|
|
37
|
+
}[] | null;
|
|
33
38
|
provider: string;
|
|
34
39
|
referenceId: string;
|
|
35
40
|
triggeredBy: "manual" | "submission" | "reverify_threshold";
|
|
36
41
|
documentTypes: ("passport" | "drivers_license" | "national_id")[];
|
|
37
42
|
decisionReason: string | null;
|
|
43
|
+
riskSignals: KycRiskSignals | null;
|
|
38
44
|
triggerDeposits: string | null;
|
|
45
|
+
decisionReceivedAt: Date | null;
|
|
39
46
|
createdAt: string;
|
|
40
47
|
updatedAt: string;
|
|
41
48
|
submittedAt: string;
|
|
@@ -43,12 +50,42 @@ export declare class KycVerificationService {
|
|
|
43
50
|
}>;
|
|
44
51
|
/**
|
|
45
52
|
* Idempotent vendor-decision apply (polling + webhook). No-op when the latest record
|
|
46
|
-
* already holds the mapped status.
|
|
53
|
+
* already holds the mapped status. `reason`/`documentTypes`/`riskSignals` are only
|
|
54
|
+
* overwritten when the caller supplies them, and `decidedAt` is stamped only on an
|
|
55
|
+
* actual decision, so a bare status reconcile never blanks out an earlier one. Emits
|
|
56
|
+
* `compliance.kyc.high_risk_signal_detected` when the supplied `riskSignals` (never a
|
|
57
|
+
* preserved prior value) trip `warrantsHighRiskTag`.
|
|
58
|
+
*
|
|
59
|
+
* `opts.receivedAt` (when the caller has it - the `kyc-decision-sync` job path always
|
|
60
|
+
* does, stamped at webhook-arrival time in the router, BEFORE the job queue can
|
|
61
|
+
* reorder anything) is a monotonicity guard: a decision older than the one already on
|
|
62
|
+
* file for this referenceId is refused, not applied. The job queue does not honour
|
|
63
|
+
* `orderingKey` (see bullmq-job-queue.ts), so two decisions for the same reference can
|
|
64
|
+
* reach here out of arrival order after a retry; without this guard the LATER-arriving
|
|
65
|
+
* (but actually OLDER) decision would win and silently overwrite a newer, correct one.
|
|
66
|
+
* A caller with no `receivedAt` (eg a direct `reconcile` call with no job path behind
|
|
67
|
+
* it) skips the guard entirely - always applies, exactly as before.
|
|
68
|
+
*
|
|
69
|
+
* A vendor `approved` is downgraded to `resubmission_requested` when the checks this
|
|
70
|
+
* call is about to persist (`opts.checks`, falling back to the existing row's `checks`
|
|
71
|
+
* when the caller supplies none - the SAME value written below, so the gate can never
|
|
72
|
+
* evaluate a different set of checks than the one that ends up on the row) contain any
|
|
73
|
+
* non-`approved` entry - see `compliance/AGENTS.md` > KYC workflow completeness.
|
|
47
74
|
*/
|
|
48
|
-
reconcile(referenceId: string, vendorStatus: KycVendorStatus,
|
|
49
|
-
|
|
75
|
+
reconcile(referenceId: string, vendorStatus: KycVendorStatus, opts?: {
|
|
76
|
+
reason?: string;
|
|
77
|
+
documentTypes?: KycDocument['type'][];
|
|
78
|
+
riskSignals?: KycRiskSignals;
|
|
79
|
+
checks?: KycCheckResult[];
|
|
80
|
+
receivedAt?: Date;
|
|
81
|
+
}): Promise<import("@openora/core/server").SerializedRow<{
|
|
82
|
+
status: "pending" | "not_started" | "approved" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
|
|
50
83
|
id: string;
|
|
51
84
|
userId: string;
|
|
85
|
+
checks: {
|
|
86
|
+
step: string;
|
|
87
|
+
status: "expired" | "unknown" | "not_started" | "approved" | "in_progress" | "in_review" | "declined";
|
|
88
|
+
}[] | null;
|
|
52
89
|
createdAt: Date;
|
|
53
90
|
updatedAt: Date;
|
|
54
91
|
provider: string;
|
|
@@ -57,16 +94,124 @@ export declare class KycVerificationService {
|
|
|
57
94
|
submittedAt: Date;
|
|
58
95
|
documentTypes: ("passport" | "drivers_license" | "national_id")[];
|
|
59
96
|
decisionReason: string | null;
|
|
97
|
+
riskSignals: KycRiskSignals | null;
|
|
60
98
|
decidedAt: Date | null;
|
|
61
99
|
triggerDeposits: string | null;
|
|
100
|
+
decisionReceivedAt: Date | null;
|
|
101
|
+
}, "createdAt" | "updatedAt" | "submittedAt" | "decidedAt"> | null>;
|
|
102
|
+
/**
|
|
103
|
+
* `kyc-decision-sync` job handler, run off the webhook request path. Enriches the
|
|
104
|
+
* reconcile through `resolveDecision`/`resolveRiskSignals` when the bound adapter
|
|
105
|
+
* implements them, falling back to a status-only reconcile when it does not. Errors
|
|
106
|
+
* (notably `PlayerNotFoundError`) propagate so the job queue retries the decision -
|
|
107
|
+
* see `compliance/AGENTS.md` > Webhook -> job flow. `receivedAt` is the webhook's
|
|
108
|
+
* arrival time (stamped by the router before enqueue, carried on the job payload) -
|
|
109
|
+
* passed through to `reconcile` as the monotonicity watermark so a job that runs
|
|
110
|
+
* out of arrival order (the driver ignores `orderingKey`) can't overwrite a newer
|
|
111
|
+
* decision with a stale one.
|
|
112
|
+
*/
|
|
113
|
+
syncDecision(referenceId: string, status: KycVendorStatus, receivedAt?: Date): Promise<import("@openora/core/server").SerializedRow<{
|
|
114
|
+
status: "pending" | "not_started" | "approved" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
|
|
115
|
+
id: string;
|
|
116
|
+
userId: string;
|
|
117
|
+
checks: {
|
|
118
|
+
step: string;
|
|
119
|
+
status: "expired" | "unknown" | "not_started" | "approved" | "in_progress" | "in_review" | "declined";
|
|
120
|
+
}[] | null;
|
|
121
|
+
createdAt: Date;
|
|
122
|
+
updatedAt: Date;
|
|
123
|
+
provider: string;
|
|
124
|
+
referenceId: string;
|
|
125
|
+
triggeredBy: "manual" | "submission" | "reverify_threshold";
|
|
126
|
+
submittedAt: Date;
|
|
127
|
+
documentTypes: ("passport" | "drivers_license" | "national_id")[];
|
|
128
|
+
decisionReason: string | null;
|
|
129
|
+
riskSignals: KycRiskSignals | null;
|
|
130
|
+
decidedAt: Date | null;
|
|
131
|
+
triggerDeposits: string | null;
|
|
132
|
+
decisionReceivedAt: Date | null;
|
|
62
133
|
}, "createdAt" | "updatedAt" | "submittedAt" | "decidedAt"> | null>;
|
|
63
134
|
getForPlayer(userId: User['id']): Promise<PlayerKycView>;
|
|
64
135
|
/**
|
|
65
|
-
* Deposit-event hook: flips a currently-
|
|
136
|
+
* Deposit-event hook: flips a currently-approved player to `resubmission_requested`
|
|
66
137
|
* once cumulative deposits cross a fresh per-currency threshold band. Idempotent twice
|
|
67
|
-
* over: skips unless presently
|
|
138
|
+
* over: skips unless presently approved, and the watermark stops a re-approved
|
|
68
139
|
* high-roller re-firing on every later deposit.
|
|
69
140
|
*/
|
|
70
141
|
handleDeposit(userId: User['id']): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Shared transaction body for `requestResubmission`/`overrideStatus`: inserts the
|
|
144
|
+
* manual `kyc_verification` history row and calls `KYC_STATUS_WRITER.setStatus` in
|
|
145
|
+
* the SAME transaction (extracted so the two documented locking/idempotency rules
|
|
146
|
+
* in `compliance/AGENTS.md` > Admin KYC actions cannot silently diverge between the
|
|
147
|
+
* two call sites). Caller has already run the idempotency pre-check
|
|
148
|
+
* (`requirePlayerRowForUpdate` + a status compare) before calling this.
|
|
149
|
+
*/
|
|
150
|
+
private applyManualDecision;
|
|
151
|
+
/**
|
|
152
|
+
* Admin-initiated: requests the player resubmit documents. Idempotent on a repeat
|
|
153
|
+
* call while the player is already at `resubmission_requested` - no duplicate history
|
|
154
|
+
* row, no duplicate `compliance.kyc.updated` emit. See `compliance/AGENTS.md` >
|
|
155
|
+
* Admin KYC actions for why the `FOR UPDATE` lock has to sit inside the transaction.
|
|
156
|
+
*/
|
|
157
|
+
requestResubmission(userId: User['id'], reason: string, actorId: User['id']): Promise<import("@openora/core/server").SerializedRow<{
|
|
158
|
+
status: "pending" | "not_started" | "approved" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
|
|
159
|
+
id: string;
|
|
160
|
+
userId: string;
|
|
161
|
+
checks: {
|
|
162
|
+
step: string;
|
|
163
|
+
status: "expired" | "unknown" | "not_started" | "approved" | "in_progress" | "in_review" | "declined";
|
|
164
|
+
}[] | null;
|
|
165
|
+
createdAt: Date;
|
|
166
|
+
updatedAt: Date;
|
|
167
|
+
provider: string;
|
|
168
|
+
referenceId: string;
|
|
169
|
+
triggeredBy: "manual" | "submission" | "reverify_threshold";
|
|
170
|
+
submittedAt: Date;
|
|
171
|
+
documentTypes: ("passport" | "drivers_license" | "national_id")[];
|
|
172
|
+
decisionReason: string | null;
|
|
173
|
+
riskSignals: KycRiskSignals | null;
|
|
174
|
+
decidedAt: Date | null;
|
|
175
|
+
triggerDeposits: string | null;
|
|
176
|
+
decisionReceivedAt: Date | null;
|
|
177
|
+
}, "createdAt" | "updatedAt" | "submittedAt" | "decidedAt">>;
|
|
178
|
+
/**
|
|
179
|
+
* Admin-initiated: forces the player to an operator-chosen status, guarded by the
|
|
180
|
+
* router's `compliance:override-limit`. `resolveManualStatus` remaps an `approved`
|
|
181
|
+
* choice to `manually_overridden`; every other choice is written verbatim. Idempotent
|
|
182
|
+
* on a repeat call resolving to the status the player already holds. Rationale for
|
|
183
|
+
* both: `compliance/AGENTS.md` > Admin KYC actions.
|
|
184
|
+
*/
|
|
185
|
+
overrideStatus(userId: User['id'], status: KycOverrideStatus, reason: string, actorId: User['id']): Promise<import("@openora/core/server").SerializedRow<{
|
|
186
|
+
status: "pending" | "not_started" | "approved" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
|
|
187
|
+
id: string;
|
|
188
|
+
userId: string;
|
|
189
|
+
checks: {
|
|
190
|
+
step: string;
|
|
191
|
+
status: "expired" | "unknown" | "not_started" | "approved" | "in_progress" | "in_review" | "declined";
|
|
192
|
+
}[] | null;
|
|
193
|
+
createdAt: Date;
|
|
194
|
+
updatedAt: Date;
|
|
195
|
+
provider: string;
|
|
196
|
+
referenceId: string;
|
|
197
|
+
triggeredBy: "manual" | "submission" | "reverify_threshold";
|
|
198
|
+
submittedAt: Date;
|
|
199
|
+
documentTypes: ("passport" | "drivers_license" | "national_id")[];
|
|
200
|
+
decisionReason: string | null;
|
|
201
|
+
riskSignals: KycRiskSignals | null;
|
|
202
|
+
decidedAt: Date | null;
|
|
203
|
+
triggerDeposits: string | null;
|
|
204
|
+
decisionReceivedAt: Date | null;
|
|
205
|
+
}, "createdAt" | "updatedAt" | "submittedAt" | "decidedAt">>;
|
|
206
|
+
/**
|
|
207
|
+
* Admin-initiated: approves every listed player through the same
|
|
208
|
+
* `overrideStatus('approved', ...)` path, so a bulk approval is recorded identically
|
|
209
|
+
* to a single one. Bounded fan-out with per-player error isolation - one failure is
|
|
210
|
+
* captured as a result row rather than aborting the batch, and the returned message
|
|
211
|
+
* is a fixed string, never the raw exception text (`compliance/AGENTS.md`).
|
|
212
|
+
*/
|
|
213
|
+
bulkApprove(userIds: User['id'][], reason: string, actorId: User['id']): Promise<BulkApproveKycResult[]>;
|
|
214
|
+
private requirePlayerRowForUpdate;
|
|
215
|
+
private latestVerificationOrThrow;
|
|
71
216
|
}
|
|
72
217
|
//# sourceMappingURL=kyc.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kyc.service.d.ts","sourceRoot":"","sources":["../../../src/compliance/service/kyc.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,
|
|
1
|
+
{"version":3,"file":"kyc.service.d.ts","sourceRoot":"","sources":["../../../src/compliance/service/kyc.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { findOneOrThrow, makeNotFoundError, serializeRow, } from '@openora/core/server';
|
|
1
|
+
import { createLogger, findOneOrThrow, makeNotFoundError, mapConcurrent, serializeRow, } from '@openora/core/server';
|
|
2
|
+
import { normalizeKycStatus, } from '@openora/core/contracts';
|
|
2
3
|
import { and, desc, eq, sql } from 'drizzle-orm';
|
|
3
4
|
import { kycVerification } from '../schema/index.js';
|
|
4
5
|
// Cross-domain reads via public /schema subpaths (ADR-0020); the wallet ledger is the
|
|
@@ -6,12 +7,16 @@ import { kycVerification } from '../schema/index.js';
|
|
|
6
7
|
import { player } from '@openora/core/pam/schema/profile';
|
|
7
8
|
import { wallet, walletTransaction } from '@openora/core/wallet/schema';
|
|
8
9
|
import { CumulativeDepositReKycTrigger } from './re-kyc-trigger.js';
|
|
10
|
+
const logger = createLogger('compliance-kyc');
|
|
9
11
|
const DEFAULT_PROVIDER = 'mock';
|
|
12
|
+
const MANUAL_PROVIDER = 'manual';
|
|
13
|
+
const BULK_APPROVE_CONCURRENCY = 10;
|
|
14
|
+
export const PlayerNotFoundError = makeNotFoundError('Player');
|
|
10
15
|
export const KycVerificationNotFoundError = makeNotFoundError('KycVerification');
|
|
11
16
|
function mapVendorStatus(vendor) {
|
|
12
17
|
switch (vendor) {
|
|
13
18
|
case 'approved':
|
|
14
|
-
return '
|
|
19
|
+
return 'approved';
|
|
15
20
|
case 'rejected':
|
|
16
21
|
return 'rejected';
|
|
17
22
|
case 'pending':
|
|
@@ -21,7 +26,31 @@ function mapVendorStatus(vendor) {
|
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
function isDecided(status) {
|
|
24
|
-
|
|
29
|
+
const normalized = normalizeKycStatus(status);
|
|
30
|
+
return (normalized === 'approved' || normalized === 'rejected' || normalized === 'manually_overridden');
|
|
31
|
+
}
|
|
32
|
+
function resolveManualStatus(status) {
|
|
33
|
+
return status === 'approved' ? 'manually_overridden' : status;
|
|
34
|
+
}
|
|
35
|
+
const HIGH_RISK_SIGNAL_KEYS = [
|
|
36
|
+
'duplicateDeviceDetected',
|
|
37
|
+
'highRiskCountryDetected',
|
|
38
|
+
];
|
|
39
|
+
function warrantsHighRiskTag(signals) {
|
|
40
|
+
return HIGH_RISK_SIGNAL_KEYS.some((key) => signals[key]);
|
|
41
|
+
}
|
|
42
|
+
const NO_STEPS_RESOLVED_CHECK = { step: 'workflow', status: 'unknown' };
|
|
43
|
+
function findIncompleteCheck(checks) {
|
|
44
|
+
if (checks === undefined) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
if (checks.length === 0) {
|
|
48
|
+
return NO_STEPS_RESOLVED_CHECK;
|
|
49
|
+
}
|
|
50
|
+
return checks.find((check) => check.status !== 'approved');
|
|
51
|
+
}
|
|
52
|
+
function describeIncompleteCheck(check) {
|
|
53
|
+
return `Vendor reported approved but "${check.step}" has not reached a successful state (status: ${check.status})`;
|
|
25
54
|
}
|
|
26
55
|
function toDto(row) {
|
|
27
56
|
return serializeRow(row, {
|
|
@@ -48,20 +77,30 @@ export class KycVerificationService {
|
|
|
48
77
|
}
|
|
49
78
|
async submit(userId, input, meta) {
|
|
50
79
|
const result = await this.kycAdapter.submit(userId, input.documents.map((d) => ({ type: d.type, frontUrl: d.frontUrl, backUrl: d.backUrl })));
|
|
51
|
-
const
|
|
80
|
+
const mappedStatus = mapVendorStatus(result.status);
|
|
81
|
+
const incompleteCheck = mappedStatus === 'approved' ? findIncompleteCheck(result.checks) : undefined;
|
|
82
|
+
const status = incompleteCheck ? 'resubmission_requested' : mappedStatus;
|
|
83
|
+
const decisionReason = incompleteCheck ? describeIncompleteCheck(incompleteCheck) : null;
|
|
52
84
|
const decided = isDecided(status);
|
|
53
|
-
const row =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
const row = await this.drizzle.db.transaction(async (trx) => {
|
|
86
|
+
const inserted = findOneOrThrow(await trx
|
|
87
|
+
.insert(kycVerification)
|
|
88
|
+
.values({
|
|
89
|
+
userId,
|
|
90
|
+
provider: this.provider,
|
|
91
|
+
referenceId: result.referenceId,
|
|
92
|
+
status,
|
|
93
|
+
documentTypes: input.documents.map((d) => d.type),
|
|
94
|
+
decisionReason,
|
|
95
|
+
checks: result.checks ?? null,
|
|
96
|
+
triggeredBy: 'submission',
|
|
97
|
+
decidedAt: decided ? new Date() : null,
|
|
98
|
+
decisionReceivedAt: new Date(),
|
|
99
|
+
})
|
|
100
|
+
.returning(), new KycVerificationNotFoundError(userId));
|
|
101
|
+
await this.statusWriter.setStatus(userId, status, { actorId: null, source: 'vendor' }, trx);
|
|
102
|
+
return inserted;
|
|
103
|
+
});
|
|
65
104
|
this.events.emit('compliance.kyc.submitted', {
|
|
66
105
|
userId,
|
|
67
106
|
referenceId: result.referenceId,
|
|
@@ -69,15 +108,34 @@ export class KycVerificationService {
|
|
|
69
108
|
ip: meta?.ip ?? null,
|
|
70
109
|
userAgent: meta?.userAgent ?? null,
|
|
71
110
|
});
|
|
72
|
-
await this.statusWriter.setStatus(userId, status, { actorId: null, source: 'vendor' });
|
|
73
111
|
return { ...toDto(row), verificationUrl: result.verificationUrl };
|
|
74
112
|
}
|
|
75
113
|
/**
|
|
76
114
|
* Idempotent vendor-decision apply (polling + webhook). No-op when the latest record
|
|
77
|
-
* already holds the mapped status.
|
|
115
|
+
* already holds the mapped status. `reason`/`documentTypes`/`riskSignals` are only
|
|
116
|
+
* overwritten when the caller supplies them, and `decidedAt` is stamped only on an
|
|
117
|
+
* actual decision, so a bare status reconcile never blanks out an earlier one. Emits
|
|
118
|
+
* `compliance.kyc.high_risk_signal_detected` when the supplied `riskSignals` (never a
|
|
119
|
+
* preserved prior value) trip `warrantsHighRiskTag`.
|
|
120
|
+
*
|
|
121
|
+
* `opts.receivedAt` (when the caller has it - the `kyc-decision-sync` job path always
|
|
122
|
+
* does, stamped at webhook-arrival time in the router, BEFORE the job queue can
|
|
123
|
+
* reorder anything) is a monotonicity guard: a decision older than the one already on
|
|
124
|
+
* file for this referenceId is refused, not applied. The job queue does not honour
|
|
125
|
+
* `orderingKey` (see bullmq-job-queue.ts), so two decisions for the same reference can
|
|
126
|
+
* reach here out of arrival order after a retry; without this guard the LATER-arriving
|
|
127
|
+
* (but actually OLDER) decision would win and silently overwrite a newer, correct one.
|
|
128
|
+
* A caller with no `receivedAt` (eg a direct `reconcile` call with no job path behind
|
|
129
|
+
* it) skips the guard entirely - always applies, exactly as before.
|
|
130
|
+
*
|
|
131
|
+
* A vendor `approved` is downgraded to `resubmission_requested` when the checks this
|
|
132
|
+
* call is about to persist (`opts.checks`, falling back to the existing row's `checks`
|
|
133
|
+
* when the caller supplies none - the SAME value written below, so the gate can never
|
|
134
|
+
* evaluate a different set of checks than the one that ends up on the row) contain any
|
|
135
|
+
* non-`approved` entry - see `compliance/AGENTS.md` > KYC workflow completeness.
|
|
78
136
|
*/
|
|
79
137
|
// referenceId is the KYC vendor's own reference, not an internal Uuid - stays a plain string.
|
|
80
|
-
async reconcile(referenceId, vendorStatus,
|
|
138
|
+
async reconcile(referenceId, vendorStatus, opts = {}) {
|
|
81
139
|
const [existing] = await this.drizzle.db
|
|
82
140
|
.select()
|
|
83
141
|
.from(kycVerification)
|
|
@@ -87,22 +145,80 @@ export class KycVerificationService {
|
|
|
87
145
|
if (!existing) {
|
|
88
146
|
return null;
|
|
89
147
|
}
|
|
90
|
-
const
|
|
148
|
+
const mappedStatus = mapVendorStatus(vendorStatus);
|
|
149
|
+
const persistedChecks = opts.checks ?? existing.checks ?? undefined;
|
|
150
|
+
const incompleteCheck = mappedStatus === 'approved' ? findIncompleteCheck(persistedChecks) : undefined;
|
|
151
|
+
const status = incompleteCheck ? 'resubmission_requested' : mappedStatus;
|
|
152
|
+
const reasonFromThisReconcile = incompleteCheck
|
|
153
|
+
? describeIncompleteCheck(incompleteCheck)
|
|
154
|
+
: opts.reason;
|
|
155
|
+
const decisionReason = reasonFromThisReconcile ?? existing.decisionReason;
|
|
91
156
|
if (existing.status === status && existing.decidedAt) {
|
|
92
157
|
return toDto(existing);
|
|
93
158
|
}
|
|
94
|
-
|
|
95
|
-
.
|
|
96
|
-
.
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
159
|
+
if (opts.receivedAt &&
|
|
160
|
+
existing.decisionReceivedAt &&
|
|
161
|
+
opts.receivedAt < existing.decisionReceivedAt) {
|
|
162
|
+
logger.warn({ referenceId, incoming: opts.receivedAt, current: existing.decisionReceivedAt }, 'stale KYC decision refused: a newer decision is already applied for this reference');
|
|
163
|
+
return toDto(existing);
|
|
164
|
+
}
|
|
165
|
+
const decided = isDecided(status);
|
|
166
|
+
const row = await this.drizzle.db.transaction(async (trx) => {
|
|
167
|
+
const updated = findOneOrThrow(await trx
|
|
168
|
+
.update(kycVerification)
|
|
169
|
+
.set({
|
|
170
|
+
status,
|
|
171
|
+
decisionReason,
|
|
172
|
+
documentTypes: opts.documentTypes ?? existing.documentTypes,
|
|
173
|
+
riskSignals: opts.riskSignals ?? existing.riskSignals,
|
|
174
|
+
checks: persistedChecks ?? null,
|
|
175
|
+
decidedAt: decided ? new Date() : existing.decidedAt,
|
|
176
|
+
decisionReceivedAt: opts.receivedAt ?? existing.decisionReceivedAt,
|
|
177
|
+
})
|
|
178
|
+
.where(eq(kycVerification.id, existing.id))
|
|
179
|
+
.returning(), new KycVerificationNotFoundError(existing.id));
|
|
180
|
+
await this.statusWriter.setStatus(existing.userId, status, { actorId: null, source: 'webhook', reason: reasonFromThisReconcile }, trx);
|
|
181
|
+
return updated;
|
|
103
182
|
});
|
|
183
|
+
if (opts.riskSignals && warrantsHighRiskTag(opts.riskSignals)) {
|
|
184
|
+
this.events.emit('compliance.kyc.high_risk_signal_detected', {
|
|
185
|
+
userId: existing.userId,
|
|
186
|
+
referenceId,
|
|
187
|
+
vpnOrTorDetected: opts.riskSignals.vpnOrTorDetected,
|
|
188
|
+
dataCenterIpDetected: opts.riskSignals.dataCenterIpDetected,
|
|
189
|
+
duplicateDeviceDetected: opts.riskSignals.duplicateDeviceDetected,
|
|
190
|
+
highRiskCountryDetected: opts.riskSignals.highRiskCountryDetected,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
104
193
|
return toDto(row);
|
|
105
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* `kyc-decision-sync` job handler, run off the webhook request path. Enriches the
|
|
197
|
+
* reconcile through `resolveDecision`/`resolveRiskSignals` when the bound adapter
|
|
198
|
+
* implements them, falling back to a status-only reconcile when it does not. Errors
|
|
199
|
+
* (notably `PlayerNotFoundError`) propagate so the job queue retries the decision -
|
|
200
|
+
* see `compliance/AGENTS.md` > Webhook -> job flow. `receivedAt` is the webhook's
|
|
201
|
+
* arrival time (stamped by the router before enqueue, carried on the job payload) -
|
|
202
|
+
* passed through to `reconcile` as the monotonicity watermark so a job that runs
|
|
203
|
+
* out of arrival order (the driver ignores `orderingKey`) can't overwrite a newer
|
|
204
|
+
* decision with a stale one.
|
|
205
|
+
*/
|
|
206
|
+
async syncDecision(referenceId, status, receivedAt) {
|
|
207
|
+
const riskSignals = this.kycAdapter.resolveRiskSignals
|
|
208
|
+
? await this.kycAdapter.resolveRiskSignals(referenceId)
|
|
209
|
+
: undefined;
|
|
210
|
+
if (!this.kycAdapter.resolveDecision) {
|
|
211
|
+
return this.reconcile(referenceId, status, { riskSignals, receivedAt });
|
|
212
|
+
}
|
|
213
|
+
const decision = await this.kycAdapter.resolveDecision(referenceId);
|
|
214
|
+
return this.reconcile(referenceId, decision.status, {
|
|
215
|
+
reason: decision.decisionReason,
|
|
216
|
+
documentTypes: decision.documentTypes,
|
|
217
|
+
checks: decision.checks,
|
|
218
|
+
riskSignals,
|
|
219
|
+
receivedAt,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
106
222
|
async getForPlayer(userId) {
|
|
107
223
|
const rows = await this.drizzle.db
|
|
108
224
|
.select()
|
|
@@ -112,9 +228,9 @@ export class KycVerificationService {
|
|
|
112
228
|
return { current: rows[0] ? toDto(rows[0]) : null, history: rows.map(toDto) };
|
|
113
229
|
}
|
|
114
230
|
/**
|
|
115
|
-
* Deposit-event hook: flips a currently-
|
|
231
|
+
* Deposit-event hook: flips a currently-approved player to `resubmission_requested`
|
|
116
232
|
* once cumulative deposits cross a fresh per-currency threshold band. Idempotent twice
|
|
117
|
-
* over: skips unless presently
|
|
233
|
+
* over: skips unless presently approved, and the watermark stops a re-approved
|
|
118
234
|
* high-roller re-firing on every later deposit.
|
|
119
235
|
*/
|
|
120
236
|
async handleDeposit(userId) {
|
|
@@ -122,7 +238,7 @@ export class KycVerificationService {
|
|
|
122
238
|
.select({ currency: player.currency, kycStatus: player.kycStatus })
|
|
123
239
|
.from(player)
|
|
124
240
|
.where(eq(player.userId, userId));
|
|
125
|
-
if (!current || current.kycStatus !== '
|
|
241
|
+
if (!current || normalizeKycStatus(current.kycStatus) !== 'approved') {
|
|
126
242
|
return;
|
|
127
243
|
}
|
|
128
244
|
const [deposited] = await this.drizzle.db
|
|
@@ -164,5 +280,121 @@ export class KycVerificationService {
|
|
|
164
280
|
});
|
|
165
281
|
this.events.emit('compliance.kyc.reverify_required', { userId, reason });
|
|
166
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Shared transaction body for `requestResubmission`/`overrideStatus`: inserts the
|
|
285
|
+
* manual `kyc_verification` history row and calls `KYC_STATUS_WRITER.setStatus` in
|
|
286
|
+
* the SAME transaction (extracted so the two documented locking/idempotency rules
|
|
287
|
+
* in `compliance/AGENTS.md` > Admin KYC actions cannot silently diverge between the
|
|
288
|
+
* two call sites). Caller has already run the idempotency pre-check
|
|
289
|
+
* (`requirePlayerRowForUpdate` + a status compare) before calling this.
|
|
290
|
+
*/
|
|
291
|
+
async applyManualDecision(trx, params) {
|
|
292
|
+
const inserted = findOneOrThrow(await trx
|
|
293
|
+
.insert(kycVerification)
|
|
294
|
+
.values({
|
|
295
|
+
userId: params.userId,
|
|
296
|
+
provider: MANUAL_PROVIDER,
|
|
297
|
+
referenceId: `${params.referenceIdPrefix}-${params.userId}-${Date.now()}`,
|
|
298
|
+
status: params.status,
|
|
299
|
+
documentTypes: [],
|
|
300
|
+
triggeredBy: 'manual',
|
|
301
|
+
decisionReason: params.reason,
|
|
302
|
+
decidedAt: params.decidedAt,
|
|
303
|
+
})
|
|
304
|
+
.returning(), new KycVerificationNotFoundError(params.userId));
|
|
305
|
+
await this.statusWriter.setStatus(params.userId, params.status, { actorId: params.actorId, reason: params.reason, source: 'manual' }, trx);
|
|
306
|
+
return toDto(inserted);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Admin-initiated: requests the player resubmit documents. Idempotent on a repeat
|
|
310
|
+
* call while the player is already at `resubmission_requested` - no duplicate history
|
|
311
|
+
* row, no duplicate `compliance.kyc.updated` emit. See `compliance/AGENTS.md` >
|
|
312
|
+
* Admin KYC actions for why the `FOR UPDATE` lock has to sit inside the transaction.
|
|
313
|
+
*/
|
|
314
|
+
async requestResubmission(userId, reason, actorId) {
|
|
315
|
+
return this.drizzle.db.transaction(async (trx) => {
|
|
316
|
+
const current = await this.requirePlayerRowForUpdate(userId, trx);
|
|
317
|
+
if (normalizeKycStatus(current.kycStatus) === 'resubmission_requested') {
|
|
318
|
+
return toDto(await this.latestVerificationOrThrow(userId, trx));
|
|
319
|
+
}
|
|
320
|
+
return this.applyManualDecision(trx, {
|
|
321
|
+
userId,
|
|
322
|
+
status: 'resubmission_requested',
|
|
323
|
+
reason,
|
|
324
|
+
actorId,
|
|
325
|
+
referenceIdPrefix: 'manual-resubmit',
|
|
326
|
+
decidedAt: null,
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Admin-initiated: forces the player to an operator-chosen status, guarded by the
|
|
332
|
+
* router's `compliance:override-limit`. `resolveManualStatus` remaps an `approved`
|
|
333
|
+
* choice to `manually_overridden`; every other choice is written verbatim. Idempotent
|
|
334
|
+
* on a repeat call resolving to the status the player already holds. Rationale for
|
|
335
|
+
* both: `compliance/AGENTS.md` > Admin KYC actions.
|
|
336
|
+
*/
|
|
337
|
+
async overrideStatus(userId, status, reason, actorId) {
|
|
338
|
+
const resolvedStatus = resolveManualStatus(status);
|
|
339
|
+
return this.drizzle.db.transaction(async (trx) => {
|
|
340
|
+
const current = await this.requirePlayerRowForUpdate(userId, trx);
|
|
341
|
+
if (normalizeKycStatus(current.kycStatus) === normalizeKycStatus(resolvedStatus)) {
|
|
342
|
+
return toDto(await this.latestVerificationOrThrow(userId, trx));
|
|
343
|
+
}
|
|
344
|
+
return this.applyManualDecision(trx, {
|
|
345
|
+
userId,
|
|
346
|
+
status: resolvedStatus,
|
|
347
|
+
reason,
|
|
348
|
+
actorId,
|
|
349
|
+
referenceIdPrefix: 'manual-override',
|
|
350
|
+
decidedAt: isDecided(resolvedStatus) ? new Date() : null,
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Admin-initiated: approves every listed player through the same
|
|
356
|
+
* `overrideStatus('approved', ...)` path, so a bulk approval is recorded identically
|
|
357
|
+
* to a single one. Bounded fan-out with per-player error isolation - one failure is
|
|
358
|
+
* captured as a result row rather than aborting the batch, and the returned message
|
|
359
|
+
* is a fixed string, never the raw exception text (`compliance/AGENTS.md`).
|
|
360
|
+
*/
|
|
361
|
+
async bulkApprove(userIds, reason, actorId) {
|
|
362
|
+
return mapConcurrent(userIds, BULK_APPROVE_CONCURRENCY, async (userId) => {
|
|
363
|
+
try {
|
|
364
|
+
await this.overrideStatus(userId, 'approved', reason, actorId);
|
|
365
|
+
return { userId, success: true, error: null };
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
// Fixed string on the wire, regardless of error type - never the raw exception
|
|
369
|
+
// text (it can carry the userId, eg PlayerNotFoundError, or a driver/constraint
|
|
370
|
+
// message). The distinction itself ("not found" vs some other failure) is
|
|
371
|
+
// already an existence oracle over an id an admin didn't otherwise confirm; the
|
|
372
|
+
// full error is logged server-side (with userId) for ops.
|
|
373
|
+
logger.error({ err, userId }, 'bulk KYC approve failed for player');
|
|
374
|
+
return { userId, success: false, error: 'Failed to approve KYC' };
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
async requirePlayerRowForUpdate(userId, tx) {
|
|
379
|
+
const rows = await tx
|
|
380
|
+
.select({ kycStatus: player.kycStatus })
|
|
381
|
+
.from(player)
|
|
382
|
+
.where(eq(player.userId, userId))
|
|
383
|
+
.for('update');
|
|
384
|
+
const row = rows[0];
|
|
385
|
+
if (!row) {
|
|
386
|
+
throw new PlayerNotFoundError(userId);
|
|
387
|
+
}
|
|
388
|
+
return row;
|
|
389
|
+
}
|
|
390
|
+
async latestVerificationOrThrow(userId, tx = this.drizzle.db) {
|
|
391
|
+
const rows = await tx
|
|
392
|
+
.select()
|
|
393
|
+
.from(kycVerification)
|
|
394
|
+
.where(eq(kycVerification.userId, userId))
|
|
395
|
+
.orderBy(desc(kycVerification.createdAt))
|
|
396
|
+
.limit(1);
|
|
397
|
+
return findOneOrThrow(rows, new KycVerificationNotFoundError(userId));
|
|
398
|
+
}
|
|
167
399
|
}
|
|
168
400
|
//# sourceMappingURL=kyc.service.js.map
|