@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.
Files changed (248) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/admin-console/contract/index.d.ts +2 -0
  3. package/dist/admin-console/contract/index.d.ts.map +1 -1
  4. package/dist/admin-console/router/index.d.ts +1 -0
  5. package/dist/admin-console/router/index.d.ts.map +1 -1
  6. package/dist/admin-console/service/backoffice.service.d.ts +1 -1
  7. package/dist/audit/plugin.d.ts.map +1 -1
  8. package/dist/audit/plugin.js +48 -1
  9. package/dist/audit/plugin.js.map +1 -1
  10. package/dist/common/errors/errors.classes.js +3 -3
  11. package/dist/common/errors/errors.classes.js.map +1 -1
  12. package/dist/common/errors/errors.mapper.js +1 -1
  13. package/dist/common/errors/errors.mapper.js.map +1 -1
  14. package/dist/compliance/__tests__/hmac-kyc-webhook-verifier.test.js +66 -12
  15. package/dist/compliance/__tests__/hmac-kyc-webhook-verifier.test.js.map +1 -1
  16. package/dist/compliance/__tests__/kyc-admin.router.test.d.ts +2 -0
  17. package/dist/compliance/__tests__/kyc-admin.router.test.d.ts.map +1 -0
  18. package/dist/compliance/__tests__/kyc-admin.router.test.js +191 -0
  19. package/dist/compliance/__tests__/kyc-admin.router.test.js.map +1 -0
  20. package/dist/compliance/__tests__/kyc-webhook.router.test.d.ts +2 -0
  21. package/dist/compliance/__tests__/kyc-webhook.router.test.d.ts.map +1 -0
  22. package/dist/compliance/__tests__/kyc-webhook.router.test.js +148 -0
  23. package/dist/compliance/__tests__/kyc-webhook.router.test.js.map +1 -0
  24. package/dist/compliance/__tests__/kyc.service.test.js +16 -10
  25. package/dist/compliance/__tests__/kyc.service.test.js.map +1 -1
  26. package/dist/compliance/__tests__/rg.router.test.js +5 -1
  27. package/dist/compliance/__tests__/rg.router.test.js.map +1 -1
  28. package/dist/compliance/adapters/hmac-kyc-webhook-verifier.d.ts +16 -3
  29. package/dist/compliance/adapters/hmac-kyc-webhook-verifier.d.ts.map +1 -1
  30. package/dist/compliance/adapters/hmac-kyc-webhook-verifier.js +47 -5
  31. package/dist/compliance/adapters/hmac-kyc-webhook-verifier.js.map +1 -1
  32. package/dist/compliance/contract/index.d.ts +312 -0
  33. package/dist/compliance/contract/index.d.ts.map +1 -1
  34. package/dist/compliance/contract/index.js +50 -1
  35. package/dist/compliance/contract/index.js.map +1 -1
  36. package/dist/compliance/drizzle/migrations/0002_condemned_monster_badoon.sql +3 -0
  37. package/dist/compliance/drizzle/migrations/0003_public_spirit.sql +1 -0
  38. package/dist/compliance/drizzle/migrations/meta/0002_snapshot.json +673 -0
  39. package/dist/compliance/drizzle/migrations/meta/0003_snapshot.json +679 -0
  40. package/dist/compliance/drizzle/migrations/meta/_journal.json +14 -0
  41. package/dist/compliance/plugin.d.ts.map +1 -1
  42. package/dist/compliance/plugin.js +23 -3
  43. package/dist/compliance/plugin.js.map +1 -1
  44. package/dist/compliance/router/index.d.ts +186 -2
  45. package/dist/compliance/router/index.d.ts.map +1 -1
  46. package/dist/compliance/router/index.js +52 -3
  47. package/dist/compliance/router/index.js.map +1 -1
  48. package/dist/compliance/schema/index.d.ts +65 -3
  49. package/dist/compliance/schema/index.d.ts.map +1 -1
  50. package/dist/compliance/schema/index.js +8 -0
  51. package/dist/compliance/schema/index.js.map +1 -1
  52. package/dist/compliance/service/kyc.service.d.ts +153 -8
  53. package/dist/compliance/service/kyc.service.d.ts.map +1 -1
  54. package/dist/compliance/service/kyc.service.js +264 -32
  55. package/dist/compliance/service/kyc.service.js.map +1 -1
  56. package/dist/contracts/adapters/audit.d.ts +1 -1
  57. package/dist/contracts/adapters/audit.d.ts.map +1 -1
  58. package/dist/contracts/adapters/audit.js.map +1 -1
  59. package/dist/contracts/adapters/identity-reader.d.ts +9 -3
  60. package/dist/contracts/adapters/identity-reader.d.ts.map +1 -1
  61. package/dist/contracts/adapters/identity-reader.js.map +1 -1
  62. package/dist/contracts/adapters/index.d.ts +4 -2
  63. package/dist/contracts/adapters/index.d.ts.map +1 -1
  64. package/dist/contracts/adapters/index.js +2 -1
  65. package/dist/contracts/adapters/index.js.map +1 -1
  66. package/dist/contracts/adapters/kyc.d.ts +90 -5
  67. package/dist/contracts/adapters/kyc.d.ts.map +1 -1
  68. package/dist/contracts/adapters/kyc.js +23 -0
  69. package/dist/contracts/adapters/kyc.js.map +1 -1
  70. package/dist/contracts/adapters/tag-evaluation-commands.d.ts +10 -0
  71. package/dist/contracts/adapters/tag-evaluation-commands.d.ts.map +1 -0
  72. package/dist/contracts/adapters/tag-evaluation-commands.js +11 -0
  73. package/dist/contracts/adapters/tag-evaluation-commands.js.map +1 -0
  74. package/dist/contracts/adapters/wallet-reader.d.ts +8 -0
  75. package/dist/contracts/adapters/wallet-reader.d.ts.map +1 -1
  76. package/dist/contracts/adapters/wallet-reader.js.map +1 -1
  77. package/dist/contracts/schemas/__tests__/events.test.js +1 -1
  78. package/dist/contracts/schemas/__tests__/player-schemas.test.d.ts +2 -0
  79. package/dist/contracts/schemas/__tests__/player-schemas.test.d.ts.map +1 -0
  80. package/dist/contracts/schemas/__tests__/player-schemas.test.js +30 -0
  81. package/dist/contracts/schemas/__tests__/player-schemas.test.js.map +1 -0
  82. package/dist/contracts/schemas/events.d.ts +84 -0
  83. package/dist/contracts/schemas/events.d.ts.map +1 -1
  84. package/dist/contracts/schemas/events.js +24 -2
  85. package/dist/contracts/schemas/events.js.map +1 -1
  86. package/dist/contracts/schemas/platform-config.d.ts +13 -1
  87. package/dist/contracts/schemas/platform-config.d.ts.map +1 -1
  88. package/dist/contracts/schemas/player.d.ts +36 -3
  89. package/dist/contracts/schemas/player.d.ts.map +1 -1
  90. package/dist/contracts/schemas/player.js +14 -0
  91. package/dist/contracts/schemas/player.js.map +1 -1
  92. package/dist/contracts/schemas/tag.d.ts +85 -1
  93. package/dist/contracts/schemas/tag.d.ts.map +1 -1
  94. package/dist/contracts/schemas/tag.js +12 -0
  95. package/dist/contracts/schemas/tag.js.map +1 -1
  96. package/dist/engagement/notifications/contract/index.d.ts +5 -1
  97. package/dist/engagement/notifications/contract/index.d.ts.map +1 -1
  98. package/dist/engagement/notifications/contract/index.js +5 -1
  99. package/dist/engagement/notifications/contract/index.js.map +1 -1
  100. package/dist/engagement/notifications/plugin.d.ts.map +1 -1
  101. package/dist/engagement/notifications/plugin.js +43 -3
  102. package/dist/engagement/notifications/plugin.js.map +1 -1
  103. package/dist/engagement/notifications/router/index.d.ts +1 -0
  104. package/dist/engagement/notifications/router/index.d.ts.map +1 -1
  105. package/dist/iam/service/iam.service.d.ts +1 -1
  106. package/dist/pam/identity/__tests__/admin-user-directory.test.js +1 -1
  107. package/dist/pam/identity/adapters/identity-reader.service.d.ts +6 -4
  108. package/dist/pam/identity/adapters/identity-reader.service.d.ts.map +1 -1
  109. package/dist/pam/identity/adapters/identity-reader.service.js +19 -1
  110. package/dist/pam/identity/adapters/identity-reader.service.js.map +1 -1
  111. package/dist/pam/identity/admin-user-directory.d.ts +1 -1
  112. package/dist/pam/identity/admin-user-directory.d.ts.map +1 -1
  113. package/dist/pam/identity/admin-user-directory.js +6 -3
  114. package/dist/pam/identity/admin-user-directory.js.map +1 -1
  115. package/dist/pam/identity/service/identity.service.d.ts.map +1 -1
  116. package/dist/pam/identity/service/identity.service.js +4 -0
  117. package/dist/pam/identity/service/identity.service.js.map +1 -1
  118. package/dist/pam/player-management/__tests__/kyc-status-writer.test.js +5 -2
  119. package/dist/pam/player-management/__tests__/kyc-status-writer.test.js.map +1 -1
  120. package/dist/pam/player-management/__tests__/player.router.test.js +27 -20
  121. package/dist/pam/player-management/__tests__/player.router.test.js.map +1 -1
  122. package/dist/pam/player-management/__tests__/player.service.test.js +30 -31
  123. package/dist/pam/player-management/__tests__/player.service.test.js.map +1 -1
  124. package/dist/pam/player-management/contract/index.d.ts +37 -9
  125. package/dist/pam/player-management/contract/index.d.ts.map +1 -1
  126. package/dist/pam/player-management/contract/index.js +0 -1
  127. package/dist/pam/player-management/contract/index.js.map +1 -1
  128. package/dist/pam/player-management/plugin.d.ts.map +1 -1
  129. package/dist/pam/player-management/plugin.js +3 -2
  130. package/dist/pam/player-management/plugin.js.map +1 -1
  131. package/dist/pam/player-management/router/index.d.ts +32 -10
  132. package/dist/pam/player-management/router/index.d.ts.map +1 -1
  133. package/dist/pam/player-management/router/index.js +48 -16
  134. package/dist/pam/player-management/router/index.js.map +1 -1
  135. package/dist/pam/player-management/service/kyc-status-writer.d.ts +3 -2
  136. package/dist/pam/player-management/service/kyc-status-writer.d.ts.map +1 -1
  137. package/dist/pam/player-management/service/kyc-status-writer.js +24 -10
  138. package/dist/pam/player-management/service/kyc-status-writer.js.map +1 -1
  139. package/dist/pam/player-management/service/player.service.d.ts +10 -9
  140. package/dist/pam/player-management/service/player.service.d.ts.map +1 -1
  141. package/dist/pam/player-management/service/player.service.js +24 -12
  142. package/dist/pam/player-management/service/player.service.js.map +1 -1
  143. package/dist/pam/profile/contract/index.d.ts +2 -0
  144. package/dist/pam/profile/contract/index.d.ts.map +1 -1
  145. package/dist/pam/profile/drizzle/migrations/0001_powerful_tiger_shark.sql +1 -0
  146. package/dist/pam/profile/drizzle/migrations/meta/0001_snapshot.json +191 -0
  147. package/dist/pam/profile/drizzle/migrations/meta/_journal.json +7 -0
  148. package/dist/pam/profile/react/profile.d.ts +2 -2
  149. package/dist/pam/profile/router/index.d.ts +2 -0
  150. package/dist/pam/profile/router/index.d.ts.map +1 -1
  151. package/dist/pam/profile/schema/index.d.ts +3 -3
  152. package/dist/pam/profile/schema/index.d.ts.map +1 -1
  153. package/dist/pam/profile/service/profile.service.d.ts +2 -2
  154. package/dist/pam/shared/player-mapper.d.ts +1 -1
  155. package/dist/pam/tag/__tests__/tag-evaluation.service.test.js +619 -19
  156. package/dist/pam/tag/__tests__/tag-evaluation.service.test.js.map +1 -1
  157. package/dist/pam/tag/__tests__/tag-mappers.test.js +25 -0
  158. package/dist/pam/tag/__tests__/tag-mappers.test.js.map +1 -1
  159. package/dist/pam/tag/__tests__/tag.router.test.js +71 -1
  160. package/dist/pam/tag/__tests__/tag.router.test.js.map +1 -1
  161. package/dist/pam/tag/__tests__/tag.service.test.js +342 -15
  162. package/dist/pam/tag/__tests__/tag.service.test.js.map +1 -1
  163. package/dist/pam/tag/contract/index.d.ts +123 -0
  164. package/dist/pam/tag/contract/index.d.ts.map +1 -1
  165. package/dist/pam/tag/contract/index.js +3 -0
  166. package/dist/pam/tag/contract/index.js.map +1 -1
  167. package/dist/pam/tag/contract/player-tag-assign-metadata.d.ts +37 -0
  168. package/dist/pam/tag/contract/player-tag-assign-metadata.d.ts.map +1 -0
  169. package/dist/pam/tag/contract/player-tag-assign-metadata.js +20 -0
  170. package/dist/pam/tag/contract/player-tag-assign-metadata.js.map +1 -0
  171. package/dist/pam/tag/drizzle/migrations/0002_silent_skaar.sql +4 -0
  172. package/dist/pam/tag/drizzle/migrations/0003_overconfident_guardian.sql +3 -0
  173. package/dist/pam/tag/drizzle/migrations/0004_curvy_wither.sql +2 -0
  174. package/dist/pam/tag/drizzle/migrations/meta/0002_snapshot.json +335 -0
  175. package/dist/pam/tag/drizzle/migrations/meta/0003_snapshot.json +343 -0
  176. package/dist/pam/tag/drizzle/migrations/meta/0004_snapshot.json +345 -0
  177. package/dist/pam/tag/drizzle/migrations/meta/_journal.json +21 -0
  178. package/dist/pam/tag/migrate.d.ts.map +1 -1
  179. package/dist/pam/tag/migrate.js +34 -0
  180. package/dist/pam/tag/migrate.js.map +1 -1
  181. package/dist/pam/tag/plugin.d.ts.map +1 -1
  182. package/dist/pam/tag/plugin.js +43 -16
  183. package/dist/pam/tag/plugin.js.map +1 -1
  184. package/dist/pam/tag/router/index.d.ts +105 -0
  185. package/dist/pam/tag/router/index.d.ts.map +1 -1
  186. package/dist/pam/tag/router/index.js +34 -18
  187. package/dist/pam/tag/router/index.js.map +1 -1
  188. package/dist/pam/tag/schema/index.d.ts +42 -3
  189. package/dist/pam/tag/schema/index.d.ts.map +1 -1
  190. package/dist/pam/tag/schema/index.js +13 -2
  191. package/dist/pam/tag/schema/index.js.map +1 -1
  192. package/dist/pam/tag/seed/data/default-tag-rules.d.ts.map +1 -1
  193. package/dist/pam/tag/seed/data/default-tag-rules.js +42 -0
  194. package/dist/pam/tag/seed/data/default-tag-rules.js.map +1 -1
  195. package/dist/pam/tag/seed/data/default-tags.d.ts.map +1 -1
  196. package/dist/pam/tag/seed/data/default-tags.js +6 -0
  197. package/dist/pam/tag/seed/data/default-tags.js.map +1 -1
  198. package/dist/pam/tag/service/tag-evaluation.service.d.ts +141 -16
  199. package/dist/pam/tag/service/tag-evaluation.service.d.ts.map +1 -1
  200. package/dist/pam/tag/service/tag-evaluation.service.js +472 -47
  201. package/dist/pam/tag/service/tag-evaluation.service.js.map +1 -1
  202. package/dist/pam/tag/service/tag-mappers.d.ts +1 -1
  203. package/dist/pam/tag/service/tag-mappers.d.ts.map +1 -1
  204. package/dist/pam/tag/service/tag-mappers.js +1 -0
  205. package/dist/pam/tag/service/tag-mappers.js.map +1 -1
  206. package/dist/pam/tag/service/tag-rule.service.d.ts +3 -3
  207. package/dist/pam/tag/service/tag.service.d.ts +141 -11
  208. package/dist/pam/tag/service/tag.service.d.ts.map +1 -1
  209. package/dist/pam/tag/service/tag.service.js +242 -23
  210. package/dist/pam/tag/service/tag.service.js.map +1 -1
  211. package/dist/server/auth/permissions.d.ts +12 -0
  212. package/dist/server/auth/permissions.d.ts.map +1 -1
  213. package/dist/server/auth/permissions.js +2 -0
  214. package/dist/server/auth/permissions.js.map +1 -1
  215. package/dist/server/db/__tests__/migrate.test.js +79 -2
  216. package/dist/server/db/__tests__/migrate.test.js.map +1 -1
  217. package/dist/server/db/migrate.d.ts +11 -0
  218. package/dist/server/db/migrate.d.ts.map +1 -1
  219. package/dist/server/db/migrate.js +3 -0
  220. package/dist/server/db/migrate.js.map +1 -1
  221. package/dist/server/kernel/bullmq-job-queue.d.ts.map +1 -1
  222. package/dist/server/kernel/bullmq-job-queue.js +17 -0
  223. package/dist/server/kernel/bullmq-job-queue.js.map +1 -1
  224. package/dist/server/plugin-host/__tests__/core-plugins.test.js +1 -1
  225. package/dist/server/plugin-host/__tests__/core-plugins.test.js.map +1 -1
  226. package/dist/server/plugin-host/define-plugin.d.ts +2 -2
  227. package/dist/server/plugin-host/define-plugin.d.ts.map +1 -1
  228. package/dist/server/plugin-host/define-plugin.js.map +1 -1
  229. package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js +60 -0
  230. package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js.map +1 -1
  231. package/dist/wallet/__tests__/wallet.service.test.js +59 -0
  232. package/dist/wallet/__tests__/wallet.service.test.js.map +1 -1
  233. package/dist/wallet/adapters/wallet-reader.service.d.ts +1 -0
  234. package/dist/wallet/adapters/wallet-reader.service.d.ts.map +1 -1
  235. package/dist/wallet/adapters/wallet-reader.service.js +18 -1
  236. package/dist/wallet/adapters/wallet-reader.service.js.map +1 -1
  237. package/dist/wallet/contract/index.d.ts +4 -0
  238. package/dist/wallet/contract/index.d.ts.map +1 -1
  239. package/dist/wallet/plugin.d.ts.map +1 -1
  240. package/dist/wallet/plugin.js +7 -4
  241. package/dist/wallet/plugin.js.map +1 -1
  242. package/dist/wallet/router/index.d.ts +2 -0
  243. package/dist/wallet/router/index.d.ts.map +1 -1
  244. package/dist/wallet/service/wallet.service.d.ts +5 -4
  245. package/dist/wallet/service/wallet.service.d.ts.map +1 -1
  246. package/dist/wallet/service/wallet.service.js +28 -8
  247. package/dist/wallet/service/wallet.service.js.map +1 -1
  248. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/compliance/schema/index.ts"],"names":[],"mappings":"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"}
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 { KycAdapter, KycStatusWriter, KycVendorStatus, PlatformConfig, User, ClientMeta } from '@openora/core/contracts';
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, reason?: string): Promise<import("@openora/core/server").SerializedRow<{
49
- status: "pending" | "not_started" | "verified" | "rejected" | "resubmission_requested" | "manually_overridden";
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-verified player to `resubmission_requested`
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 `verified`, and the watermark stops a re-verified
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,EAId,KAAK,QAAQ,EACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EACf,eAAe,EAEf,cAAc,EACd,IAAI,EACJ,UAAU,EACX,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAiC,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIvF,eAAO,MAAM,4BAA4B,2BAAuC,CAAC;AAyBjF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,eAAe,CAAC;IAC9B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;gBAEhC,IAAI,EAAE,mBAAmB;IASrC,OAAO,KAAK,QAAQ,GAEnB;IAEK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,UAAU;;;;;;;;;;;;;;;;IAiCzE;;;OAGG;IAEG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;IAgC7E,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAS9D;;;;;OAKG;IACG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;CA+DvC"}
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 'verified';
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
- return status === 'verified' || status === 'rejected';
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 status = mapVendorStatus(result.status);
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 = findOneOrThrow(await this.drizzle.db
54
- .insert(kycVerification)
55
- .values({
56
- userId,
57
- provider: this.provider,
58
- referenceId: result.referenceId,
59
- status,
60
- documentTypes: input.documents.map((d) => d.type),
61
- triggeredBy: 'submission',
62
- decidedAt: decided ? new Date() : null,
63
- })
64
- .returning(), new KycVerificationNotFoundError(userId));
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, reason) {
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 status = mapVendorStatus(vendorStatus);
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
- const row = findOneOrThrow(await this.drizzle.db
95
- .update(kycVerification)
96
- .set({ status, decisionReason: reason ?? null, decidedAt: new Date() })
97
- .where(eq(kycVerification.id, existing.id))
98
- .returning(), new KycVerificationNotFoundError(existing.id));
99
- await this.statusWriter.setStatus(existing.userId, status, {
100
- actorId: null,
101
- source: 'webhook',
102
- reason,
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-verified player to `resubmission_requested`
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 `verified`, and the watermark stops a re-verified
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 !== 'verified') {
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