update nice visuel
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import { prisma } from './db.js';
|
||||
import { formatFiat, paymentsProvider } from './shopCatalog.js';
|
||||
import { getPurchasableOsuPack } from './shopOsu.js';
|
||||
|
||||
/**
|
||||
* Create a pending osu top-up order.
|
||||
* Mock provider completes client-side via /confirm.
|
||||
* Stripe provider (when configured) would return a clientSecret.
|
||||
*/
|
||||
export async function createOsuCheckout(userId, packId) {
|
||||
const pack = await getPurchasableOsuPack(packId);
|
||||
if (!pack) {
|
||||
const err = new Error('Unknown or disabled osu pack');
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const provider = paymentsProvider();
|
||||
|
||||
const order = await prisma.paymentOrder.create({
|
||||
data: {
|
||||
userId: Number(userId),
|
||||
packId: pack.id,
|
||||
osuAmount: pack.osu,
|
||||
priceCents: pack.priceCents,
|
||||
currency: pack.currency,
|
||||
status: 'pending',
|
||||
provider,
|
||||
meta: JSON.stringify({
|
||||
packName: pack.name,
|
||||
displayPrice: formatFiat(pack.priceCents, pack.currency),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
if (provider === 'stripe') {
|
||||
// Placeholder for Stripe PaymentIntent wiring.
|
||||
// Set PAYMENTS_PROVIDER=stripe + STRIPE_SECRET_KEY when ready.
|
||||
const err = new Error(
|
||||
'Stripe checkout is configured but not wired yet — set PAYMENTS_PROVIDER=mock for local buys'
|
||||
);
|
||||
err.status = 501;
|
||||
throw err;
|
||||
}
|
||||
|
||||
return {
|
||||
order: serializeOrder(order),
|
||||
pack: {
|
||||
id: pack.id,
|
||||
name: pack.name,
|
||||
osu: pack.osu,
|
||||
priceCents: pack.priceCents,
|
||||
currency: pack.currency,
|
||||
displayPrice: formatFiat(pack.priceCents, pack.currency),
|
||||
},
|
||||
mode: 'mock',
|
||||
/** Client calls POST /api/shop/osu/confirm with this id in mock mode */
|
||||
confirmable: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a mock checkout and credit osu (idempotent).
|
||||
*/
|
||||
export async function confirmMockOsuPayment(userId, orderId) {
|
||||
const id = String(orderId || '');
|
||||
if (!id) {
|
||||
const err = new Error('Missing order id');
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const order = await tx.paymentOrder.findUnique({ where: { id } });
|
||||
if (!order || order.userId !== Number(userId)) {
|
||||
const err = new Error('Order not found');
|
||||
err.status = 404;
|
||||
throw err;
|
||||
}
|
||||
if (order.status === 'paid') {
|
||||
const user = await tx.user.findUnique({ where: { id: userId } });
|
||||
return { order: serializeOrder(order), user, alreadyPaid: true };
|
||||
}
|
||||
if (order.status !== 'pending') {
|
||||
const err = new Error(`Order is ${order.status}`);
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
if (order.provider !== 'mock') {
|
||||
const err = new Error('This order must be completed by the payment provider');
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const updatedOrder = await tx.paymentOrder.update({
|
||||
where: { id: order.id },
|
||||
data: { status: 'paid', paidAt: now },
|
||||
});
|
||||
|
||||
const user = await tx.user.update({
|
||||
where: { id: userId },
|
||||
data: { osuBalance: { increment: order.osuAmount } },
|
||||
});
|
||||
|
||||
await tx.transaction.create({
|
||||
data: {
|
||||
userId,
|
||||
type: 'osu_purchase',
|
||||
amount: String(order.osuAmount),
|
||||
meta: JSON.stringify({
|
||||
orderId: order.id,
|
||||
packId: order.packId,
|
||||
priceCents: order.priceCents,
|
||||
currency: order.currency,
|
||||
provider: order.provider,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
return { order: serializeOrder(updatedOrder), user, alreadyPaid: false };
|
||||
});
|
||||
}
|
||||
|
||||
export function serializeOrder(order) {
|
||||
return {
|
||||
id: order.id,
|
||||
packId: order.packId,
|
||||
osuAmount: order.osuAmount,
|
||||
priceCents: order.priceCents,
|
||||
currency: order.currency,
|
||||
status: order.status,
|
||||
provider: order.provider,
|
||||
displayPrice: formatFiat(order.priceCents, order.currency),
|
||||
createdAt: order.createdAt,
|
||||
paidAt: order.paidAt || null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user