From 8a376dfa53efaab83cf80944c1d2c17ed8742d20 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Mon, 24 Aug 2026 20:52:58 +0700 Subject: [PATCH] Create demo KYC pricing contexts --- graphql-contracts | 2 +- src/schemas/m2m.ts | 90 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/graphql-contracts b/graphql-contracts index 1932411..2080dce 160000 --- a/graphql-contracts +++ b/graphql-contracts @@ -1 +1 @@ -Subproject commit 1932411ebf923a196f1f9bcf51cea40d8b7f76f1 +Subproject commit 2080dce5b4851b474cead1b2dc3f95882019bbf8 diff --git a/src/schemas/m2m.ts b/src/schemas/m2m.ts index ec943b9..c16a2c1 100644 --- a/src/schemas/m2m.ts +++ b/src/schemas/m2m.ts @@ -1,3 +1,4 @@ +import { createHash } from 'node:crypto' import { prisma } from '../db.js' export const m2mTypeDefs = `#graphql @@ -22,9 +23,47 @@ export const m2mTypeDefs = `#graphql type Mutation { createKycProfile(kycApplicationId: String!): CreateKycProfileResult createKycMonitoring(kycApplicationId: String!): CreateKycProfileResult + ensureDemoPricingContextForUser(userId: String!, countryCode: String!): KycPricingContext! } ` +function demoProfileUuid(userId: string): string { + const hex = createHash('sha256') + .update(`kyc:demo-pricing-context:${userId}`) + .digest('hex') + .slice(0, 32) + .split('') + hex[12] = '4' + hex[16] = '8' + const value = hex.join('') + return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}` +} + +function normalizedCountryCode(countryCode: string): string { + const normalized = countryCode.trim().toUpperCase() + if (!/^[A-Z]{2}$/.test(normalized)) { + throw new Error('Demo pricing country code must contain two letters') + } + return normalized +} + +function mapPricingContext(profile: { + userId: string + uuid: string + countryCode: string + updatedAt: Date +}) { + if (profile.countryCode.length !== 2) { + throw new Error(`KYC pricing profile ${profile.uuid} has no valid country code`) + } + return { + userId: profile.userId, + profileUuid: profile.uuid, + countryCode: profile.countryCode.toUpperCase(), + resolvedAt: profile.updatedAt.toISOString(), + } +} + async function createProfile(_: unknown, args: { kycApplicationId: string }) { try { const app = await prisma.kYCApplication.findUnique({ @@ -72,7 +111,7 @@ async function pricingContextForUser(_: unknown, args: { userId: string }) { where: { userId: args.userId, workflowStatus: { - in: ['active', 'approved'], + in: ['active', 'approved', 'demo'], }, }, orderBy: { @@ -80,15 +119,45 @@ async function pricingContextForUser(_: unknown, args: { userId: string }) { }, }) if (profile === null) return null - if (profile.countryCode.length !== 2) { - throw new Error(`Active KYC profile ${profile.uuid} has no valid country code`) - } - return { - userId: profile.userId, - profileUuid: profile.uuid, - countryCode: profile.countryCode.toUpperCase(), - resolvedAt: profile.updatedAt.toISOString(), - } + return mapPricingContext(profile) +} + +async function ensureDemoPricingContextForUser( + _: unknown, + args: { userId: string; countryCode: string }, +) { + const existing = await prisma.kYCProfile.findFirst({ + where: { + userId: args.userId, + workflowStatus: { + in: ['active', 'approved', 'demo'], + }, + }, + orderBy: { + updatedAt: 'desc', + }, + }) + if (existing !== null) return mapPricingContext(existing) + + const countryCode = normalizedCountryCode(args.countryCode) + const profileUuid = demoProfileUuid(args.userId) + const profile = await prisma.kYCProfile.upsert({ + where: { uuid: profileUuid }, + create: { + uuid: profileUuid, + userId: args.userId, + teamName: 'Demo workspace', + countryCode, + workflowStatus: 'demo', + }, + update: { + userId: args.userId, + teamName: 'Demo workspace', + countryCode, + workflowStatus: 'demo', + }, + }) + return mapPricingContext(profile) } export const m2mResolvers = { @@ -99,5 +168,6 @@ export const m2mResolvers = { Mutation: { createKycProfile: createProfile, createKycMonitoring: createProfile, + ensureDemoPricingContextForUser, }, }