Add new payment service with repository, error handling, and validation Add exceptions package with database and validation utilities Update database package with new schema and zod types Add turbo start script for development
193 lines
6.5 KiB
TypeScript
193 lines
6.5 KiB
TypeScript
import { z } from 'zod';
|
|
import { Prisma } from '../prisma';
|
|
|
|
/////////////////////////////////////////
|
|
// HELPER FUNCTIONS
|
|
/////////////////////////////////////////
|
|
|
|
// JSON
|
|
//------------------------------------------------------
|
|
|
|
export type NullableJsonInput = Prisma.JsonValue | null | 'JsonNull' | 'DbNull' | Prisma.NullTypes.DbNull | Prisma.NullTypes.JsonNull;
|
|
|
|
export const transformJsonNull = (v?: NullableJsonInput) => {
|
|
if (!v || v === 'DbNull') return Prisma.DbNull;
|
|
if (v === 'JsonNull') return Prisma.JsonNull;
|
|
return v;
|
|
};
|
|
|
|
export const JsonValueSchema: z.ZodType<Prisma.JsonValue> = z.lazy(() =>
|
|
z.union([
|
|
z.string(),
|
|
z.number(),
|
|
z.boolean(),
|
|
z.literal(null),
|
|
z.record(z.lazy(() => JsonValueSchema.optional())),
|
|
z.array(z.lazy(() => JsonValueSchema)),
|
|
])
|
|
);
|
|
|
|
export type JsonValueType = z.infer<typeof JsonValueSchema>;
|
|
|
|
export const NullableJsonValue = z
|
|
.union([JsonValueSchema, z.literal('DbNull'), z.literal('JsonNull')])
|
|
.nullable()
|
|
.transform((v) => transformJsonNull(v));
|
|
|
|
export type NullableJsonValueType = z.infer<typeof NullableJsonValue>;
|
|
|
|
export const InputJsonValueSchema: z.ZodType<Prisma.InputJsonValue> = z.lazy(() =>
|
|
z.union([
|
|
z.string(),
|
|
z.number(),
|
|
z.boolean(),
|
|
z.object({ toJSON: z.function(z.tuple([]), z.any()) }),
|
|
z.record(z.lazy(() => z.union([InputJsonValueSchema, z.literal(null)]))),
|
|
z.array(z.lazy(() => z.union([InputJsonValueSchema, z.literal(null)]))),
|
|
])
|
|
);
|
|
|
|
export type InputJsonValueType = z.infer<typeof InputJsonValueSchema>;
|
|
|
|
// DECIMAL
|
|
//------------------------------------------------------
|
|
|
|
export const DecimalJsLikeSchema: z.ZodType<Prisma.DecimalJsLike> = z.object({
|
|
d: z.array(z.number()),
|
|
e: z.number(),
|
|
s: z.number(),
|
|
toFixed: z.function(z.tuple([]), z.string()),
|
|
})
|
|
|
|
export const DECIMAL_STRING_REGEX = /^(?:-?Infinity|NaN|-?(?:0[bB][01]+(?:\.[01]+)?(?:[pP][-+]?\d+)?|0[oO][0-7]+(?:\.[0-7]+)?(?:[pP][-+]?\d+)?|0[xX][\da-fA-F]+(?:\.[\da-fA-F]+)?(?:[pP][-+]?\d+)?|(?:\d+|\d*\.\d+)(?:[eE][-+]?\d+)?))$/;
|
|
|
|
export const isValidDecimalInput =
|
|
(v?: null | string | number | Prisma.DecimalJsLike): v is string | number | Prisma.DecimalJsLike => {
|
|
if (v === undefined || v === null) return false;
|
|
return (
|
|
(typeof v === 'object' && 'd' in v && 'e' in v && 's' in v && 'toFixed' in v) ||
|
|
(typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) ||
|
|
typeof v === 'number'
|
|
)
|
|
};
|
|
|
|
/////////////////////////////////////////
|
|
// ENUMS
|
|
/////////////////////////////////////////
|
|
|
|
export const TransactionIsolationLevelSchema = z.enum(['ReadUncommitted','ReadCommitted','RepeatableRead','Serializable']);
|
|
|
|
export const TelegramSessionScalarFieldEnumSchema = z.enum(['id','data','createdAt','updatedAt']);
|
|
|
|
export const TelegramUserScalarFieldEnumSchema = z.enum(['id','username','isBlocked','firstName','lastName','languageCode','isPremium','createdAt','updatedAt']);
|
|
|
|
export const PaymentScalarFieldEnumSchema = z.enum(['id','telegramUserId','amount','currency','status','provider','providerId','metadata','createdAt','updatedAt']);
|
|
|
|
export const PlanScalarFieldEnumSchema = z.enum(['id','code','name','description','durationDays','dailyGenerations','price','currency']);
|
|
|
|
export const SubscriptionScalarFieldEnumSchema = z.enum(['id','telegramUserId','planId','startedAt','expiresAt','canceledAt','createdAt','updatedAt']);
|
|
|
|
export const SortOrderSchema = z.enum(['asc','desc']);
|
|
|
|
export const JsonNullValueInputSchema = z.enum(['JsonNull',]).transform((value) => (value === 'JsonNull' ? Prisma.JsonNull : value));
|
|
|
|
export const NullableJsonNullValueInputSchema = z.enum(['DbNull','JsonNull',]).transform((value) => value === 'JsonNull' ? Prisma.JsonNull : value === 'DbNull' ? Prisma.DbNull : value);
|
|
|
|
export const QueryModeSchema = z.enum(['default','insensitive']);
|
|
|
|
export const JsonNullValueFilterSchema = z.enum(['DbNull','JsonNull','AnyNull',]).transform((value) => value === 'JsonNull' ? Prisma.JsonNull : value === 'DbNull' ? Prisma.JsonNull : value === 'AnyNull' ? Prisma.AnyNull : value);
|
|
|
|
export const NullsOrderSchema = z.enum(['first','last']);
|
|
|
|
export const PaymentStatusSchema = z.enum(['PENDING','COMPLETED','FAILED','REFUNDED']);
|
|
|
|
export type PaymentStatusType = `${z.infer<typeof PaymentStatusSchema>}`
|
|
|
|
/////////////////////////////////////////
|
|
// MODELS
|
|
/////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////
|
|
// TELEGRAM SESSION SCHEMA
|
|
/////////////////////////////////////////
|
|
|
|
export const TelegramSessionSchema = z.object({
|
|
id: z.string(),
|
|
data: JsonValueSchema,
|
|
createdAt: z.coerce.date(),
|
|
updatedAt: z.coerce.date(),
|
|
})
|
|
|
|
export type TelegramSession = z.infer<typeof TelegramSessionSchema>
|
|
|
|
/////////////////////////////////////////
|
|
// TELEGRAM USER SCHEMA
|
|
/////////////////////////////////////////
|
|
|
|
export const TelegramUserSchema = z.object({
|
|
id: z.string(),
|
|
username: z.string().nullable(),
|
|
isBlocked: z.boolean(),
|
|
firstName: z.string().nullable(),
|
|
lastName: z.string().nullable(),
|
|
languageCode: z.string().nullable(),
|
|
isPremium: z.boolean().nullable(),
|
|
createdAt: z.coerce.date(),
|
|
updatedAt: z.coerce.date(),
|
|
})
|
|
|
|
export type TelegramUser = z.infer<typeof TelegramUserSchema>
|
|
|
|
/////////////////////////////////////////
|
|
// PAYMENT SCHEMA
|
|
/////////////////////////////////////////
|
|
|
|
export const PaymentSchema = z.object({
|
|
status: PaymentStatusSchema,
|
|
id: z.string().cuid(),
|
|
telegramUserId: z.string(),
|
|
amount: z.instanceof(Prisma.Decimal, { message: "Field 'amount' must be a Decimal. Location: ['Models', 'Payment']"}),
|
|
currency: z.string(),
|
|
provider: z.string(),
|
|
providerId: z.string().nullable(),
|
|
metadata: JsonValueSchema.nullable(),
|
|
createdAt: z.coerce.date(),
|
|
updatedAt: z.coerce.date(),
|
|
})
|
|
|
|
export type Payment = z.infer<typeof PaymentSchema>
|
|
|
|
/////////////////////////////////////////
|
|
// PLAN SCHEMA
|
|
/////////////////////////////////////////
|
|
|
|
export const PlanSchema = z.object({
|
|
id: z.string().uuid(),
|
|
code: z.string(),
|
|
name: z.string(),
|
|
description: z.string().nullable(),
|
|
durationDays: z.number().int(),
|
|
dailyGenerations: z.number().int(),
|
|
price: z.number().int(),
|
|
currency: z.string(),
|
|
})
|
|
|
|
export type Plan = z.infer<typeof PlanSchema>
|
|
|
|
/////////////////////////////////////////
|
|
// SUBSCRIPTION SCHEMA
|
|
/////////////////////////////////////////
|
|
|
|
export const SubscriptionSchema = z.object({
|
|
id: z.string().uuid(),
|
|
telegramUserId: z.string(),
|
|
planId: z.string(),
|
|
startedAt: z.coerce.date(),
|
|
expiresAt: z.coerce.date(),
|
|
canceledAt: z.coerce.date().nullable(),
|
|
createdAt: z.coerce.date(),
|
|
updatedAt: z.coerce.date(),
|
|
})
|
|
|
|
export type Subscription = z.infer<typeof SubscriptionSchema>
|