예제 #1
0
    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
예제 #2
0
    def refund_callback(self, app_id, mch_id, payment_serial_number, refund_serial_number, amount):
        usecases = WechatPaymentUseCases()
        encrypt_data = f"""<root>
<out_refund_no><![CDATA[{refund_serial_number}]]></out_refund_no>
<out_trade_no><![CDATA[{payment_serial_number}]]></out_trade_no>
<refund_account><![CDATA[REFUND_SOURCE_RECHARGE_FUNDS]]></refund_account>
<refund_fee><![CDATA[{amount}]]></refund_fee>
<refund_id><![CDATA[50000008402018100906610349654]]></refund_id>
<refund_recv_accout><![CDATA[支付用户零钱]]></refund_recv_accout>
<refund_request_source><![CDATA[API]]></refund_request_source>
<refund_status><![CDATA[SUCCESS]]></refund_status>
<settlement_refund_fee><![CDATA[{amount}]]></settlement_refund_fee>
<settlement_total_fee><![CDATA[{amount}]]></settlement_total_fee>
<success_time><![CDATA[2018-10-09 15:03:04]]></success_time>
<total_fee><![CDATA[{amount}]]></total_fee>
<transaction_id><![CDATA[4200000178201810090180106008]]></transaction_id>
</root>"""
        req_info = usecases.api_instance_without_cert.encrypt_data(encrypt_data)
        params = dict(
            appid=app_id,
            mch_id=mch_id,
            nonce_str=uuid.uuid4().hex,
            return_code='SUCCESS',
            req_info=req_info
        )

        usecases.api_instance_without_cert.sign_message(params)
        xml = usecases.api_instance_without_cert.build_xml_message(params)

        url = reverse('wechat_refund_callback')
        resp = self.client.post(url, xml, content_type='text/xml')
        return resp
예제 #3
0
    def payment_callback(self, app_id, mch_id, openid, payment_serial_number, total_fee):
        params = dict(
            appid=app_id,
            bank_type='CFT',
            cash_fee=str(total_fee),
            fee_type='CNY',
            is_subscribe='N',
            mch_id=mch_id,
            nonce_str=uuid.uuid4().hex,
            openid=openid,
            out_trade_no=payment_serial_number,
            result_code='SUCCESS',
            return_code='SUCCESS',
            time_end=timezone.now().strftime('%Y%m%d%H%M%S'),
            total_fee=total_fee,
            trade_type='JSAPI',
            transaction_id=uuid.uuid4().hex
        )

        usecases = WechatPaymentUseCases()
        usecases.api_instance_without_cert.sign_message(params)
        xml = usecases.api_instance_without_cert.build_xml_message(params)

        url = reverse('wechat_payment_callback')
        resp = self.client.post(url, xml, content_type='text/xml')
        return resp
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
    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
예제 #7
0
def wechat_refund_status_sync(refund_serial_number):
    use_cases = WechatPaymentUseCases()
    try:
        refund_info = use_cases.api_instance_without_cert.refund_query(
            refund_serial_number=refund_serial_number)
    except (ApiRequestError, ApiReturnedError) as e:
        logger.error('wechat_refund_status_sync: Call refund query error:')
        logger.error(e)
        return

    logger.debug('refund_info:' + json.dumps(refund_info))
    if 'refund_status_0' not in refund_info or 'out_refund_no_0' not in refund_info \
            or 'refund_fee_0' not in refund_info:
        logger.error(
            "wechat_refund_status_sync: 'refund_status_0' not in refund_info "
            "or 'out_refund_no_0' not in refund_info "
            "or 'refund_fee_0' not in refund_info")
        logger.error(json.dumps(refund_info))
        return

    if refund_serial_number != refund_info['out_refund_no_0']:
        logger.error(f'wechat_refund_status_sync: out_refund_no_0 mismatch: '
                     f'refund_serial_number:{refund_serial_number} '
                     f'out_refund_no_0:{refund_info["out_refund_no_0"]}')
        return

    # get the payment from DB
    try:
        refund = Refund.objects.get(
            serial_number=refund_info['out_refund_no_0'])
    except Refund.DoesNotExist:
        logger.error(
            f'wechat_refund_status_sync: '
            f'cannot find refund in DB: {refund_info["out_refund_no_0"]}')
        return

    if refund_info['refund_status_0'] == 'PROCESSING':  # 处理中,等待下一次轮询
        return

    if refund_info['refund_status_0'] != 'SUCCESS':  # 退款失败
        use_cases.on_refund_fail(refund)

        # send wechat message to merchant
        refund_manager = RefundManager(refund)
        refund_manager.send_refund_fail_message()
        return

    # 退款成功
    payment = refund.payment
    paid_price = payment.order_price - payment.coupon.discount \
        if payment.coupon else payment.order_price

    # check the price
    if paid_price != int(refund_info['refund_fee_0']):
        logger.error('wechat_refund_status_sync: refund_fee_0 mismatch')
        return

    use_cases.on_refund_success(refund)

    # send wechat message to merchant
    refund_manager = RefundManager(refund)
    refund_manager.send_refund_success_message()
예제 #8
0
#   Copyright (c) 2018 麦禾互动. All rights reserved.

import logging

from dynaconf import settings as dynasettings

from common.doclink.exceptions import ApiRequestError, ApiReturnedError
from common.error_handler import MerchantError
from common.model_manager.base import ModelObjectManagerBase
from common.model_manager.utils import set_amount
from common.payment.alipay import AlipayUseCases
from common.payment.exceptions import BalanceInsufficient
from common.payment.wechat import WechatPaymentUseCases

logger = logging.getLogger(__name__)
wechat_payment = WechatPaymentUseCases()
alipay_payment = AlipayUseCases()


class AccountManager(ModelObjectManagerBase):
    """self.obj = account_model_instance"""
    def __init__(self, *args, **kwargs):
        super(AccountManager, self).__init__(*args, **kwargs)

    def wechat_withdraw(
            self,
            amount,
            client_ip,
            app_id=dynasettings.SUBSCRIPTION_ACCOUNT_APP_ID_MERCHANT):
        """微信提现 amount为0时不进行任何操作"""
        if amount != 0:
예제 #9
0
    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
예제 #10
0
    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
예제 #11
0
    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
예제 #12
0
    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)
예제 #13
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