test commit

This commit is contained in:
Kirill
2026-05-19 11:25:23 +05:00
parent f8867f6457
commit 5adbe9baa7
81 changed files with 6549 additions and 3108 deletions
+96 -124
View File
@@ -1,142 +1,114 @@
import { prisma } from "../lib/prisma.js";
import { escapeHtml } from "../lib/escape-html.js";
import { getOtherUploadMaxFileBytes } from "../lib/upload-limits.js";
import { saveImageBufferToUploads } from "../lib/upload-images.js";
import { NOTIFICATION_EVENTS } from "../../../shared/constants/notification-events.js";
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
import { escapeHtml } from '../lib/escape-html.js'
import { prisma } from '../lib/prisma.js'
import { saveImageBufferToUploads } from '../lib/upload-images.js'
import { getOtherUploadMaxFileBytes } from '../lib/upload-limits.js'
export async function registerUserPaymentRoutes(fastify) {
fastify.post(
"/api/me/orders/:id/pay",
{ preHandler: [fastify.authenticate] },
async (request, reply) => {
const userId = request.user.sub;
const { id } = request.params;
const order = await prisma.order.findFirst({ where: { id, userId } });
if (!order) return reply.code(404).send({ error: "Заказ не найден" });
fastify.post('/api/me/orders/:id/pay', { preHandler: [fastify.authenticate] }, async (request, reply) => {
const userId = request.user.sub
const { id } = request.params
const order = await prisma.order.findFirst({ where: { id, userId } })
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
const paymentMethod = order.paymentMethod ?? "online";
if (paymentMethod === "on_pickup") {
return reply
.code(409)
.send({
error:
"Для этого заказа оплата при получении — кнопка оплаты не нужна.",
});
}
const paymentMethod = order.paymentMethod ?? 'online'
if (paymentMethod === 'on_pickup') {
return reply.code(409).send({
error: 'Для этого заказа оплата при получении — кнопка оплаты не нужна.',
})
}
if (order.status !== "PENDING_PAYMENT") {
return reply
.code(409)
.send({ error: "Сейчас нельзя выполнить оплату для этого заказа" });
}
if (order.status !== 'PENDING_PAYMENT') {
return reply.code(409).send({ error: 'Сейчас нельзя выполнить оплату для этого заказа' })
}
if (!request.isMultipart()) {
return reply
.code(400)
.send({
error:
"Отправьте multipart/form-data: поле detail и/или файл receipt",
});
}
if (!request.isMultipart()) {
return reply.code(400).send({
error: 'Отправьте multipart/form-data: поле detail и/или файл receipt',
})
}
let detail = "";
let receiptBuffer = null;
let receiptFilename = "";
try {
const otherLimit = getOtherUploadMaxFileBytes();
const parts = request.parts({
limits: {
fileSize: otherLimit,
files: 2,
},
});
for await (const part of parts) {
if (part.file) {
if (part.fieldname === "receipt") {
if (receiptBuffer !== null) {
return reply
.code(400)
.send({ error: "Допускается один файл receipt" });
}
receiptBuffer = await part.toBuffer();
receiptFilename = part.filename ?? "receipt";
let detail = ''
let receiptBuffer = null
let receiptFilename = ''
try {
const otherLimit = getOtherUploadMaxFileBytes()
const parts = request.parts({
limits: {
fileSize: otherLimit,
files: 2,
},
})
for await (const part of parts) {
if (part.file) {
if (part.fieldname === 'receipt') {
if (receiptBuffer !== null) {
return reply.code(400).send({ error: 'Допускается один файл receipt' })
}
} else if (part.fieldname === "detail") {
detail = String(part.value ?? "").trim();
receiptBuffer = await part.toBuffer()
receiptFilename = part.filename ?? 'receipt'
}
}
} catch (err) {
const msg =
err instanceof Error ? err.message : "Не удалось разобрать форму";
return reply.code(400).send({ error: msg });
}
const hasDetail = detail.length > 0;
const hasReceipt = receiptBuffer !== null && receiptBuffer.length > 0;
if (!hasDetail && !hasReceipt) {
return reply
.code(400)
.send({
error: "Укажите текст о платеже и/или прикрепите изображение чека",
});
}
const maxDetail = 2000;
if (detail.length > maxDetail) {
return reply
.code(400)
.send({ error: `Текст не длиннее ${maxDetail} символов` });
}
let attachmentUrl = null;
if (hasReceipt) {
try {
attachmentUrl = await saveImageBufferToUploads(
receiptFilename,
receiptBuffer,
);
} catch (err) {
const message =
err instanceof Error ? err.message : "Не удалось сохранить файл";
const statusCode =
err &&
typeof err === "object" &&
"statusCode" in err &&
Number.isInteger(err.statusCode)
? Number(err.statusCode)
: 400;
return reply.code(statusCode).send({ error: message });
} else if (part.fieldname === 'detail') {
detail = String(part.value ?? '').trim()
}
}
} catch (err) {
const msg = err instanceof Error ? err.message : 'Не удалось разобрать форму'
return reply.code(400).send({ error: msg })
}
const bodyHtml = hasDetail
? `<p>${escapeHtml(detail).replace(/\r\n|\n|\r/g, "<br/>")}</p>`
: "";
const messageText = `<p><strong>Подтверждение оплаты (перевод ВТБ / Сбербанк)</strong></p>${bodyHtml}`;
const hasDetail = detail.length > 0
const hasReceipt = receiptBuffer !== null && receiptBuffer.length > 0
if (!hasDetail && !hasReceipt) {
return reply.code(400).send({
error: 'Укажите текст о платеже и/или прикрепите изображение чека',
})
}
const maxDetail = 2000
if (detail.length > maxDetail) {
return reply.code(400).send({ error: `Текст не длиннее ${maxDetail} символов` })
}
let attachmentUrl = null
if (hasReceipt) {
try {
await prisma.$transaction(async (tx) => {
await tx.orderMessage.create({
data: {
orderId: id,
authorType: "user",
text: messageText,
attachmentUrl,
},
});
});
attachmentUrl = await saveImageBufferToUploads(receiptFilename, receiptBuffer)
} catch (err) {
return reply.code(500).send({ error: "Не удалось сохранить оплату" });
const message = err instanceof Error ? err.message : 'Не удалось сохранить файл'
const statusCode =
err && typeof err === 'object' && 'statusCode' in err && Number.isInteger(err.statusCode)
? Number(err.statusCode)
: 400
return reply.code(statusCode).send({ error: message })
}
}
request.server.eventBus.emit(NOTIFICATION_EVENTS.PAYMENT_STATUS_CHANGED, {
orderId: id,
userId,
paymentStatus: "pending",
});
const bodyHtml = hasDetail ? `<p>${escapeHtml(detail).replace(/\r\n|\n|\r/g, '<br/>')}</p>` : ''
const messageText = `<p><strong>Подтверждение оплаты (перевод ВТБ / Сбербанк)</strong></p>${bodyHtml}`
return { ok: true, status: "PENDING_PAYMENT" };
},
);
try {
await prisma.$transaction(async (tx) => {
await tx.orderMessage.create({
data: {
orderId: id,
authorType: 'user',
text: messageText,
attachmentUrl,
},
})
})
} catch {
return reply.code(500).send({ error: 'Не удалось сохранить оплату' })
}
request.server.eventBus.emit(NOTIFICATION_EVENTS.PAYMENT_STATUS_CHANGED, {
orderId: id,
userId,
paymentStatus: 'pending',
})
return { ok: true, status: 'PENDING_PAYMENT' }
})
}