Ejemplo n.º 1
0
class TestMerchantAdminModelManager(TestCase):
    def setUp(self):
        self.factory = PayunionFactory()
        self.merchant = self.factory.create_merchant()
        self.merchant_admin = self.factory.create_merchant_admin(
            work_merchant=self.merchant)
        self.manager = MerchantAdminModelManager()

    def test_has_openid(self):
        # create multiple merchant
        for _ in range(10):
            self.factory.create_merchant()

        self.assertEqual(
            self.manager.get_merchant_admin(
                self.merchant_admin.wechat_unionid), self.merchant_admin)

        old_unionid = self.merchant_admin.wechat_unionid
        self.merchant_admin.wechat_unionid = 'another openid'
        self.merchant_admin.save()
        self.assertIsNone(self.manager.get_merchant_admin(old_unionid))

    def test_remove_cashier(self):
        cashier = self.factory.create_merchant_admin(
            status=SYSTEM_USER_STATUS['USING'],
            work_merchant=self.merchant,
            merchant_admin_type=MERCHANT_ADMIN_TYPES['CASHIER'])
        self.manager.remove_cashier(cashier)
        cashier = MerchantAdmin.objects.get(id=cashier.id)
        self.assertEqual(cashier.status, SYSTEM_USER_STATUS['DISABLED'])
Ejemplo n.º 2
0
    def prepare_data(self):
        from .base_data import create_base_data
        create_base_data()
        fake_factory = PayunionFactory()
        curr_time = timezone.now()
        yesterday = curr_time - timedelta(days=1)

        client = fake_factory.create_client(status=SYSTEM_USER_STATUS['USING'])
        inviter = fake_factory.create_marketer(
            inviter_type=MARKETER_TYPES['SALESMAN'],
            status=SYSTEM_USER_STATUS['USING'])
        merchant = fake_factory.create_merchant(
            status=MERCHANT_STATUS['USING'], inviter=inviter)
        merchant.type = MERCHANT_TYPE['ENTERPRISE']
        merchant.save()

        pay_money = 10 * 100
        payment = Payment.objects.create(
            serial_number="20181102172102347024194949482317",
            datetime=yesterday,
            pay_channel=PAY_CHANNELS['WECHAT'],
            status=PAYMENT_STATUS['FINISHED'],
            merchant=merchant,
            client=client,
            order_price=pay_money,
            coupon=None,
            coupon_granted=True,
            platform_share=0,
            inviter_share=0,
            originator_share=0,
        )
        merchant.account.balance = pay_money
        merchant.account.withdrawable_balance = pay_money

        pay_money = 5 * 100
        payment = Payment.objects.create(
            serial_number="20181102172106671737943971236656",
            datetime=yesterday,
            pay_channel=PAY_CHANNELS['ALIPAY'],
            status=PAYMENT_STATUS['FINISHED'],
            merchant=merchant,
            client=client,
            order_price=5 * 100,
            coupon=None,
            coupon_granted=True,
            platform_share=0,
            inviter_share=0,
            originator_share=0,
        )

        merchant.account.alipay_balance = pay_money
        merchant.account.alipay_withdrawable_balance = pay_money
        merchant.account.save()

        return merchant
Ejemplo n.º 3
0
class TestMerchantAdminManager(TestCase):
    def setUp(self):
        self.factory = PayunionFactory()
        self.merchant = self.factory.create_merchant()
        self.merchant_admin = self.factory.create_merchant_admin(
            work_merchant=self.merchant, alipay_user_name='周杰偷')
        self.manager = MerchantAdminManager(self.merchant_admin)

    def test_work_merchant(self):
        merchant = self.manager.work_merchant.obj
        self.assertEqual(merchant, self.merchant)

    def test_insensitive_alipay_user_name(self):
        self.assertEqual(self.manager.insensitive_alipay_user_name, '**偷')
Ejemplo n.º 4
0
class TestWechatPaySteps(APITestCase):
    def setUp(self):
        # We use a faked ALIPAY_PUBLIC_KEY to mock the alipay response.
        # ALIPAY_PUBLIC_KEY and its corresponding private key is generated by ourselves.
        self.ALIPAY_PUBLIC_KEY_backup = dynasettings.ALIPAY_PUBLIC_KEY
        dynasettings.ALIPAY_PUBLIC_KEY = AutoTestConfig.self_generated_alipay_public_key
        alipay_payment.api_instance.alipay_public_key = AutoTestConfig.self_generated_alipay_public_key

        self.factory = PayunionFactory()

        try:
            self.platform_account = Account.objects.get(id=1)
        except Account.DoesNotExist:
            self.platform_account = self.factory.create_account(id=1, balance=0,
                                                                withdrawable_balance=0,
                                                                alipay_balance=0,
                                                                alipay_withdrawable_balance=0)
        self.originator_account = self.factory.create_account(real_name='引流商户', balance=0,
                                                              withdrawable_balance=0,
                                                              alipay_balance=0,
                                                              alipay_withdrawable_balance=0)
        self.merchant_account = self.factory.create_account(real_name='收款', balance=0,
                                                            withdrawable_balance=0,
                                                            alipay_balance=0,
                                                            alipay_withdrawable_balance=0)
        self.inviter_account = self.factory.create_account(real_name='邀请人', balance=0,
                                                           withdrawable_balance=0,
                                                           alipay_balance=0,
                                                           alipay_withdrawable_balance=0)
        self.inviter = self.factory.create_marketer(account=self.inviter_account)

        self.test_client = self.factory.create_client(openid='1234567890', status=0)
        self.merchant = self.factory.create_merchant(account=self.merchant_account,
                                                     inviter=self.inviter,
                                                     status=MERCHANT_STATUS.USING)
        self.originator = self.factory.create_merchant(account=self.originator_account)
        self.rule = self.factory.create_coupon_rule(merchant=self.merchant,
                                                    valid_strategy=VALID_STRATEGY.EXPIRATION,
                                                    expiration_days=30)
        self.coupon = self.factory.create_coupon(rule=self.rule, client=self.test_client,
                                                 originator_merchant=self.originator,
                                                 min_charge=1000, discount=100,
                                                 obtain_datetime=timezone.now(),
                                                 status=COUPON_STATUS['NOT_USED'])

        self.merchant_admin = self.factory.create_merchant_admin(
            merchant_admin_type=MERCHANT_ADMIN_TYPES.ADMIN,
            work_merchant=self.merchant
        )
        super(TestWechatPaySteps, self).setUpTestData()

    def tearDown(self):
        dynasettings.ALIPAY_PUBLIC_KEY = self.ALIPAY_PUBLIC_KEY_backup
        alipay_payment.api_instance.alipay_public_key = self.ALIPAY_PUBLIC_KEY_backup

    def test_workflow(self):
        client_steps = ClientSteps()

        # Login
        code = uuid.uuid4().hex

        def validate(request):
            params = parse_qs(request.query)
            self.assertEqual(params['app_id'][0], str(dynasettings.ALIPAY_APP_ID))
            self.assertEqual(params['code'][0], code)
            self.assertEqual(params['grant_type'][0], 'authorization_code')
            return True

        login_result = client_steps.alipay_login(code,
                                                 mocked_user_id=self.test_client.openid,
                                                 validate=validate)
        access_token = login_result['access_token']
        self.assertEqual(len(access_token), 32)

        # Get user info
        me_info = client_steps.me(access_token)
        assert me_info

        # Get all coupons
        coupons = client_steps.get_coupons(access_token)
        self.assertEqual(len(coupons), 1)
        self.assertEqual(coupons[0]['id'], self.coupon.id)
        self.assertEqual(coupons[0]['rule']['merchant']['name'], self.merchant.name)
        self.assertEqual(coupons[0]['discount'], self.coupon.discount)
        self.assertEqual(coupons[0]['min_charge'], self.coupon.min_charge)

        # Get merchant info
        merchant_info = client_steps.get_merchant_info(access_token,
                                                       self.merchant.payment_qr_code.uuid)
        self.assertTrue(merchant_info)
        self.assertTrue(merchant_info['id'])
        self.assertTrue(merchant_info['name'])
        self.assertTrue(merchant_info['description'])
        self.assertTrue(merchant_info['avatar_url'])
        self.assertTrue(isinstance(merchant_info['status'], int))

        # Place order
        alipay_payment_steps = AlipayPaymentSteps(
            access_token)
        shared_pay_steps = SharedPaySteps(access_token)

        place_order_result = alipay_payment_steps.place_order(
            self.merchant.id, 1000,
            self.coupon.id)
        assert place_order_result
        self.assertEqual(place_order_result['code'], '10000')
        self.assertEqual(place_order_result['msg'], 'Success')
        self.assertEqual(len(place_order_result['out_trade_no']), 32)

        # Cancel the order
        cancel_order_result = alipay_payment_steps.cancel_order(
            place_order_result['out_trade_no'])
        assert cancel_order_result['new_coupon']
        self.coupon = Coupon.objects.get(id=cancel_order_result['new_coupon']['id'])
        assert self.coupon

        # Place another order
        place_order_result = alipay_payment_steps.place_order(
            self.merchant.id, 1000,
            self.coupon.id)
        assert place_order_result
        self.assertEqual(place_order_result['code'], '10000')
        self.assertEqual(place_order_result['msg'], 'Success')
        self.assertEqual(len(place_order_result['out_trade_no']), 32)

        poll_result_result = shared_pay_steps.poll_result(
            place_order_result['out_trade_no'],
            None, None, None
        )
        assert poll_result_result['payment']['status'] == 0

        # Payment callback
        callback_response = alipay_payment_steps.payment_callback(
            dynasettings.ALIPAY_APP_ID,
            self.test_client.openid,
            place_order_result['out_trade_no'],
            900,
        )
        assert '"OK"' == callback_response.content.decode()

        poll_result_result = shared_pay_steps.poll_result(
            place_order_result['out_trade_no'],
            None, None, None
        )
        assert poll_result_result['payment']['status'] == 1

        # login merchant
        code = uuid.uuid4().hex
        resp = MerchantLoginMockStep(self).login(
            code,
            mocked_unionid=self.merchant_admin.wechat_unionid)
        token = resp.json()['token']
        assert token

        # get merchant info
        me = MerchantStep(self, token=token).me().json()
        self.assertEqual(me['name'], self.merchant.name)

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['out_trade_no']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.FROZEN)

        # refund
        refund_result = TransactionStep(self, token=token).refund(
            payment_id=place_order_result['out_trade_no']).json()
        self.assertEqual(refund_result, {})

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['out_trade_no']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.REFUND)

        # ********************************************
        # Place another to test unfreeze
        place_order_result = alipay_payment_steps.place_order(
            self.merchant.id, 1000,
            Coupon.objects.filter(client=self.test_client).order_by('-id').first().id)

        # Payment callback
        alipay_payment_steps.payment_callback(
            dynasettings.ALIPAY_APP_ID,
            self.test_client.openid,
            place_order_result['out_trade_no'],
            900,
        )

        # Unfreeze and check result.
        shared_pay_steps.unfreeze_immediately()

        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['out_trade_no']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.FINISHED)
Ejemplo n.º 5
0
class PaymentBaseTestCases(TestCase):
    def __init__(self, methodName='runTest'):
        super().__init__(methodName)
        self.factory = PayunionFactory()

    def setUp(self):
        self.merchants = []
        self.merchants.append(
            self.factory.create_merchant(
                location_lon=39.913811,
                location_lat=116.410189,
                name='王府井百货',
                status=config.MERCHANT_STATUS.USING,
            ))

        self.merchants.append(
            self.factory.create_merchant(
                location_lon=39.909631,
                location_lat=116.423407,
                name='好苑建国饭店',
                status=config.MERCHANT_STATUS.USING,
            ))

        self.merchants.append(
            self.factory.create_merchant(
                location_lon=39.908775,
                location_lat=116.409802,
                name='北京饭店',
                status=config.MERCHANT_STATUS.USING,
            ))

        self.merchants.append(
            self.factory.create_merchant(
                location_lon=39.906668,
                location_lat=116.426454,
                name='湖南大厦',
                status=config.MERCHANT_STATUS.USING,
            ))

        self.merchants.append(
            self.factory.create_merchant(
                location_lon=39.960701,
                location_lat=116.323457,
                name='天作国际',
                status=config.MERCHANT_STATUS.USING,
            ))

        self.merchants.append(
            self.factory.create_merchant(
                location_lon=39.970503,
                location_lat=116.321054,
                name='当代商城',
                status=config.MERCHANT_STATUS.USING,
            ))

        for m in self.merchants:
            self.factory.create_coupon_rule(merchant=m, stock=100)

        self.payment = self.factory.create_payment(
            status=config.PAYMENT_STATUS.FROZEN, coupon_granted=False)

    def test_grant_coupons(self):
        coupons = PaymentBaseUseCases.grant_coupons(self.payment, 39.913384,
                                                    116.414437, 10)

        assert len(coupons) == 3
        valid_ids = {m.id for m in self.merchants[:4]}
        actual_ids = {c.rule.merchant.id for c in coupons}

        assert actual_ids < valid_ids

    def test_grant_coupons1(self):
        coupons = PaymentBaseUseCases.grant_coupons(self.payment, 39.962215,
                                                    116.32899310, 10)

        assert len(coupons) == 2
        valid_ids = {m.id for m in self.merchants[4:]}
        actual_ids = {c.rule.merchant.id for c in coupons}

        assert actual_ids <= valid_ids
Ejemplo n.º 6
0
class GetMerchantInfoTestCase(ClientLoggedInMixin, APITestCase):
    def setUp(self):
        self.factory = PayunionFactory()

        try:
            self.platform_account = Account.objects.get(id=1)
        except Account.DoesNotExist:
            self.platform_account = self.factory.create_account(id=1, balance=0,
                                                                withdrawable_balance=0,
                                                                alipay_balance=0,
                                                                alipay_withdrawable_balance=0)
        self.originator_account = self.factory.create_account(real_name='引流商户', balance=0,
                                                              withdrawable_balance=0,
                                                              alipay_balance=0,
                                                              alipay_withdrawable_balance=0)
        self.merchant_account = self.factory.create_account(real_name='收款', balance=0,
                                                            withdrawable_balance=0,
                                                            alipay_balance=0,
                                                            alipay_withdrawable_balance=0)
        self.inviter_account = self.factory.create_account(real_name='邀请人', balance=0,
                                                           withdrawable_balance=0,
                                                           alipay_balance=0,
                                                           alipay_withdrawable_balance=0)
        self.inviter = self.factory.create_marketer(account=self.inviter_account)

        self.test_client = self.factory.create_client(openid='oUkVN5ZFKfLOkAFwkk4oGYVc0rfg',
                                                      status=0)
        self.merchant = self.factory.create_merchant(account=self.merchant_account,
                                                     status=1,
                                                     inviter=self.inviter)
        self.originator = self.factory.create_merchant(account=self.originator_account)
        self.rule = self.factory.create_coupon_rule(merchant=self.merchant)
        self.coupon = self.factory.create_coupon(rule=self.rule, client=self.test_client,
                                                 originator_merchant=self.originator,
                                                 min_charge=1000, discount=100,
                                                 status=COUPON_STATUS['NOT_USED'])

        self.merchant_admin = self.factory.create_merchant_admin(
            merchant_admin_type=MERCHANT_ADMIN_TYPES.ADMIN,
            work_merchant=self.merchant
        )
        super(GetMerchantInfoTestCase, self).setUp()

    def test_payment_entry(self):
        url = reverse('payment_entry')
        response = self.client.get(url + '?uuid=bb0add6a-f424-4cb7-862e-fcd98d6447e7')
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response.url, 'https://openauth.alipaydev.com/oauth2/publicAppAuthorize.htm?app_id=2016091800543597&scope=auth_base&redirect_uri=http%3A//payweb.alpha.muchbo.com/pay.html&state=bb0add6a-f424-4cb7-862e-fcd98d6447e7')

    def test_get_merchant_info(self):
        url = reverse('payment-merchant-info')
        response = self.client.get(url, data={'uuid': self.merchant.payment_qr_code.uuid},
                                   format='json')
        resp_json = response.json()
        self.assertEqual(resp_json, {
            'id': self.merchant.id,
            'name': self.merchant.name,
            'description': self.merchant.description,
            'avatar_url': self.merchant.avatar_url,
            'status': self.merchant.status,
        })

    def test_place_order_with_coupon(self):
        url = reverse('place_order')
        response = self.client.post(
            url,
            data=dict(
                merchant_id=self.merchant.id,
                order_price=1000,
                channel=0,
                coupon_id=self.coupon.id,
            ),
            format='json')
        resp_json = response.json()
        assert len(resp_json['payment_serial_number']) == 32
        assert len(resp_json['client_payment_params']) == 6
        assert len(resp_json['client_payment_params']['appId']) == 18
        assert len(resp_json['client_payment_params']['timeStamp']) == 10
        assert len(resp_json['client_payment_params']['nonceStr']) == 32
        assert len(resp_json['client_payment_params']['package']) == 46
        assert resp_json['client_payment_params']['signType'] == 'MD5'
        assert len(resp_json['client_payment_params']['paySign']) == 32
        return resp_json

    def test_place_order_without_coupon(self):
        url = reverse('place_order')
        response = self.client.post(
            url,
            data=dict(
                merchant_id=self.merchant.id,
                order_price=1000,
                channel=0,
                coupon_id=None,
            ),
            format='json')
        resp_json = response.json()
        assert len(resp_json['payment_serial_number']) == 32
        assert len(resp_json['client_payment_params']) == 6
        assert len(resp_json['client_payment_params']['appId']) == 18
        assert len(resp_json['client_payment_params']['timeStamp']) == 10
        assert len(resp_json['client_payment_params']['nonceStr']) == 32
        assert len(resp_json['client_payment_params']['package']) == 46
        assert resp_json['client_payment_params']['signType'] == 'MD5'
        assert len(resp_json['client_payment_params']['paySign']) == 32
        return resp_json
Ejemplo n.º 7
0
class AlipayTestCases(TestCase):
    def __init__(self, methodName='runTest'):
        super().__init__(methodName)
        self.factory = PayunionFactory()

    def setUp(self):
        try:
            self.platform_account = Account.objects.get(id=1)
        except Account.DoesNotExist:
            self.platform_account = self.factory.create_account(
                id=1,
                balance=0,
                withdrawable_balance=0,
                alipay_balance=0,
                alipay_withdrawable_balance=0)

        self.originator_account = self.factory.create_account(
            real_name='引流商户',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)

        self.merchant_account = self.factory.create_account(
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)

        self.inviter_account = self.factory.create_account(
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.inviter = self.factory.create_marketer(
            account=self.inviter_account)
        self.client = self.factory.create_client(openid='1234567890')
        self.merchant = self.factory.create_merchant(
            account=self.merchant_account, inviter=self.inviter)
        self.originator = self.factory.create_merchant(
            account=self.originator_account)
        self.rule = self.factory.create_coupon_rule(merchant=self.merchant)
        self.coupon = self.factory.create_coupon(
            rule=self.rule,
            client=self.client,
            originator_merchant=self.originator,
            status=COUPON_STATUS['NOT_USED'],
            min_charge=1000,
            discount=100)

        self.merchant_admin = self.factory.create_merchant_admin(
            merchant_admin_type=MERCHANT_ADMIN_TYPES.ADMIN,
            work_merchant=self.merchant)

    def test_place_order(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def place_order(self, notify_url, order_title,
                            payment_serial_number, total_amount, buyer_id):
                assert notify_url == 'http://test.com'
                assert order_title == self.test_case.merchant.name
                assert len(payment_serial_number) == 32
                assert total_amount == 1900
                assert buyer_id == '1234567890'

                return {
                    'code': '10000',
                    'msg': 'Success',
                    'out_trade_no': payment_serial_number,
                    'trade_no': '2018110522001483870500447579'
                }

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        result = alipay_usecases.place_order(self.client, self.merchant,
                                             self.coupon, 2000,
                                             'http://test.com')
        assert result['trade_no'] == '2018110522001483870500447579'

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        assert new_payment
        assert len(new_payment.serial_number) == 32
        assert len(new_payment.transactions.all()) == 0
        assert (timezone.now() - new_payment.datetime) < timedelta(seconds=30)
        assert new_payment.pay_channel == PAY_CHANNELS.ALIPAY
        assert new_payment.merchant_id == self.merchant.id
        assert new_payment.status == PAYMENT_STATUS.UNPAID
        assert new_payment.client_id == self.client.id
        assert new_payment.order_price == 2000
        assert new_payment.coupon_id == self.coupon.id
        assert new_payment.platform_share == round(1900 * PLATFORM_SHARE_RATE)
        assert new_payment.inviter_share == round(1900 * INVITER_SHARE_RATE)
        assert new_payment.originator_share == round(1900 *
                                                     ORIGINATOR_SHARE_RATE)
        assert new_payment.note is None

    def test_place_order_with_retry(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def place_order(self, notify_url, order_title,
                            payment_serial_number, total_amount, buyer_id):
                assert notify_url == 'http://test.com'
                assert order_title == self.test_case.merchant.name
                assert len(payment_serial_number) == 32
                assert total_amount == 1900
                assert buyer_id == '1234567890'

                raise ApiReturnedError(40000, {})

            def query_payment(self, payment_serial_number):
                return {
                    'code': '10000',
                    'msg': 'Success',
                    'buyer_logon_id': 'cyv***@sandbox.com',
                    'buyer_pay_amount': '0.00',
                    'buyer_user_id': '2088102176283877',
                    'buyer_user_type': 'PRIVATE',
                    'invoice_amount': '0.00',
                    'out_trade_no': payment_serial_number,
                    'point_amount': '0.00',
                    'receipt_amount': '0.00',
                    'total_amount': '0.88',
                    'trade_no': '2018110522001483870500447579',
                    'trade_status': 'WAIT_BUYER_PAY'
                }

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        result = alipay_usecases.place_order(self.client, self.merchant,
                                             self.coupon, 2000,
                                             'http://test.com')
        assert result['out_trade_no']

        new_payment = Payment.objects.all().order_by('-datetime')[0]
        assert result['out_trade_no'] == new_payment.serial_number

        assert new_payment
        assert len(new_payment.serial_number) == 32
        assert len(new_payment.transactions.all()) == 0
        assert (timezone.now() - new_payment.datetime) < timedelta(seconds=30)
        assert new_payment.pay_channel == PAY_CHANNELS.ALIPAY
        assert new_payment.merchant_id == self.merchant.id
        assert new_payment.status == PAYMENT_STATUS.UNPAID
        assert new_payment.client_id == self.client.id
        assert new_payment.order_price == 2000
        assert new_payment.coupon_id == self.coupon.id
        assert new_payment.platform_share == round(1900 * PLATFORM_SHARE_RATE)
        assert new_payment.inviter_share == round(1900 * INVITER_SHARE_RATE)
        assert new_payment.originator_share == round(1900 *
                                                     ORIGINATOR_SHARE_RATE)
        assert new_payment.note is None

    def test_place_order_without_coupon(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def place_order(self, notify_url, order_title,
                            payment_serial_number, total_amount, buyer_id):
                assert notify_url == 'http://test.com'
                assert order_title == self.test_case.merchant.name
                assert len(payment_serial_number) == 32
                assert total_amount == 2000
                assert buyer_id == '1234567890'

                return 'place_order_success'

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        result = alipay_usecases.place_order(self.client, self.merchant, None,
                                             2000, 'http://test.com')
        assert result == 'place_order_success'

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        assert new_payment
        assert len(new_payment.serial_number) == 32
        assert len(new_payment.transactions.all()) == 0
        assert (timezone.now() - new_payment.datetime) < timedelta(seconds=30)
        assert new_payment.pay_channel == PAY_CHANNELS.ALIPAY
        assert new_payment.merchant_id == self.merchant.id
        assert new_payment.status == PAYMENT_STATUS.UNPAID
        assert new_payment.client_id == self.client.id
        assert new_payment.order_price == 2000
        assert new_payment.coupon is None
        assert new_payment.platform_share == 0
        assert new_payment.inviter_share == 0
        assert new_payment.originator_share == 0
        assert new_payment.note is None

    def test_cancel_order(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def cancel(self, payment_serial_number):
                assert len(payment_serial_number) == 32
                return 'cancel_order_success'

        self.test_place_order()

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        new_coupon = alipay_usecases.cancel_order(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 0
        assert payment.status == PAYMENT_STATUS.CANCELLED
        assert payment.coupon.status == COUPON_STATUS.DESTROYED

        assert new_coupon.rule == payment.coupon.rule
        assert new_coupon.client == payment.coupon.client
        assert new_coupon.discount == payment.coupon.discount
        assert new_coupon.min_charge == payment.coupon.min_charge
        assert new_coupon.originator_merchant == payment.coupon.originator_merchant
        assert new_coupon.status == COUPON_STATUS.NOT_USED
        assert new_coupon.obtain_datetime == payment.coupon.obtain_datetime
        assert new_coupon.use_datetime is None

    def test_cancel_order_without_coupon(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def cancel(self, payment_serial_number):
                assert len(payment_serial_number) == 32
                return 'cancel_order_success'

        self.test_place_order_without_coupon()

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        new_coupon = alipay_usecases.cancel_order(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 0
        assert payment.status == PAYMENT_STATUS.CANCELLED
        assert payment.coupon is None

        assert new_coupon is None

    def test_on_payment_success(self):
        self.test_place_order()
        new_payment = Payment.objects.all().order_by('-datetime')[0]
        alipay_usecases = AlipayUseCases()
        alipay_usecases.on_payment_success(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 3
        assert payment.datetime == new_payment.datetime
        assert payment.pay_channel == PAY_CHANNELS.ALIPAY
        assert payment.status == PAYMENT_STATUS.FROZEN
        assert payment.merchant_id == self.merchant.id
        assert payment.client_id == self.client.id
        assert payment.order_price == 2000
        assert payment.coupon_id == self.coupon.id
        assert payment.platform_share == round(1900 * PLATFORM_SHARE_RATE)
        assert payment.inviter_share == round(1900 * INVITER_SHARE_RATE)
        assert payment.originator_share == round(1900 * ORIGINATOR_SHARE_RATE)
        assert payment.note is None

        # Check the transactions.
        transactions = payment.transactions.all().order_by('id')
        assert transactions[0].object_id == payment.serial_number
        assert transactions[
            0].content_object.serial_number == payment.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.PLATFORM_RECEIVE
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.platform_account.id
        assert transactions[0].amount == 1900
        assert transactions[0].balance_after_transaction == 1900

        assert transactions[1].object_id == payment.serial_number
        assert transactions[
            1].content_object.serial_number == payment.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EXPEND_MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == -1900 + (payment.platform_share +
                                                  payment.inviter_share +
                                                  payment.originator_share)
        assert transactions[1].balance_after_transaction == (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)

        assert transactions[2].object_id == payment.serial_number
        assert transactions[
            2].content_object.serial_number == payment.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.merchant_account.id
        assert transactions[2].amount == 1900 - (payment.platform_share +
                                                 payment.inviter_share +
                                                 payment.originator_share)
        assert transactions[2].balance_after_transaction == 1900 - (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.alipay_balance == (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)
        assert self.platform_account.alipay_withdrawable_balance == 0

        assert self.merchant_account.alipay_balance == 1900 - (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)
        assert self.merchant_account.alipay_withdrawable_balance == 0

    def test_on_payment_success_without_coupon(self):
        self.test_place_order_without_coupon()
        new_payment = Payment.objects.all().order_by('-datetime')[0]
        alipay_usecases = AlipayUseCases()
        alipay_usecases.on_payment_success(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 3
        assert payment.datetime == new_payment.datetime
        assert payment.pay_channel == PAY_CHANNELS.ALIPAY
        assert payment.status == PAYMENT_STATUS.FROZEN
        assert payment.merchant_id == self.merchant.id
        assert payment.client_id == self.client.id
        assert payment.order_price == 2000
        assert payment.coupon is None
        assert payment.platform_share == 0
        assert payment.inviter_share == 0
        assert payment.originator_share == 0
        assert payment.note is None

        # Check the transactions.
        transactions = payment.transactions.all().order_by('id')
        assert transactions[0].object_id == payment.serial_number
        assert transactions[
            0].content_object.serial_number == payment.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.PLATFORM_RECEIVE
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.platform_account.id
        assert transactions[0].amount == 2000
        assert transactions[0].balance_after_transaction == 2000

        assert transactions[1].object_id == payment.serial_number
        assert transactions[
            1].content_object.serial_number == payment.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EXPEND_MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == -2000
        assert transactions[1].balance_after_transaction == 0

        assert transactions[2].object_id == payment.serial_number
        assert transactions[
            2].content_object.serial_number == payment.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.merchant_account.id
        assert transactions[2].amount == 2000
        assert transactions[2].balance_after_transaction == 2000

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.alipay_balance == 0
        assert self.platform_account.alipay_withdrawable_balance == 0

        assert self.merchant_account.alipay_balance == 2000
        assert self.merchant_account.alipay_withdrawable_balance == 0

    def test_refund(self):
        self.test_on_payment_success()
        payment = Payment.objects.all().order_by('-datetime')[0]

        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY
                self.payment = payment

            def refund(self, payment_serial_number, refund_serial_number,
                       refund_amount):
                assert payment_serial_number == self.payment.serial_number
                assert len(refund_serial_number) == 32
                assert refund_amount == 1900

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        alipay_usecases.request_refund(payment)

        # Check the refund data
        refund = Refund.objects.all().order_by('-datetime')[0]
        assert len(refund.serial_number) == 32
        assert len(refund.transactions.all()) == 3
        assert (timezone.now() - refund.datetime) < timedelta(seconds=30)
        assert refund.status == REFUND_STATUS.FINISHED
        assert refund.payment.serial_number == payment.serial_number

        # Check the payment data
        payment.refresh_from_db()
        assert payment.status == PAYMENT_STATUS.REFUND

        # Check the transactions.
        transactions = refund.transactions.all().order_by('id')
        assert transactions[0].object_id == refund.serial_number
        assert transactions[
            0].content_object.serial_number == refund.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MERCHANT_REFUND
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.merchant_account.id
        assert transactions[0].amount == -1900 + (payment.platform_share +
                                                  payment.inviter_share +
                                                  payment.originator_share)
        assert transactions[0].balance_after_transaction == 0

        assert transactions[1].object_id == refund.serial_number
        assert transactions[
            1].content_object.serial_number == refund.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EARNING_MERCHANT_REFUND
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == 1900 - (payment.platform_share +
                                                 payment.inviter_share +
                                                 payment.originator_share)
        assert transactions[1].balance_after_transaction == 1900

        assert transactions[2].object_id == refund.serial_number
        assert transactions[
            2].content_object.serial_number == refund.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.PLATFORM_REFUND
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.platform_account.id
        assert transactions[2].amount == -1900
        assert transactions[2].balance_after_transaction == 0

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.alipay_balance == 0
        assert self.platform_account.alipay_withdrawable_balance == 0

        assert self.merchant_account.alipay_balance == 0
        assert self.merchant_account.alipay_withdrawable_balance == 0

        # check the coupon
        self.assertEqual(payment.coupon.status, COUPON_STATUS.DESTROYED)

        coupon = payment.coupon
        new_coupon = payment.coupon.client.coupon_set.exclude(
            status=COUPON_STATUS.DESTROYED).first()
        self.assertEqual(new_coupon.rule, coupon.rule)
        self.assertEqual(new_coupon.discount, coupon.discount)
        self.assertEqual(new_coupon.min_charge, coupon.min_charge)
        self.assertEqual(new_coupon.originator_merchant,
                         coupon.originator_merchant)
        self.assertEqual(new_coupon.status, COUPON_STATUS.NOT_USED)
        self.assertEqual(new_coupon.obtain_datetime, coupon.obtain_datetime)

    def test_refund_without_coupon(self):
        self.test_on_payment_success_without_coupon()
        payment = Payment.objects.all().order_by('-datetime')[0]

        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY
                self.payment = payment

            def refund(self, payment_serial_number, refund_serial_number,
                       refund_amount):
                assert payment_serial_number == self.payment.serial_number
                assert len(refund_serial_number) == 32
                assert refund_amount == 2000

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_usecases = AlipayUseCases()
        alipay_usecases.request_refund(payment)

        # Check the refund data
        refund = Refund.objects.all().order_by('-datetime')[0]
        assert len(refund.serial_number) == 32
        assert len(refund.transactions.all()) == 3
        assert (timezone.now() - refund.datetime) < timedelta(seconds=30)
        assert refund.status == REFUND_STATUS.FINISHED
        assert refund.payment.serial_number == payment.serial_number

        # Check the payment data
        payment.refresh_from_db()
        assert payment.status == PAYMENT_STATUS.REFUND

        # Check the transactions.
        transactions = refund.transactions.all().order_by('id')
        assert transactions[0].object_id == refund.serial_number
        assert transactions[
            0].content_object.serial_number == refund.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MERCHANT_REFUND
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.merchant_account.id
        assert transactions[0].amount == -2000
        assert transactions[0].balance_after_transaction == 0

        assert transactions[1].object_id == refund.serial_number
        assert transactions[
            1].content_object.serial_number == refund.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EARNING_MERCHANT_REFUND
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == 2000
        assert transactions[1].balance_after_transaction == 2000

        assert transactions[2].object_id == refund.serial_number
        assert transactions[
            2].content_object.serial_number == refund.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.PLATFORM_REFUND
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.platform_account.id
        assert transactions[2].amount == -2000
        assert transactions[2].balance_after_transaction == 0

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.alipay_balance == 0
        assert self.platform_account.alipay_withdrawable_balance == 0

        assert self.merchant_account.alipay_balance == 0
        assert self.merchant_account.alipay_withdrawable_balance == 0

    def test_payment_unfreeze(self):
        self.test_on_payment_success()
        new_payment = Payment.objects.all().order_by('-datetime')[0]
        alipay_usecases = AlipayUseCases()
        alipay_usecases.on_payment_unfreeze(new_payment)

        new_payment.refresh_from_db()
        assert new_payment.status == PAYMENT_STATUS.FINISHED

        self.platform_account.refresh_from_db()
        assert self.platform_account.alipay_balance == new_payment.platform_share
        assert self.platform_account.alipay_withdrawable_balance == 0

        self.merchant_account.refresh_from_db()
        assert self.merchant_account.alipay_balance == 1900 - sum(
            (new_payment.platform_share, new_payment.inviter_share,
             new_payment.originator_share))
        assert self.merchant_account.alipay_withdrawable_balance == 1900 - sum(
            (new_payment.platform_share, new_payment.inviter_share,
             new_payment.originator_share))

        self.inviter_account.refresh_from_db()
        assert self.inviter_account.alipay_balance == new_payment.inviter_share
        assert self.inviter_account.alipay_withdrawable_balance == new_payment.inviter_share

        self.originator_account.refresh_from_db()
        assert self.originator_account.alipay_balance == new_payment.originator_share
        assert self.originator_account.alipay_withdrawable_balance == new_payment.originator_share

    def test_payment_unfreeze_without_coupon(self):
        self.test_on_payment_success_without_coupon()

        new_payment = Payment.objects.all().order_by('-datetime')[0]
        alipay_usecases = AlipayUseCases()
        alipay_usecases.on_payment_unfreeze(new_payment)

        new_payment.refresh_from_db()
        assert new_payment.status == PAYMENT_STATUS.FINISHED

        self.platform_account.refresh_from_db()
        assert self.platform_account.alipay_balance == 0
        assert self.platform_account.alipay_withdrawable_balance == 0

        self.merchant_account.refresh_from_db()
        assert self.merchant_account.alipay_balance == 2000
        assert self.merchant_account.alipay_withdrawable_balance == 2000

        self.inviter_account.refresh_from_db()
        assert self.inviter_account.alipay_balance == 0
        assert self.inviter_account.alipay_withdrawable_balance == 0

        self.originator_account.refresh_from_db()
        assert self.originator_account.alipay_balance == 0
        assert self.originator_account.alipay_withdrawable_balance == 0

    def test_merchant_withdraw(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def pay_to_alipay(self, serial_number, receiver_alipay_id, amount,
                              desc, payee_type, payee_real_name):
                assert len(serial_number) == 32
                assert receiver_alipay_id == self.test_case.merchant_admin.alipay_userid
                assert amount == 1000
                assert desc == '付款联盟提现'
                return 'refund_request_success'

        self.merchant_account.alipay_balance = 10000
        self.merchant_account.alipay_withdrawable_balance = 5000
        self.merchant_account.save()

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_balance_usecases = AlipayUseCases()
        alipay_balance_usecases.withdraw(self.merchant_account, 1000)

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 1
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.merchant_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FINISHED

        # Check the transaction
        transactions = withdraw.transactions.all().order_by('id')
        assert transactions[0].object_id == withdraw.serial_number
        assert transactions[
            0].content_object.serial_number == withdraw.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MERCHANT_WITHDRAW
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.merchant_account.id
        assert transactions[0].amount == -1000
        assert transactions[0].balance_after_transaction == 9000

        # Check the balance
        self.merchant_account.refresh_from_db()
        assert self.merchant_account.alipay_balance == 9000
        assert self.merchant_account.alipay_withdrawable_balance == 4000

    def test_merchant_withdraw_insufficient(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def pay_to_alipay(self, serial_number, receiver_alipay_id, amount,
                              desc, payee_type, payee_real_name):
                assert len(serial_number) == 32
                assert receiver_alipay_id == self.test_case.merchant_admin.alipay_userid
                assert amount == 1000
                assert desc == '付款联盟提现'
                return 'refund_request_success'

        self.merchant_account.alipay_balance = 10000
        self.merchant_account.alipay_withdrawable_balance = 500
        self.merchant_account.save()

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_balance_usecases = AlipayUseCases()

        self.assertRaises(BalanceInsufficient,
                          alipay_balance_usecases.withdraw,
                          self.merchant_account, 1000)

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 0
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.merchant_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FAILED

        # Check the balance
        self.merchant_account.refresh_from_db()
        assert self.merchant_account.alipay_balance == 10000
        assert self.merchant_account.alipay_withdrawable_balance == 500

    def test_marketer_withdraw(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def pay_to_alipay(self, serial_number, receiver_alipay_id, amount,
                              desc, payee_type, payee_real_name):
                assert len(serial_number) == 32
                assert receiver_alipay_id == self.test_case.inviter.alipay_id
                assert amount == 1000
                assert desc == '付款联盟提现'
                return 'refund_request_success'

        self.inviter_account.alipay_balance = 10000
        self.inviter_account.alipay_withdrawable_balance = 5000
        self.inviter_account.save()

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_balance_usecases = AlipayUseCases()
        alipay_balance_usecases.withdraw(self.inviter_account, 1000)

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 1
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.inviter_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FINISHED

        # Check the transaction
        transactions = withdraw.transactions.all().order_by('id')
        assert transactions[0].object_id == withdraw.serial_number
        assert transactions[
            0].content_object.serial_number == withdraw.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MARKETER_WITHDRAW
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.inviter_account.id
        assert transactions[0].amount == -1000
        assert transactions[0].balance_after_transaction == 9000

        # Check the balance
        self.inviter_account.refresh_from_db()
        assert self.inviter_account.alipay_balance == 9000
        assert self.inviter_account.alipay_withdrawable_balance == 4000

    def test_marketer_withdraw_insufficient(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def pay_to_alipay(self, serial_number, receiver_alipay_id, amount,
                              desc, payee_type, payee_real_name):
                assert len(serial_number) == 32
                assert receiver_alipay_id == self.test_case.inviter.alipay_id
                assert amount == 1000
                assert desc == '付款联盟提现'
                return 'refund_request_success'

        self.inviter_account.alipay_balance = 10000
        self.inviter_account.alipay_withdrawable_balance = 500
        self.inviter_account.save()

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_balance_usecases = AlipayUseCases()

        self.assertRaises(BalanceInsufficient,
                          alipay_balance_usecases.withdraw,
                          self.inviter_account, 1000)

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 0
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.inviter_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FAILED

        # Check the balance
        self.inviter_account.refresh_from_db()
        assert self.inviter_account.alipay_balance == 10000
        assert self.inviter_account.alipay_withdrawable_balance == 500

    def test_marketer_transfer(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def pay_to_alipay(self, serial_number, receiver_alipay_id, amount,
                              desc, payee_type, payee_real_name):
                assert len(serial_number) == 32
                assert receiver_alipay_id == self.test_case.inviter.alipay_id
                assert amount == 10
                assert desc == '付款联盟邀请人支付宝账号验证'
                return 'refund_request_success'

        self.platform_account.alipay_balance = 1000
        self.platform_account.alipay_withdrawable_balance = 500
        self.platform_account.save()

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_balance_usecases = AlipayUseCases()
        alipay_balance_usecases.transfer(self.inviter.wechat_unionid,
                                         self.inviter.alipay_id,
                                         self.inviter.name, 10)

        transfer = TransferRecord.objects.all().order_by('-datetime')[0]

        # Check transfer
        assert len(transfer.serial_number) == 32
        assert (timezone.now() - transfer.datetime) < timedelta(seconds=30)
        assert transfer.account_number == self.inviter.alipay_id
        assert transfer.account_name == self.inviter.name
        assert transfer.amount == 10
        assert transfer.status == WITHDRAW_STATUS.FINISHED

    def test_marketer_transfer_insufficient(self):
        class AlipayApisMock(object):
            test_case = self

            def __init__(self, app_id, private_key, alipay_public_key):
                assert app_id == dynasettings.ALIPAY_APP_ID
                assert private_key == dynasettings.ALIPAY_APP_PRIVATE_KEY
                assert alipay_public_key == dynasettings.ALIPAY_PUBLIC_KEY

            def pay_to_alipay(self, serial_number, receiver_alipay_id, amount,
                              desc, payee_type, payee_real_name):
                assert len(serial_number) == 32
                assert receiver_alipay_id == self.test_case.inviter.alipay_id
                assert amount == 10
                assert desc == '付款联盟邀请人支付宝账号验证'
                return 'refund_request_success'

        AlipayUseCases.api_cls = AlipayApisMock
        alipay_balance_usecases = AlipayUseCases()

        self.assertRaises(BalanceInsufficient,
                          alipay_balance_usecases.transfer,
                          self.inviter.wechat_unionid, self.inviter.alipay_id,
                          self.inviter.name, 10)

        transfer = TransferRecord.objects.all().order_by('-datetime')[0]

        # Check transfer
        assert len(transfer.serial_number) == 32
        assert (timezone.now() - transfer.datetime) < timedelta(seconds=30)
        assert transfer.account_number == self.inviter.alipay_id
        assert transfer.account_name == self.inviter.name
        assert transfer.amount == 10
        assert transfer.status == WITHDRAW_STATUS.FAILED
Ejemplo n.º 8
0
class TestWechatPaySteps(APITestCase):
    def setUp(self):
        self.factory = PayunionFactory()

        try:
            self.platform_account = Account.objects.get(id=1)
        except Account.DoesNotExist:
            self.platform_account = self.factory.create_account(
                id=1,
                balance=0,
                withdrawable_balance=0,
                alipay_balance=0,
                alipay_withdrawable_balance=0)
        self.originator_account = self.factory.create_account(
            real_name='引流商户',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.merchant_account = self.factory.create_account(
            real_name='收款',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.inviter_account = self.factory.create_account(
            real_name='邀请人',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.inviter = self.factory.create_marketer(
            account=self.inviter_account)

        self.test_client = self.factory.create_client(openid='1234567890',
                                                      status=0)
        self.merchant = self.factory.create_merchant(
            account=self.merchant_account,
            inviter=self.inviter,
            status=MERCHANT_STATUS.USING)
        self.originator = self.factory.create_merchant(
            account=self.originator_account)
        self.rule = self.factory.create_coupon_rule(
            merchant=self.merchant,
            valid_strategy=VALID_STRATEGY.EXPIRATION,
            expiration_days=30)
        self.coupon = self.factory.create_coupon(
            rule=self.rule,
            client=self.test_client,
            originator_merchant=self.originator,
            min_charge=1000,
            discount=100,
            obtain_datetime=timezone.now(),
            status=COUPON_STATUS['NOT_USED'])

        self.merchant_admin = self.factory.create_merchant_admin(
            merchant_admin_type=MERCHANT_ADMIN_TYPES.ADMIN,
            work_merchant=self.merchant)
        super(TestWechatPaySteps, self).setUpTestData()

    def test_workflow(self):
        client_steps = ClientSteps()

        # Login
        code = uuid.uuid4().hex

        def validate(request):
            params = parse_qs(request.query)
            self.assertEqual(params['appid'][0],
                             dynasettings.CLIENT_MINI_APP_ID)
            self.assertEqual(params['secret'][0],
                             dynasettings.CLIENT_MINI_APP_SECRET)
            self.assertEqual(params['js_code'][0], code)
            self.assertEqual(params['grant_type'][0], 'authorization_code')
            return True

        login_result = client_steps.wechat_login(
            code,
            self.test_client.openid,
            's84hYB8FH2ck1sayryNbCg==',
            self.test_client.wechat_unionid,
            validate=validate)
        access_token = login_result['access_token']
        self.assertEqual(len(access_token), 32)

        # Get user info
        me_info = client_steps.me(access_token)
        assert me_info

        # Set phone number
        set_phone_result = client_steps.set_phone(
            access_token,
            iv='hfN9fRyw7F4oezqYRAzj8g==',
            encrypted_data=
            'InF6jgIwHeH2ov7KqGhMXGD5w4Km464346EWahygQ56VM6tqEP9uJCSbgZhV6ar/nw'
            'NLnW1vh04ngYyNWVm1JiM6PD6AD9/A5SS4DgsiPGcbVqlixkWbsLbNciW0uTbLT2PM'
            'S4yEGiih+G/wGojToxh0hfivGo/AdgmXtKjbGrzB7aF9S6bHY0V9QF8Q+LxwcNrc+r'
            'DxZXYVFYc9TgJIaw==')
        self.assertEqual(set_phone_result, {'phone_known': True})

        # Get all coupons
        coupons = client_steps.get_coupons(access_token)
        self.assertEqual(len(coupons), 1)
        self.assertEqual(coupons[0]['id'], self.coupon.id)
        self.assertEqual(coupons[0]['rule']['merchant']['name'],
                         self.merchant.name)
        self.assertEqual(coupons[0]['discount'], self.coupon.discount)
        self.assertEqual(coupons[0]['min_charge'], self.coupon.min_charge)

        # Get merchant info
        merchant_info = client_steps.get_merchant_info(
            access_token, self.merchant.payment_qr_code.uuid)
        self.assertTrue(merchant_info)
        self.assertTrue(merchant_info['id'])
        self.assertTrue(merchant_info['name'])
        self.assertTrue(merchant_info['description'])
        self.assertTrue(merchant_info['avatar_url'])
        self.assertTrue(isinstance(merchant_info['status'], int))

        # Place order
        wechat_pay_steps = WechatPaySteps(access_token,
                                          dynasettings.CLIENT_MINI_APP_ID,
                                          dynasettings.WECHAT_MERCHANT_ID)
        shared_pay_stpes = SharedPaySteps(access_token)

        place_order_result = wechat_pay_steps.place_order(
            self.merchant.id, 1000, self.coupon.id)

        assert place_order_result
        self.assertEqual(place_order_result['client_payment_params']['appId'],
                         dynasettings.CLIENT_MINI_APP_ID)
        self.assertEqual(
            len(place_order_result['client_payment_params']['timeStamp']), 10)
        self.assertEqual(
            len(place_order_result['client_payment_params']['nonceStr']), 32)
        self.assertEqual(
            len(place_order_result['client_payment_params']['paySign']), 32)
        self.assertEqual(
            len(place_order_result['client_payment_params']['package']), 46)
        self.assertIn('prepay_id=',
                      place_order_result['client_payment_params']['package'])
        self.assertEqual(len(place_order_result['payment_serial_number']), 32)

        # Cancel the order
        cancel_order_result = wechat_pay_steps.cancel_order(
            place_order_result['payment_serial_number'])
        assert cancel_order_result['new_coupon']
        self.coupon = Coupon.objects.get(
            id=cancel_order_result['new_coupon']['id'])
        assert self.coupon

        # Place another order
        wechat_pay_steps = WechatPaySteps(access_token,
                                          dynasettings.CLIENT_MINI_APP_ID,
                                          dynasettings.WECHAT_MERCHANT_ID)
        place_order_result = wechat_pay_steps.place_order(
            self.merchant.id, 1000, self.coupon.id)

        assert place_order_result
        self.assertEqual(place_order_result['client_payment_params']['appId'],
                         dynasettings.CLIENT_MINI_APP_ID)
        self.assertEqual(
            len(place_order_result['client_payment_params']['timeStamp']), 10)
        self.assertEqual(
            len(place_order_result['client_payment_params']['nonceStr']), 32)
        self.assertEqual(
            len(place_order_result['client_payment_params']['paySign']), 32)
        self.assertEqual(
            len(place_order_result['client_payment_params']['package']), 46)
        self.assertIn('prepay_id=',
                      place_order_result['client_payment_params']['package'])
        self.assertEqual(len(place_order_result['payment_serial_number']), 32)

        poll_result_result = shared_pay_stpes.poll_result(
            place_order_result['payment_serial_number'], None, None, None)
        assert poll_result_result['payment']['status'] == 0

        # Payment callback
        callback_response = wechat_pay_steps.payment_callback(
            dynasettings.CLIENT_MINI_APP_ID,
            dynasettings.WECHAT_MERCHANT_ID,
            self.test_client.openid,
            place_order_result['payment_serial_number'],
            900,
        )
        assert 'SUCCESS' in str(callback_response.content)

        poll_result_result = shared_pay_stpes.poll_result(
            place_order_result['payment_serial_number'], None, None, None)
        assert poll_result_result['payment']['status'] == 1

        # login merchant
        resp = MerchantLoginMockStep(self).login(
            unionid=self.merchant_admin.wechat_unionid)
        token = resp.json()['token']

        # get merchant info
        me = MerchantStep(self, token=token).me().json()
        self.assertEqual(me['name'], self.merchant.name)

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.FROZEN)

        # refund
        refund_result = TransactionStep(self, token=token).refund(
            payment_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(refund_result, {})

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'],
                         config.PAYMENT_STATUS.REFUND_REQUESTED)

        # refund callback
        refund_callback_result = wechat_pay_steps.refund_callback(
            dynasettings.CLIENT_MINI_APP_ID, dynasettings.WECHAT_MERCHANT_ID,
            place_order_result['payment_serial_number'],
            Refund.objects.get(payment__serial_number=place_order_result[
                'payment_serial_number']).serial_number, 900)
        self.assertTrue('SUCCESS' in str(refund_callback_result.content))

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.REFUND)

        # ********************************************
        # Place another to test status sync
        place_order_result = wechat_pay_steps.place_order(
            self.merchant.id, 1000, None)

        # Payment callback
        wechat_pay_steps.payment_callback(
            dynasettings.CLIENT_MINI_APP_ID,
            dynasettings.WECHAT_MERCHANT_ID,
            self.test_client.openid,
            place_order_result['payment_serial_number'],
            1000,
        )

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.FROZEN)

        # Sync refund status, expects nothing happens, because no refund in process.
        redis_cli = StrictRedis.from_url(BROKER_URL)
        queue_length = redis_cli.llen('payunion')
        wechat_pay_steps.refund_status_sync()
        queue_length1 = redis_cli.llen('payunion')
        self.assertEqual(queue_length, queue_length1)

        # refund
        refund_result = TransactionStep(self, token=token).refund(
            payment_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(refund_result, {})

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'],
                         config.PAYMENT_STATUS.REFUND_REQUESTED)

        # Sync refund status
        redis_cli = StrictRedis.from_url(BROKER_URL)
        queue_length = redis_cli.llen('payunion')
        wechat_pay_steps.refund_status_sync()
        queue_length1 = redis_cli.llen('payunion')
        self.assertEqual(queue_length + 1, queue_length1)
        task_info = redis_cli.lpop('payunion')
        task_info = json.loads(task_info)
        self.assertEqual(task_info['headers']['task'],
                         'puser.tasks.wechat_refund_status_sync')
        self.assertEqual(
            task_info['headers']['argsrepr'],
            repr((Refund.objects.get(payment__serial_number=place_order_result[
                'payment_serial_number']).serial_number, )))

        # Because celery is not started, call the task directly
        wechat_pay_steps.call_task_refund_status_sync(
            Refund.objects.get(payment__serial_number=place_order_result[
                'payment_serial_number']).serial_number)

        # get order detail
        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'], config.PAYMENT_STATUS.REFUND)

        # ********************************************
        # Place another to test unfreeze
        place_order_result = wechat_pay_steps.place_order(
            self.merchant.id, 1000,
            Coupon.objects.filter(
                client=self.test_client).order_by('-id').first().id)

        # Payment callback
        wechat_pay_steps.payment_callback(
            dynasettings.CLIENT_MINI_APP_ID,
            dynasettings.WECHAT_MERCHANT_ID,
            self.test_client.openid,
            place_order_result['payment_serial_number'],
            900,
        )

        # Unfreeze and check result.
        shared_pay_stpes.unfreeze_immediately()

        order_detail = TransactionStep(self, token=token).retrieve(
            transaction_id=place_order_result['payment_serial_number']).json()
        self.assertEqual(order_detail['status'],
                         config.PAYMENT_STATUS.FINISHED)
Ejemplo n.º 9
0
class WechatPayTestCases(TestCase):
    def __init__(self, methodName='runTest'):
        super().__init__(methodName)
        self.factory = PayunionFactory()

    def setUp(self):
        try:
            self.platform_account = Account.objects.get(id=1)
        except Account.DoesNotExist:
            self.platform_account = self.factory.create_account(
                id=1,
                balance=0,
                withdrawable_balance=0,
                alipay_balance=0,
                alipay_withdrawable_balance=0)
        self.originator_account = self.factory.create_account(
            real_name='引流商户',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.merchant_account = self.factory.create_account(
            real_name='收款',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.inviter_account = self.factory.create_account(
            real_name='邀请人',
            balance=0,
            withdrawable_balance=0,
            alipay_balance=0,
            alipay_withdrawable_balance=0)
        self.inviter = self.factory.create_marketer(
            account=self.inviter_account)

        self.client = self.factory.create_client(openid='1234567890')
        self.merchant = self.factory.create_merchant(
            account=self.merchant_account, inviter=self.inviter)
        self.originator = self.factory.create_merchant(
            account=self.originator_account)
        self.rule = self.factory.create_coupon_rule(merchant=self.merchant)
        self.coupon = self.factory.create_coupon(
            rule=self.rule,
            client=self.client,
            originator_merchant=self.originator,
            min_charge=1000,
            discount=100,
            status=COUPON_STATUS['NOT_USED'])

        self.merchant_admin = self.factory.create_merchant_admin(
            merchant_admin_type=MERCHANT_ADMIN_TYPES.ADMIN,
            work_merchant=self.merchant)

    def test_place_order(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def place_order(self, order_title, payment_serial_number,
                            total_fee, spbill_create_ip, notify_url, openid):
                assert notify_url == 'http://test.com'
                assert order_title == self.test_case.merchant.name
                assert len(payment_serial_number) == 32
                assert total_fee == 1900
                assert openid == self.test_case.client.openid
                assert spbill_create_ip == '111.222.111.222'

                return {'prepay_id': 'prepay_id123'}

            def generate_client_payment_params(self, prepay_id):
                assert prepay_id == 'prepay_id123'
                return 'place_order_success'

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        result = wechat_pay_usecases.place_order(
            self.client,
            self.merchant,
            self.coupon,
            2000,
            '111.222.111.222',
            'http://test.com',
        )
        assert result['client_payment_params'] == 'place_order_success'
        assert len(result['payment_serial_number']) == 32

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        assert new_payment
        assert len(new_payment.serial_number) == 32
        assert len(new_payment.transactions.all()) == 0
        assert (timezone.now() - new_payment.datetime) < timedelta(seconds=30)
        assert new_payment.pay_channel == PAY_CHANNELS.WECHAT
        assert new_payment.merchant_id == self.merchant.id
        assert new_payment.status == PAYMENT_STATUS.UNPAID
        assert new_payment.client_id == self.client.id
        assert new_payment.order_price == 2000
        assert new_payment.coupon_id == self.coupon.id
        assert new_payment.platform_share == round(1900 * PLATFORM_SHARE_RATE)
        assert new_payment.inviter_share == round(1900 * INVITER_SHARE_RATE)
        assert new_payment.originator_share == round(1900 *
                                                     ORIGINATOR_SHARE_RATE)
        assert new_payment.note is None

    def test_place_order_without_coupon(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def place_order(self, order_title, payment_serial_number,
                            total_fee, spbill_create_ip, notify_url, openid):
                assert notify_url == 'http://test.com'
                assert order_title == self.test_case.merchant.name
                assert len(payment_serial_number) == 32
                assert total_fee == 2000
                assert openid == self.test_case.client.openid
                assert spbill_create_ip == '111.222.111.222'

                return {'prepay_id': 'prepay_id123'}

            def generate_client_payment_params(self, prepay_id):
                assert prepay_id == 'prepay_id123'
                return 'place_order_success'

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        result = wechat_pay_usecases.place_order(
            self.client,
            self.merchant,
            None,
            2000,
            '111.222.111.222',
            'http://test.com',
        )
        assert result['client_payment_params'] == 'place_order_success'
        assert len(result['payment_serial_number']) == 32

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        assert new_payment
        assert len(new_payment.serial_number) == 32
        assert len(new_payment.transactions.all()) == 0
        assert (timezone.now() - new_payment.datetime) < timedelta(seconds=30)
        assert new_payment.pay_channel == PAY_CHANNELS.WECHAT
        assert new_payment.merchant_id == self.merchant.id
        assert new_payment.status == PAYMENT_STATUS.UNPAID
        assert new_payment.client_id == self.client.id
        assert new_payment.order_price == 2000
        assert new_payment.coupon is None
        assert new_payment.platform_share == 0
        assert new_payment.inviter_share == 0
        assert new_payment.originator_share == 0
        assert new_payment.note is None

    def test_cancel_order(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def cancel(self, payment_serial_number):
                assert len(payment_serial_number) == 32
                return 'cancel_order_success'

        self.test_place_order()

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        new_coupon = wechat_pay_usecases.cancel_order(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 0
        assert payment.status == PAYMENT_STATUS.CANCELLED
        assert payment.coupon.status == COUPON_STATUS.DESTROYED

        assert new_coupon.rule == payment.coupon.rule
        assert new_coupon.client == payment.coupon.client
        assert new_coupon.discount == payment.coupon.discount
        assert new_coupon.min_charge == payment.coupon.min_charge
        assert new_coupon.originator_merchant == payment.coupon.originator_merchant
        assert new_coupon.status == COUPON_STATUS.NOT_USED
        assert new_coupon.obtain_datetime == payment.coupon.obtain_datetime
        assert new_coupon.use_datetime is None

    def test_cancel_order_without_coupon(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def cancel(self, payment_serial_number):
                assert len(payment_serial_number) == 32
                return 'cancel_order_success'

        self.test_place_order_without_coupon()

        new_payment = Payment.objects.all().order_by('-datetime')[0]

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        new_coupon = wechat_pay_usecases.cancel_order(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 0
        assert payment.status == PAYMENT_STATUS.CANCELLED
        assert payment.coupon is None

        assert new_coupon is None

    def test_on_payment_success(self):
        self.test_place_order()
        new_payment = Payment.objects.all().order_by('-datetime')[0]
        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.on_payment_success(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 3
        assert payment.datetime == new_payment.datetime
        assert payment.pay_channel == PAY_CHANNELS.WECHAT
        assert payment.status == PAYMENT_STATUS.FROZEN
        assert payment.merchant_id == self.merchant.id
        assert payment.client_id == self.client.id
        assert payment.order_price == 2000
        assert payment.coupon_id == self.coupon.id
        assert payment.platform_share == round(1900 * PLATFORM_SHARE_RATE)
        assert payment.inviter_share == round(1900 * INVITER_SHARE_RATE)
        assert payment.originator_share == round(1900 * ORIGINATOR_SHARE_RATE)
        assert payment.note is None

        # Check the transactions.
        transactions = payment.transactions.all().order_by('id')
        assert transactions[0].object_id == payment.serial_number
        assert transactions[
            0].content_object.serial_number == payment.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.PLATFORM_RECEIVE
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.platform_account.id
        assert transactions[0].amount == 1900
        assert transactions[0].balance_after_transaction == 1900

        assert transactions[1].object_id == payment.serial_number
        assert transactions[
            1].content_object.serial_number == payment.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EXPEND_MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == -1900 + (payment.platform_share +
                                                  payment.inviter_share +
                                                  payment.originator_share)
        assert transactions[1].balance_after_transaction == (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)

        assert transactions[2].object_id == payment.serial_number
        assert transactions[
            2].content_object.serial_number == payment.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.merchant_account.id
        assert transactions[2].amount == 1900 - (payment.platform_share +
                                                 payment.inviter_share +
                                                 payment.originator_share)
        assert transactions[2].balance_after_transaction == 1900 - (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.balance == (payment.platform_share +
                                                 payment.inviter_share +
                                                 payment.originator_share)
        assert self.platform_account.withdrawable_balance == 0

        assert self.merchant_account.balance == 1900 - (
            payment.platform_share + payment.inviter_share +
            payment.originator_share)
        assert self.merchant_account.withdrawable_balance == 0

    def test_on_payment_success_without_coupon(self):
        self.test_place_order_without_coupon()
        new_payment = Payment.objects.all().order_by('-datetime')[0]
        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.on_payment_success(new_payment)

        # Check the payment data
        payment = Payment.objects.get(serial_number=new_payment.serial_number)
        assert payment.serial_number == new_payment.serial_number
        assert len(payment.transactions.all()) == 3
        assert payment.datetime == new_payment.datetime
        assert payment.pay_channel == PAY_CHANNELS.WECHAT
        assert payment.status == PAYMENT_STATUS.FROZEN
        assert payment.merchant_id == self.merchant.id
        assert payment.client_id == self.client.id
        assert payment.order_price == 2000
        assert payment.coupon is None
        assert payment.platform_share == 0
        assert payment.inviter_share == 0
        assert payment.originator_share == 0
        assert payment.note is None

        # Check the transactions.
        transactions = payment.transactions.all().order_by('id')
        assert transactions[0].object_id == payment.serial_number
        assert transactions[
            0].content_object.serial_number == payment.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.PLATFORM_RECEIVE
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.platform_account.id
        assert transactions[0].amount == 2000
        assert transactions[0].balance_after_transaction == 2000

        assert transactions[1].object_id == payment.serial_number
        assert transactions[
            1].content_object.serial_number == payment.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EXPEND_MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == -2000
        assert transactions[1].balance_after_transaction == 0

        assert transactions[2].object_id == payment.serial_number
        assert transactions[
            2].content_object.serial_number == payment.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.MERCHANT_RECEIVE
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.merchant_account.id
        assert transactions[2].amount == 2000
        assert transactions[2].balance_after_transaction == 2000

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.balance == 0
        assert self.platform_account.withdrawable_balance == 0

        assert self.merchant_account.balance == 2000
        assert self.merchant_account.withdrawable_balance == 0

    def test_refund(self):
        self.test_on_payment_success()
        payment = Payment.objects.all().order_by('-datetime')[0]

        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY
                self.payment = payment

            def refund(self, payment_serial_number, refund_serial_number,
                       total_fee, refund_fee, notify_url):
                assert payment_serial_number == self.payment.serial_number
                assert len(refund_serial_number) == 32
                assert total_fee == 1900
                assert refund_fee == 1900
                assert notify_url == 'http://test.com'
                return 'refund_request_success'

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        result = wechat_pay_usecases.request_refund(
            payment, notify_url='http://test.com')
        assert result == 'refund_request_success'

        # Check the refund data
        refund = Refund.objects.all().order_by('-datetime')[0]
        assert len(refund.serial_number) == 32
        assert len(refund.transactions.all()) == 0
        assert (timezone.now() - refund.datetime) < timedelta(seconds=30)
        assert refund.status == REFUND_STATUS.REQUESTED
        assert refund.payment.serial_number == payment.serial_number

    def test_on_refund_success(self):
        self.test_refund()
        refund = Refund.objects.all().order_by('-datetime')[0]

        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.on_refund_success(refund)

        # Check the payment data
        payment = Payment.objects.all().order_by('-datetime')[0]
        assert payment.status == PAYMENT_STATUS.REFUND

        # Check the refund
        refund.refresh_from_db()
        assert len(refund.serial_number) == 32
        assert len(refund.transactions.all()) == 3
        assert (timezone.now() - refund.datetime) < timedelta(seconds=30)
        assert refund.status == REFUND_STATUS.FINISHED
        assert refund.payment.serial_number == payment.serial_number

        # Check the transactions.
        transactions = refund.transactions.all().order_by('id')
        assert transactions[0].object_id == refund.serial_number
        assert transactions[
            0].content_object.serial_number == refund.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MERCHANT_REFUND
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.merchant_account.id
        assert transactions[0].amount == -1900 + (payment.platform_share +
                                                  payment.inviter_share +
                                                  payment.originator_share)
        assert transactions[0].balance_after_transaction == 0

        assert transactions[1].object_id == refund.serial_number
        assert transactions[
            1].content_object.serial_number == refund.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EARNING_MERCHANT_REFUND
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == 1900 - (payment.platform_share +
                                                 payment.inviter_share +
                                                 payment.originator_share)
        assert transactions[1].balance_after_transaction == 1900

        assert transactions[2].object_id == refund.serial_number
        assert transactions[
            2].content_object.serial_number == refund.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.PLATFORM_REFUND
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.platform_account.id
        assert transactions[2].amount == -1900
        assert transactions[2].balance_after_transaction == 0

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.balance == 0
        assert self.platform_account.withdrawable_balance == 0

        assert self.merchant_account.balance == 0
        assert self.merchant_account.withdrawable_balance == 0

        # check the coupon
        self.assertEqual(payment.coupon.status, COUPON_STATUS.DESTROYED)

        coupon = payment.coupon
        new_coupon = payment.coupon.client.coupon_set.exclude(
            status=COUPON_STATUS.DESTROYED).first()
        self.assertEqual(new_coupon.rule, coupon.rule)
        self.assertEqual(new_coupon.discount, coupon.discount)
        self.assertEqual(new_coupon.min_charge, coupon.min_charge)
        self.assertEqual(new_coupon.originator_merchant,
                         coupon.originator_merchant)
        self.assertEqual(new_coupon.status, COUPON_STATUS.NOT_USED)
        self.assertEqual(new_coupon.obtain_datetime, coupon.obtain_datetime)

    def test_refund_without_coupon(self):
        self.test_on_payment_success_without_coupon()
        payment = Payment.objects.all().order_by('-datetime')[0]

        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY
                self.payment = payment

            def refund(self, payment_serial_number, refund_serial_number,
                       total_fee, refund_fee, notify_url):
                assert payment_serial_number == self.payment.serial_number
                assert len(refund_serial_number) == 32
                assert total_fee == 2000
                assert refund_fee == 2000
                assert notify_url == 'http://test.com'
                return 'refund_request_success'

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        result = wechat_pay_usecases.request_refund(
            payment, notify_url='http://test.com')
        assert result == 'refund_request_success'

        # Check the refund data
        refund = Refund.objects.all().order_by('-datetime')[0]
        assert len(refund.serial_number) == 32
        assert len(refund.transactions.all()) == 0
        assert (timezone.now() - refund.datetime) < timedelta(seconds=30)
        assert refund.status == REFUND_STATUS.REQUESTED
        assert refund.payment.serial_number == payment.serial_number

    def test_on_refund_success_without_coupon(self):
        self.test_refund_without_coupon()
        refund = Refund.objects.all().order_by('-datetime')[0]

        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.on_refund_success(refund)

        # Check the payment data
        payment = Payment.objects.all().order_by('-datetime')[0]
        assert payment.status == PAYMENT_STATUS.REFUND

        # Check the refund
        refund.refresh_from_db()
        assert len(refund.serial_number) == 32
        assert len(refund.transactions.all()) == 3
        assert (timezone.now() - refund.datetime) < timedelta(seconds=30)
        assert refund.status == REFUND_STATUS.FINISHED
        assert refund.payment.serial_number == payment.serial_number

        # Check the transactions.
        transactions = refund.transactions.all().order_by('id')
        assert transactions[0].object_id == refund.serial_number
        assert transactions[
            0].content_object.serial_number == refund.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MERCHANT_REFUND
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.merchant_account.id
        assert transactions[0].amount == -2000
        assert transactions[0].balance_after_transaction == 0

        assert transactions[1].object_id == refund.serial_number
        assert transactions[
            1].content_object.serial_number == refund.serial_number
        assert transactions[
            1].transaction_type == TRANSACTION_TYPE.PLATFORM_EARNING_MERCHANT_REFUND
        assert (timezone.now() -
                transactions[1].datetime) < timedelta(seconds=30)
        assert transactions[1].account_id == self.platform_account.id
        assert transactions[1].amount == 2000
        assert transactions[1].balance_after_transaction == 2000

        assert transactions[2].object_id == refund.serial_number
        assert transactions[
            2].content_object.serial_number == refund.serial_number
        assert transactions[
            2].transaction_type == TRANSACTION_TYPE.PLATFORM_REFUND
        assert (timezone.now() -
                transactions[2].datetime) < timedelta(seconds=30)
        assert transactions[2].account_id == self.platform_account.id
        assert transactions[2].amount == -2000
        assert transactions[2].balance_after_transaction == 0

        # Check the account
        self.platform_account.refresh_from_db()
        self.merchant_account.refresh_from_db()
        assert self.platform_account.balance == 0
        assert self.platform_account.withdrawable_balance == 0

        assert self.merchant_account.balance == 0
        assert self.merchant_account.withdrawable_balance == 0

    def test_merchant_withdraw(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def pay_to_wechat(self,
                              partner_trade_no,
                              openid,
                              amount,
                              desc,
                              spbill_create_ip,
                              app_id=None):
                assert len(partner_trade_no) == 32
                assert openid == self.test_case.merchant_admin.wechat_openid
                assert amount == 1000
                assert desc == '付款联盟提现'
                assert spbill_create_ip == '111.222.111.222'
                return 'refund_request_success'

        self.merchant_account.balance = 10000
        self.merchant_account.withdrawable_balance = 5000
        self.merchant_account.save()

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.withdraw(self.merchant_account, 1000,
                                     '111.222.111.222')

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 1
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.merchant_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FINISHED

        # Check the transaction
        transactions = withdraw.transactions.all().order_by('id')
        assert transactions[0].object_id == withdraw.serial_number
        assert transactions[
            0].content_object.serial_number == withdraw.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MERCHANT_WITHDRAW
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.merchant_account.id
        assert transactions[0].amount == -1000
        assert transactions[0].balance_after_transaction == 9000

        # Check the balance
        self.merchant_account.refresh_from_db()
        assert self.merchant_account.balance == 9000
        assert self.merchant_account.withdrawable_balance == 4000

    def test_merchant_withdraw_insufficient(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def pay_to_wechat(self,
                              partner_trade_no,
                              openid,
                              amount,
                              desc,
                              spbill_create_ip,
                              app_id=None):
                assert len(partner_trade_no) == 32
                assert openid == self.test_case.merchant_admin.wechat_openid
                assert true_name == self.test_case.merchant_account.real_name
                assert amount == 1000
                assert desc == '付款联盟提现'
                assert spbill_create_ip == '111.222.111.222'
                return 'refund_request_success'

        self.merchant_account.balance = 10000
        self.merchant_account.withdrawable_balance = 500
        self.merchant_account.save()

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()

        self.assertRaises(BalanceInsufficient, wechat_pay_usecases.withdraw,
                          self.merchant_account, 1000, '111.222.111.222')

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 0
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.merchant_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FAILED

        # Check the balance
        self.merchant_account.refresh_from_db()
        assert self.merchant_account.balance == 10000
        assert self.merchant_account.withdrawable_balance == 500

    def test_inviter_withdraw(self):
        class WechatPayApisMock(object):
            test_case = self

            def __init__(self,
                         app_id,
                         mch_id,
                         mch_key,
                         wechat_public_key,
                         cert=None,
                         cert_key=None):
                assert app_id == dynasettings.CLIENT_MINI_APP_ID
                assert mch_id == dynasettings.WECHAT_MERCHANT_ID
                assert mch_key == dynasettings.WECHAT_MERCHANT_API_KEY
                assert wechat_public_key == dynasettings.WECHAT_PUBLIC_KEY
                assert cert is None or cert == dynasettings.WECHAT_MERCHANT_CERT
                assert cert_key is None or cert_key == dynasettings.WECHAT_MERCHANT_CERT_KEY

            def pay_to_wechat(self,
                              partner_trade_no,
                              openid,
                              amount,
                              desc,
                              spbill_create_ip,
                              app_id=None):
                assert len(partner_trade_no) == 32
                assert openid == self.test_case.inviter.wechat_openid
                assert amount == 1000
                assert desc == '付款联盟提现'
                assert spbill_create_ip == '111.222.111.222'
                return 'refund_request_success'

        self.inviter_account.balance = 100000
        self.inviter_account.withdrawable_balance = 50000
        self.inviter_account.save()

        WechatPaymentUseCases.api_cls = WechatPayApisMock
        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.withdraw(self.inviter_account, 1000,
                                     '111.222.111.222')

        withdraw = Withdraw.objects.all().order_by('-datetime')[0]

        # Check withdraw
        assert len(withdraw.serial_number) == 32
        assert len(withdraw.transactions.all()) == 1
        assert (timezone.now() - withdraw.datetime) < timedelta(seconds=30)
        assert withdraw.account_id == self.inviter_account.id
        assert withdraw.amount == 1000
        assert withdraw.status == WITHDRAW_STATUS.FINISHED

        # Check the transaction
        transactions = withdraw.transactions.all().order_by('id')
        assert transactions[0].object_id == withdraw.serial_number
        assert transactions[
            0].content_object.serial_number == withdraw.serial_number
        assert transactions[
            0].transaction_type == TRANSACTION_TYPE.MARKETER_WITHDRAW
        assert (timezone.now() -
                transactions[0].datetime) < timedelta(seconds=30)
        assert transactions[0].account_id == self.inviter_account.id
        assert transactions[0].amount == -1000
        assert transactions[0].balance_after_transaction == 99000

        # Check the balance
        self.inviter_account.refresh_from_db()
        assert self.inviter_account.balance == 99000
        assert self.inviter_account.withdrawable_balance == 49000

    def test_payment_unfreeze(self):
        self.test_on_payment_success()
        new_payment = Payment.objects.all().order_by('-datetime')[0]
        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.on_payment_unfreeze(new_payment)

        new_payment.refresh_from_db()
        assert new_payment.status == PAYMENT_STATUS.FINISHED

        self.platform_account.refresh_from_db()
        assert self.platform_account.balance == new_payment.platform_share
        assert self.platform_account.withdrawable_balance == 0

        self.merchant_account.refresh_from_db()
        assert self.merchant_account.balance == 1900 - sum(
            (new_payment.platform_share, new_payment.inviter_share,
             new_payment.originator_share))
        assert self.merchant_account.withdrawable_balance == 1900 - sum(
            (new_payment.platform_share, new_payment.inviter_share,
             new_payment.originator_share))

        self.inviter_account.refresh_from_db()
        assert self.inviter_account.balance == new_payment.inviter_share
        assert self.inviter_account.withdrawable_balance == new_payment.inviter_share

        self.originator_account.refresh_from_db()
        assert self.originator_account.balance == new_payment.originator_share
        assert self.originator_account.withdrawable_balance == new_payment.originator_share

    def test_payment_unfreeze_without_coupon(self):
        self.test_on_payment_success_without_coupon()

        new_payment = Payment.objects.all().order_by('-datetime')[0]
        wechat_pay_usecases = WechatPaymentUseCases()
        wechat_pay_usecases.on_payment_unfreeze(new_payment)

        new_payment.refresh_from_db()
        assert new_payment.status == PAYMENT_STATUS.FINISHED

        self.platform_account.refresh_from_db()
        assert self.platform_account.balance == 0
        assert self.platform_account.withdrawable_balance == 0

        self.merchant_account.refresh_from_db()
        assert self.merchant_account.balance == 2000
        assert self.merchant_account.withdrawable_balance == 2000

        self.inviter_account.refresh_from_db()
        assert self.inviter_account.balance == 0
        assert self.inviter_account.withdrawable_balance == 0

        self.originator_account.refresh_from_db()
        assert self.originator_account.balance == 0
        assert self.originator_account.withdrawable_balance == 0