/** * 支付控制器:创建支付 / 查询状态 / 模拟成功(仅 mock) */ const express = require('express'); const config = require('../config'); const { ok, fail } = require('../utils/response'); const gateway = require('../services/pay_gateway.service'); const orderService = require('../services/order.service'); const router = express.Router(); /** * 创建支付 * POST /v1/payment/:channel/ * body: { product_id, source?: 'app'|'web', currency?: 'usd'|'cny' } */ router.post('/:channel/', async (req, res) => { try { const { channel } = req.params; const { product_id, source = 'app', currency } = req.body || {}; const data = await gateway.createPayment({ channel, productId: product_id, source, currency }); return ok(res, data); } catch (e) { const status = e.code === 'PRODUCT_NOT_FOUND' ? 404 : e.code === 'BAD_CHANNEL' ? 400 : 500; return fail(res, status, e.code || 'PAYMENT_CREATE_FAILED', e.message); } }); /** * 查询支付状态(前端轮询/最终确认) * GET /v1/payment/:payment_id */ router.get('/:payment_id', (req, res) => { return ok(res, orderService.getStatus(req.params.payment_id)); }); /** * 模拟支付结果(仅 MOCK_MODE):走与真实回调相同的 markPaid 幂等流程 * POST /v1/payment/simulate/:result result: success|fail * body: { payment_id } */ router.post('/simulate/:result', (req, res) => { if (!config.mock) { return fail(res, 403, 'MOCK_DISABLED', 'simulate only available in MOCK_MODE'); } const { payment_id } = req.body || {}; const result = req.params.result; if (!payment_id) return fail(res, 400, 'BAD_REQUEST', 'payment_id required'); let changed = false; if (result === 'success') { changed = orderService.markPaid(payment_id, { channel: 'mock', channelTradeNo: `mock_${payment_id}` }); } else { changed = Boolean(orderService.getOrder(payment_id)); } return ok(res, { payment_id, simulated: changed, result }); }); module.exports = router;