CreateOrder.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. const API = require("../../lib/API.js")
  2. const db = require("../../plugin/DataBase/db.js")
  3. const Redis = require('../../plugin/DataBase/Redis')
  4. const { BaseStdResponse } = require("../../BaseStdResponse.js")
  5. const AccessControl = require("../../lib/AccessControl.js")
  6. const crypto = require('crypto')
  7. const axios = require('axios')
  8. const config = require('../../config.json')
  9. function generateOrderId() {
  10. const now = new Date()
  11. const pad = (n, w = 2) => n.toString().padStart(w, '0')
  12. return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}` +
  13. `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}` +
  14. `${pad(now.getMilliseconds(), 3)}`
  15. }
  16. function generatePaymentSign(params, key) {
  17. const sorted = Object.keys(params).sort()
  18. const query = sorted.map(k => `${k}=${params[k]}`).join('&') + key
  19. return crypto.createHash('md5').update(query, 'utf8').digest('hex')
  20. }
  21. // async function getPayStatus(order_no) {
  22. // const endpoint = config.pay.url + '/api/findorder'
  23. // try {
  24. // const res = await axios.post(endpoint, {order_no, type: 1})
  25. // } catch (error) {
  26. // }
  27. // }
  28. class CreateOrder extends API {
  29. constructor() {
  30. super()
  31. this.setPath('/Order/CreateOrder')
  32. this.setMethod('POST')
  33. }
  34. async onRequest(req, res) {
  35. const { uuid, session, goods_id, pay_type } = req.body
  36. if ([uuid, session, goods_id, pay_type].some(v => v === '' || v === null || v === undefined)) {
  37. return res.json({
  38. ...BaseStdResponse.MISSING_PARAMETER,
  39. endpoint: 1513126
  40. })
  41. }
  42. const sessionValid = await AccessControl.checkSession(uuid, session)
  43. if (!sessionValid) {
  44. return res.status(401).json({
  45. ...BaseStdResponse.ACCESS_DENIED
  46. })
  47. }
  48. try {
  49. const goodsSql = 'SELECT name, price, num, state FROM goods WHERE id = ?'
  50. const goodsRows = await db.query(goodsSql, [goods_id])
  51. if (!goodsRows || goodsRows.length !== 1) {
  52. return res.json({
  53. ...BaseStdResponse.ERR,
  54. msg: '商品不存在',
  55. endpoint: 1513126
  56. })
  57. }
  58. const goods = goodsRows[0]
  59. if (goods.num < 1 || goods.state !== 1) {
  60. return res.json({
  61. ...BaseStdResponse.ERR,
  62. msg: '商品已下架或库存不足',
  63. endpoint: 1513126
  64. })
  65. }
  66. const createTime = Date.now()
  67. const orderId = generateOrderId()
  68. const insertSql = `
  69. INSERT INTO orders (orderId, create_user, create_time, goods_id, price, pay_type)
  70. VALUES (?, ?, ?, ?, ?, ?)
  71. `
  72. const result = await db.query(insertSql, [
  73. orderId, uuid, createTime, goods_id, goods.price, pay_type
  74. ])
  75. const updateSql = 'UPDATE goods SET num = num - 1 WHERE id = ?'
  76. await db.query(updateSql, [goods_id])
  77. if (result && result.affectedRows > 0) {
  78. const paymentConfig = config.pay || {}
  79. if (!paymentConfig.pid || !paymentConfig.url || !paymentConfig.key || !paymentConfig.return_url) {
  80. return res.json({
  81. ...BaseStdResponse.ERR,
  82. msg: '支付配置错误,请联系管理员'
  83. })
  84. }
  85. const payParams = {
  86. pid: paymentConfig.pid,
  87. type: pay_type,
  88. out_trade_no: orderId,
  89. notify_url: `${config.url}/Order/CallBack`,
  90. return_url: paymentConfig.return_url + orderId,
  91. name: goods.name,
  92. money: goods.price
  93. }
  94. const sign = generatePaymentSign(payParams, paymentConfig.key)
  95. payParams.sign = sign
  96. payParams.sign_type = 'MD5'
  97. await Redis.set(`payData:${orderId}`, JSON.stringify(payParams), {
  98. EX: 300
  99. })
  100. res.json({
  101. ...BaseStdResponse.OK,
  102. id: orderId,
  103. pay: {
  104. payUrl: `${paymentConfig.url}/submit.php`,
  105. payData: payParams
  106. }
  107. })
  108. 定时器轮询订单状态
  109. try {
  110. // 定时轮询订单状态,最多持续5分钟(300秒),每次间隔5秒
  111. const MAX_RETRIES = 60 // 5分钟 / 5秒
  112. const DELAY = 5000 // 5秒
  113. const queryUrl = `${paymentConfig.url}/api.php?act=order&pid=${paymentConfig.pid}&key=${paymentConfig.key}&out_trade_no=${orderId}`
  114. const pollOrderStatus = async (retry = 0) => {
  115. if (retry >= MAX_RETRIES) {
  116. this.logger.info(`订单超时未支付,订单号:${orderId}`)
  117. await db.query('UPDATE orders SET state = 3 WHERE orderId = ?', [orderId]);
  118. return
  119. }
  120. try {
  121. const queryRes = await axios.get(queryUrl)
  122. const queryData = queryRes.data
  123. if (queryData.code === 1 && queryData.status === 1) {
  124. const { trade_no, out_trade_no, type } = queryData
  125. const time = Date.now()
  126. let sql = 'UPDATE orders SET state = 1, pay_type = ?, pay_id = ?, pay_time = ? WHERE orderId = ? AND state = 0'
  127. const result = await db.query(sql, [type, trade_no, time, out_trade_no])
  128. if (result.affectedRows > 0) {
  129. // 查询订单与商品信息
  130. sql = `
  131. SELECT
  132. g.lepao_count,
  133. g.ic_count,
  134. g.vip,
  135. a.create_user
  136. FROM
  137. orders a
  138. LEFT JOIN
  139. goods g
  140. ON
  141. a.goods_id = g.id
  142. WHERE
  143. a.orderId = ?
  144. `
  145. const rows = await db.query(sql, [out_trade_no])
  146. if (!rows || rows.length !== 1) {
  147. this.logger.error(`订单商品信息异常,订单号:${out_trade_no}`)
  148. await db.query('UPDATE orders SET state = 4 WHERE orderId = ?', [out_trade_no])
  149. return
  150. }
  151. const { lepao_count, ic_count, vip, create_user } = rows[0]
  152. sql = 'UPDATE users SET lepao_count = lepao_count + ?, ic_count = ic_count + ?, vip = ? WHERE uuid = ?'
  153. const updateUser = await db.query(sql, [lepao_count, ic_count, vip, create_user])
  154. if (!updateUser || updateUser.affectedRows !== 1) {
  155. this.logger.error(`更新用户失败,UUID: ${create_user}`)
  156. await db.query('UPDATE orders SET state = 4 WHERE orderId = ?', [out_trade_no])
  157. }
  158. sql = 'UPDATE orders SET state = 2 WHERE orderId = ?'
  159. await db.query(sql, [out_trade_no])
  160. this.logger.info(`订单处理成功:${out_trade_no}`)
  161. return // 成功处理后终止轮询
  162. } else {
  163. this.logger.warn(`订单不存在或已处理:${out_trade_no}`)
  164. }
  165. }
  166. // 未支付,继续轮询
  167. setTimeout(() => pollOrderStatus(retry + 1), DELAY)
  168. } catch (error) {
  169. this.logger.warn(`轮询支付状态失败:${error.stack}`)
  170. setTimeout(() => pollOrderStatus(retry + 1), DELAY)
  171. }
  172. }
  173. // 启动轮询
  174. pollOrderStatus()
  175. } catch {
  176. this.logger.info(`获取订单支付状态失败!${error.stack}`)
  177. }
  178. } else {
  179. return res.json({
  180. ...BaseStdResponse.ERR,
  181. msg: '创建订单失败',
  182. endpoint: 7894378
  183. })
  184. }
  185. } catch (err) {
  186. this.logger.error(`创建订单失败!${err.stack}`)
  187. return res.json({
  188. ...BaseStdResponse.ERR,
  189. msg: "创建订单异常,请联系管理员"
  190. })
  191. }
  192. }
  193. }
  194. module.exports.CreateOrder = CreateOrder