暂无描述

views.py 11KB

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