refactor: extract findUserOrder helper
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
import { describe, it, expect, vi } from 'vitest'
|
||||||
|
import { findUserOrder } from '../find-user-order.js'
|
||||||
|
|
||||||
|
describe('findUserOrder', () => {
|
||||||
|
it('returns order when found', async () => {
|
||||||
|
const mockOrder = { id: '1', userId: 'user1' }
|
||||||
|
const prisma = { order: { findFirst: vi.fn().mockResolvedValue(mockOrder) } }
|
||||||
|
const result = await findUserOrder(prisma, '1', 'user1')
|
||||||
|
expect(result).toEqual(mockOrder)
|
||||||
|
expect(prisma.order.findFirst).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ where: { id: '1', userId: 'user1' } }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws 404 when order not found', async () => {
|
||||||
|
const prisma = { order: { findFirst: vi.fn().mockResolvedValue(null) } }
|
||||||
|
await expect(findUserOrder(prisma, '999', 'user1')).rejects.toMatchObject({ statusCode: 404 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes include option', async () => {
|
||||||
|
const mockOrder = { id: '1', userId: 'user1', items: [] }
|
||||||
|
const prisma = { order: { findFirst: vi.fn().mockResolvedValue(mockOrder) } }
|
||||||
|
const result = await findUserOrder(prisma, '1', 'user1', { items: true })
|
||||||
|
expect(result).toEqual(mockOrder)
|
||||||
|
expect(prisma.order.findFirst).toHaveBeenCalledWith(expect.objectContaining({ include: { items: true } }))
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export async function findUserOrder(prisma, orderId, userId, include = {}) {
|
||||||
|
const order = await prisma.order.findFirst({
|
||||||
|
where: { id: orderId, userId },
|
||||||
|
include,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!order) {
|
||||||
|
throw Object.assign(new Error('Order not found'), { statusCode: 404 })
|
||||||
|
}
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
||||||
@@ -1,40 +1,48 @@
|
|||||||
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
|
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
|
||||||
|
import { asyncHandler } from '../lib/async-handler.js'
|
||||||
|
import { findUserOrder } from '../lib/find-user-order.js'
|
||||||
import { prisma } from '../lib/prisma.js'
|
import { prisma } from '../lib/prisma.js'
|
||||||
|
|
||||||
export async function registerUserMessageRoutes(fastify) {
|
export async function registerUserMessageRoutes(fastify) {
|
||||||
fastify.get('/api/me/orders/:id/messages', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.get(
|
||||||
const userId = request.user.sub
|
'/api/me/orders/:id/messages',
|
||||||
const { id } = request.params
|
{ preHandler: [fastify.authenticate] },
|
||||||
const order = await prisma.order.findFirst({ where: { id, userId } })
|
asyncHandler(async (request, reply) => {
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
const userId = request.user.sub
|
||||||
const items = await prisma.orderMessage.findMany({
|
const { id } = request.params
|
||||||
where: { orderId: id },
|
await findUserOrder(prisma, id, userId)
|
||||||
orderBy: { createdAt: 'asc' },
|
const items = await prisma.orderMessage.findMany({
|
||||||
})
|
where: { orderId: id },
|
||||||
return { items }
|
orderBy: { createdAt: 'asc' },
|
||||||
})
|
})
|
||||||
|
return { items }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
fastify.post('/api/me/orders/:id/messages', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.post(
|
||||||
const userId = request.user.sub
|
'/api/me/orders/:id/messages',
|
||||||
const { id } = request.params
|
{ preHandler: [fastify.authenticate] },
|
||||||
const order = await prisma.order.findFirst({ where: { id, userId } })
|
asyncHandler(async (request, reply) => {
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
const userId = request.user.sub
|
||||||
const text = String(request.body?.text || '').trim()
|
const { id } = request.params
|
||||||
if (!text) return reply.code(400).send({ error: 'Сообщение пустое' })
|
await findUserOrder(prisma, id, userId)
|
||||||
if (text.length > 2000) return reply.code(400).send({ error: 'Сообщение слишком длинное' })
|
const text = String(request.body?.text || '').trim()
|
||||||
const msg = await prisma.orderMessage.create({
|
if (!text) return reply.code(400).send({ error: 'Сообщение пустое' })
|
||||||
data: { orderId: id, authorType: 'user', text },
|
if (text.length > 2000) return reply.code(400).send({ error: 'Сообщение слишком длинное' })
|
||||||
})
|
const msg = await prisma.orderMessage.create({
|
||||||
|
data: { orderId: id, authorType: 'user', text },
|
||||||
|
})
|
||||||
|
|
||||||
request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_MESSAGE_SENT, {
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_MESSAGE_SENT, {
|
||||||
orderId: id,
|
orderId: id,
|
||||||
authorType: 'user',
|
authorType: 'user',
|
||||||
messageId: msg.id,
|
messageId: msg.id,
|
||||||
preview: text,
|
preview: text,
|
||||||
})
|
})
|
||||||
|
|
||||||
return reply.code(201).send({ item: msg })
|
return reply.code(201).send({ item: msg })
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
fastify.get('/api/me/messages/unread-count', { preHandler: [fastify.authenticate] }, async (request) => {
|
fastify.get('/api/me/messages/unread-count', { preHandler: [fastify.authenticate] }, async (request) => {
|
||||||
const userId = request.user.sub
|
const userId = request.user.sub
|
||||||
@@ -116,18 +124,21 @@ export async function registerUserMessageRoutes(fastify) {
|
|||||||
return { items }
|
return { items }
|
||||||
})
|
})
|
||||||
|
|
||||||
fastify.post('/api/me/orders/:id/messages/read', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.post(
|
||||||
const userId = request.user.sub
|
'/api/me/orders/:id/messages/read',
|
||||||
const { id } = request.params
|
{ preHandler: [fastify.authenticate] },
|
||||||
const order = await prisma.order.findFirst({ where: { id, userId } })
|
asyncHandler(async (request, reply) => {
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
const userId = request.user.sub
|
||||||
|
const { id } = request.params
|
||||||
|
await findUserOrder(prisma, id, userId)
|
||||||
|
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
await prisma.userOrderMessageReadState.upsert({
|
await prisma.userOrderMessageReadState.upsert({
|
||||||
where: { userId_orderId: { userId, orderId: id } },
|
where: { userId_orderId: { userId, orderId: id } },
|
||||||
create: { userId, orderId: id, lastReadAt: now },
|
create: { userId, orderId: id, lastReadAt: now },
|
||||||
update: { lastReadAt: now },
|
update: { lastReadAt: now },
|
||||||
})
|
})
|
||||||
return { ok: true }
|
return { ok: true }
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
|
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
|
||||||
import { asyncHandler } from '../lib/async-handler.js'
|
import { asyncHandler } from '../lib/async-handler.js'
|
||||||
import { isDeliveryCarrier } from '../lib/delivery-carrier.js'
|
import { isDeliveryCarrier } from '../lib/delivery-carrier.js'
|
||||||
|
import { findUserOrder } from '../lib/find-user-order.js'
|
||||||
import { prisma } from '../lib/prisma.js'
|
import { prisma } from '../lib/prisma.js'
|
||||||
|
|
||||||
export async function registerUserOrderRoutes(fastify) {
|
export async function registerUserOrderRoutes(fastify) {
|
||||||
@@ -207,11 +208,10 @@ export async function registerUserOrderRoutes(fastify) {
|
|||||||
asyncHandler(async (request, reply) => {
|
asyncHandler(async (request, reply) => {
|
||||||
const userId = request.user.sub
|
const userId = request.user.sub
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const order = await prisma.order.findFirst({
|
const order = await findUserOrder(prisma, id, userId, {
|
||||||
where: { id, userId },
|
items: true,
|
||||||
include: { items: true, messages: { orderBy: { createdAt: 'asc' } } },
|
messages: { orderBy: { createdAt: 'asc' } },
|
||||||
})
|
})
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
|
||||||
return { item: order }
|
return { item: order }
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -219,14 +219,10 @@ export async function registerUserOrderRoutes(fastify) {
|
|||||||
fastify.get(
|
fastify.get(
|
||||||
'/api/me/orders/:id/review-eligibility',
|
'/api/me/orders/:id/review-eligibility',
|
||||||
{ preHandler: [fastify.authenticate] },
|
{ preHandler: [fastify.authenticate] },
|
||||||
async (request, reply) => {
|
asyncHandler(async (request, reply) => {
|
||||||
const userId = request.user.sub
|
const userId = request.user.sub
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const order = await prisma.order.findFirst({
|
const order = await findUserOrder(prisma, id, userId, { items: true })
|
||||||
where: { id, userId },
|
|
||||||
include: { items: true },
|
|
||||||
})
|
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
|
||||||
if (order.status !== 'DONE') {
|
if (order.status !== 'DONE') {
|
||||||
return { canReview: false, items: [] }
|
return { canReview: false, items: [] }
|
||||||
}
|
}
|
||||||
@@ -253,7 +249,7 @@ export async function registerUserOrderRoutes(fastify) {
|
|||||||
hasReview: reviewed.has(x.productId),
|
hasReview: reviewed.has(x.productId),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
fastify.post(
|
fastify.post(
|
||||||
@@ -262,8 +258,7 @@ export async function registerUserOrderRoutes(fastify) {
|
|||||||
asyncHandler(async (request, reply) => {
|
asyncHandler(async (request, reply) => {
|
||||||
const userId = request.user.sub
|
const userId = request.user.sub
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const order = await prisma.order.findFirst({ where: { id, userId } })
|
const order = await findUserOrder(prisma, id, userId)
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
|
||||||
|
|
||||||
const okDelivery = order.deliveryType === 'delivery' && order.status === 'SHIPPED'
|
const okDelivery = order.deliveryType === 'delivery' && order.status === 'SHIPPED'
|
||||||
const okPickup = order.deliveryType === 'pickup' && order.status === 'READY_FOR_PICKUP'
|
const okPickup = order.deliveryType === 'pickup' && order.status === 'READY_FOR_PICKUP'
|
||||||
|
|||||||
+145
-140
@@ -1,149 +1,154 @@
|
|||||||
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
|
import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
|
||||||
|
import { asyncHandler } from '../lib/async-handler.js'
|
||||||
|
import { findUserOrder } from '../lib/find-user-order.js'
|
||||||
import { prisma } from '../lib/prisma.js'
|
import { prisma } from '../lib/prisma.js'
|
||||||
import { createPayment, buildReceipt, getPayment } from '../lib/yookassa.js'
|
import { createPayment, buildReceipt, getPayment } from '../lib/yookassa.js'
|
||||||
|
|
||||||
export async function registerUserPaymentRoutes(fastify) {
|
export async function registerUserPaymentRoutes(fastify) {
|
||||||
fastify.post('/api/me/orders/:id/pay', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
fastify.post(
|
||||||
const userId = request.user.sub
|
'/api/me/orders/:id/pay',
|
||||||
const userEmail = request.user.email
|
{ preHandler: [fastify.authenticate] },
|
||||||
|
asyncHandler(async (request, reply) => {
|
||||||
|
const userId = request.user.sub
|
||||||
|
const userEmail = request.user.email
|
||||||
|
|
||||||
if (!userEmail) {
|
if (!userEmail) {
|
||||||
return reply.code(422).send({ error: 'Для онлайн-оплаты необходим email в профиле' })
|
return reply.code(422).send({ error: 'Для онлайн-оплаты необходим email в профиле' })
|
||||||
}
|
|
||||||
|
|
||||||
const { id } = request.params
|
|
||||||
|
|
||||||
const order = await prisma.order.findFirst({
|
|
||||||
where: { id, userId },
|
|
||||||
include: { items: true },
|
|
||||||
})
|
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
|
||||||
|
|
||||||
if (order.paymentMethod === 'on_pickup') {
|
|
||||||
return reply.code(409).send({
|
|
||||||
error: 'Для этого заказа оплата при получении — онлайн-оплата недоступна',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (order.status !== 'PENDING_PAYMENT') {
|
|
||||||
return reply.code(409).send({ error: 'Сейчас нельзя выполнить оплату для этого заказа' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!order.deliveryFeeLocked) {
|
|
||||||
return reply.code(409).send({
|
|
||||||
error: 'Стоимость доставки ещё утверждается — оплата станет доступна позже',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingPayment = await prisma.payment.findFirst({
|
|
||||||
where: {
|
|
||||||
orderId: id,
|
|
||||||
status: { in: ['pending', 'waiting_for_capture'] },
|
|
||||||
OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }],
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
})
|
|
||||||
|
|
||||||
if (existingPayment && existingPayment.confirmationUrl) {
|
|
||||||
return { confirmationUrl: existingPayment.confirmationUrl }
|
|
||||||
}
|
|
||||||
|
|
||||||
const idempotencyKey = `${id}-${Date.now()}`
|
|
||||||
const clientUrl = (process.env.CLIENT_PUBLIC_URL || 'http://127.0.0.1:5173').replace(/\/$/, '')
|
|
||||||
const returnUrl = `${clientUrl}/me/orders/${id}?paid=1`
|
|
||||||
const clientIp = request.ip
|
|
||||||
|
|
||||||
const amount = {
|
|
||||||
value: (order.totalCents / 100).toFixed(2),
|
|
||||||
currency: order.currency,
|
|
||||||
}
|
|
||||||
|
|
||||||
const receipt = buildReceipt({
|
|
||||||
orderItems: order.items,
|
|
||||||
deliveryFeeCents: order.deliveryFeeCents,
|
|
||||||
userEmail: userEmail,
|
|
||||||
})
|
|
||||||
|
|
||||||
let result
|
|
||||||
try {
|
|
||||||
result = await createPayment({
|
|
||||||
amount,
|
|
||||||
description: `Оплата заказа №${order.id.slice(-6)}`,
|
|
||||||
receipt,
|
|
||||||
confirmation: { type: 'redirect', return_url: returnUrl },
|
|
||||||
metadata: { orderId: order.id },
|
|
||||||
idempotencyKey,
|
|
||||||
clientIp,
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
request.log.error({ err, orderId: id }, 'YooKassa createPayment failed')
|
|
||||||
return reply.code(502).send({
|
|
||||||
error: 'Не удалось создать платёж. Платёжный сервис временно недоступен.',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.payment.create({
|
|
||||||
data: {
|
|
||||||
orderId: order.id,
|
|
||||||
yookassaPaymentId: result.paymentId,
|
|
||||||
status: result.status,
|
|
||||||
amountCents: order.totalCents,
|
|
||||||
currency: order.currency,
|
|
||||||
confirmationUrl: result.confirmationUrl,
|
|
||||||
expiresAt: result.expiresAt ? new Date(result.expiresAt) : null,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return { confirmationUrl: result.confirmationUrl }
|
|
||||||
})
|
|
||||||
|
|
||||||
fastify.get('/api/me/orders/:orderId/payment', { preHandler: [fastify.authenticate] }, async (request, reply) => {
|
|
||||||
const userId = request.user.sub
|
|
||||||
const { orderId } = request.params
|
|
||||||
|
|
||||||
const order = await prisma.order.findFirst({ where: { id: orderId, userId } })
|
|
||||||
if (!order) return reply.code(404).send({ error: 'Заказ не найден' })
|
|
||||||
|
|
||||||
const payment = await prisma.payment.findFirst({
|
|
||||||
where: { orderId },
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
})
|
|
||||||
if (!payment) {
|
|
||||||
return { status: null, paid: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payment.status === 'succeeded' || payment.status === 'canceled') {
|
|
||||||
return { status: payment.status, paid: payment.status === 'succeeded' }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ykPayment = await getPayment(payment.yookassaPaymentId)
|
|
||||||
|
|
||||||
if (ykPayment.status !== payment.status) {
|
|
||||||
await prisma.payment.update({
|
|
||||||
where: { id: payment.id },
|
|
||||||
data: { status: ykPayment.status },
|
|
||||||
})
|
|
||||||
|
|
||||||
if (ykPayment.status === 'succeeded' && order.status === 'PENDING_PAYMENT') {
|
|
||||||
const updated = await prisma.order.updateMany({
|
|
||||||
where: { id: orderId, status: 'PENDING_PAYMENT' },
|
|
||||||
data: { status: 'PAID' },
|
|
||||||
})
|
|
||||||
if (updated.count > 0) {
|
|
||||||
request.server.eventBus.emit(NOTIFICATION_EVENTS.PAYMENT_STATUS_CHANGED, {
|
|
||||||
orderId,
|
|
||||||
userId: order.userId,
|
|
||||||
paymentStatus: 'paid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { status: ykPayment.status, paid: ykPayment.paid }
|
const { id } = request.params
|
||||||
} catch (err) {
|
|
||||||
request.log.error({ err }, '[user-payments] Operation failed')
|
const order = await findUserOrder(prisma, id, userId, { items: true })
|
||||||
return { status: payment.status, paid: payment.status === 'succeeded' }
|
|
||||||
}
|
if (order.paymentMethod === 'on_pickup') {
|
||||||
})
|
return reply.code(409).send({
|
||||||
|
error: 'Для этого заказа оплата при получении — онлайн-оплата недоступна',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order.status !== 'PENDING_PAYMENT') {
|
||||||
|
return reply.code(409).send({ error: 'Сейчас нельзя выполнить оплату для этого заказа' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!order.deliveryFeeLocked) {
|
||||||
|
return reply.code(409).send({
|
||||||
|
error: 'Стоимость доставки ещё утверждается — оплата станет доступна позже',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingPayment = await prisma.payment.findFirst({
|
||||||
|
where: {
|
||||||
|
orderId: id,
|
||||||
|
status: { in: ['pending', 'waiting_for_capture'] },
|
||||||
|
OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }],
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (existingPayment && existingPayment.confirmationUrl) {
|
||||||
|
return { confirmationUrl: existingPayment.confirmationUrl }
|
||||||
|
}
|
||||||
|
|
||||||
|
const idempotencyKey = `${id}-${Date.now()}`
|
||||||
|
const clientUrl = (process.env.CLIENT_PUBLIC_URL || 'http://127.0.0.1:5173').replace(/\/$/, '')
|
||||||
|
const returnUrl = `${clientUrl}/me/orders/${id}?paid=1`
|
||||||
|
const clientIp = request.ip
|
||||||
|
|
||||||
|
const amount = {
|
||||||
|
value: (order.totalCents / 100).toFixed(2),
|
||||||
|
currency: order.currency,
|
||||||
|
}
|
||||||
|
|
||||||
|
const receipt = buildReceipt({
|
||||||
|
orderItems: order.items,
|
||||||
|
deliveryFeeCents: order.deliveryFeeCents,
|
||||||
|
userEmail: userEmail,
|
||||||
|
})
|
||||||
|
|
||||||
|
let result
|
||||||
|
try {
|
||||||
|
result = await createPayment({
|
||||||
|
amount,
|
||||||
|
description: `Оплата заказа №${order.id.slice(-6)}`,
|
||||||
|
receipt,
|
||||||
|
confirmation: { type: 'redirect', return_url: returnUrl },
|
||||||
|
metadata: { orderId: order.id },
|
||||||
|
idempotencyKey,
|
||||||
|
clientIp,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
request.log.error({ err, orderId: id }, 'YooKassa createPayment failed')
|
||||||
|
return reply.code(502).send({
|
||||||
|
error: 'Не удалось создать платёж. Платёжный сервис временно недоступен.',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.payment.create({
|
||||||
|
data: {
|
||||||
|
orderId: order.id,
|
||||||
|
yookassaPaymentId: result.paymentId,
|
||||||
|
status: result.status,
|
||||||
|
amountCents: order.totalCents,
|
||||||
|
currency: order.currency,
|
||||||
|
confirmationUrl: result.confirmationUrl,
|
||||||
|
expiresAt: result.expiresAt ? new Date(result.expiresAt) : null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return { confirmationUrl: result.confirmationUrl }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
fastify.get(
|
||||||
|
'/api/me/orders/:orderId/payment',
|
||||||
|
{ preHandler: [fastify.authenticate] },
|
||||||
|
asyncHandler(async (request, reply) => {
|
||||||
|
const userId = request.user.sub
|
||||||
|
const { orderId } = request.params
|
||||||
|
|
||||||
|
const order = await findUserOrder(prisma, orderId, userId)
|
||||||
|
|
||||||
|
const payment = await prisma.payment.findFirst({
|
||||||
|
where: { orderId },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
})
|
||||||
|
if (!payment) {
|
||||||
|
return { status: null, paid: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payment.status === 'succeeded' || payment.status === 'canceled') {
|
||||||
|
return { status: payment.status, paid: payment.status === 'succeeded' }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ykPayment = await getPayment(payment.yookassaPaymentId)
|
||||||
|
|
||||||
|
if (ykPayment.status !== payment.status) {
|
||||||
|
await prisma.payment.update({
|
||||||
|
where: { id: payment.id },
|
||||||
|
data: { status: ykPayment.status },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ykPayment.status === 'succeeded' && order.status === 'PENDING_PAYMENT') {
|
||||||
|
const updated = await prisma.order.updateMany({
|
||||||
|
where: { id: orderId, status: 'PENDING_PAYMENT' },
|
||||||
|
data: { status: 'PAID' },
|
||||||
|
})
|
||||||
|
if (updated.count > 0) {
|
||||||
|
request.server.eventBus.emit(NOTIFICATION_EVENTS.PAYMENT_STATUS_CHANGED, {
|
||||||
|
orderId,
|
||||||
|
userId: order.userId,
|
||||||
|
paymentStatus: 'paid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: ykPayment.status, paid: ykPayment.paid }
|
||||||
|
} catch (err) {
|
||||||
|
request.log.error({ err }, '[user-payments] Operation failed')
|
||||||
|
return { status: payment.status, paid: payment.status === 'succeeded' }
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user