예제 #1
0
    def request_refund(self, payment):
        with transaction.atomic():
            payment = Payment.objects.select_for_update().get(
                serial_number=payment.serial_number)

            if payment.status == PAYMENT_STATUS['FROZEN']:
                new_refund = Refund.objects.create(
                    serial_number=generate_serial_number(),
                    status=REFUND_STATUS['REQUESTED'],
                    payment=payment)
            elif payment.status == PAYMENT_STATUS['REFUND_FAILED']:
                new_refund = Refund.objects.select_for_update().get(
                    payment=payment)
                new_refund.status = REFUND_STATUS['REQUESTED']
                new_refund.save()
            else:
                raise InvalidStatusError()

            payment.status = PAYMENT_STATUS['REFUND_REQUESTED']
            payment.save()

            paid_price = payment.order_price - payment.coupon.discount if payment.coupon else payment.order_price

        try:
            result = self.api_instance.refund(
                payment_serial_number=payment.serial_number,
                refund_serial_number=new_refund.serial_number,
                refund_amount=paid_price)
            self.on_refund_success(new_refund)
            return result

        except (ApiRequestError, ApiReturnedError) as e:
            self.on_refund_fail(new_refund)
            raise e
예제 #2
0
    def place_order(self, client, merchant, coupon, order_price, notify_url):
        """
        Place a pre-payment order.
        :param client: client instance, required
        :param merchant: merchant instance, required
        :param coupon: coupon instance, optional
        :param order_price:
        :param notify_url:
        :return:
        :raises: db.DatabaseError, ApiRequestError, ApiReturnedError
        """
        paid_price = order_price - coupon.discount if coupon else order_price

        with transaction.atomic():
            # update coupon status
            if coupon:
                updated = Coupon.objects.filter(
                    id=coupon.id, status=COUPON_STATUS.NOT_USED).update(
                        status=COUPON_STATUS.USED, use_datetime=timezone.now())

                if not updated:
                    raise InvalidStatusError()

            # Create the payment record
            new_payment = Payment.objects.create(
                serial_number=generate_serial_number(),
                pay_channel=PAY_CHANNELS['ALIPAY'],
                status=PAYMENT_STATUS['UNPAID'],
                merchant=merchant,
                client=client,
                coupon=coupon,
                order_price=order_price,
                platform_share=round(max(0, paid_price) *
                                     PLATFORM_SHARE_RATE) if coupon else 0,
                inviter_share=round(max(0, paid_price) *
                                    INVITER_SHARE_RATE) if coupon else 0,
                originator_share=round(
                    max(0, paid_price) *
                    ORIGINATOR_SHARE_RATE) if coupon else 0,
            )

            try:
                result = self.api_instance.place_order(
                    notify_url=notify_url,
                    order_title=merchant.name,
                    payment_serial_number=new_payment.serial_number,
                    total_amount=paid_price,
                    buyer_id=client.openid)
            except (ApiRequestError, ApiReturnedError):
                # if error occurs, send a query request to check the order.
                sleep(1)
                result = self.api_instance.query_payment(
                    new_payment.serial_number)

            return result
예제 #3
0
    def place_order(self, client, merchant, coupon, order_price, client_ip,
                    notify_url):
        """
        Place an pre-payment order.
        :param client: client instance, required
        :param merchant: merchant instance, required
        :param coupon: coupon instance, optional
        :param order_price:
        :param client_ip:
        :param notify_url:
        :return:
        :raises: db.DatabaseError, ApiRequestError, ApiReturnedError
        """
        paid_price = order_price - coupon.discount if coupon else order_price
        with transaction.atomic():
            # update coupon status
            if coupon:
                updated = Coupon.objects.filter(
                    id=coupon.id, status=COUPON_STATUS.NOT_USED).update(
                        status=COUPON_STATUS.USED, use_datetime=timezone.now())

                if not updated:
                    raise InvalidStatusError()

            # Create the payment record
            new_payment = Payment.objects.create(
                serial_number=generate_serial_number(),
                pay_channel=PAY_CHANNELS['WECHAT'],
                status=PAYMENT_STATUS['UNPAID'],
                merchant=merchant,
                client=client,
                coupon=coupon,
                order_price=order_price,
                platform_share=round(max(0, paid_price) *
                                     PLATFORM_SHARE_RATE) if coupon else 0,
                inviter_share=round(max(0, paid_price) *
                                    INVITER_SHARE_RATE) if coupon else 0,
                originator_share=round(
                    max(0, paid_price) *
                    ORIGINATOR_SHARE_RATE) if coupon else 0,
            )

            result = self.api_instance_without_cert.place_order(
                order_title=merchant.name,
                payment_serial_number=new_payment.serial_number,
                total_fee=order_price -
                coupon.discount if coupon else order_price,
                spbill_create_ip=client_ip,
                notify_url=notify_url,
                openid=client.openid)

            return dict(client_payment_params=self.api_instance_without_cert.
                        generate_client_payment_params(result['prepay_id']),
                        payment_serial_number=new_payment.serial_number)
예제 #4
0
    def on_refund_fail(cls, refund):
        with transaction.atomic():
            payment = Payment.objects.select_for_update().get(
                serial_number=refund.payment.serial_number)
            refund = Refund.objects.select_for_update().get(
                serial_number=refund.serial_number)

            if refund.status != REFUND_STATUS['REQUESTED'] \
                    or payment.status != PAYMENT_STATUS['REFUND_REQUESTED']:
                raise InvalidStatusError()

            refund.status = REFUND_STATUS['FAILED']
            refund.save()

            payment.status = PAYMENT_STATUS['REFUND_FAILED']
            payment.save()
예제 #5
0
    def cancel_order(self, payment):
        """
        Cancel the order
        :param payment:
        :return: InvalidStatusError, ApiRequestError, ApiReturnedError
        """
        with transaction.atomic():
            coupon = None
            if payment.coupon_id:
                coupon = Coupon.objects.select_for_update().get(
                    id=payment.coupon_id)

            payment = Payment.objects.select_for_update().get(
                serial_number=payment.serial_number)

            if payment.status != PAYMENT_STATUS.UNPAID:
                raise InvalidStatusError()

            payment.status = PAYMENT_STATUS['CANCELLED']
            payment.save()

            self.api_instance.cancel(payment.serial_number)

            if coupon:  # 有使用优惠券的情况,设置原优惠券为销毁状态,并退给用户一张新的优惠券
                coupon.status = COUPON_STATUS.DESTROYED
                coupon.save()

                new_coupon = Coupon.objects.create(
                    rule_id=coupon.rule_id,
                    client_id=coupon.client_id,
                    discount=coupon.discount,
                    min_charge=coupon.min_charge,
                    originator_merchant_id=coupon.originator_merchant_id,
                    status=COUPON_STATUS.NOT_USED,
                    obtain_datetime=coupon.obtain_datetime,
                )
                return new_coupon
            else:
                return None
예제 #6
0
    def on_payment_unfreeze(cls, payment):
        """
        unfreeze payment.
        :param payment:
        :return:
        :raise: InvalidStatusError
        """
        with PayChannelContext(payment.pay_channel):
            with transaction.atomic():
                timestamp = timezone.now()
                accounts = None
                merchant_account = None

                if payment.coupon:  # 使用了优惠券的情况
                    merchant_account_id = payment.merchant.account_id
                    originator_account_id = payment.coupon.originator_merchant.account_id
                    inviter_account_id = payment.merchant.inviter.account_id

                    accounts = AccountProxy.objects.select_for_update().filter(
                        id__in=(PLATFORM_ACCOUNT_ID, merchant_account_id,
                                originator_account_id, inviter_account_id))
                else:
                    merchant_account_id = payment.merchant.account_id
                    merchant_account = AccountProxy.objects.select_for_update(
                    ).get(id=merchant_account_id)

                payment = Payment.objects.select_for_update().select_related(
                    'coupon').get(serial_number=payment.serial_number)

                if payment.status != PAYMENT_STATUS.FROZEN:
                    raise InvalidStatusError()

                if payment.coupon:  # 使用了优惠券的情况
                    accounts = {a.id: a for a in accounts}
                    if len(accounts) != 4:
                        logger.error('Cannot find all the accounts:{}'.format(
                            repr((PLATFORM_ACCOUNT_ID, merchant_account_id,
                                  originator_account_id, inviter_account_id))))
                        raise AccountProxy.DoesNotExist()

                    platform_account = accounts[PLATFORM_ACCOUNT_ID]
                    merchant_account = accounts[merchant_account_id]
                    originator_account = accounts[originator_account_id]
                    inviter_account = accounts[inviter_account_id]

                    platform_account.channel_balance -= payment.originator_share + payment.inviter_share
                    platform_account.save()

                    originator_account.channel_balance += payment.originator_share
                    originator_account.channel_withdrawable_balance += payment.originator_share
                    originator_account.save()

                    inviter_account.channel_balance += payment.inviter_share
                    inviter_account.channel_withdrawable_balance += payment.inviter_share
                    inviter_account.save()

                    payment.status = PAYMENT_STATUS.FINISHED
                    payment.save()

                    merchant_account.channel_withdrawable_balance += payment.order_price - payment.coupon.discount - sum(
                        (payment.platform_share, payment.originator_share,
                         payment.inviter_share))
                    merchant_account.save()

                    # 记录Transaction
                    Transaction.objects.bulk_create([
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EXPEND_MERCHANT_SHARE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-payment.originator_share,
                                    balance_after_transaction=platform_account.
                                    channel_balance + payment.inviter_share),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EXPEND_MARKETER_SHARE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-payment.inviter_share,
                                    balance_after_transaction=platform_account.
                                    channel_balance),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EXPEND_PLATFORM_SHARE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-payment.platform_share,
                                    balance_after_transaction=platform_account.
                                    channel_balance - payment.platform_share),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_SHARE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=payment.platform_share,
                                    balance_after_transaction=platform_account.
                                    channel_balance),
                        Transaction(
                            content_object=payment,
                            transaction_type=TRANSACTION_TYPE[
                                'MERCHANT_SHARE'],
                            datetime=timestamp,
                            account=originator_account,
                            amount=payment.originator_share,
                            balance_after_transaction=originator_account.
                            channel_balance),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'MARKETER_SHARE'],
                                    datetime=timestamp,
                                    account=inviter_account,
                                    amount=payment.inviter_share,
                                    balance_after_transaction=inviter_account.
                                    channel_balance)
                    ])

                else:  # 没有使用优惠券的情况
                    payment.status = PAYMENT_STATUS.FINISHED
                    payment.save()

                    merchant_account.channel_withdrawable_balance += payment.order_price
                    merchant_account.save()
예제 #7
0
    def on_payment_success(cls, payment):
        """
        :param payment:
        :return:
        """
        with PayChannelContext(payment.pay_channel):
            with transaction.atomic():
                timestamp = timezone.now()

                platform_account = AccountProxy.objects.select_for_update(
                ).get(id=PLATFORM_ACCOUNT_ID)
                merchant_account = AccountProxy.objects.select_for_update(
                ).get(id=payment.merchant.account.id)
                payment = Payment.objects.select_for_update().get(
                    serial_number=payment.serial_number)

                if payment.status != PAYMENT_STATUS['UNPAID']:
                    raise InvalidStatusError()  # Duplicated callback
                payment.status = PAYMENT_STATUS['FROZEN']
                payment.save()

                if not payment.coupon:  # 没有使用优惠券的情况
                    merchant_account.channel_balance = merchant_account.channel_balance + payment.order_price
                    merchant_account.save()

                    Transaction.objects.bulk_create([
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_RECEIVE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=payment.order_price,
                                    balance_after_transaction=platform_account.
                                    channel_balance + payment.order_price),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EXPEND_MERCHANT_RECEIVE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-payment.order_price,
                                    balance_after_transaction=platform_account.
                                    channel_balance),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'MERCHANT_RECEIVE'],
                                    datetime=timestamp,
                                    account=merchant_account,
                                    amount=payment.order_price,
                                    balance_after_transaction=merchant_account.
                                    channel_balance)
                    ])
                else:  # 使用了优惠券的情况
                    paid_price = payment.order_price - payment.coupon.discount
                    total_share = payment.platform_share + payment.inviter_share + payment.originator_share

                    platform_account.channel_balance = platform_account.channel_balance + total_share
                    platform_account.save()
                    merchant_account.channel_balance = merchant_account.channel_balance + paid_price - total_share
                    merchant_account.save()

                    Transaction.objects.bulk_create([
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_RECEIVE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=paid_price,
                                    balance_after_transaction=platform_account.
                                    channel_balance + paid_price -
                                    total_share),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EXPEND_MERCHANT_RECEIVE'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-(paid_price - total_share),
                                    balance_after_transaction=platform_account.
                                    channel_balance),
                        Transaction(content_object=payment,
                                    transaction_type=TRANSACTION_TYPE[
                                        'MERCHANT_RECEIVE'],
                                    datetime=timestamp,
                                    account=merchant_account,
                                    amount=paid_price - total_share,
                                    balance_after_transaction=merchant_account.
                                    channel_balance)
                    ])
예제 #8
0
    def on_refund_success(cls, refund):

        with transaction.atomic():
            timestamp = timezone.now()

            platform_account = AccountProxy.objects.select_for_update().get(
                id=PLATFORM_ACCOUNT_ID)
            merchant_account = AccountProxy.objects.select_for_update().get(
                id=refund.payment.merchant.account.id)

            coupon = refund.payment.coupon
            if coupon:
                coupon = Coupon.objects.select_for_update().get(id=coupon.id)

            payment = Payment.objects.select_for_update().get(
                serial_number=refund.payment.serial_number)
            refund = Refund.objects.select_for_update().get(
                serial_number=refund.serial_number)

            with PayChannelContext(payment.pay_channel):
                if refund.status != REFUND_STATUS['REQUESTED'] \
                        or payment.status != PAYMENT_STATUS['REFUND_REQUESTED']:
                    raise InvalidStatusError()

                refund.status = REFUND_STATUS['FINISHED']
                refund.save()

                payment.status = PAYMENT_STATUS['REFUND']
                payment.save()

                if not coupon:  # 没有使用优惠券
                    merchant_account.channel_balance = merchant_account.channel_balance - payment.order_price
                    merchant_account.save()

                    Transaction.objects.bulk_create([
                        Transaction(content_object=refund,
                                    transaction_type=TRANSACTION_TYPE[
                                        'MERCHANT_REFUND'],
                                    datetime=timestamp,
                                    account=merchant_account,
                                    amount=-payment.order_price,
                                    balance_after_transaction=merchant_account.
                                    channel_balance),
                        Transaction(content_object=refund,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EARNING_MERCHANT_REFUND'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=payment.order_price,
                                    balance_after_transaction=platform_account.
                                    channel_balance + payment.order_price),
                        Transaction(content_object=refund,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_REFUND'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-payment.order_price,
                                    balance_after_transaction=platform_account.
                                    channel_balance)
                    ])
                else:  # 有使用优惠券的情况
                    paid_price = payment.order_price - payment.coupon.discount
                    total_share = payment.platform_share + payment.inviter_share + payment.originator_share

                    merchant_account.channel_balance = merchant_account.channel_balance - (
                        paid_price - total_share)
                    merchant_account.save()

                    platform_account.channel_balance = platform_account.channel_balance - total_share
                    platform_account.save()

                    Transaction.objects.bulk_create([
                        Transaction(content_object=refund,
                                    transaction_type=TRANSACTION_TYPE[
                                        'MERCHANT_REFUND'],
                                    datetime=timestamp,
                                    account=merchant_account,
                                    amount=-(paid_price - total_share),
                                    balance_after_transaction=merchant_account.
                                    channel_balance),
                        Transaction(content_object=refund,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_EARNING_MERCHANT_REFUND'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=(paid_price - total_share),
                                    balance_after_transaction=platform_account.
                                    channel_balance + paid_price),
                        Transaction(content_object=refund,
                                    transaction_type=TRANSACTION_TYPE[
                                        'PLATFORM_REFUND'],
                                    datetime=timestamp,
                                    account=platform_account,
                                    amount=-paid_price,
                                    balance_after_transaction=platform_account.
                                    channel_balance)
                    ])

                    # 设置原优惠券为销毁状态,并退给用户一张新的优惠券
                    coupon.status = COUPON_STATUS.DESTROYED
                    coupon.save()

                    new_coupon = Coupon.objects.create(
                        rule_id=coupon.rule_id,
                        client_id=coupon.client_id,
                        discount=coupon.discount,
                        min_charge=coupon.min_charge,
                        originator_merchant_id=coupon.originator_merchant_id,
                        status=COUPON_STATUS.NOT_USED,
                        obtain_datetime=coupon.obtain_datetime,
                    )