index.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. const cloud = require('wx-server-sdk');
  2. cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
  3. const db = cloud.database();
  4. // 支付成功回调(微信支付结果通知)
  5. // 处理:能量师订阅升级 + 佣金结算
  6. exports.main = async (event) => {
  7. const {
  8. outTradeNo, // 订单号
  9. transactionId, // 微信支付订单号
  10. totalFee, // 支付金额(分)
  11. attach, // 附加数据
  12. } = event;
  13. // 获取支付用户的 openId
  14. const { OPENID } = cloud.getWXContext();
  15. try {
  16. // 1. 查询订单是否已处理(幂等)
  17. const orderRes = await db.collection('orders').where({
  18. outTradeNo,
  19. }).get();
  20. if (orderRes.data.length > 0) {
  21. return { code: 0, message: '订单已处理' };
  22. }
  23. // 2. 写订单记录
  24. const orderData = {
  25. outTradeNo,
  26. transactionId,
  27. userId: OPENID,
  28. type: 'practitioner_annual',
  29. amount: totalFee,
  30. paymentMethod: 'wechat_pay',
  31. status: 'paid',
  32. paidAt: db.serverDate(),
  33. createdAt: db.serverDate(),
  34. };
  35. await db.collection('orders').add({ data: orderData });
  36. // 3. 更新用户为能量师(有效期 1 年)
  37. const now = new Date();
  38. const expireDate = new Date(now);
  39. expireDate.setFullYear(expireDate.getFullYear() + 1);
  40. // 查询已有用户
  41. const userRes = await db.collection('users').where({
  42. _openid: OPENID,
  43. }).get();
  44. const userData = {
  45. role: 'practitioner',
  46. practitionerExpireAt: expireDate.toISOString(),
  47. updatedAt: db.serverDate(),
  48. };
  49. if (userRes.data.length === 0) {
  50. // 新用户注册(一般不会走到这里,但以防万一)
  51. userData._openid = OPENID;
  52. userData.referralCode = generateReferralCode();
  53. userData.createdAt = db.serverDate();
  54. await db.collection('users').add({ data: userData });
  55. } else {
  56. // 已有用户,生成或保留推广码
  57. const user = userRes.data[0];
  58. if (!user.referralCode) {
  59. userData.referralCode = generateReferralCode();
  60. }
  61. await db.collection('users').doc(user._id).update({ data: userData });
  62. }
  63. // 4. 佣金结算:查用户的上级
  64. const user = (await db.collection('users').where({ _openid: OPENID }).get()).data[0];
  65. const invitedBy = user.invitedBy;
  66. if (invitedBy) {
  67. // 查上级是否为有效的能量师
  68. const parentRes = await db.collection('users').where({ _openid: invitedBy }).get();
  69. if (parentRes.data.length > 0) {
  70. const parent = parentRes.data[0];
  71. const isParentPractitioner = parent.practitionerExpireAt
  72. && new Date(parent.practitionerExpireAt).getTime() > Date.now();
  73. if (isParentPractitioner) {
  74. // 一级佣金:30%
  75. const level1Amount = Math.round(totalFee * 0.30);
  76. await db.collection('commissions').add({
  77. data: {
  78. fromUser: OPENID,
  79. toUser: invitedBy,
  80. level: 1,
  81. amount: level1Amount,
  82. sourceOrderId: outTradeNo,
  83. status: 'pending',
  84. createdAt: db.serverDate(),
  85. updatedAt: db.serverDate(),
  86. },
  87. });
  88. // 更新上级的推广统计
  89. const stats = parent.referralStats || {};
  90. await db.collection('users').doc(parent._id).update({
  91. data: {
  92. 'referralStats.convertedCount': (stats.convertedCount || 0) + 1,
  93. },
  94. });
  95. // 查上上级(二级佣金)
  96. const grandParentInvitedBy = parent.invitedBy;
  97. if (grandParentInvitedBy) {
  98. const gpRes = await db.collection('users').where({ _openid: grandParentInvitedBy }).get();
  99. if (gpRes.data.length > 0) {
  100. const gp = gpRes.data[0];
  101. const isGpPractitioner = gp.practitionerExpireAt
  102. && new Date(gp.practitionerExpireAt).getTime() > Date.now();
  103. if (isGpPractitioner) {
  104. // 二级佣金:10%
  105. const level2Amount = Math.round(totalFee * 0.10);
  106. await db.collection('commissions').add({
  107. data: {
  108. fromUser: OPENID,
  109. toUser: grandParentInvitedBy,
  110. level: 2,
  111. amount: level2Amount,
  112. sourceOrderId: outTradeNo,
  113. status: 'pending',
  114. createdAt: db.serverDate(),
  115. updatedAt: db.serverDate(),
  116. },
  117. });
  118. }
  119. }
  120. }
  121. }
  122. }
  123. }
  124. return { code: 0, message: 'success' };
  125. } catch (err) {
  126. console.error('[paymentCallback] error:', err);
  127. return { code: 500, error: err.message };
  128. }
  129. };
  130. // 生成唯一6位推广码
  131. function generateReferralCode() {
  132. const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  133. let code = '';
  134. for (let i = 0; i < 6; i++) {
  135. code += chars[Math.floor(Math.random() * chars.length)];
  136. }
  137. return code;
  138. }