feat: init project with miniprogram, cloud functions and admin console
这个提交包含在:
@@ -0,0 +1,138 @@
|
||||
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 || {}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户