Пример #1
0
def get_refundable_transaction(transaction_id=None, external_id=None):
    """
    Retrieves a refundable transaction from database or raises.
    raises: exceptions.GatewayException
    """
    if transaction_id is None and external_id is None:
        raise exceptions.GatewayException('Transaction id is required')
    try:
        if transaction_id:
            transaction = Transaction.objects.get(id=transaction_id,
                                                  gateway__name='stripe')
        else:
            transaction = Transaction.objects.get(external_id=external_id,
                                                  gateway__name='stripe')
    except Transaction.DoesNotExist:
        raise exceptions.GatewayException(
            'Transaction {} does not exist'.format(transaction_id))
    except Transaction.MultipleObjectsReturned:
        raise exceptions.GatewayException(
            'Multiple transactions with id {}'.format(transaction_id))
    if transaction.status not in REFUNDABLE_TRANSACTION_STATUS:
        raise exceptions.GatewayException(
            'Unable to refund transaction with status {}.'.format(
                transaction.status))
    return transaction
Пример #2
0
def setup_recurring(request):
    invoice_id = request.data.get('invoice', None)
    payment_intent = json.loads(request.data.get('payment_intent', "{}"))
    payment_method = payment_intent.get('payment_method', None)
    if not payment_method:
        raise exceptions.GatewayException(
            _('Cannot process request without enough details. Missing Stripe payment method id.'
              ))
    if not invoice_id:
        raise exceptions.GatewayException(_('Missing invoice id.'))
    try:
        db_invoice = Invoice.objects.get(pk=invoice_id,
                                         client__in=request.user.clients.all())
    except Invoice.DoesNotExist:
        raise exceptions.GatewayException(
            'Invoice {} does not exist'.format(invoice_id))
    stripe.api_key = conf.secret_key
    client = db_invoice.client  # type: Client
    existing_recurrent_payments_config = RecurringPayments.objects.filter(
        client=client).first()
    if not existing_recurrent_payments_config:
        try:
            stripe_customer = stripe.Customer.create(
                payment_method=payment_method,
                address=dict(
                    line1=client.address1 if client.address1 else None,
                    city=client.city if client.city else None,
                    country=client.country if client.country else None,
                    state=client.state if client.state else None,
                    postal_code=client.zip_code if client.zip_code else None,
                ),
                email=client.email,
                name=client.name,
                phone=client.phone,
            )
        except Exception as e:
            raise exceptions.GatewayException(
                _('Could not create customer in Stripe: {}').format(str(e)))
        try:
            recurrent_payments_config = RecurringPayments.objects.create(
                client=client,
                active=True,
                stripe_payment_method=payment_method,
                stripe_customer_id=stripe_customer.id,
            )
            create_new_recurring_payments_ordering(
                client=recurrent_payments_config.client,
                gateway_app_name=StripeConfig.name)
        except Exception as e:
            del e  # unused
    else:
        LOG.error(
            'Client {} has already set up recurrent payments for stripe.'.
            format(client.id))

    relative_url = 'billing/invoices/{}'.format(invoice_id)
    return HttpResponseRedirect(
        fleio_join_url(settings.FRONTEND_URL, relative_url))
Пример #3
0
def pay_invoice(request):
    invoice_id = request.query_params.get('invoice')
    if invoice_id is None:
        raise exceptions.GatewayException("An 'invoice' parameter is required")
    try:
        inv = Invoice.objects.get(pk=invoice_id,
                                  client__in=request.user.clients.all())
    except Invoice.DoesNotExist:
        raise exceptions.GatewayException(
            'Invoice {} does not exist'.format(invoice_id))
    if inv.balance <= 0:
        raise exceptions.InvoicePaymentException(
            'Invoice {} is already paid'.format(invoice_id),
            invoice_id=invoice_id)

    amount = str(inv.balance)
    pay_message = _('Pay {} {} for invoice {}'.format(amount, inv.currency.pk,
                                                      inv.pk))

    recurring = request.query_params.get('recurring', False)
    recurring = True if (recurring == 'True' or recurring == 'true'
                         or recurring == '1') else False

    if recurring:
        existing_recurrent_payments_config = RecurringPayments.objects.filter(
            client=inv.client).first()
        if existing_recurrent_payments_config:
            raise exceptions.GatewayException(
                _('Recurrent payments were already configured for stripe.'))
        token_form = SetupRecurringPaymentsForm(initial={'invoice': inv.pk})
        stripe.api_key = conf.secret_key
        payment_intent = stripe.PaymentIntent.create(
            amount=utils.convert_amount_to_api(inv.balance, inv.currency.pk),
            currency=inv.currency.pk.lower(),
            setup_future_usage='off_session',
            metadata={"invoice": inv.pk},
        )
        return render(request=request,
                      template_name='stripe/setup_recurring.html',
                      context={
                          'payment_vars':
                          json.dumps({'publicKey': conf.public_key}),
                          'pay_message':
                          pay_message,
                          'client_secret':
                          payment_intent.client_secret,
                          'token_form':
                          token_form
                      })
    token_form = SimpleInvoicePaymentForm(initial={'invoice': inv.pk})
    return render(request=request,
                  template_name='stripe/pay_invoice.html',
                  context={
                      'payment_vars':
                      json.dumps({'publicKey': conf.public_key}),
                      'pay_message': pay_message,
                      'token_form': token_form
                  })
Пример #4
0
def callback(request):
    """
    The gateway callback method
    :type request: rest_framework.request.Request
    """
    stripe.api_key = conf.secret_key
    try:
        event = stripe.Webhook.construct_event(
            payload=request.body,
            sig_header=request.META.get(
                'STRIPE_SIGNATURE', request.META.get('HTTP_STRIPE_SIGNATURE')),
            secret=conf.signing_secret)
    except Exception as e:
        LOG.error(e)
        raise exceptions.GatewayException('Invalid payload')

    event_dict = event.to_dict()
    if event_dict['type'] == "payment_intent.succeeded":
        intent = event_dict['data']['object']
        charges = intent.get('charges', {})
        charges_list = charges.get('data', [])
        charge_dict = charges_list[len(charges_list) - 1]  # use latest charge
        process_charge(charge_dict)
    elif event_dict['type'] == "payment_intent.payment_failed":
        pass

    return Response({'detail': 'OK'}, status=status.HTTP_200_OK)
Пример #5
0
def capture(request):
    transaction_id = request.query_params.get('transaction')
    transaction = Transaction.objects.get(id=transaction_id)
    invoice = transaction.invoice
    payu_client = PayUClient(invoice_id=invoice.id, request=request)

    try:
        response = payu_client.capture_order(
            transaction_external_id=transaction.external_id)
    except Exception as e:
        raise gateway_exceptions.GatewayException(
            'Invalid payu capture: {}'.format(str(e)))
    if response.status_code == 200 or response.status_code == 201:
        transaction.status = TransactionStatus.CONFIRMED
        try:
            transaction.save()
        except Exception as e:
            # do nothing if it's being updated on the callback
            del e  # unused
        return Response({'detail': 'Ok'})
    response_text = json.loads(response.text)
    status = response_text.get('status')
    status_desc = status.get('statusDesc')
    raise Exception('Could not make capture action. {}'.format(
        status_desc if status_desc else ''))
Пример #6
0
def refund(request):
    transaction_id = request.query_params.get('transaction')
    refund_amount = request.query_params.get('refund_amount')
    trans = Transaction.objects.get(id=transaction_id)
    transaction_amount = trans.amount
    transaction_extra = trans.extra
    api_invoice_id = utils.invoice_id_to_api(trans.invoice_id)
    currency = trans.currency_id
    timestamp = utils.generate_timestamp()
    nonce = utils.generate_nonce()
    assert refund_amount is None or refund_amount <= transaction_amount, (
        'Refund amount should be less than'
        ' the transaction amount.')
    is_recurring = transaction_extra.get('is_recurring', False)
    if is_recurring:
        # FIXME: fix refund to recurring transaction
        # recurring_data = RecurringPayments.objects.filter(client=trans.invoice.client).first()
        rrn = transaction_extra.get('RRN', '')
        int_ref = transaction_extra.get('INT_REF', '')
    else:
        rrn = transaction_extra.get('RRN', '')
        int_ref = transaction_extra.get('INT_REF', '')

    if refund_amount and refund_amount < transaction_amount:
        trtype = 25  # partial refund
    else:
        trtype = 24  # full refund
        refund_amount = transaction_amount
    refund_amount = str(refund_amount)
    p_sign = utils.calculate_p_sign(conf.encryption_key, api_invoice_id,
                                    refund_amount, currency, rrn, int_ref,
                                    trtype, conf.terminal, timestamp, nonce,
                                    conf.callback_url)
    form_vars = dict(ORDER=api_invoice_id,
                     AMOUNT=refund_amount,
                     CURRENCY=currency,
                     RRN=rrn,
                     INT_REF=int_ref,
                     TRTYPE=trtype,
                     TERMINAL=conf.terminal,
                     TIMESTAMP=timestamp,
                     NONCE=nonce,
                     BACKREF=conf.callback_url,
                     P_SIGN=p_sign)
    romcard_form = RomcardCaptureRefundForm(form_vars)
    romcard_form_action = conf.url
    if romcard_form.is_valid():
        return render(request=request,
                      template_name='romcard/refund_or_capture.html',
                      context={
                          'redirect_message': _('Redirecting to RomCard'),
                          'page_title': _('Refund payment'),
                          'form': romcard_form,
                          'form_action': romcard_form_action
                      })
    else:
        LOG.error('Invalid romcard data: {}'.format(romcard_form.errors))
        raise exceptions.GatewayException('Invalid romcard data: {}'.format(
            romcard_form.errors))
Пример #7
0
def capture(request):
    transaction_id = request.query_params.get('transaction')
    transaction = Transaction.objects.get(id=transaction_id)
    now = utcnow()
    data = {
        'MERCHANT': conf.merchant,
        'ORDER_REF': transaction.external_id,
        'ORDER_AMOUNT': str(transaction.amount),
        'ORDER_CURRENCY': transaction.currency.code,
        'IDN_DATE': now.strftime('%Y-%m-%d %H:%M:%S'),
    }
    # TODO: from python 3.6 use **data instead of getting each value as keys are ordered in the dict
    order_hash = PayURoUtils.calculate_signature([
        data.get('MERCHANT'),
        data.get('ORDER_REF'),
        data.get('ORDER_AMOUNT'),
        data.get('ORDER_CURRENCY'),
        data.get('IDN_DATE')
    ])
    data['ORDER_HASH'] = order_hash
    response = requests.post(url=CAPTURE_ORDER_URL, data=data)
    if response.status_code == 200 or response.status_code == 201:
        response_text = response.text
        response_text = response_text.replace('<EPAYMENT>', '')
        response_params = response_text.split('|')
        order_ref = response_params[0]
        response_code = response_params[1]
        if response_code == '1':
            existing_transaction = Transaction.objects.filter(
                external_id=order_ref).first()
            if existing_transaction:
                existing_transaction.status = TransactionStatus.CONFIRMED
                existing_transaction.save(update_fields=['status'])
            else:
                raise gateway_exceptions.GatewayException(
                    _('Could not find existing transaction to mark as captured in db.'
                      ))
        else:
            raise gateway_exceptions.GatewayException(
                _('Could not perform capture action. Details: {}').format(
                    CAPTURE_ERROR_CODE_MEANINGS.get(response_code)))
    else:
        raise gateway_exceptions.GatewayException(
            _('Error when making request to capture transaction.'))
    return Response({'detail': _('Ok')})
Пример #8
0
def refund(request):
    if request.method == 'GET':
        transaction_id = request.query_params.get('transaction')
        transaction = get_refundable_transaction(transaction_id=transaction_id)
        refund_form = StripeRefundForm(
            initial={'transaction_id': transaction.external_id})
        return render(request=request,
                      template_name='stripe/refund_prep.html',
                      context={'refund_form': refund_form})
    else:
        # TODO: use request.data for StripeRefundForm when we support partial refunds and choosing amount is possible
        refund_form_data = copy.deepcopy(request.data)
        transaction_id = refund_form_data['transaction_id']
        db_transaction = Transaction.objects.filter(
            external_id=transaction_id).first()  # type: Transaction
        refund_form_data['amount'] = db_transaction.amount
        refund_form = StripeRefundForm(refund_form_data)
        if refund_form.is_valid():
            stripe.api_key = conf.secret_key
            external_id = refund_form.cleaned_data['transaction_id']
            transaction = get_refundable_transaction(external_id=external_id)
            try:
                re = stripe.Refund.create(
                    charge=external_id,
                    amount=utils.convert_amount_to_api(
                        amount=refund_form.cleaned_data['amount'],
                        currency=transaction.currency_id),
                    reason=refund_form.cleaned_data.get(
                        'reason', 'requested_by_user'))
            except stripe.error.InvalidRequestError as e:
                LOG.exception(e)
                raise exceptions.GatewayException(e)
            except StripeError as e:
                LOG.exception(e)
                raise exceptions.GatewayException(_('Refund failed'))
            process_refund(re)
            return HttpResponseRedirect(
                fleio_join_url(settings.FRONTEND_URL, 'staff/billing/journal'))
        else:
            return render(request=request,
                          template_name='stripe/refund_prep.html',
                          context={'refund_form': refund_form})
Пример #9
0
def pay_invoice(request):
    invoice_id = request.query_params.get('invoice')
    try:
        Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all())  # type: Invoice
    except Invoice.DoesNotExist:
        raise gateway_exceptions.GatewayException(_('Invoice {} does not exist').format(invoice_id))
    try:
        configure()
        payment = create_payment(invoice_id)
    except Exception as create_payment_exception:
        raise gateway_exceptions.InvoicePaymentException(message=str(create_payment_exception), invoice_id=invoice_id)

    return HttpResponseRedirect(get_approval_url(payment))
Пример #10
0
def pay_invoice(request):
    invoice_id = request.query_params.get('invoice')
    try:
        Invoice.objects.get(
            pk=invoice_id,
            client__in=request.user.clients.all())  # type: Invoice
    except Invoice.DoesNotExist:
        raise gateway_exceptions.GatewayException(
            _('Invoice {} does not exist').format(invoice_id))
    try:
        payu_client = PayUClient(invoice_id=invoice_id, request=request)
        return payu_client.create_order()
    except Exception as create_payment_exception:
        raise gateway_exceptions.InvoicePaymentException(
            message=str(create_payment_exception), invoice_id=invoice_id)
Пример #11
0
def charge(request):
    """Endpoint for creating a charge based on a token"""
    token_form = SimpleInvoicePaymentForm(request.POST)
    if not token_form.is_valid():
        raise exceptions.GatewayException(token_form.errors)
    invoice_id = token_form.cleaned_data['invoice']
    charge_token = token_form.cleaned_data['token']
    try:
        inv = Invoice.objects.get(pk=invoice_id,
                                  client__in=request.user.clients.all())
    except Invoice.DoesNotExist:
        raise exceptions.InvoicePaymentException(
            'Invoice {} does not exist'.format(invoice_id),
            invoice_id=invoice_id)
    if inv.balance <= 0:
        raise exceptions.InvoicePaymentException(
            'Invoice {} is already paid'.format(invoice_id),
            invoice_id=invoice_id)
    stripe.api_key = conf.secret_key
    token_id = charge_token.get('id')
    if token_id is None:
        raise exceptions.InvoicePaymentException('Charge failed',
                                                 invoice_id=invoice_id)
    try:
        charge_result = stripe.Charge.create(
            amount=utils.convert_amount_to_api(inv.balance, inv.currency.pk),
            currency=inv.currency.pk.lower(),
            description=_('Invoice {} payment').format(inv.pk),
            metadata={"invoice": inv.pk},
            source=token_id,
            idempotency_key=None)
    except CardError as e:
        LOG.exception(e)
        body = e.json_body
        err = body.get('error', {})
        raise exceptions.InvoicePaymentException(err.get(
            'message', 'Charge failed'),
                                                 invoice_id=invoice_id)
    except StripeError as e:
        LOG.exception(e)
        raise exceptions.InvoicePaymentException('Charge failed',
                                                 invoice_id=invoice_id)
    process_charge(charge=charge_result)
    relative_url = 'billing/invoices/{}'.format(invoice_id)
    return HttpResponseRedirect(
        fleio_join_url(settings.FRONTEND_URL, relative_url))
Пример #12
0
def capture(request):
    transaction_id = request.query_params.get('transaction')
    trans = Transaction.objects.get(id=transaction_id)
    invoice = trans.invoice
    api_invoice_id = utils.invoice_id_to_api(invoice.pk)
    amount = str(trans.amount)
    currency = trans.currency_id
    transaction_extra = trans.extra
    timestamp = utils.generate_timestamp()
    nonce = utils.generate_nonce()
    trtype = utils.TransactionType.SALES_COMPLETION

    p_sign = utils.calculate_p_sign(conf.encryption_key, api_invoice_id,
                                    amount, currency, trans.external_id,
                                    transaction_extra.get('INT_REF'), trtype,
                                    conf.terminal, timestamp, nonce,
                                    conf.callback_url)
    form_vars = {
        'ORDER': api_invoice_id,
        'AMOUNT': amount,
        'CURRENCY': currency,
        'RRN': trans.external_id,
        'INT_REF': transaction_extra.get('INT_REF'),
        'TRTYPE': trtype,
        'TERMINAL': conf.terminal,
        'TIMESTAMP': timestamp,
        'NONCE': nonce,
        'BACKREF': conf.callback_url,
        'P_SIGN': p_sign
    }

    romcard_form = RomcardCaptureRefundForm(form_vars)
    romcard_form_action = conf.url
    if romcard_form.is_valid():
        return render(request=request,
                      template_name='romcard/refund_or_capture.html',
                      context={
                          'redirect_message': _('Redirecting to RomCard'),
                          'page_title': _('Capture payment'),
                          'form': romcard_form,
                          'form_action': romcard_form_action
                      })
    else:
        LOG.error('Invalid romcard data: {}'.format(romcard_form.errors))
        raise exceptions.GatewayException('Invalid romcard data: {}'.format(
            romcard_form.errors))
Пример #13
0
def process_charge(charge):
    """Process a charge result"""
    gateway = Gateway.objects.get(name='stripe')
    external_id = charge.id
    transaction_id = None
    transaction_status = charge_status(charge)
    invoice_id = charge.metadata.get('invoice')
    if external_id is not None:
        try:
            existing_transaction = Transaction.objects.get(
                external_id=external_id, gateway=gateway)
            transaction_id = existing_transaction.id
        except Transaction.DoesNotExist:
            real_amount = utils.convert_amount_from_api(
                charge.amount, charge.currency)
            serializer_data = {
                'invoice': invoice_id,
                'external_id': external_id,
                'amount': real_amount,
                'currency': charge.currency.upper(),
                'gateway': gateway.pk,
                'fee': gateway.get_fee(amount=real_amount),
                'date_initiated':
                datetime.datetime.fromtimestamp(charge.created),
                'extra': {},
                'status': transaction_status
            }
            tr_ser = AddTransactionSerializer(data=serializer_data)
            if tr_ser.is_valid(raise_exception=False):
                new_transaction = tr_ser.save()
                transaction_id = new_transaction.id
            else:
                LOG.error('Could not process charge for stripe: {}'.format(
                    json.dumps(tr_ser.errors)))
                raise exceptions.InvoicePaymentException(
                    'Could not process charge for invoice {}.',
                    invoice_id=invoice_id)
        else:
            # Update the transaction status only
            existing_transaction.status = transaction_status
            existing_transaction.save(update_fields=['status'])
    gateway.log_callback(external_id=external_id,
                         status=charge.status,
                         data=charge,
                         error=(charge.status == 'failed'),
                         error_code=charge.failure_code,
                         error_info=charge.failure_message)
    if transaction_status == TransactionStatus.FAILURE:
        raise exceptions.GatewayException(charge.failure_message or 'error')
    else:
        if transaction_status == TransactionStatus.SUCCESS:
            activity_helper.start_generic_activity(
                category_name='stripe',
                activity_class='stripe payment',
                invoice_id=invoice_id)

            invoice_add_payment.delay(invoice_id=invoice_id,
                                      amount=utils.convert_amount_from_api(
                                          charge.amount, charge.currency),
                                      currency_code=charge.currency.upper(),
                                      transaction_id=transaction_id)

            activity_helper.end_activity()
Пример #14
0
def refund(request):
    gateway = Gateway.objects.get(name='payu')
    transaction_id = request.query_params.get('transaction')
    transaction = Transaction.objects.get(id=transaction_id)
    invoice = transaction.invoice
    payu_client = PayUClient(invoice_id=invoice.id, request=request)
    if transaction.status == Transaction.TRANSACTION_STATUS.CONFIRMED:
        # do a refund for transaction status confirmed
        try:
            response = payu_client.refund(
                transaction_external_id=transaction.external_id)
        except Exception as e:
            raise gateway_exceptions.GatewayException(
                'Invalid payu refund action: {}'.format(str(e)))
        if response.status_code == 200 or response.status_code == 201:
            response_text = json.loads(response.text)
            refund_data = response_text.get('refund', {})
            new_refund_transaction = {
                'invoice':
                invoice.id,
                'external_id':
                transaction.external_id,
                'amount':
                str(
                    PayUUtils.get_payu_amount_in_fleio_amount(
                        refund_data.get('amount'))),
                'currency':
                refund_data.get('currencyCode'),
                'gateway':
                gateway.pk,
                'fee':
                gateway.get_fee(amount=decimal.Decimal(transaction.amount)),
                'date_initiated':
                refund_data.get('creationDateTime'),
                'extra': {
                    'refundId': refund_data.get('refundId'),
                    'extRefundId': refund_data.get('extRefundId'),
                },
                'refunded_transaction':
                transaction.pk,
                'status':
                TransactionStatus.REFUNDED
            }
            transaction_serializer = AddTransactionSerializer(
                data=new_refund_transaction)
            if transaction_serializer.is_valid(raise_exception=False):
                new_transaction = transaction_serializer.save()
                activity_helper.start_generic_activity(
                    category_name='payu',
                    activity_class='payu payment refund',
                    invoice_id=invoice.id)

                invoice_refund_payment.delay(
                    transaction_id=transaction_id,
                    amount=PayUUtils.get_payu_amount_in_fleio_amount(
                        refund_data.get('amount')),
                    to_client_credit=False,
                    new_transaction_id=new_transaction.pk)

                activity_helper.end_activity()
            else:
                LOG.error('PayU transaction error: {}'.format(
                    transaction_serializer.errors))
                raise gateway_exceptions.InvoicePaymentException(
                    'Transaction error', invoice_id=invoice.id)
            return Response({'detail': 'Ok'})
    else:
        # do a cancellation request for when transaction was not yet confirmed
        try:
            payu_client.cancel(transaction_external_id=transaction.external_id)
        except Exception as e:
            raise gateway_exceptions.GatewayException(
                'Invalid payu refund action: {}'.format(str(e)))
        return Response({'detail': 'Ok'})
    response_text = json.loads(response.text)
    status = response_text.get('status')
    status_desc = status.get('statusDesc')
    raise Exception('Could not make capture action. {}'.format(
        status_desc if status_desc else ''))
Пример #15
0
def callback(request):
    if not PayUUtils.validate_open_payu_signature(
            open_payu_signature=request.headers.get('OpenPayu-Signature',
                                                    None),
            body=request.body,
    ):
        raise gateway_exceptions.GatewayException(
            'Did not receive or received invalid OpenPayu-Signature')
    gateway = Gateway.objects.get(name='payu')
    order_details = request.data.get('order', None)
    if not order_details:
        raise Exception('No order details')
    external_id = order_details.get('orderId')
    invoice_id = PayUUtils.get_invoice_id_from_external_order_id(
        external_order_id=order_details.get('extOrderId'))
    transaction_status = order_details.get('status')
    total_amount = PayUUtils.get_payu_amount_in_fleio_amount(
        amount=order_details.get('totalAmount'))
    if order_details.get('status') == PayUTransactionStatus.canceled:
        # process refund for pre auth transaction
        existing_transaction = Transaction.objects.filter(
            external_id=external_id,
            gateway=gateway,
            refunded_transaction__isnull=True).first()
        if not existing_transaction:
            raise gateway_exceptions.GatewayException(
                'Could not process cancellation notification because there is no transaction to refund'
            )
        new_refund_transaction = {
            'invoice': invoice_id,
            'external_id': external_id,
            'amount': str(total_amount),
            'currency': order_details.get('currencyCode'),
            'gateway': gateway.pk,
            'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)),
            'date_initiated': utcnow(),
            'extra': {},
            'refunded_transaction': existing_transaction.pk,
            'status': TransactionStatus.REFUNDED
        }
        transaction_serializer = AddTransactionSerializer(
            data=new_refund_transaction)
        if transaction_serializer.is_valid(raise_exception=False):
            new_transaction = transaction_serializer.save()
            activity_helper.start_generic_activity(
                category_name='payu',
                activity_class='payu payment refund',
                invoice_id=existing_transaction.invoice.id)

            invoice_refund_payment.delay(
                transaction_id=existing_transaction.id,
                amount=total_amount,
                to_client_credit=False,
                new_transaction_id=new_transaction.pk)

            activity_helper.end_activity()
        else:
            LOG.error('PayU transaction error: {}'.format(
                transaction_serializer.errors))
            raise gateway_exceptions.GatewayException(
                'Could not process cancellation notification because serialized data is invalid'
            )
        return Response({'detail': 'Ok'})
    # process pre-auth, confirmation notifications
    existing_transaction = Transaction.objects.filter(
        external_id=external_id,
        gateway=gateway,
        refunded_transaction__isnull=True).first()
    if existing_transaction:
        # Update the transaction status and mark invoice as paid (still needs capture if automatic capture is not
        # enabled in PayU) if client successfully made the payment
        existing_transaction.status = PayUTransactionStatus.to_transaction_model_status.get(
            transaction_status)
        existing_transaction.save(update_fields=['status'])
        if (transaction_status
                == PayUTransactionStatus.waiting_for_confirmation
                or (transaction_status == PayUTransactionStatus.completed
                    and existing_transaction.invoice
                    and existing_transaction.invoice.is_unpaid())):
            activity_helper.start_generic_activity(
                category_name='payu',
                activity_class='payu payment',
                invoice_id=invoice_id)
            invoice_add_payment.delay(
                invoice_id=invoice_id,
                amount=total_amount,
                currency_code=order_details.get('currencyCode'),
                transaction_id=existing_transaction.id)
            activity_helper.end_activity()
        return Response({'detail': 'Ok'})
    elif transaction_status == PayUTransactionStatus.pending:
        serializer_data = {
            'invoice':
            invoice_id,
            'external_id':
            external_id,
            'amount':
            total_amount,
            'currency':
            order_details.get('currencyCode'),
            'gateway':
            gateway.pk,
            'fee':
            gateway.get_fee(amount=decimal.Decimal(total_amount)),
            'date_initiated':
            order_details.get('orderCreateDate'),
            'extra': {},
            'status':
            PayUTransactionStatus.to_transaction_model_status.get(
                transaction_status)
        }
        add_transaction_serializer = AddTransactionSerializer(
            data=serializer_data)
        if add_transaction_serializer.is_valid(raise_exception=False):
            add_transaction_serializer.save()
        else:
            LOG.error('PayU transaction error: {}'.format(
                add_transaction_serializer.errors))
            raise gateway_exceptions.InvoicePaymentException(
                'Transaction error', invoice_id=invoice_id)
    return Response({'detail': 'Ok'})
Пример #16
0
def refund(request):
    gateway = Gateway.objects.get(name='payuro')
    transaction_id = request.query_params.get('transaction')
    transaction = Transaction.objects.get(id=transaction_id)
    invoice = transaction.invoice
    now = utcnow()
    data = {
        'MERCHANT': conf.merchant,
        'ORDER_REF': transaction.external_id,
        'ORDER_AMOUNT': str(transaction.amount),
        'ORDER_CURRENCY': transaction.currency.code,
        'IRN_DATE': now.strftime('%Y-%m-%d %H:%M:%S'),
        'AMOUNT': str(transaction.amount),
    }
    # TODO: from python 3.6 use **data instead of getting each value as keys are ordered in the dict
    order_hash = PayURoUtils.calculate_signature([
        data.get('MERCHANT'),
        data.get('ORDER_REF'),
        data.get('ORDER_AMOUNT'),
        data.get('ORDER_CURRENCY'),
        data.get('IRN_DATE'),
        data.get('AMOUNT')
    ])
    data['ORDER_HASH'] = order_hash
    response = requests.post(url=REFUND_ORDER_URL, data=data)
    if response.status_code == 200 or response.status_code == 201:
        response_text = response.text
        response_text = response_text.replace('<EPAYMENT>', '')
        response_params = response_text.split('|')
        order_ref = response_params[0]
        response_code = response_params[1]
        if response_code == '1':
            existing_transaction = Transaction.objects.filter(
                external_id=order_ref).first()
            if existing_transaction:
                new_refund_transaction = {
                    'invoice':
                    invoice.id,
                    'external_id':
                    transaction.external_id,
                    'amount':
                    str(transaction.amount),
                    'currency':
                    transaction.currency.code,
                    'gateway':
                    gateway.pk,
                    'fee':
                    gateway.get_fee(
                        amount=decimal.Decimal(transaction.amount)),
                    'date_initiated':
                    now,
                    'extra': {},
                    'refunded_transaction':
                    transaction.pk,
                    'status':
                    TransactionStatus.REFUNDED
                }
                transaction_serializer = AddTransactionSerializer(
                    data=new_refund_transaction)
                if transaction_serializer.is_valid(raise_exception=False):
                    new_transaction = transaction_serializer.save()
                    activity_helper.start_generic_activity(
                        category_name='payuro',
                        activity_class='payuro payment refund',
                        invoice_id=invoice.id)

                    invoice_refund_payment.delay(
                        transaction_id=transaction_id,
                        amount=transaction.amount,
                        to_client_credit=False,
                        new_transaction_id=new_transaction.pk)

                    activity_helper.end_activity()
                else:
                    LOG.error(
                        _('PayU Ro transaction error: {}').format(
                            transaction_serializer.errors))
                    raise gateway_exceptions.GatewayException(
                        _('Transaction error {}').format(
                            transaction_serializer.errors))

            else:
                raise gateway_exceptions.GatewayException(
                    _('Could not find existing transaction to mark as captured in db.'
                      ))
        else:
            raise gateway_exceptions.GatewayException(
                _('Could not perform capture action. Details: {}').format(
                    REFUND_ERROR_CODE_MEANINGS.get(response_code)))
    else:
        raise gateway_exceptions.GatewayException(
            _('Error when making request to capture transaction.'))
    return Response({'detail': _('Ok')})
Пример #17
0
def callback(request):
    request_body = request.body
    if not PayURoUtils.verify_hash(request_body):
        raise gateway_exceptions.GatewayException(
            _('Did not receive or received invalid signature.'))
    gateway = Gateway.objects.get(name='payuro')
    invoice_id = request.data.get('REFNOEXT', None)
    if not invoice_id:
        raise gateway_exceptions.GatewayException(
            _('Missing fleio invoice id.'))
    external_id = request.data.get('REFNO')
    total_amount = request.data.get('IPN_TOTALGENERAL', None)
    currency = request.data.get('CURRENCY')
    status = request.data.get('ORDERSTATUS')
    invoice = Invoice.objects.get(id=invoice_id)
    if 'TOKEN_HASH' in request.data:
        try:
            recurrent_payments_config = RecurringPayments.objects.create(
                client=invoice.client,
                active=True,
                token_hash=request.data['TOKEN_HASH'],
                ipn_cc_mask=request.data['IPN_CC_MASK'],
                ipn_cc_exp_date=request.data['IPN_CC_EXP_DATE'],
            )
            create_new_recurring_payments_ordering(
                client=recurrent_payments_config.client,
                gateway_app_name=PayURoConfig.name)
        except Exception as e:
            del e  # unused
            pass
    if status == PayUROOrderStatus.preauth:
        transaction_status = PayUROOrderStatus.to_fleio_transaction_status[
            PayUROOrderStatus.preauth]
        serializer_data = {
            'invoice': invoice_id,
            'external_id': external_id,
            'amount': total_amount,
            'currency': currency,
            'gateway': gateway.pk,
            'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)),
            'date_initiated': request.data.get('SALEDATE'),
            'extra': {},
            'status': transaction_status
        }
        add_transaction_serializer = AddTransactionSerializer(
            data=serializer_data)
        if add_transaction_serializer.is_valid(raise_exception=False):
            db_transaction = add_transaction_serializer.save()
            activity_helper.start_generic_activity(
                category_name='payuro',
                activity_class='payuro payment',
                invoice_id=invoice_id)
            invoice_add_payment.delay(invoice_id=invoice_id,
                                      amount=total_amount,
                                      currency_code=currency,
                                      transaction_id=db_transaction.id)
            activity_helper.end_activity()

        else:
            LOG.error(
                _('PayU Ro transaction error: {}').format(
                    add_transaction_serializer.errors))
            raise gateway_exceptions.GatewayException('Add transaction error')

    ipn_response = PayURoUtils.build_ipn_response(params=request_body)
    return Response({'detail': ipn_response})
Пример #18
0
def pay_invoice(request):
    invoice_id = request.query_params.get('invoice')
    recurring = request.query_params.get('recurring', False)
    recurring = True if (recurring == 'True' or recurring == 'true'
                         or recurring == '1') else False
    if invoice_id is None:
        raise exceptions.GatewayException("An 'invoice' parameter is required")
    try:
        inv = Invoice.objects.get(pk=invoice_id,
                                  client__in=request.user.clients.all())
    except Invoice.DoesNotExist:
        raise exceptions.GatewayException(
            'Invoice {} does not exist'.format(invoice_id))
    if inv.balance <= 0:
        raise exceptions.GatewayException(
            'Invoice {} is already paid'.format(invoice_id))
    trtype = str(utils.TransactionType.PRE_AUTHORIZATION)
    language_code = 'ro' if request.LANGUAGE_CODE == 'ro' else 'en'
    lang = language_code
    description = 'Invoice {}'.format(inv.pk)
    timestamp = utils.generate_timestamp()
    nonce = utils.generate_nonce()
    amount = str(inv.balance)
    api_invoice_id = utils.invoice_id_to_api(invoice_id)
    recur_exp = utils.generate_recur_timestamp(days=conf.recur_exp)
    romcard_gateway = Gateway.objects.filter(
        name=get_gateway_name_from_app_name(
            gateway_app_name=RomcardConfig.name)).first()
    if not romcard_gateway:
        raise exceptions.GatewayException(_('Could not find the gateway.'))
    args = [
        conf.encryption_key, amount, inv.currency.pk, api_invoice_id,
        description, conf.merchant_name, conf.merchant_url, conf.merchant_no,
        conf.terminal, conf.email, trtype, '', '', timestamp, nonce,
        conf.callback_url
    ]
    if romcard_gateway.recurring_payments_enabled and recurring:
        args.append(conf.recur_days)
        args.append(recur_exp)
    elif romcard_gateway.recurring_payments_enabled:
        args.append('')
        args.append('')
    p_sign = utils.calculate_p_sign(*args)
    fvars = {
        'AMOUNT': amount,
        'CURRENCY': inv.currency.pk,
        'ORDER': api_invoice_id,
        'DESC': description,
        'MERCH_NAME': conf.merchant_name,
        'MERCH_URL': conf.merchant_url,
        'MERCHANT': conf.merchant_no,
        'TERMINAL': conf.terminal,
        'EMAIL': conf.email,
        'TRTYPE': trtype,
        'COUNTRY': '',
        'MERCH_GMT': '',
        'TIMESTAMP': timestamp,
        'NONCE': nonce,
        'BACKREF': conf.callback_url
    }
    if romcard_gateway.recurring_payments_enabled:
        fvars['RECUR_FREQ'] = '' if not recurring else conf.recur_days
        fvars['RECUR_EXP'] = '' if not recurring else recur_exp
    fvars['P_SIGN'] = p_sign
    fvars['LANG'] = lang
    if recurring:
        client = get_client_by_invoice_id(invoice_id=invoice_id)
        try:
            RecurringPayments.objects.create(client=client)
        except IntegrityError:
            # already exists for this client
            pass
    else:
        recurrent_payments_config = RecurringPayments.objects.filter(
            client=inv.client).first()
        if recurrent_payments_config:
            _remove_recurrent_payments_config(recurrent_payments_config)

    if recurring or romcard_gateway.recurring_payments_enabled:
        romcard_form = RomcardSubscribeForm(fvars)
    else:
        romcard_form = RomcardPayForm(fvars)
    romcard_form_action = conf.url
    if romcard_form.is_valid():
        return render(request=request,
                      template_name='romcard/pay_invoice.html',
                      context={
                          'redirect_message': _('Redirecting to RomCard'),
                          'page_title': _('Invoice payment'),
                          'form': romcard_form,
                          'form_action': romcard_form_action
                      })
    else:
        LOG.error('Invalid romcard data: {}'.format(romcard_form.errors))
        raise exceptions.GatewayException('Invalid data provided')
Пример #19
0
def pay_invoice(request):
    invoice_id = request.query_params.get('invoice')
    recurring = request.query_params.get('recurring', False)
    recurring = True if (recurring == 'True' or recurring == 'true'
                         or recurring == '1') else False
    if invoice_id is None:
        raise exceptions.GatewayException(_('Missing invoice parameter.'))
    try:
        db_invoice = Invoice.objects.get(
            pk=invoice_id,
            client__in=request.user.clients.all())  # type: Invoice
    except Invoice.DoesNotExist:
        raise exceptions.GatewayException(
            _('Invoice {} does not exist').format(invoice_id))
    if db_invoice.balance <= 0:
        raise exceptions.GatewayException(
            _('Invoice {} is already paid').format(invoice_id))
    client = db_invoice.client  # type: Client
    now = utcnow()
    # params
    order_ref = '{}'.format(db_invoice.id)
    order_date = now.strftime('%Y-%m-%d %H-%M-%S')
    multi_items = True if db_invoice.items.count() > 1 else False
    order_pname = [
        '{}'.format(item.item_type) for item in db_invoice.items.all()
    ]
    if multi_items:
        order_pcode = []
        counter = 1
        for item in db_invoice.items.all():
            order_pcode.append('{}{}'.format(item.item_type, str(counter)))
            counter = counter + 1
    else:
        order_pcode = [
            '{}'.format(item.item_type) for item in db_invoice.items.all()
        ]
    order_pinfo = [
        '{}'.format(item.item_type) for item in db_invoice.items.all()
    ]
    order_price = [str(item.amount) for item in db_invoice.items.all()]
    order_price_type = ['GROSS' for x in db_invoice.items.all()]
    order_qty = ['1' for x in db_invoice.items.all()]
    order_vat = ['0' for x in db_invoice.items.all()]
    pay_method = 'CCVISAMC'
    order_shipping = ''
    discount = '0'
    client_phone = client.phone if client.phone else '-'
    client_country = client.country.upper() if client.country else ''
    language = request.user.language.upper() if request.user.language else 'RO'
    testorder = conf.testorder
    back_ref = fleio_join_url(settings.FRONTEND_URL, 'billing/invoices')

    order_hash = PayURoUtils.calculate_signature(params=[
        conf.merchant, order_ref, order_date, order_pname, order_pcode,
        order_pinfo, order_price, order_qty, order_vat, order_shipping,
        db_invoice.currency.code, discount, pay_method, order_price_type,
        testorder
    ])

    return render(
        request=request,
        template_name='payuro/pay_invoice.html',
        context={
            'redirect_message':
            _('Redirecting to PayU'),
            'page_title':
            _('Invoice payment'),
            'form_action':
            CREATE_ORDER_URL,
            # inputs
            'MERCHANT':
            conf.merchant,
            'ORDER_REF':
            order_ref,
            'ORDER_DATE':
            order_date,
            'MULTI_ITEMS':
            multi_items,
            'ORDER_PNAME':
            order_pname if len(order_pname) > 1 else order_pname[0],
            'ORDER_PCODE':
            order_pcode if len(order_pcode) > 1 else order_pcode[0],
            'ORDER_PINFO':
            order_pinfo if len(order_pinfo) > 1 else order_pinfo[0],
            'ORDER_PRICE':
            order_price if len(order_price) > 1 else order_price[0],
            'ORDER_PRICE_TYPE':
            order_price_type
            if len(order_price_type) > 1 else order_price_type[0],
            'ORDER_QTY':
            order_qty if len(order_qty) > 1 else order_qty[0],
            'ORDER_VAT':
            order_vat if len(order_vat) > 1 else order_vat[0],
            'PRICES_CURRENCY':
            db_invoice.currency.code,
            'PAY_METHOD':
            pay_method,
            'ORDER_SHIPPING':
            order_shipping,
            'TESTORDER':
            testorder,
            'DISCOUNT':
            discount,
            'BILL_FNAME':
            client.first_name,
            'BILL_LNAME':
            client.last_name,
            'BILL_EMAIL':
            client.email,
            'BILL_PHONE':
            client_phone,
            'BILL_COUNTRYCODE':
            client_country,
            'LANGUAGE':
            language,
            'BACK_REF':
            back_ref,
            'ORDER_HASH':
            str(order_hash, encoding='utf-8'),
            'RECURRING':
            True if recurring else False,
        })
Пример #20
0
def process_refund(refund):
    # FIXME(tomo): This does not support partial refunds
    # Partial refunds should be allowed if the invoice balance is higher than the requested amount
    # Right now, creating a partial refund will set the transaction to refunded even if you refund
    # less than the payment value.
    gateway = Gateway.objects.get(name='stripe')
    external_id = refund.id
    existing_transaction_id = None
    invoice_id = None
    if refund.status == 'succeeded':
        if external_id is not None:
            try:
                existing_transaction = Transaction.objects.get(
                    external_id=refund.charge, gateway=gateway)
                existing_transaction_id = existing_transaction.pk
                invoice_id = existing_transaction.invoice_id
            except Transaction.DoesNotExist:
                raise exceptions.GatewayException('Transaction does not exist')
            else:
                existing_transaction.status = TransactionStatus.REFUNDED
                existing_transaction.save(update_fields=['status'])
        gateway.log_callback(external_id=external_id,
                             status=refund.status,
                             data=refund,
                             error=False)
        # Create a new refund transaction
        real_amount = utils.convert_amount_from_api(refund.amount,
                                                    refund.currency)
        created_date = datetime.datetime.fromtimestamp(refund.created)
        new_transaction_status = TransactionStatus.REFUNDED
        new_refund_transaction = {
            'invoice': invoice_id,
            'external_id': external_id,
            'amount': real_amount,
            'currency': refund.currency,
            'gateway': gateway.pk,
            'fee': gateway.get_fee(amount=decimal.Decimal(real_amount)),
            'date_initiated': created_date,
            'extra': {
                'balance_transaction': refund.balance_transaction
            },
            'refunded_transaction': existing_transaction_id,
            'status': new_transaction_status
        }
        tr_ser = AddTransactionSerializer(data=new_refund_transaction)
        if tr_ser.is_valid(raise_exception=False):
            new_transaction = tr_ser.save()
            new_transaction_id = new_transaction.id

            activity_helper.start_generic_activity(
                category_name='stripe',
                activity_class='stripe payment refund',
                invoice_id=invoice_id)

            invoice_refund_payment.delay(
                transaction_id=new_transaction.refunded_transaction.pk,
                amount=utils.convert_amount_from_api(refund.amount,
                                                     refund.currency),
                to_client_credit=False,
                new_transaction_id=new_transaction_id)

            activity_helper.end_activity()
        else:
            LOG.error(tr_ser.errors)
            raise exceptions.InvoicePaymentException(
                'Unable to process refund', invoice_id=invoice_id)
    elif refund.status == 'failed':
        gateway.log_callback(external_id=external_id,
                             status=refund.status,
                             data=refund,
                             error=(refund.status == 'failed'),
                             error_info=refund.failure_reason)
        raise exceptions.GatewayException(
            _('Refund for transaction {} failed').format(external_id))