el="diff-ba65c4ef9bc7b3a9d0c6c489fb810602350c1258R146">146
+ class Meta:
+ verbose_name = _('直播间商品信息')
+ verbose_name_plural = _('直播间商品信息')
+
+ def __unicode__(self):
+ return self.pk
+
+class RoomOrderInfo(BaseModelMixin):
+ """
+ # Trade State of Wechat Query
+ SUCCESS ——— 支付成功
+ REFUND ——— 转入退款
+ NOTPAY ——— 未支付
+ CLOSED ——— 已关闭
+ REVOKED ——— 已撤销(刷卡支付)
+ USERPAYING ——— 用户支付中
+ PAYERROR ——— 支付失败(其他原因,如银行返回失败)
+ """
+
+ WAITING_PAY = 0
+ PAID = 1
+ FAIL = 2
+ # DELETED = 9
+
+ PAY_STATUS = (
+ (WAITING_PAY, '待支付'),
+ (PAID, '已支付'),
+ (FAIL, '已失败'),
+ # (DELETED, '已删除'),
+ )
+
+ order_id = ShortUUIDField(_('order_id'), max_length=32, blank=True, help_text='订单唯一标识', unique=True)
+ user_id = models.CharField(_('user_id'), max_length=32, help_text='用户唯一标识', db_index=True)
+ room_id = models.CharField(_('room_id'), max_length=32, help_text='房间唯一标识', db_index=True)
+ anchor_id = models.CharField(_('anchor_id'), max_length=32, blank=True, help_text='主播唯一标识')
+ goods_id = models.CharField(_('goods_id'), max_length=32, blank=True, help_text='商品唯一标识')
+ share_openid = models.CharField(_('share_openid'), max_length=32, blank=True, help_text='转发用户 openid')
+
+ amount = models.IntegerField(_('amount'), default=0, help_text='数量')
+
+ name = models.CharField(_('name'), max_length=255, blank=True, null=True, help_text='姓名')
+ phone = models.CharField(_('phone'), max_length=255, blank=True, null=True, help_text='电话')
+ address = models.CharField(_('address'), max_length=255, blank=True, null=True, help_text='地址')
+
+ tracking_number = models.CharField(_('tracking_number'), max_length=255, blank=True, null=True, help_text='快递单号')
+ has_send_template_message = models.BooleanField(_('has_send_template_message'), default=True, help_text='是否已发送模版消息', db_index=True)
+
+ prepay_id = models.CharField(_('prepay_id'), max_length=64, blank=True, null=True, help_text='预支付交易会话标识')
+ transaction_id = models.CharField(_('transaction_id'), max_length=32, blank=True, null=True, help_text='交易单号')
+
+ body = models.CharField(_('body'), max_length=255, blank=True, null=True, help_text='商品描述')
+ total_fee = models.IntegerField(_('total_fee'), default=0, help_text='总金额')
+
+ trade_type = models.CharField(_('trade_type'), max_length=255, blank=True, null=True, help_text='支付方式')
+
+ pay_status = models.IntegerField(_('pay_status'), choices=PAY_STATUS, default=WAITING_PAY, help_text='支付状态', db_index=True)
+ paid_at = models.DateTimeField(_('paid_at'), blank=True, null=True, help_text=_('支付时间'))
+
+ reback_status = models.BooleanField(_('reback_status'), default=False, help_text='退款状态', db_index=True)
+ reback_at = models.DateTimeField(_('reback_at'), blank=True, null=True, help_text=_('退款时间'))
+
+ # 微信统一下单
+ unifiedorder_result = models.TextField(_('unifiedorder_result'), blank=True, null=True, help_text=_('统一下单结果'))
+ # 微信支付回调
+ notify_msg = models.TextField(_('notify_msg'), blank=True, null=True, help_text='回调信息')
+
+ class Meta:
+ verbose_name = _('直播间订单')
+ verbose_name_plural = _('直播间订单')
+
+ def __unicode__(self):
+ return self.pk
@@ -0,0 +1,3 @@ |
||
| 1 |
+from django.test import TestCase |
|
| 2 |
+ |
|
| 3 |
+# Create your tests here. |
@@ -0,0 +1,248 @@ |
||
| 1 |
+from django.shortcuts import render |
|
| 2 |
+from django_query import get_query_value |
|
| 3 |
+from django.db import transaction |
|
| 4 |
+from django_response import response |
|
| 5 |
+from django.shortcuts import HttpResponse |
|
| 6 |
+from django_logit import logit |
|
| 7 |
+from pywe_exception import WeChatPayException |
|
| 8 |
+from pywe_pay import WeChatPay |
|
| 9 |
+from pywe_pay_notify import check_pay_notify |
|
| 10 |
+from pywe_response import WXPAY_NOTIFY_FAIL, WXPAY_NOTIFY_SUCCESS |
|
| 11 |
+from TimeConvert import TimeConvert as tc |
|
| 12 |
+ |
|
| 13 |
+from django.conf import settings |
|
| 14 |
+ |
|
| 15 |
+from live.models import liveGoodsInfo, RoomGoodsInfo, RoomOrderInfo, RoomInfo, AnchorInfo |
|
| 16 |
+from account.models import UserInfo |
|
| 17 |
+ |
|
| 18 |
+from utils.error.errno_utils import OrderStatusCode, UserStatusCode |
|
| 19 |
+ |
|
| 20 |
+WECHAT = settings.WECHAT |
|
| 21 |
+ |
|
| 22 |
+def room_goods_detail(request): |
|
| 23 |
+ goods_id = get_query_value(request, 'goods_id', '') |
|
| 24 |
+ room_id = get_query_value(request, 'room_id', '') |
|
| 25 |
+ |
|
| 26 |
+ # 校验直播间和商品 |
|
| 27 |
+ try: |
|
| 28 |
+ room_goods_info = RoomGoodsInfo.objects.get(goods_id=goods_id, room_id=room_id) |
|
| 29 |
+ except: |
|
| 30 |
+ return response() |
|
| 31 |
+ |
|
| 32 |
+ try: |
|
| 33 |
+ goods_info = liveGoodsInfo.objects.get(goods_id=goods_id) |
|
| 34 |
+ except: |
|
| 35 |
+ return response() |
|
| 36 |
+ |
|
| 37 |
+ return response(200, 'Get Room Goods Detail Success', '获取直播间商品成功', data={
|
|
| 38 |
+ 'goods_info': {
|
|
| 39 |
+ 'goods_img': goods_info.goods_img_url, |
|
| 40 |
+ 'name': goods_info.name, |
|
| 41 |
+ 'price_type': goods_info.price_type, |
|
| 42 |
+ 'price': goods_info.price, |
|
| 43 |
+ 'price2': goods_info.price2, |
|
| 44 |
+ 'inventory': room_goods_info.inventory, |
|
| 45 |
+ }, |
|
| 46 |
+ 'anchor_id': room_goods_info.anchor_id, |
|
| 47 |
+ }) |
|
| 48 |
+ |
|
| 49 |
+WECHAT = settings.WECHAT |
|
| 50 |
+ |
|
| 51 |
+def room_anchor_details(request): |
|
| 52 |
+ room_id = request.POST.get('room_id', '')
|
|
| 53 |
+ |
|
| 54 |
+ try: |
|
| 55 |
+ room = RoomInfo.objects.get(room_id=room_id) |
|
| 56 |
+ except: |
|
| 57 |
+ return response() |
|
| 58 |
+ |
|
| 59 |
+ try: |
|
| 60 |
+ anchor = AnchorInfo.objects.get(anchor_id=room.anchor_id) |
|
| 61 |
+ except: |
|
| 62 |
+ return response() |
|
| 63 |
+ |
|
| 64 |
+ rooms = RoomInfo.objects.get(anchor_id=anchor_id) |
|
| 65 |
+ rooms = [room.anchorData for room in rooms] |
|
| 66 |
+ |
|
| 67 |
+ return response(200, 'Get Room Anchor Details Success', '获取主播详情成功', data={
|
|
| 68 |
+ 'anchor': {
|
|
| 69 |
+ 'anchor_id': anchor.anchor_id, |
|
| 70 |
+ 'anchor_name': anchor.anchor_name, |
|
| 71 |
+ 'anchor_avatar': anchor.anchor_avatar_url, |
|
| 72 |
+ 'anchor_intro': anchor.anchor_intro |
|
| 73 |
+ }, |
|
| 74 |
+ 'rooms': rooms |
|
| 75 |
+ }) |
|
| 76 |
+ |
|
| 77 |
+@logit |
|
| 78 |
+@transaction.atomic |
|
| 79 |
+def live_order_create(request): |
|
| 80 |
+ """ 订单创建 """ |
|
| 81 |
+ user_id = request.POST.get('user_id', '')
|
|
| 82 |
+ room_id = request.POST.get('room_id', '')
|
|
| 83 |
+ anchor_id = request.POST.get('anchor_id', '')
|
|
| 84 |
+ goods_id = request.POST.get('goods_id', '')
|
|
| 85 |
+ share_openid = request.POST.get('share_openid', '')
|
|
| 86 |
+ |
|
| 87 |
+ name = request.POST.get('name', '')
|
|
| 88 |
+ phone = request.POST.get('phone', '')
|
|
| 89 |
+ address = request.POST.get('address', '')
|
|
| 90 |
+ |
|
| 91 |
+ amount = int(request.POST.get('amount', 0))
|
|
| 92 |
+ total_fee = int(request.POST.get('total_fee', 0)) # 总金额,单位分
|
|
| 93 |
+ |
|
| 94 |
+ body = request.POST.get('body', '尖货直播') # 商品描述
|
|
| 95 |
+ |
|
| 96 |
+ # 用户校验 |
|
| 97 |
+ try: |
|
| 98 |
+ user = UserInfo.objects.get(user_id=user_id, status=True) |
|
| 99 |
+ user.consignee_name = name |
|
| 100 |
+ user.consignee_phone = phone |
|
| 101 |
+ user.consignee_address = address |
|
| 102 |
+ user.save() |
|
| 103 |
+ except UserInfo.DoesNotExist: |
|
| 104 |
+ return response(UserStatusCode.USER_NOT_FOUND) |
|
| 105 |
+ |
|
| 106 |
+ # 校验直播间和商品 |
|
| 107 |
+ try: |
|
| 108 |
+ room_goods_info = RoomGoodsInfo.objects.get(goods_id=goods_id, room_id=room_id, anchor_id=anchor_id) |
|
| 109 |
+ except: |
|
| 110 |
+ return response(400001, 'Room Goods Not Found', description='直播间商品不存在') |
|
| 111 |
+ |
|
| 112 |
+ # 金额校验 |
|
| 113 |
+ try: |
|
| 114 |
+ goods_info = liveGoodsInfo.objects.get(goods_id=goods_id) |
|
| 115 |
+ if amount * int(goods_info.price) != total_fee: |
|
| 116 |
+ return response(404091, 'FEE Check Fail', description='金额校验失败') |
|
| 117 |
+ except: |
|
| 118 |
+ return response(404091, 'FEE Check Fail', description='金额校验失败') |
|
| 119 |
+ |
|
| 120 |
+ # 消库存 |
|
| 121 |
+ room_goods_info.inventory -= 1 |
|
| 122 |
+ room_goods_info.save() |
|
| 123 |
+ |
|
| 124 |
+ # JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里 |
|
| 125 |
+ trade_type = 'JSAPI' |
|
| 126 |
+ |
|
| 127 |
+ # 根据 trade_type 获取 wechat 配置 |
|
| 128 |
+ wxcfg = WECHAT.get(trade_type, {})
|
|
| 129 |
+ # WeChatPay 初始化 |
|
| 130 |
+ wxpay = WeChatPay(wxcfg.get('appID'), wxcfg.get('apiKey'), wxcfg.get('mchID'))
|
|
| 131 |
+ |
|
| 132 |
+ # 生成订单 |
|
| 133 |
+ order = RoomOrderInfo.objects.create( |
|
| 134 |
+ user_id=user_id, |
|
| 135 |
+ room_id=room_id, |
|
| 136 |
+ anchor_id=anchor_id, |
|
| 137 |
+ goods_id=goods_id, |
|
| 138 |
+ share_openid=share_openid, |
|
| 139 |
+ amount=amount, |
|
| 140 |
+ total_fee=total_fee, |
|
| 141 |
+ trade_type=trade_type, |
|
| 142 |
+ name=name, |
|
| 143 |
+ phone=phone, |
|
| 144 |
+ address=address, |
|
| 145 |
+ ) |
|
| 146 |
+ |
|
| 147 |
+ try: |
|
| 148 |
+ prepay_data = wxpay.order.create( |
|
| 149 |
+ body=body, |
|
| 150 |
+ notify_url=settings.API_DOMAIN + 'live/pay/notify_url', |
|
| 151 |
+ out_trade_no=order.order_id, |
|
| 152 |
+ total_fee=total_fee, |
|
| 153 |
+ trade_type=trade_type, |
|
| 154 |
+ openid=user.openid, # 可选,用户在商户appid下的唯一标识。trade_type=JSAPI,此参数必传 |
|
| 155 |
+ ) |
|
| 156 |
+ except WeChatPayException as e: |
|
| 157 |
+ order.unifiedorder_result = e.args |
|
| 158 |
+ order.save() |
|
| 159 |
+ return response(OrderStatusCode.UNIFIED_ORDER_FAIL) |
|
| 160 |
+ |
|
| 161 |
+ prepay_id = prepay_data.get('prepay_id', '')
|
|
| 162 |
+ order.prepay_id = prepay_id |
|
| 163 |
+ order.save() |
|
| 164 |
+ |
|
| 165 |
+ wxpay_params = wxpay.jsapi.get_jsapi_params(prepay_id) |
|
| 166 |
+ |
|
| 167 |
+ |
|
| 168 |
+ return response(200, 'Order Create Success', '订单创建成功', {
|
|
| 169 |
+ 'order_id': order.order_id, |
|
| 170 |
+ 'prepay_id': prepay_id, |
|
| 171 |
+ 'wxpay_params': wxpay_params, |
|
| 172 |
+ }) |
|
| 173 |
+ |
|
| 174 |
+def live_order_cancel(request): |
|
| 175 |
+ user_id = request.POST.get('user_id', '')
|
|
| 176 |
+ order_id = request.POST.get('order_id', '')
|
|
| 177 |
+ prepay_id = request.POST.get('prepay_id', '')
|
|
| 178 |
+ |
|
| 179 |
+ try: |
|
| 180 |
+ order = RoomOrderInfo.objects.get(user_id=user_id, order_id=order_id, prepay_id=prepay_id) |
|
| 181 |
+ except: |
|
| 182 |
+ return response(400001, 'Order Not Found', description='直播间订单不存在') |
|
| 183 |
+ |
|
| 184 |
+ if order.pay_status == RoomOrderInfo.FAIL: |
|
| 185 |
+ return response(200, 'Order Cancel Success', '订单取消成功') |
|
| 186 |
+ |
|
| 187 |
+ order.pay_status = RoomOrderInfo.FAIL |
|
| 188 |
+ order.save() |
|
| 189 |
+ |
|
| 190 |
+ try: |
|
| 191 |
+ goods_info = RoomGoodsInfo.objects.get(room_id=order.room_id, goods_id=order.goods_id, anchor_id=order.anchor_id) |
|
| 192 |
+ goods_info.inventory += order.amount |
|
| 193 |
+ goods_info.save() |
|
| 194 |
+ except: |
|
| 195 |
+ return response(400001, 'Room Goods Not Found', description='直播间商品不存在') |
|
| 196 |
+ |
|
| 197 |
+ return response(200, 'Order Cancel Success', '订单取消成功') |
|
| 198 |
+ |
|
| 199 |
+def order_paid_success(order): |
|
| 200 |
+ if order.pay_status == RoomOrderInfo.PAID: |
|
| 201 |
+ return |
|
| 202 |
+ |
|
| 203 |
+ order.pay_status = RoomOrderInfo.PAID |
|
| 204 |
+ order.paid_at = tc.utc_datetime() |
|
| 205 |
+ order.save() |
|
| 206 |
+ |
|
| 207 |
+ try: |
|
| 208 |
+ goods_info = RoomGoodsInfo.objects.get(room_id=order.room_id, goods_id=order.goods_id, anchor_id=order.anchor_id) |
|
| 209 |
+ goods_info.sale_infos += [order.order_id] |
|
| 210 |
+ goods_info.save() |
|
| 211 |
+ except: |
|
| 212 |
+ return |
|
| 213 |
+ |
|
| 214 |
+def order_paid_fail(order): |
|
| 215 |
+ if order.pay_status == RoomGoodsInfo.FAIL: |
|
| 216 |
+ return |
|
| 217 |
+ |
|
| 218 |
+ order.pay_status = RoomGoodsInfo.FAIL |
|
| 219 |
+ order.save() |
|
| 220 |
+ |
|
| 221 |
+ try: |
|
| 222 |
+ goods_info = RoomGoodsInfo.objects.get(room_id=order.room_id, goods_id=order.goods_id, anchor_id=order.anchor_id) |
|
| 223 |
+ goods_info.inventory += order.amount |
|
| 224 |
+ goods_info.save() |
|
| 225 |
+ except: |
|
| 226 |
+ return |
|
| 227 |
+ |
|
| 228 |
+@logit |
|
| 229 |
+@transaction.atomic |
|
| 230 |
+def notify_url_api(request): |
|
| 231 |
+ """ 支付异步通知回调地址 """ |
|
| 232 |
+ notify_data, success = check_pay_notify(request.body, wx_configs=settings.WECHAT) |
|
| 233 |
+ if not success: |
|
| 234 |
+ return HttpResponse(WXPAY_NOTIFY_FAIL) |
|
| 235 |
+ |
|
| 236 |
+ order = RoomOrderInfo.objects.select_for_update().get(order_id=notify_data.get('out_trade_no', ''), status=True)
|
|
| 237 |
+ |
|
| 238 |
+ order.notify_msg = request.body |
|
| 239 |
+ order.transaction_id = notify_data.get('transaction_id', '')
|
|
| 240 |
+ order.save() |
|
| 241 |
+ |
|
| 242 |
+ result_code = notify_data.get('result_code', '')
|
|
| 243 |
+ if result_code == 'SUCCESS': |
|
| 244 |
+ live_views.order_paid_success(order) |
|
| 245 |
+ else: |
|
| 246 |
+ live_views.order_paid_fail(order) |
|
| 247 |
+ |
|
| 248 |
+ return HttpResponse(WXPAY_NOTIFY_SUCCESS) |
@@ -18,8 +18,11 @@ from account.models import UserInfo |
||
| 18 | 18 |
from goods.models import GoodsInfo, PackGoodsInfo, PackGoodsSaleInfo, PackInfo |
| 19 | 19 |
from kol.models import KOLInfo |
| 20 | 20 |
from pay.models import OrderInfo |
| 21 |
+from live.models import RoomOrderInfo |
|
| 21 | 22 |
from utils.error.errno_utils import KOLStatusCode, OrderStatusCode, PackGoodsStatusCode, PackStatusCode, UserStatusCode |
| 22 | 23 |
|
| 24 |
+from live import views as live_views |
|
| 25 |
+ |
|
| 23 | 26 |
|
| 24 | 27 |
WECHAT = settings.WECHAT |
| 25 | 28 |
|
@@ -215,19 +218,22 @@ def wx_notify_url_api(request): |
||
| 215 | 218 |
if not success: |
| 216 | 219 |
return HttpResponse(WXPAY_NOTIFY_FAIL) |
| 217 | 220 |
|
| 221 |
+ #尖货接龙订单 |
|
| 218 | 222 |
try: |
| 219 | 223 |
order = OrderInfo.objects.select_for_update().get(order_id=notify_data.get('out_trade_no', ''), status=True)
|
| 220 |
- except OrderInfo.DoesNotExist: |
|
| 221 |
- return HttpResponse(WXPAY_NOTIFY_FAIL) |
|
| 224 |
+ order.notify_msg = request.body |
|
| 225 |
+ order.transaction_id = notify_data.get('transaction_id', '')
|
|
| 226 |
+ order.save() |
|
| 222 | 227 |
|
| 223 |
- order.notify_msg = request.body |
|
| 224 |
- order.transaction_id = notify_data.get('transaction_id', '')
|
|
| 225 |
- order.save() |
|
| 228 |
+ result_code = notify_data.get('result_code', '')
|
|
| 229 |
+ if result_code == 'SUCCESS': |
|
| 230 |
+ order_paid_success(order) |
|
| 231 |
+ else: |
|
| 232 |
+ order_paid_fail(order) |
|
| 233 |
+ |
|
| 234 |
+ return HttpResponse(WXPAY_NOTIFY_SUCCESS) |
|
| 235 |
+ except: |
|
| 236 |
+ return HttpResponse(WXPAY_NOTIFY_FAIL) |
|
| 226 | 237 |
|
| 227 |
- result_code = notify_data.get('result_code', '')
|
|
| 228 |
- if result_code == 'SUCCESS': |
|
| 229 |
- order_paid_success(order) |
|
| 230 |
- else: |
|
| 231 |
- order_paid_fail(order) |
|
| 232 | 238 |
|
| 233 | 239 |
return HttpResponse(WXPAY_NOTIFY_SUCCESS) |