拍爱

views.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. # -*- coding: utf-8 -*-
  2. from django.conf import settings
  3. from django.db import transaction
  4. from django.shortcuts import HttpResponse
  5. from django_logit import logit
  6. from django_response import response
  7. from paginator import pagination
  8. from pywe_exception import WeChatPayException
  9. from pywe_pay import WeChatPay
  10. from pywe_pay_notify import check_pay_notify
  11. from pywe_response import WXPAY_NOTIFY_FAIL, WXPAY_NOTIFY_SUCCESS
  12. from pywe_sign import check_signature
  13. from TimeConvert import TimeConvert as tc
  14. from account.models import UserIncomeExpensesInfo, UserInfo
  15. from group.models import GroupPhotoInfo, GroupPhotoOrderInfo
  16. from pay.models import OrderInfo
  17. from photo.models import PhotosInfo
  18. from utils.error.errno_utils import (GroupPhotoStatusCode, OrderStatusCode, UserStatusCode, WechatStatusCode,
  19. WithdrawStatusCode)
  20. from utils.price_utils import get_group_photo_price
  21. from utils.redis.rbrief import set_brief_info
  22. from utils.redis.rorder import set_lensman_order_record
  23. from utils.wx_utils import get_trade_type, get_user_openid
  24. WECHAT = settings.WECHAT
  25. @logit
  26. @transaction.atomic
  27. def wx_order_create_api(request):
  28. """ 订单创建 """
  29. group_id = request.POST.get('group_id', '')
  30. user_id = request.POST.get('user_id', '')
  31. photo_id = request.POST.get('photo_id', '')
  32. photo_type = request.POST.get('photo_type', 'nomark') # nomark for 去除水印, origin for 获取高清图
  33. photo_type_int = OrderInfo.ORIGIN if photo_type == 'origin' else OrderInfo.NOMARK
  34. # 用户校验
  35. try:
  36. user = UserInfo.objects.get(user_id=user_id)
  37. except UserInfo.DoesNotExist:
  38. return response(UserStatusCode.USER_NOT_FOUND)
  39. # 群组照片校验
  40. try:
  41. group_photo = GroupPhotoInfo.objects.get(photo_id=photo_id, status=True)
  42. except GroupPhotoInfo.DoesNotExist:
  43. return response(GroupPhotoStatusCode.GROUP_PHOTO_NOT_FOUND)
  44. # 判断是否重复购买
  45. if OrderInfo.objects.filter(photo_id=photo_id, photo_type=photo_type_int, from_uid=user_id, pay_status=OrderInfo.PAID, status=True).exists():
  46. return response(OrderStatusCode.WX_ORDER_PAID_ALREADY_EXISTS)
  47. body = request.POST.get('body', '') # 商品描述
  48. total_fee = int(request.POST.get('total_fee', 0)) # 总金额,单位分
  49. # 金额校验
  50. if get_group_photo_price(group_photo, photo_type) != total_fee:
  51. return response(OrderStatusCode.FEE_CHECK_FAIL)
  52. # 获取 from_uid, to_uid
  53. from_uid = user_id
  54. to_uid = group_photo.lensman_id or group_photo.user_id
  55. # JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
  56. trade_type = request.POST.get('trade_type', '')
  57. # 根据 trade_type 获取 wechat 配置
  58. wxcfg = WECHAT.get(trade_type, {})
  59. # WeChatPay 初始化
  60. wxpay = WeChatPay(wxcfg.get('appID'), wxcfg.get('apiKey'), wxcfg.get('mchID'))
  61. # 生成订单
  62. order = OrderInfo.objects.create(
  63. group_id=group_id,
  64. photo_id=photo_id,
  65. lensman_photo_id=group_photo.lensman_photo_id,
  66. photo_type=photo_type_int,
  67. from_uid=from_uid,
  68. to_uid=to_uid,
  69. session_id=group_photo.session_id,
  70. total_fee=total_fee,
  71. trade_type=trade_type,
  72. )
  73. try:
  74. prepay_data = wxpay.order.create(
  75. body=body,
  76. notify_url=settings.API_DOMAIN + '/wx/notify_url',
  77. out_trade_no=order.order_id,
  78. total_fee=total_fee,
  79. trade_type=get_trade_type(trade_type),
  80. openid=get_user_openid(user, trade_type), # 可选,用户在商户appid下的唯一标识。trade_type=JSAPI,此参数必传
  81. )
  82. except WeChatPayException as e:
  83. order.unifiedorder_result = e.message
  84. order.save()
  85. return response(OrderStatusCode.WX_UNIFIED_ORDER_FAIL)
  86. prepay_id = prepay_data.get('prepay_id', '')
  87. order.prepay_id = prepay_id
  88. order.save()
  89. if trade_type == 'JSAPI' or trade_type == 'MINIAPP':
  90. wxpay_params = wxpay.jsapi.get_jsapi_params(prepay_id)
  91. elif trade_type == 'APP':
  92. wxpay_params = wxpay.order.get_appapi_params(prepay_id)
  93. return response(200, 'Order Create Success', u'订单创建成功', {
  94. 'order_id': order.order_id,
  95. 'prepay_id': prepay_id,
  96. 'wxpay_params': wxpay_params,
  97. })
  98. def order_paid_success(order):
  99. if order.pay_status == OrderInfo.PAID:
  100. return
  101. if order.photo_type == OrderInfo.NOMARK:
  102. order.photo_status = OrderInfo.FETCHED
  103. order.pay_status = OrderInfo.PAID
  104. order.paid_at = tc.utc_datetime()
  105. order.save()
  106. porder, created = GroupPhotoOrderInfo.objects.select_for_update().get_or_create(
  107. group_id=order.group_id,
  108. session_id=order.session_id,
  109. user_id=order.from_uid,
  110. photo_id=order.photo_id,
  111. lensman_photo_id=order.lensman_photo_id,
  112. )
  113. photo = PhotosInfo.objects.get(
  114. lensman_id=order.to_uid,
  115. session_id=order.session_id,
  116. photo_id=order.lensman_photo_id,
  117. )
  118. if order.photo_type == OrderInfo.NOMARK:
  119. porder.m_photo_path = photo.m_photo_path
  120. elif order.photo_type == OrderInfo.ORIGIN:
  121. porder.r_photo_path = photo.r_photo_path
  122. porder.save()
  123. set_lensman_order_record(porder)
  124. to_uid = order.to_uid
  125. total_fee = order.total_fee
  126. try:
  127. user = UserInfo.objects.select_for_update().get(user_id=to_uid)
  128. except UserInfo.DoesNotExist:
  129. return
  130. if order.photo_type == OrderInfo.NOMARK:
  131. # 余额增加
  132. amount, freeze_income_amount = total_fee, 0
  133. user.balance += amount
  134. # Redis 数值更新
  135. set_brief_info(to_uid, order.photo_type, total_fee)
  136. # 余额记录
  137. UserIncomeExpensesInfo.objects.create(
  138. user_id=to_uid,
  139. photo_id=order.photo_id,
  140. type=UserIncomeExpensesInfo.INCOME,
  141. amount=amount,
  142. balance=user.balance,
  143. freeze_income_amount=freeze_income_amount,
  144. freeze_income_balance=user.freeze_income_balance,
  145. remark=u'图片购买',
  146. )
  147. elif order.photo_type == OrderInfo.ORIGIN:
  148. amount, freeze_income_amount = 0, total_fee
  149. user.freeze_income_balance += freeze_income_amount
  150. user.save()
  151. def order_paid_fail(order):
  152. if order.pay_status == OrderInfo.FAIL:
  153. return
  154. order.pay_status = OrderInfo.FAIL
  155. order.save()
  156. @logit
  157. @transaction.atomic
  158. def wx_order_query_api(request):
  159. """ 订单查询 """
  160. order_id = request.POST.get('order_id', '')
  161. transaction_id = request.POST.get('transaction_id', '')
  162. try:
  163. order = OrderInfo.objects.select_for_update().get(order_id=order_id, status=True)
  164. except OrderInfo.DoesNotExist:
  165. return response(OrderStatusCode.WX_ORDER_NOT_FOUND)
  166. if order.pay_status == OrderInfo.PAID:
  167. return response(200, 'Order Pay Success', u'订单支付成功')
  168. elif order.pay_status == OrderInfo.FAIL:
  169. return response(OrderStatusCode.WX_ORDER_PAY_FAIL)
  170. # 根据 trade_type 获取 wechat 配置
  171. wxcfg = WECHAT.get(order.trade_type, {})
  172. # WeChatPay 初始化
  173. wxpay = WeChatPay(wxcfg.get('appID'), wxcfg.get('apiKey'), wxcfg.get('mchID'))
  174. # 订单查询
  175. query_data = wxpay.order.query(transaction_id, order_id)
  176. # 签名校验
  177. if not check_signature(query_data, wxcfg.get('apiKey')):
  178. return response(OrderStatusCode.SIGN_CHECK_FAIL)
  179. order.notify_msg = query_data
  180. order.transaction_id = query_data.get('transaction_id', '')
  181. order.save()
  182. # 交易状态
  183. trade_state = query_data.get('trade_state')
  184. # 订单状态判断更新
  185. if trade_state == 'SUCCESS': # 订单支付成功
  186. order_paid_success(order)
  187. return response(200, 'Order Pay Success', u'订单支付成功')
  188. elif trade_state == 'NOTPAY': # 订单未支付
  189. return response(OrderStatusCode.WX_ORDER_NOT_PAY)
  190. elif trade_state == 'USERPAYING': # 订单支付中
  191. return response(OrderStatusCode.WX_ORDER_PAYING)
  192. else: # 订单支付失败
  193. order_paid_fail(order)
  194. return response(OrderStatusCode.WX_ORDER_PAY_FAIL)
  195. @logit
  196. @transaction.atomic
  197. def wx_order_list_api(request):
  198. """ 订单列表 """
  199. user_id = request.POST.get('user_id', '')
  200. page = int(request.POST.get('page', 1))
  201. num = int(request.POST.get('num', settings.ORDER_NUM_PER_PAGE))
  202. orders = OrderInfo.objects.filter(from_uid=user_id, pay_status=OrderInfo.PAID, status=True).order_by('-pk')
  203. orders, left = pagination(orders, page, num)
  204. orders = [order.data(user_id) for order in orders]
  205. return response(200, 'Get Order List Success', u'获取订单列表成功', {
  206. 'orders': orders,
  207. 'left': left,
  208. })
  209. @logit
  210. @transaction.atomic
  211. def wx_order_detail_api(request):
  212. """ 订单详情 """
  213. user_id = request.POST.get('user_id', '')
  214. order_id = request.POST.get('order_id', '')
  215. try:
  216. order = OrderInfo.objects.get(order_id=order_id, status=True)
  217. except OrderInfo.DoesNotExist:
  218. return response(OrderStatusCode.WX_ORDER_NOT_FOUND)
  219. if user_id not in [order.from_uid, order.to_uid]:
  220. return response(OrderStatusCode.NO_DETAIL_PERMISSION)
  221. return response(200, 'Get Order Detail Success', u'获取订单详情成功', order.data(user_id))
  222. @logit
  223. @transaction.atomic
  224. def wx_notify_url_api(request):
  225. """ 支付异步通知回调地址 """
  226. notify_data, success = check_pay_notify(request.body, wx_configs=settings.WECHAT)
  227. if not success:
  228. return HttpResponse(WXPAY_NOTIFY_FAIL)
  229. try:
  230. order = OrderInfo.objects.select_for_update().get(order_id=notify_data.get('out_trade_no', ''), status=True)
  231. except OrderInfo.DoesNotExist:
  232. return HttpResponse(WXPAY_NOTIFY_FAIL)
  233. order.notify_msg = request.body
  234. order.transaction_id = notify_data.get('transaction_id', '')
  235. order.save()
  236. result_code = notify_data.get('result_code', '')
  237. if result_code == 'SUCCESS':
  238. order_paid_success(order)
  239. else:
  240. order_paid_fail(order)
  241. return HttpResponse(WXPAY_NOTIFY_SUCCESS)
  242. @logit
  243. @transaction.atomic
  244. def wx_balance_withdraw_api(request):
  245. user_id = request.POST.get('user_id', '')
  246. # 用户校验
  247. try:
  248. user = UserInfo.objects.select_for_update().get(user_id=user_id)
  249. except UserInfo.DoesNotExist:
  250. return response(UserStatusCode.USER_NOT_FOUND)
  251. # JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
  252. trade_type = request.POST.get('trade_type', '')
  253. # TRANSFER--企业付款、PACKET--现金红包, 余额提现接口withdraw_type的传参可参考这里
  254. withdraw_type = request.POST.get('withdraw_type', 'TRANSFER')
  255. amount = int(request.POST.get('amount', 0))
  256. if not user.openid:
  257. return response(WechatStatusCode.OPENID_NOT_FOUND)
  258. if user.balance < amount:
  259. return response(WithdrawStatusCode.BALANCE_NOT_ENOUGH)
  260. # 根据 trade_type 获取 wechat 配置
  261. wxcfg = WECHAT.get(trade_type, {})
  262. # WeChatPay 初始化
  263. wxpay = WeChatPay(wxcfg.get('appID'), wxcfg.get('apiKey'), wxcfg.get('mchID'), mch_cert=wxcfg.get('mch_cert'), mch_key=wxcfg.get('mch_key'))
  264. if withdraw_type == 'TRANSFER':
  265. ret_data = wxpay.transfer.transfer(user.openid, amount, u'摄影师余额提现,企业付款', check_name='NO_CHECK')
  266. elif withdraw_type == 'PACKET':
  267. wxrpk = wxcfg.get('redpack', {})
  268. ret_data = wxpay.redpack.send(
  269. user.openid,
  270. amount,
  271. send_name=wxrpk.get('SEND_NAME'),
  272. nick_name=wxrpk.get('NICK_NAME'),
  273. act_name=wxrpk.get('ACT_NAME'),
  274. wishing=wxrpk.get('WISHING'),
  275. remark=wxrpk.get('REMARK'),
  276. )
  277. # 根据 ret_data 判断是否提现成功, 成功则减余额, 失败则提示
  278. user.balance -= amount
  279. user.save()
  280. return response(200, 'Withdraw Success', u'提现成功', {})