def get(self, order_no):
        order = TeamOrder.get_or_404(order_no=order_no)

        self.write({
            "order_no": order.order_no,
            "team_id": order.team_id,
            "state": TeamOrder.OrderState(order.state).name
        })
    def post(self, match_id):
        """"
        TODO: 如果回调时赛事已满员则自动退款处理

        Parteam Api request example: {
          "message": "成功",
          "code": 200,
          "attribute": {
              "userId": 169,
              "orderNo": "11354642167546",
              "paymentMethod":1, // 支付类型 1微信,2支付宝
              "orderState":2 // // 订单状态 1 待付款 2 已付款 3 超时未付款 4 已发货
          }
        }
        """
        data = self.request.body.decode()
        data = json.loads(data)

        if not data.get("attribute"):
            raise ApiException(400, "未包含 attribute")

        order = data['attribute']
        member = MatchMember.get_or_404(pt_order_no=order['orderNo'])

        if order['orderState'] in (2, 4):

            # 将状态为未支付报名状态修改为正常
            if member.state == MatchMember.MatchMemberState.wait_pay:

                payment_method = (TeamOrder.OrderPaymentMethod.WXPAY,
                                  TeamOrder.OrderPaymentMethod.ALIPAY
                                  )[order['paymentMethod'] - 1]

                with self.db.transaction():
                    MatchMember.update(
                        state=MatchMember.MatchMemberState.normal).where(
                            MatchMember.id == member.id).execute()

                    TeamOrder.update(
                        paid=datetime.now(),
                        state=TeamOrder.OrderState.TRADE_BUYER_PAID,
                        gateway_trade_no=order['orderNo'],
                        payment_method=payment_method).where(
                            TeamOrder.id == member.order_id).execute()

                # 统计赛事人数
                Match.update_members_count(member.match_id)
                if member.group_id > 0:
                    MatchGroup.update_members_count(member.group_id)

            match_notify.join_match_done\
                .apply_async(args=[match_id, member.id], retry=True)

        self.write_success()
Beispiel #3
0
    def get(self):
        parteam = Parteam(self.settings["parteam_api_url"])

        state = intval(self.get_argument("state", 100))
        start = self.get_argument("start", "")
        end = self.get_argument("end", "")

        orders = TeamOrder.select(
            TeamOrder,
            MatchMember
        ).join(
            MatchMember,
            on=(TeamOrder.id == MatchMember.order_id).alias("match_member")
        ).where(
            TeamOrder.team == self.current_team
        ).order_by(
            TeamOrder.id.desc()
        )

        if start:
            start = "%s 00:00:00" % start
            orders = orders.where(TeamOrder.created >= start)

        if end:
            end = "%s 23:59:59" % end
            orders = orders.where(TeamOrder.created <= end)

        if state != 100:
            orders = orders.where(TeamOrder.state ==
                                  TeamOrder.OrderState(state))

        query = self.paginate_query(orders)
        orders = []
        for item in query:
            orders.append(dict(item.info,
                               order_type_name=item.order_type_name,
                               state_name=item.state_name,
                               payment_method_name=item.payment_method_name,
                               match_member=item.match_member.info))

        user_ids = [order["match_member"]["user_id"] for order in orders
                    if order["match_member"]["user_id"]]

        if user_ids:
            user_infos = parteam.parteam_user(user_ids)
            for order in orders:
                order["user_info"] =\
                    user_infos.get(order["match_member"]["user_id"])

        self.render("orders/orders.html",
                    states=TeamOrder.ORDER_STATES,
                    orders=orders,
                    pagination=query.pagination
                    )
Beispiel #4
0
    def get(self):
        """订单列表"""

        state = intval(self.get_argument("state", 100))
        start = self.get_argument("start", "")
        end = self.get_argument("end", "")

        query = TeamOrder.select(
            TeamOrder,
            Team,
            Activity,
        ).join(
            Team, on=(Team.id == TeamOrder.team).alias("team")
        ).switch(
            TeamOrder
        ).join(
            Activity,
            join_type=JOIN.LEFT_OUTER,
            on=(Activity.id == TeamOrder.activity_id).alias("activity")
        )

        is_restrict_by_areas = self.current_user.is_restrict_by_areas()
        if is_restrict_by_areas:
            query = query.join(
                User,
                on=(User.id == Team.owner_id)
            ).where(
                User.province << self.current_user.valid_manage_provinces
            )

        kw = self.get_argument("kw", "")
        if kw:
            query = query.where(TeamOrder.title ** ("%%%s%%" % kw))

        if start:
            start = "%s 00:00:00" % start
            query = query.where(TeamOrder.created >= start)

        if end:
            end = "%s 23:59:59" % end
            query = query.where(TeamOrder.created <= end)

        if state != 100:
            query = query.where(TeamOrder.state == TeamOrder.OrderState(state))

        query = query.order_by(TeamOrder.id.desc())
        orders = self.paginate_query(query)

        self.render("order/orders.html",
                    states=TeamOrder.ORDER_STATES,
                    orders=orders,
                    )
 def initial_data(self):
     self.team_owner = User.create(name='test_activity')
     self.team = Team.create(name='club_test_activity',
                             owner_id=self.team_owner.id)
     self.user = self.creator = User.create(name='activity_creator')
     self.activity = Activity.create(team=self.team,
                                     creator=self.creator,
                                     price='10',
                                     vip_price='8',
                                     leader=self.creator,
                                     title='just a test',
                                     description='description',
                                     need_nickname=True,
                                     join_end="3000-12-30 22:59:59",
                                     max_members=10,
                                     start_time='3000-01-01 00:00:01',
                                     end_time='3000-12-31 23:59:59')
     self.activity_2 = Activity.create(team=self.team,
                                       creator=self.creator,
                                       price='10',
                                       vip_price='8',
                                       leader=self.creator,
                                       title='just a test',
                                       description='description',
                                       need_nickname=True,
                                       join_end="3000-12-30 22:59:59",
                                       max_members=10,
                                       start_time='3000-01-01 00:00:01',
                                       end_time='3000-12-31 23:59:59')
     self.activity_member = ActivityMember.create(
         activity=self.activity,
         user=self.user,
         total_fee='0.01',
         nickname="leave activities",
         payment_method=TeamOrder.OrderPaymentMethod.WXPAY.value,
         payment_state=TeamOrder.OrderState.TRADE_BUYER_PAID,
     )
     TeamMemberService.new_order(team=self.team,
                                 activity_id=self.activity.id,
                                 user=self.user,
                                 order_type=TeamOrder.OrderType.ACTIVITY,
                                 payment_method='wxpay',
                                 total_fee=self.activity.price,
                                 payment_fee=self.activity.price,
                                 title='TestLeave')
     TeamOrder.update(state=TeamOrder.OrderState.TRADE_BUYER_PAID)\
         .where(TeamOrder.activity_id == self.activity.id,
                TeamOrder.user == self.user)\
         .execute()
Beispiel #6
0
    def test_leave_match(self):
        faker = Faker()
        mocked_user = {
            "userId": 192,
            "ptToken": faker.pystr(),
            "nickName": faker.name(),
            "userHeadPicUrl": faker.url()
        }

        mocked_parteam = mock.Mock(spec=ParteamMixin.get_session,
                                   return_value=mocked_user)
        ParteamMixin.get_session = mocked_parteam

        team_order = mixer.blend(TeamOrder,
                                 order_no=TeamOrder.get_new_order_no(),
                                 total_fee=0.01)
        mixer.blend(MatchMember,
                    match_id=self.match.id,
                    user_id=mocked_user["userId"],
                    group_id=0,
                    order_id=team_order.id,
                    total_fee=0.01)
        mocked_refund = mock.Mock(spec=Parteam.order_refund, return_value=True)
        Parteam.order_refund = mocked_refund

        url = self.LEAVE_MATCH.format(match_id=self.match.id)
        body = {}
        self.auth_user = ParteamUser(mocked_user)
        response = self.fetch(url, method="POST", body=json.dumps(body))
        self.assertEqual(204, response.code, response.body.decode())
Beispiel #7
0
    def add_member_and_finish_request(self, activity, order, total_fee,
                                      member_state, payment_state, form,
                                      extra_fields):
        """
        Args:
            activity: 要参加的活动,
            order: 订单
            total_fee: 总金额,
            member_state: ActivityMember.state,
            payment_state: TeamOrder.state
            form:
            extra_fields: 参加活动的自定义字段
        """
        activity.add_member(
            user_id=self.current_user.id,
            users_count=form['users_count'],
            price=activity.price,
            free_times=0,
            total_fee=total_fee,
            order_id=order.id if order else 0,
            order_no=order.order_no if order else 0,
            payment_state=payment_state,
            state=member_state,
            **extra_fields,
        )

        self.sync_user_info(extra_fields)
        self.auto_join_team(activity.team)

        self.write_success(
            state=member_state,
            payment_state=TeamOrder.OrderState(payment_state).name,
            order_no=order.order_no if order else ""
        )
    def test_online_pay_activity(self):
        url = self.JOIN_ACTIVITY.format(activity_id=self.activity.id)
        body = {"payment": "wxpay", "nickname": "Nick name"}
        self.auth_user = User.create(name='join activity')
        response = self.fetch(url,
                              method='POST',
                              body=json.dumps(body),
                              params={'team_id': self.team.id})
        self.assertEqual(200, response.code, response.body)

        order = TeamOrder.get_or_none(user=self.auth_user,
                                      activity_id=self.activity.id)
        self.assertIsNotNone(order, "加入活动时订单 `TeamOrder` 未创建")

        result = json.loads(response.body.decode())
        expect = {
            "status": "ok",
            "state": ActivityMember.ActivityMemberState.wait_confirm.value,
            "payment_state": TeamOrder.OrderState.WAIT_BUYER_PAY.value,
            "order_no": order.order_no
        }
        self.assertDictEqual(expect, result, result)

        member = ActivityMember.get_or_none(activity=self.activity,
                                            user=self.auth_user)
        self.assertIsNotNone(member, "加入活动后 `ActivityMember` 未添加记录")
Beispiel #9
0
    def test_get_members(self):
        total = random.randint(1, 10)
        fake_users = {}
        for i in range(1, total + 1):
            parteam_user = self.parteam_user_generator(i)
            fake_users[i] = parteam_user

            order = mixer.blend(TeamOrder,
                                order_no=TeamOrder.get_new_order_no(),
                                total_fee=0.01)
            mixer.blend(MatchMember,
                        match_id=self.match.id,
                        user_id=parteam_user.id,
                        order_id=order.id,
                        extra_attrs={},
                        total_fee=0.01)

        mocked_parteam_user = mock.Mock(Parteam.parteam_user,
                                        return_value=fake_users)
        Parteam.parteam_user = mocked_parteam_user

        url = self.MEMBERS_PATH.format(match_id=self.match.id)
        response = self.fetch(url, method="GET")
        self.assertEqual(200, response.code, response.body.decode())
        result = json.loads(response.body.decode())
        self.assertEqual(total, len(result["members"]), result)
Beispiel #10
0
    def refund(self, reason=""):
        """ 退款
        """
        # 未支付或已退款,无需退款
        if TeamOrder.OrderState(
                self.payment_state) != TeamOrder.OrderState.TRADE_BUYER_PAID:
            return True

        order = TeamOrder.get_or_none(id=self.order_id)
        if order is None:
            return True

        # 退款
        order.refund(reason=reason, free_times=self.free_times)

        return True
    def test_cash_pay(self):
        activity = Activity.create(team=self.team,
                                   creator=self.creator,
                                   price='10',
                                   vip_price='8',
                                   leader=self.creator,
                                   payment_type=Activity.PaymentType.CASH_PAY,
                                   title='no online pay',
                                   description='on need online pay',
                                   start_time='3000-01-01 00:00:01',
                                   end_time='3000-12-31 23:59:59',
                                   join_end="4000-01-01 22:59:59",
                                   max_members=10)

        url = self.JOIN_ACTIVITY.format(activity_id=activity.id)
        body = {
            "payment": TeamOrder.OrderPaymentMethod.WXPAY.value,
            "nickname": "Nick name"
        }
        self.auth_user = User.create(name='no need online pay')
        response = self.fetch(url,
                              method='POST',
                              body=json.dumps(body),
                              params={'team_id': self.team.id})
        result = json.loads(response.body.decode())
        expect = {
            "status": "ok",
            "state": ActivityMember.ActivityMemberState.confirmed.value,
            "payment_state": TeamOrder.OrderState.WAIT_BUYER_PAY.value,
            "order_no": ""
        }
        self.assertDictEqual(expect, result, result)

        self.assertEqual(200, response.code, response.body)
        with self.assertRaises(TeamOrder.DoesNotExist):
            TeamOrder.get(
                user=self.auth_user,
                activity_id=activity.id,
            )
        member = ActivityMember.get(activity=activity, user=self.auth_user)

        # 现在支付, 直接确认
        self.assertEqual(ActivityMember.ActivityMemberState.confirmed.value,
                         member.state, member.state)
Beispiel #12
0
    def post(self):
        action = self.get_argument("action")

        if action == "refund":
            order_id = self.get_argument("order_id")
            order = TeamOrder.get_or_404(id=order_id)
            if order.is_refund_failed():
                order.refund()

        self.write_success()
    def post(self, order_no):
        order = TeamOrder.get_or_404(order_no=order_no, user=self.current_user)

        self.check_order_state(order)
        notify_url = ""
        paystr = self.create_ws_mobile_pay(out_trade_no=order.order_no,
                                           subject=order.title,
                                           body=order.title,
                                           total_fee=order.payment_fee,
                                           notify_url=notify_url)
        self.write({"paystr": paystr})
    def _finish_order(self, order: TeamOrder, arguments: dict,
                      payment: PaymentMethod):
        """
        完成支付流程
        Args:
            order: TeamOrder
            arguments: dict, 微信服务器回调的 body
            payment: PaymentMethod, 支付方式

        Returns:

        """

        logging.debug('开始终结支付订单[{0}]'.format(order.order_no))
        if payment == PaymentMethod.ALIPAY:
            # 支付宝
            gateway_account = arguments["buyer_email"]
            gateway_trade_no = arguments["trade_no"]
        else:
            # 默认违心支付
            gateway_trade_no = arguments['transaction_id']
            gateway_account = arguments['openid']

        TeamOrder.update(state=TeamOrder.OrderState.TRADE_BUYER_PAID,
                         paid=datetime.datetime.now(),
                         gateway_trade_no=gateway_trade_no,
                         gateway_account=gateway_account) \
            .where(TeamOrder.order_no == order.order_no) \
            .execute()
        ActivityMember \
            .update(payment_method=payment.value,
                    payment_state=TeamOrder.OrderState.TRADE_BUYER_PAID,
                    state=ActivityMember.ActivityMemberState.confirmed,
                    paid=datetime.datetime.now(),
                    confirmed=datetime.datetime.now()) \
            .where(ActivityMember.activity == order.activity_id) \
            .execute()

        # 更新活动成员数
        Activity.update_members_count(order.activity_id)
Beispiel #15
0
    def recharge(cls, amount, member, payment, operator):
        """
        会员充值
        Args:
            amount: 充值金额
            member: 充值会员
            payment: 支付方式, 'wxpay', 'alipay', 'cash'
            operator: 操作者

        Returns:

        """
        order_type = 1

        with BaseModel._meta.database.atomic() as txn:
            order = TeamOrder.create(
                order_no=TeamOrder.get_new_order_no(),
                team=member.team,
                user=member.user,
                order_type=order_type,
                payment_method=payment,
            )
    def test_cash_free_activity(self):
        """
        参加免费活动不需要支付
        线下支付活动的 ActivityMember.state 应该为 `确认` confirmed
        """

        activity = self.create_free_activity(
            payment_type=Activity.PaymentType.CASH_PAY.value)

        url = self.JOIN_ACTIVITY.format(activity_id=activity.id)
        body = {
            "payment": TeamOrder.OrderPaymentMethod.WXPAY.value,
            "nickname": "Nick name"
        }

        self.auth_user = User.create(name='free activity')
        response = self.fetch(url,
                              method='POST',
                              body=json.dumps(body),
                              params={'team_id': self.team.id})
        self.assertEqual(200, response.code, response.body)
        result = json.loads(response.body.decode())
        expect = {
            "status": "ok",
            "state": ActivityMember.ActivityMemberState.confirmed.value,
            "payment_state": TeamOrder.OrderState.TRADE_BUYER_PAID.value,
            "order_no": ""
        }
        self.assertDictEqual(expect, result, result)

        with self.assertRaises(TeamOrder.DoesNotExist):
            TeamOrder.get(user=self.auth_user, activity_id=activity.id)

        member = ActivityMember.get(activity=activity, user=self.auth_user)
        self.assertEqual(ActivityMember.ActivityMemberState.confirmed.value,
                         member.state, member.state)
 def get_order(self, order_no: str) -> Union[TeamOrder, None]:
     """
     获取 TeamOrder
     :param order_no:
     :return:
     """
     try:
         order = TeamOrder.select()\
             .where(TeamOrder.order_no == order_no)\
             .get()
     except TeamOrder.DoesNotExist:
         msg = "订单不存在"
         logging.error(msg)
         order = None
     return order
Beispiel #18
0
 def new_order(cls, team, activity_id, user, order_type, payment_method,
               total_fee, payment_fee, title):
     """
     Args:
         total_fee: 订单金额
         payment_fee: 实付金额
         team:
         user:
         order_type: TeamOrder.Order_TYPES 订单类型 参加活动/消费
         payment_method: 支付方式
         activity_id:
         batch_id:
     """
     order = TeamOrder.create(order_no=TeamOrder.get_new_order_no(),
                              team=team,
                              user=user,
                              title=title,
                              order_type=order_type,
                              payment_fee=payment_fee,
                              payment_method=payment_method,
                              total_fee=total_fee,
                              activity_id=activity_id,
                              state=TeamOrder.OrderState.WAIT_BUYER_PAY)
     return order
    def get(self, order_no):
        # order = TeamOrder.get_or_404(user=self.current_user,
        #                              order_no=order_no)
        try:
            order = TeamOrder.select(TeamOrder, Activity)\
                .join(Activity, on=(TeamOrder.activity_id == Activity.id)
                      .alias("activity"))\
                .where(TeamOrder.user == self.current_user,
                       TeamOrder.order_no == order_no)
            user = User.select().where(User.id == self.current_user.id)
            team = Team.select()
            order_pf = prefetch(order, user, team).get()
        except TeamOrder.DoesNotExist:
            raise ApiException(404, "订单不存在")

        self.write(SecureOrderSerializer(instance=order_pf).data)
Beispiel #20
0
    def get(self, user_id):

        query = TeamOrder.select().where(TeamOrder.team == self.current_team,
                                         TeamOrder.user == user_id)

        query = self.paginate_query(query, per_page=20)

        orders = []
        for order in query:
            orders_info = order.info
            orders_info['state_name'] = order.state_name
            orders_info['refund_state_name'] = order.refund_state_name
            orders_info['payment_method_name'] = order.payment_method_name
            orders_info['order_type_name'] = order.order_type_name
            orders.append(orders_info)

        self.write({"orders": orders, "pagination": query.pagination.info})
    def post(self):

        order_no = self.get_argument('order_no', None)
        trade_type = self.get_argument("trade_type", "jsapi")

        if trade_type.upper() not in ("NATIVE", "JSAPI"):
            raise ApiException(400, '交易类型不支持')

        order = TeamOrder.get_or_404(order_no=order_no, user=self.current_user)

        self.check_order_state(order)

        wx_order = yield self.handle_wxpay(order,
                                           trade_type=trade_type.upper(),
                                           body=order.title)

        self.write(wx_order)
    def get(self):
        """当前登录用户的订单列表"""
        query = TeamOrder.select(
            TeamOrder,
            Activity
        ).join(
            Activity, on=(Activity.id == TeamOrder.activity_id).alias(
                "activity")
        ).where(TeamOrder.user == self.current_user) \
            .order_by(TeamOrder.id.desc())

        query = self.filter_query(query)
        page = self.paginate_query(query)
        data = self.get_paginated_data(page=page,
                                       alias="orders",
                                       serializer=SecureOrderSerializer)

        self.write(data)
Beispiel #23
0
    def test_cancel(self):
        faker = Faker()
        user = mixer.blend(User)
        team = mixer.blend(Team, sport=self.sport)
        match = mixer.blend(Match,
                            state=Match.MatchState.opening.value,
                            team_id=team.id)
        total_fee = 10.01
        total_members = randint(1, 10)
        for _ in range(0, total_members):
            order = mixer.blend(TeamOrder,
                                team=team,
                                user=user,
                                order_no=TeamOrder.get_new_order_no(),
                                total_fee=total_fee,
                                payment_fee=total_fee)
            mixer.blend(MatchMember,
                        match_id=match.id,
                        total_fee=total_fee,
                        order_id=order.id,
                        pt_order_no=faker.pystr())

        MatchService.cancel(match, user)
    def leave(cls,
              user_id,
              match: Match,
              notify_url: str = None,
              insists=False,
              role: int = 1):
        """
        退出赛事

        :param user_id:
        :param match:
        :param notify_url: 派队回调地址
        :param insists: 强制退出
        :param role: 退赛发起人, 1 用户, 2 赛事方
        :return:
        """
        logging.debug("notify_url: {0}, insists: {1}".format(
            notify_url, insists))

        if not insists and notify_url is None:
            raise AssertionError("非强制退出 `insists=False` 操作需要提供退款回调"
                                 "地址 `notify_url`")

        if insists is False and not match.can_leave():
            raise MatchException("赛事无法退出")

        # 退出赛事
        member = MatchMember.get(user_id=user_id,
                                 match_id=match.id)  # type: MatchMember
        with Match._meta.database.transaction():
            if insists:
                match.leave(member)

            # Warning: 数据库事物中尝试远程 HTTP 调用, 需要修复
            else:
                # match.leave_request(member)
                # 调用退款接口
                pt = Parteam(app.settings["parteam_api_url"])
                if member.order_id:
                    # 有支付信息, 调用退款接口
                    order = TeamOrder.get(
                        id=member.order_id)  # type: TeamOrder
                    refund_fee = int(order.payment_fee * 100)
                    try:
                        pt.order_refund(user_id=user_id,
                                        order_no=member.pt_order_no,
                                        refund_fee=refund_fee,
                                        notify_url=notify_url,
                                        role=role)
                    except NotPaidError as e:
                        logging.warning("调用派队退款接口发现订单未支付: {0}".format(str(e)))
                        TeamOrder.update(state=TeamOrder.OrderState
                                         .TRADE_CLOSED_BY_USER.value) \
                            .where(TeamOrder.id == member.order_id) \
                            .execute()
                        match.leave(member)
                    except RefundError as e:
                        raise MatchException(e)
                    else:
                        # 更新订单状态为 `退款`, 订单退款状态为 `全部退款`
                        TeamOrder.update(
                            state=TeamOrder.OrderState.TRADE_CLOSED.value,
                            refund_state=TeamOrder.OrderRefundState.FULL_REFUNDED.value,
                            refunded_time=datetime.now())\
                            .where(TeamOrder.id == member.order_id)\
                            .execute()
                        match.leave(member)
                else:
                    # 无支付信息直接退赛
                    match.leave(member)
    def initial_data(self):
        self.leader = User.create(name="imleader")
        self.team = Team.create(name="test_finish_activity",
                                owner_id=self.leader.id)
        self.activity = Activity.create(
            team=self.team,
            creator=self.leader,
            leader=self.leader,
            title="test_finish_activity",
            description="test_stoped_activity",
            price=Decimal(15),
            max_members=30,
            start_time=datetime.datetime.now() - datetime.timedelta(hours=2),
            end_time=datetime.datetime.now() - datetime.timedelta(hours=1),
            payment_type=0,
            repeat_type="weekly",
            state=Activity.ActivityState.opening)

        self.online_paid_amount = 0

        for i in range(0, 5):
            user = User.create(name="test_%s" % i)
            order = TeamOrder.create(
                team=self.team,
                user=user,
                order_type=0,
                activity_id=self.activity.id,
                title="test_finish",
                order_no=TeamOrder.get_new_order_no(),
                credit_fee=0,
                total_fee=self.activity.price,
                payment_fee=self.activity.price,
                payment_method="wxpay",
                state=TeamOrder.OrderState.TRADE_BUYER_PAID,
                paid=self.activity.start_time,
                created=self.activity.start_time,
                finished=self.activity.start_time)
            ActivityMember.create(
                team=self.team,
                activity=self.activity,
                user=user,
                price=self.activity.price,
                users_count=1,
                total_fee=self.activity.price,
                payment_state=order.state,
                payment_method="wxpay",
                order_id=order.id,
                order_no=order.order_no,
                state=ActivityMember.ActivityMemberState.confirmed,
                free_times=0,
            )

            self.online_paid_amount += self.activity.price

        for i in range(0, 3):
            user = User.create(name="test2_%s" % i)
            order = TeamOrder.create(team=self.team,
                                     user=user,
                                     order_type=0,
                                     activity_id=self.activity.id,
                                     title="test_finish",
                                     order_no=TeamOrder.get_new_order_no(),
                                     credit_fee=0,
                                     total_fee=self.activity.price,
                                     payment_fee=self.activity.price,
                                     payment_method="wxpay",
                                     state=TeamOrder.OrderState.WAIT_BUYER_PAY,
                                     created=self.activity.start_time,
                                     finished=self.activity.start_time)
            ActivityMember.create(
                team=self.team,
                activity=self.activity,
                user=user,
                price=self.activity.price,
                users_count=1,
                total_fee=self.activity.price,
                payment_state=order.state,
                payment_method="wxpay",
                order_id=order.id,
                order_no=order.order_no,
                state=ActivityMember.ActivityMemberState.wait_confirm,
                free_times=0,
            )
def refund(order_no, refund_fee, is_retry=False):
    """
    Args:
        order_no: int, 订单号
        refund_fee: 退款金额
    """

    wxpay = WxPay(appid=current_app.settings["wxpay_appid"],
                  mch_id=current_app.settings["wxpay_mchid"],
                  secret_key=current_app.settings["wxpay_secret_key"],
                  ca_certs=current_app.settings["wxpay_ca_certs"],
                  client_cert=current_app.settings["wxpay_api_client_cert"],
                  client_key=current_app.settings["wxpay_api_client_key"])

    order = TeamOrder.get(order_no=order_no)
    if order.payment_method == order.OrderPaymentMethod.WXPAY.value and \
            order.refund_state in (
                TeamOrder.OrderRefundState.PARTIAL_REFUNDING.value,
                TeamOrder.OrderRefundState.PARTIAL_REFUNDED.value,
                TeamOrder.OrderRefundState.FULL_REFUNDING.value,
                TeamOrder.OrderRefundState.FULL_REFUNDED.value
            ):

        if is_retry:
            out_trade_no = 'J%s' % order.order_no
        else:
            out_trade_no = 'N%s' % order.order_no

        try:
            response = wxpay.refund(out_trade_no=out_trade_no,
                                    out_refund_no="R%s" % order.order_no,
                                    total_fee=order.total_fee,
                                    refund_fee=refund_fee,
                                    # transaction_id=order.gateway_trade_no,
                                    op_user_id=order.gateway_account)
        except Exception as e:
            logging.error("refund fail, order_no:{0} exception:{1}".format(out_trade_no, e))
            response = None

        if response and response["result_code"] == "SUCCESS":
            logging.debug(response)
            TeamOrder.update(
                refund_state=TeamOrder.OrderRefundState.FULL_REFUNDED.value,
                refunded_fee=refund_fee,
                refunded_time=datetime.datetime.now(),
                state=TeamOrder.OrderState.TRADE_CLOSED,
            )\
                .where(TeamOrder.order_no == order.order_no)\
                .execute()

        # 换个订单号重试
        elif not is_retry:
            refund(order_no, refund_fee, is_retry=True)
        else:
            # 标记退款失败
            TeamOrder.update(
                refund_state=TeamOrder.OrderRefundState.FULL_REFUND_FAILED.value,
            ).where(TeamOrder.order_no == order.order_no)\
                .execute()

            logging.error("refund fail,order_no:{0} response:{1}".format(order_no, response))
    def post(self, match_id):
        """ 报名赛事

        :match_id: 赛事ID
        :returns:

        """

        match = Match.get_or_404(id=match_id)

        # 检查赛事是否可以报名
        result = match.can_join()
        self.has_joined(match, user_id=self.current_user.id)

        if not result['can']:
            raise ApiException(403,
                               result['reason'],
                               log_message="报名失败:{0}, {1}".format(
                                   match_id, result['reason']))

        team = Team.get_or_404(id=match.team_id)

        # 分组比赛模式
        group = None
        if match.group_type == 1:
            group_id = self.get_argument("group_id")
            group = MatchGroup.get_or_none(id=group_id, match_id=match_id)
            if group is None:
                raise ApiException(404, "赛事分组不存在")

            # 分组是否已报满
            if group.max_members <= group.members_count:
                raise ApiException(403, "赛事分组已报满")

            total_fee = group.price

        else:
            total_fee = match.price

        name = self.get_argument("name")
        mobile = self.get_argument("mobile")

        if not name:
            raise ApiException(400, "请填写名称")

        if not mobile:
            raise ApiException(400, "请填写手机号码")

        elif not is_mobile(mobile):
            raise ApiException(400, "手机号码格式有误")

        # TODO: 上线前移除
        # member = MatchMember.get_or_none(match_id=match.id,
        #                                  user_id=self.current_user.id)
        # if member is not None:
        #     raise ApiException(403, "你已报名此赛事,无需重复报名")

        extra_attrs = self.parse_options(match_id)

        with self.db.transaction() as txn:

            # 零元赛事无需支付不生成订单
            order = None
            if total_fee > 0:
                order = TeamOrder.create(
                    order_no=TeamOrder.get_new_order_no(),
                    team=team,
                    user=team.owner_id,
                    title=match.title,
                    order_type=TeamOrder.OrderType.MATCH,
                    payment_fee=total_fee,
                    total_fee=total_fee,
                    activity_id=match.id,
                    state=TeamOrder.OrderState.WAIT_BUYER_PAY)

            other_attrs = {}
            if "avatar" in match.fields:
                other_attrs['avatar_key'] = self.upload_photo("avatar", "头像")

            if "idcard_photo" in match.fields:
                other_attrs['idcard_front'] = self.upload_photo(
                    "idcard_front", "证件照片正面")
                other_attrs['idcard_back'] = self.upload_photo(
                    "idcard_back", "证件照片背面")

            gender = self.get_argument("gender", "")
            if gender in ("0", "1"):
                gender = ("f", "m")[int(gender)]
            else:
                gender = "n"

            if order and order.state == TeamOrder.OrderState.WAIT_BUYER_PAY:
                member_state = MatchMember.MatchMemberState.wait_pay
            else:
                member_state = MatchMember.MatchMemberState.normal

            team.add_member(self.current_user.id,
                            nick=name,
                            state=TeamMember.TeamMemberState.normal)
            member = MatchMember.create(
                match_id=match.id,
                group_id=group.id if group else 0,
                member_type=match.join_type,
                user_id=self.current_user.id,
                name=name,
                mobile=mobile,
                gender=gender,
                age=self.get_argument("age", "0"),
                is_leader=self.get_argument("is_leader", False),
                realname=self.get_argument("realname", ""),
                idcard_number=self.get_argument("idcard_number", ""),
                extra_attrs=extra_attrs,
                order_id=order.id if order else 0,
                total_fee=total_fee,
                state=member_state,
                **other_attrs)

            # 如果需要付费则生成支付订单
            if total_fee > 0:

                if match.refund_expire:
                    refund_expire = match.refund_expire.strftime(
                        "%Y%m%d%H%M%S")
                else:
                    refund_expire = datetime.now().strftime("%Y%m%d%H%M%S")

                resp = self.parteam_request(
                    "/match/openapi/createOrderInfo.do",
                    post_args={
                        "orderValue":
                        match.id,
                        "eachFee":
                        int(total_fee * 100),
                        "num":
                        1,
                        "totalFee":
                        int(total_fee * 100),
                        "subject":
                        match.title,
                        "userId":
                        self.current_user.id,
                        "notifyUrl":
                        urljoin(
                            self.request.full_url(),
                            self.reverse_url('rest_match_join_notify',
                                             match.id)),
                        "version":
                        1,
                        "expDatetime":
                        refund_expire,
                        "tradeType":
                        "APP" if self.device_type.lower() in ("ios", "android")
                        else "WEB",
                    })

                if "orderNo" not in resp:
                    txn.rollback()
                    raise ApiException(
                        400,
                        "创建订单失败",
                        log_message="match order fail:{0}".format(resp))

                MatchMember.update(pt_order_no=resp['orderNo']).where(
                    MatchMember.id == member.id).execute()

            # 统计赛事人数
            Match.update_members_count(match.id)
            if group:
                MatchGroup.update_members_count(group.id)

            member = MatchMember.get_or_404(id=member.id)

        member_info = member.info
        member_info['order'] = {
            "orderNo": member.pt_order_no,
            "orderValue": match.id,
            "eachFee": int(total_fee * 100),
            "num": 1,
            "totalFee": int(total_fee * 100),
            "subject": match.title,
            "userId": self.current_user.id,
        }

        self.write(member_info)
def finish_activity(activity_id):
    """结算活动场次

        1. 将用户在线支付费用转到俱乐部账户
        2. 标记场次和订单状态为完成
        3. 记录俱乐部账户变化
    """

    activity = Activity.get_or_404(id=activity_id)

    if activity.end_time > datetime.now():
        raise Exception("活动场次未结束")

    if activity.state == Activity.ActivityState.cancelled:
        raise Exception("活动场次已取消")

    if activity.state == Activity.ActivityState.finished:
        raise Exception("活动场次已结算")

    # 计算在线支付完成交易的总额
    online_paid_amount = TeamOrder.select(fn.SUM(TeamOrder.payment_fee)).where(
        TeamOrder.activity_id == activity_id,
        TeamOrder.state >= TeamOrder.OrderState.TRADE_BUYER_PAID.value,
        TeamOrder.refund_state == TeamOrder.OrderRefundState.NO_REFUND,
        TeamOrder.payment_method <<
        TeamOrder.ONLINE_PAYMENT_METHODS).scalar() or 0

    # 计算余额支付完成交易的总额
    credit_paid_amount = TeamOrder.select(fn.SUM(TeamOrder.credit_fee)).where(
        TeamOrder.activity_id == activity_id, TeamOrder.state >=
        TeamOrder.OrderState.TRADE_BUYER_PAID.value).scalar() or 0

    # 使用次数
    free_times_amount = ActivityMember.select(fn.SUM(
        ActivityMember.free_times)).where(
            ActivityMember.state ==
            ActivityMember.ActivityMemberState.confirmed).scalar() or 0

    # online_paid_amount= DecimalField(default=Decimal(0), verbose_name="在线支付收入")
    # credit_paid_amount= DecimalField(default=Decimal(0), verbose_name="余额支付收入")
    # cash_paid_amount= DecimalField(default=Decimal(0), verbose_name="现金支付收入")
    # free_times_amount = IntegerField(default=0, verbose_name="次卡支付数量")

    with app.db.transaction() as txn:
        team = Team.select().where(
            Team.id == activity.team.id).for_update().get()

        # 将收入打到俱乐部账上
        Team.update(
            credit=Team.credit + online_paid_amount,
            total_receipts=Team.total_receipts + online_paid_amount,
            updated=datetime.now()).where(Team.id == team.id).execute()

        # 将订单修改状态为已完成
        TeamOrder.update(
            state=TeamOrder.OrderState.TRADE_FINISHED.value,
            finished=datetime.now()).where(
                TeamOrder.activity_id == activity_id,
                TeamOrder.state == TeamOrder.OrderState.TRADE_BUYER_PAID.value,
                TeamOrder.refund_state ==
                TeamOrder.OrderRefundState.NO_REFUND.value).execute()

        # 修改场次状态为已结算
        Activity.update(state=Activity.ActivityState.finished,
                        finished=datetime.now(),
                        online_paid_amount=online_paid_amount,
                        credit_paid_amount=credit_paid_amount,
                        free_times_amount=free_times_amount).where(
                            Activity.id == activity_id).execute()

        # 记录俱乐部账户变更
        TeamAccountLog.create(team_id=team.id,
                              credit_change=online_paid_amount,
                              change_type=0,
                              credit_before=team.credit,
                              credit_after=team.credit + online_paid_amount,
                              note="活动结算:%s(%s)" %
                              (activity.title, activity.start_time),
                              activity_id=activity_id,
                              operator_id=0)

    # 生成下期活动
    gen_next_activity(activity_id)