var db = require('../../common/db') var auth = require('../../common/auth') var log = require('../../common/log') var RESPONSE = require('../../common/response').RESPONSE var ERROR_CODES = require('../../common/response').ERROR_CODES exports.main_handler = async function (event) { try { var method = event.httpMethod || 'GET' var path = event.path || '' var body = parseBody(event) if (method === 'GET' && path === '/api/v1/subscription') { return await getSubscription(event) } if (method === 'POST' && path === '/api/v1/subscription/purchase') { return await purchaseSubscription(event, body) } if (method === 'POST' && path === '/api/v1/subscription/verify') { return await verifyPayment(event, body) } return RESPONSE.error(ERROR_CODES.PARAM_ERROR.code, 'unknown route') } catch (err) { console.error('subscription error:', err) return RESPONSE.error(ERROR_CODES.SERVER_ERROR.code, err.message) } } async function getSubscription(event) { var user = auth.extractUser(event) if (!user) return RESPONSE.error(ERROR_CODES.INVALID_TOKEN.code, ERROR_CODES.INVALID_TOKEN.message) var subs = await db.query( 'SELECT id, type, plan, status, trial_used, started_at AS start_time, expired_at AS expire_time ' + 'FROM subscriptions ' + 'WHERE user_id = ? AND status = 1 AND expired_at > NOW() ' + 'ORDER BY expired_at DESC LIMIT 1', [user.user_id] ) if (subs.length === 0) { return RESPONSE.success({ status: 0, plan: null, start_time: null, expire_time: null, remaining_days: 0 }) } var sub = subs[0] var remainingMs = new Date(sub.expire_time) - new Date() var remainingDays = Math.ceil(remainingMs / 86400000) return RESPONSE.success({ status: sub.type === 1 ? 1 : 2, plan: sub.plan, start_time: sub.start_time, expire_time: sub.expire_time, remaining_days: remainingDays }) } async function purchaseSubscription(event, body) { var user = auth.extractUser(event) if (!user) return RESPONSE.error(ERROR_CODES.INVALID_TOKEN.code, ERROR_CODES.INVALID_TOKEN.message) if (!body.plan) { return RESPONSE.error(ERROR_CODES.PARAM_ERROR.code, 'missing plan') } var planDays = { monthly: 30, quarterly: 90, yearly: 365 } var days = planDays[body.plan] || 30 var orderId = 'ORD' + Date.now() + Math.random().toString(36).substring(2, 8) return RESPONSE.success({ order_id: orderId, payment_params: { plan: body.plan, days: days, amount: 0, channel: body.payment_method || 'wechat' } }) } async function verifyPayment(event, body) { var user = auth.extractUser(event) if (!user) return RESPONSE.error(ERROR_CODES.INVALID_TOKEN.code, ERROR_CODES.INVALID_TOKEN.message) if (!body.order_id) { return RESPONSE.error(ERROR_CODES.PARAM_ERROR.code, 'missing order_id') } var planDays = { monthly: 30, quarterly: 90, yearly: 365 } var currentSub = await db.query( 'SELECT id, expired_at FROM subscriptions WHERE user_id = ? AND status = 1 AND expired_at > NOW() ORDER BY expired_at DESC LIMIT 1', [user.user_id] ) var startDate = new Date() if (currentSub.length > 0 && new Date(currentSub[0].expired_at) > startDate) { startDate = new Date(currentSub[0].expired_at) } var plan = body.plan || 'monthly' var days = planDays[plan] || 30 var expiredAt = new Date(startDate.getTime() + days * 86400000) await db.insert( 'INSERT INTO subscriptions (user_id, type, plan, status, started_at, expired_at, source, source_id) VALUES (?, 2, ?, 1, ?, ?, ?, ?)', [user.user_id, plan, startDate.toISOString().slice(0, 19).replace('T', ' '), expiredAt.toISOString().slice(0, 19).replace('T', ' '), 'purchase', body.order_id] ) await log.writeLog({ operator_type: 2, operator_id: user.user_id, target_type: 'subscription', target_id: body.order_id, action: 'purchase', remark: 'plan=' + plan + ',days=' + days }) return RESPONSE.success({ status: 2, plan: plan, expire_time: expiredAt.toISOString().slice(0, 19).replace('T', ' ') }) } function parseBody(event) { if (event.body) { try { return JSON.parse(event.body) } catch (e) { return {} } } return event.queryStringParameters || {} }