Exemple #1
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('support', contribution.pk, 'request')

    if contribution.transaction_id is None:
        messages.error(request,
            _('A refund cannot be applied for yet. Please try again later. '
              'If this error persists contact [email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_instant_refund():
        try:
            paypal.refund(contribution.paykey)
        except PaypalError, e:
            paypal_log.error('Paypal error with refund', exc_info=True)
            messages.error(request, _('There was an error with your instant '
                                      'refund.'))
            contribution.record_failed_refund(e)
            return redirect('account.purchases')

        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('account.purchases')
Exemple #2
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        paypal_log.info('Refund issued for contribution %r' % contribution.pk)
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        url = absolutify(urlparams(addon.get_dev_url('issue_refund'),
                                   transaction_id=contribution.transaction_id))
        template = jingo.render_to_string(request,
            wizard.tpl('emails/refund-request.txt'),
            context={'addon': addon,
                     'form': form,
                     'user': request.amo_user,
                     'contribution': contribution,
                     'refund_url': url})
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name),
                  template, settings.NOBODY_EMAIL,
                  [smart_str(addon.support_email)])
        return redirect(reverse('users.support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Exemple #3
0
def refund_reason(request, contribution, wizard):
    if not contribution.can_we_refund():
        return http.HttpResponseForbidden()

    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('support', contribution.pk, 'request')

    if contribution.transaction_id is None:
        messages.error(request,
            _('A refund cannot be applied for yet. Please try again later. '
              'If this error persists contact [email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_instant_refund():
        try:
            paypal.refund(contribution.paykey)
        except PaypalError:
            paypal_log.error('Paypal error with refund', exc_info=True)
            messages.error(request, _('There was an error with your instant '
                                      'refund.'))
            return redirect('account.purchases')

        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('account.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        reason = form.cleaned_data['text']
        template = jingo.render_to_string(request,
            wizard.tpl('emails/refund-request.txt'),
            context={'product': addon,
                     'form': form,
                     'user': request.amo_user,
                     'contribution': contribution,
                     'refund_url': contribution.get_absolute_refund_url(),
                     'refund_reason': reason})
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))

        # L10n: %s is the app name.
        support_mail(_(u'New Refund Request for %s' % addon.name),
                     template, sender=settings.NOBODY_EMAIL,
                     recipients=[smart_str(addon.support_email)])

        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(reverse('support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'),
                         {'product': addon, 'contribution': contribution,
                          'form': form, 'title': _('Request Refund')})
Exemple #4
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        paypal_log.info('Refund issued for contribution %r' % contribution.pk)
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        url = absolutify(urlparams(addon.get_dev_url('issue_refund'),
                                   transaction_id=contribution.transaction_id))
        template = jingo.render_to_string(request,
            wizard.tpl('emails/refund-request.txt'),
            context={'addon': addon,
                     'form': form,
                     'user': request.amo_user,
                     'contribution': contribution,
                     'refund_url': url})
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name),
                  template, settings.NOBODY_EMAIL,
                  [smart_str(addon.support_email)])
        return redirect(reverse('users.support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Exemple #5
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not "request" in wizard.get_progress():
        return redirect("users.support", contribution.pk, "request")
    if contribution.transaction_id is None:
        messages.error(
            request,
            _(
                "A refund cannot be applied for yet. Please try again"
                " later. If this error persists contact "
                "[email protected]."
            ),
        )
        paypal_log.info("Refund requested for contribution with no " "transaction_id: %r" % (contribution.pk,))
        return redirect("users.purchases")

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        # TODO: Consider requiring a refund reason for instant refunds.
        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info("Refund %r issued for contribution %r" % (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _("Refund is being processed."))
        return redirect("users.purchases")

    form = forms.ContactForm(request.POST or None)
    if request.method == "POST" and form.is_valid():
        reason = form.cleaned_data["text"]
        template = jingo.render_to_string(
            request,
            wizard.tpl("emails/refund-request.txt"),
            context={
                "addon": addon,
                "form": form,
                "user": request.amo_user,
                "contribution": contribution,
                "refund_url": contribution.get_absolute_refund_url(),
                "refund_reason": reason,
            },
        )
        log.info("Refund request sent by user: %s for addon: %s" % (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(
            _(u"New Refund Request for %s" % addon.name),
            template,
            settings.NOBODY_EMAIL,
            [smart_str(addon.support_email)],
        )
        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(reverse("users.support", args=[contribution.pk, "refund-sent"]))

    return wizard.render(request, wizard.tpl("refund.html"), {"form": form})
Exemple #6
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')
    if contribution.transaction_id is None:
        messages.error(
            request,
            _('A refund cannot be applied for yet. Please try again'
              ' later. If this error persists contact '
              '[email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % (contribution.pk, ))
        return redirect('users.purchases')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        # TODO: Consider requiring a refund reason for instant refunds.
        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        reason = form.cleaned_data['text']
        template = jingo.render_to_string(
            request,
            wizard.tpl('emails/refund-request.txt'),
            context={
                'addon': addon,
                'form': form,
                'user': request.amo_user,
                'contribution': contribution,
                'refund_url': contribution.get_absolute_refund_url(),
                'refund_reason': reason
            })
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name), template,
                  settings.NOBODY_EMAIL, [smart_str(addon.support_email)])
        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(
            reverse('users.support', args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Exemple #7
0
def issue_refund(request, addon_id, addon, webapp=False):
    txn_id = request.REQUEST.get("transaction_id")
    if not txn_id:
        raise http.Http404
    form_enabled = True
    contribution = get_object_or_404(
        Contribution, transaction_id=txn_id, type__in=[amo.CONTRIB_PURCHASE, amo.CONTRIB_INAPP]
    )

    if hasattr(contribution, "refund") and contribution.refund.status not in (amo.REFUND_PENDING, amo.REFUND_FAILED):
        # If it's not pending, we've already taken action.
        messages.error(request, _("Refund already processed."))
        form_enabled = False

    elif request.method == "POST":
        if "issue" in request.POST:
            if waffle.flag_is_active(request, "solitude-payments"):
                try:
                    response = client.post_refund(data={"uuid": contribution.transaction_id})
                except client.Error, e:
                    contribution.record_failed_refund(e)
                    paypal_log.error("Refund failed for: %s" % txn_id, exc_info=True)
                    messages.error(request, _("There was an error with " "the refund."))
                    return redirect(addon.get_dev_url("refunds"))
                results = response["response"]

            else:
                # TODO(solitude): remove this.
                try:
                    results = paypal.refund(contribution.paykey)
                except PaypalError, e:
                    contribution.record_failed_refund(e)
                    paypal_log.error("Refund failed for: %s" % txn_id, exc_info=True)
                    messages.error(request, _("There was an error with " "the refund."))
                    return redirect(addon.get_dev_url("refunds"))

            for res in results:
                if res["refundStatus"] == "ALREADY_REVERSED_OR_REFUNDED":
                    paypal_log.debug(
                        "Refund attempt for already-refunded paykey: %s, %s"
                        % (contribution.paykey, res["receiver.email"])
                    )
                    messages.error(request, _("Refund was previously issued; " "no action taken."))
                    return redirect(addon.get_dev_url("refunds"))
                elif res["refundStatus"] == "NO_API_ACCESS_TO_RECEIVER":
                    paypal_log.debug(
                        "Refund attempt for product %s with no "
                        "refund token: %s, %s" % (contribution.addon.pk, contribution.paykey, res["receiver.email"])
                    )
                    messages.error(
                        request,
                        _("A refund can't be issued at this time. We've " "notified an admin; please try again later."),
                    )
                    return redirect(addon.get_dev_url("refunds"))

            contribution.mail_approved()
            amo.log(amo.LOG.REFUND_GRANTED, addon, contribution.user)
            refund = contribution.enqueue_refund(amo.REFUND_APPROVED)
            paypal_log.info("Refund %r issued for contribution %r" % (refund.pk, contribution.pk))
            messages.success(request, _("Refund issued."))
Exemple #8
0
def issue_refund(request, addon_id, addon, webapp=False):
    txn_id = request.REQUEST.get('transaction_id')
    if not txn_id:
        raise http.Http404
    form_enabled = True
    contribution = get_object_or_404(Contribution, transaction_id=txn_id,
                                     type__in=[amo.CONTRIB_PURCHASE,
                                               amo.CONTRIB_INAPP])

    if (hasattr(contribution, 'refund') and
        contribution.refund.status not in (amo.REFUND_PENDING,
                                           amo.REFUND_FAILED)):
        # If it's not pending, we've already taken action.
        messages.error(request, _('Refund already processed.'))
        form_enabled = False

    elif request.method == 'POST':
        if 'issue' in request.POST:
            try:
                results = paypal.refund(contribution.paykey)
            except PaypalError, e:
                contribution.record_failed_refund(e)
                paypal_log.error('Refund failed for: %s' % txn_id,
                                 exc_info=True)
                messages.error(request, _('There was an error with '
                                          'the refund.'))
                return redirect(addon.get_dev_url('refunds'))

            for res in results:
                if res['refundStatus'] == 'ALREADY_REVERSED_OR_REFUNDED':
                    paypal_log.debug(
                        'Refund attempt for already-refunded paykey: %s, %s' %
                        (contribution.paykey, res['receiver.email']))
                    messages.error(request, _('Refund was previously issued; '
                                              'no action taken.'))
                    return redirect(addon.get_dev_url('refunds'))
                elif res['refundStatus'] == 'NO_API_ACCESS_TO_RECEIVER':
                    paypal_log.debug('Refund attempt for product %s with no '
                                     'refund token: %s, %s' %
                                    (contribution.addon.pk,
                                     contribution.paykey,
                                     res['receiver.email']))
                    messages.error(request,
                        _("A refund can't be issued at this time. We've "
                          "notified an admin; please try again later."))
                    return redirect(addon.get_dev_url('refunds'))

            contribution.mail_approved()
            refund = contribution.enqueue_refund(amo.REFUND_APPROVED)
            paypal_log.info('Refund %r issued for contribution %r' %
                            (refund.pk, contribution.pk))
            messages.success(request, _('Refund issued.'))
        else:
            contribution.mail_declined()
            # TODO: Consider requiring a rejection reason for declined refunds.
            refund = contribution.enqueue_refund(amo.REFUND_DECLINED)
            paypal_log.info('Refund %r declined for contribution %r' %
                            (refund.pk, contribution.pk))
            messages.success(request, _('Refund declined.'))
        return redirect(addon.get_dev_url('refunds'))
Exemple #9
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')
    if contribution.transaction_id is None:
        messages.error(request,
                       _('A refund cannot be applied for yet. Please try again'
                         ' later. If this error persists contact '
                         '[email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % (contribution.pk,))
        return redirect('users.purchases')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        # TODO: Consider requiring a refund reason for instant refunds.
        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        reason = form.cleaned_data['text']
        template = jingo.render_to_string(request,
            wizard.tpl('emails/refund-request.txt'),
            context={'addon': addon,
                     'form': form,
                     'user': request.amo_user,
                     'contribution': contribution,
                     'refund_url': contribution.get_absolute_refund_url(),
                     'refund_reason': reason})
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name),
                  template, settings.NOBODY_EMAIL,
                  [smart_str(addon.support_email)])
        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(reverse('users.support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Exemple #10
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('support', contribution.pk, 'request')

    if contribution.transaction_id is None:
        messages.error(
            request,
            _('A refund cannot be applied for yet. Please try again later. '
              'If this error persists contact [email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_refunded():
        messages.error(request, _('This has already been refunded.'))
        paypal_log.info('Already refunded: %s' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_instant_refund():
        if waffle.flag_is_active(request, 'solitude-payments'):
            try:
                client.post_refund(data={'uuid': contribution.transaction_id})
            except client.Error, e:
                paypal_log.error('Paypal error with refund', exc_info=True)
                messages.error(
                    request,
                    _('There was an error with your '
                      'instant refund.'))
                contribution.record_failed_refund(e, request.amo_user)
                return redirect('account.purchases')
        else:
            # TODO(solitude): remove this.
            try:
                paypal.refund(contribution.paykey)
            except PaypalError, e:
                paypal_log.error('Paypal error with refund', exc_info=True)
                messages.error(
                    request,
                    _('There was an error with your '
                      'instant refund.'))
                contribution.record_failed_refund(e, request.amo_user)
                return redirect('account.purchases')
Exemple #11
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not "request" in wizard.get_progress():
        return redirect("support", contribution.pk, "request")

    if contribution.transaction_id is None:
        messages.error(
            request,
            _(
                "A refund cannot be applied for yet. Please try again later. "
                "If this error persists contact [email protected]."
            ),
        )
        paypal_log.info("Refund requested for contribution with no " "transaction_id: %r" % contribution.pk)
        return redirect("account.purchases")

    if contribution.is_refunded():
        messages.error(request, _("This has already been refunded."))
        paypal_log.info("Already refunded: %s" % contribution.pk)
        return redirect("account.purchases")

    if contribution.is_instant_refund():
        if waffle.flag_is_active(request, "solitude-payments"):
            try:
                client.post_refund(data={"uuid": contribution.transaction_id})
            except client.Error, e:
                paypal_log.error("Paypal error with refund", exc_info=True)
                messages.error(request, _("There was an error with your " "instant refund."))
                contribution.record_failed_refund(e)
                return redirect("account.purchases")
        else:
            # TODO(solitude): remove this.
            try:
                paypal.refund(contribution.paykey)
            except PaypalError, e:
                paypal_log.error("Paypal error with refund", exc_info=True)
                messages.error(request, _("There was an error with your " "instant refund."))
                contribution.record_failed_refund(e)
                return redirect("account.purchases")
Exemple #12
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('support', contribution.pk, 'request')

    if contribution.transaction_id is None:
        messages.error(request,
            _('A refund cannot be applied for yet. Please try again later. '
              'If this error persists contact [email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_refunded():
        messages.error(request, _('This has already been refunded.'))
        paypal_log.info('Already refunded: %s' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_instant_refund():
        if waffle.flag_is_active(request, 'solitude-payments'):
            try:
                client.post_refund(data={'uuid': contribution.transaction_id})
            except client.Error, e:
                paypal_log.error('Paypal error with refund', exc_info=True)
                messages.error(request, _('There was an error with your '
                                          'instant refund.'))
                contribution.record_failed_refund(e, request.amo_user)
                return redirect('account.purchases')
        else:
            # TODO(solitude): remove this.
            try:
                paypal.refund(contribution.paykey)
            except PaypalError, e:
                paypal_log.error('Paypal error with refund', exc_info=True)
                messages.error(request, _('There was an error with your '
                                          'instant refund.'))
                contribution.record_failed_refund(e, request.amo_user)
                return redirect('account.purchases')
Exemple #13
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('support', contribution.pk, 'request')

    if contribution.transaction_id is None:
        messages.error(request,
            _('A refund cannot be applied for yet. Please try again later. '
              'If this error persists contact [email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_refunded():
        messages.error(request, _('This has already been refunded.'))
        paypal_log.info('Already refunded: %s' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_instant_refund():
        try:
            paypal.refund(contribution.paykey)
        except PaypalError, e:
            paypal_log.error('Paypal error with refund', exc_info=True)
            messages.error(request, _('There was an error with your instant '
                                      'refund.'))
            contribution.record_failed_refund(e)
            return redirect('account.purchases')

        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        amo.log(amo.LOG.REFUND_INSTANT, addon)
        return redirect('account.purchases')
Exemple #14
0
 def test_refund_error(self, _call):
     _call.side_effect = paypal.PaypalError
     with self.assertRaises(paypal.PaypalError):
         paypal.refund("fake-paykey")
Exemple #15
0
 def test_refund_success(self, opener):
     """
     Making refund requests returns the refund info.
     """
     opener.return_value = StringIO(good_refund_string)
     eq_(paypal.refund("fake-paykey"), good_refund_data)
Exemple #16
0
 def test_refund_wrong_status(self, opener):
     opener.return_value = StringIO(error_refund_string)
     with self.assertRaises(paypal.PaypalError):
         paypal.refund("fake-paykey")
Exemple #17
0
def issue_refund(request, addon_id, addon, webapp=False):
    txn_id = request.REQUEST.get('transaction_id')
    if not txn_id:
        raise http.Http404
    form_enabled = True
    contribution = get_object_or_404(
        Contribution,
        transaction_id=txn_id,
        type__in=[amo.CONTRIB_PURCHASE, amo.CONTRIB_INAPP])

    if (hasattr(contribution, 'refund') and contribution.refund.status
            not in (amo.REFUND_PENDING, amo.REFUND_FAILED)):
        # If it's not pending, we've already taken action.
        messages.error(request, _('Refund already processed.'))
        form_enabled = False

    elif request.method == 'POST':
        if 'issue' in request.POST:
            try:
                results = paypal.refund(contribution.paykey)
            except PaypalError, e:
                contribution.record_failed_refund(e)
                paypal_log.error('Refund failed for: %s' % txn_id,
                                 exc_info=True)
                messages.error(request,
                               _('There was an error with '
                                 'the refund.'))
                return redirect(addon.get_dev_url('refunds'))

            for res in results:
                if res['refundStatus'] == 'ALREADY_REVERSED_OR_REFUNDED':
                    paypal_log.debug(
                        'Refund attempt for already-refunded paykey: %s, %s' %
                        (contribution.paykey, res['receiver.email']))
                    messages.error(
                        request,
                        _('Refund was previously issued; '
                          'no action taken.'))
                    return redirect(addon.get_dev_url('refunds'))
                elif res['refundStatus'] == 'NO_API_ACCESS_TO_RECEIVER':
                    paypal_log.debug(
                        'Refund attempt for product %s with no '
                        'refund token: %s, %s' %
                        (contribution.addon.pk, contribution.paykey,
                         res['receiver.email']))
                    messages.error(
                        request,
                        _("A refund can't be issued at this time. We've "
                          "notified an admin; please try again later."))
                    return redirect(addon.get_dev_url('refunds'))

            contribution.mail_approved()
            amo.log(amo.LOG.REFUND_GRANTED, addon, contribution.user)
            refund = contribution.enqueue_refund(amo.REFUND_APPROVED)
            paypal_log.info('Refund %r issued for contribution %r' %
                            (refund.pk, contribution.pk))
            messages.success(request, _('Refund issued.'))
        else:
            contribution.mail_declined()
            amo.log(amo.LOG.REFUND_DECLINED, addon, contribution.user)
            # TODO: Consider requiring a rejection reason for declined refunds.
            refund = contribution.enqueue_refund(amo.REFUND_DECLINED)
            paypal_log.info('Refund %r declined for contribution %r' %
                            (refund.pk, contribution.pk))
            messages.success(request, _('Refund declined.'))
        return redirect(addon.get_dev_url('refunds'))
Exemple #18
0
 def test_refund_success(self, opener):
     """
     Making refund requests returns the refund info.
     """
     opener.return_value.text = good_refund_string
     eq_(paypal.refund('fake-paykey'), good_refund_data)
Exemple #19
0
 def test_refund_wrong_status(self, opener):
     opener.return_value.text = error_refund_string
     with self.assertRaises(paypal.PaypalError):
         paypal.refund('fake-paykey')
Exemple #20
0
 def test_refundFailure(self, opener):
     opener.return_value = StringIO(error_refund_string)
     with self.assertRaises(paypal.PaypalError):
         paypal.refund('fake-txnid')
Exemple #21
0
 def test_refundSuccess(self, opener):
     """
     Making refund requests returns the refund info.
     """
     opener.return_value = StringIO(good_refund_string)
     eq_(paypal.refund('fake-txnid'), good_refund_data)
Exemple #22
0
 def test_refund_no_refund_token(self, opener):
     opener.return_value.text = no_token_refund_string
     d = paypal.refund('fake-paykey')
     eq_(d[0]['refundStatus'], 'NO_API_ACCESS_TO_RECEIVER')
Exemple #23
0
 def test_refund_no_refund_token(self, opener):
     opener.return_value.text = no_token_refund_string
     d = paypal.refund('fake-paykey')
     eq_(d[0]['refundStatus'], 'NO_API_ACCESS_TO_RECEIVER')
Exemple #24
0
 def test_refund_processing_failed(self, opener):
     opener.return_value.text = processing_failed_refund_string
     d = paypal.refund('fake-paykey')
     eq_(d[0]['refundStatus'], 'NO_API_ACCESS_TO_RECEIVER')
Exemple #25
0
 def test_refund_processing_failed(self, opener):
     opener.return_value = StringIO(processing_failed_refund_string)
     d = paypal.refund("fake-paykey")
     eq_(d[0]["refundStatus"], "NO_API_ACCESS_TO_RECEIVER")
Exemple #26
0
def issue_refund(request, addon_id, addon, webapp=False):
    txn_id = request.REQUEST.get("transaction_id")
    if not txn_id:
        raise http.Http404
    form_enabled = True
    contribution = get_object_or_404(Contribution, transaction_id=txn_id, type=amo.CONTRIB_PURCHASE)

    if hasattr(contribution, "refund") and contribution.refund.status != amo.REFUND_PENDING:
        # If it's not pending, we've already taken action.
        messages.error(request, _("Refund already processed."))
        form_enabled = False

    elif request.method == "POST":
        if "issue" in request.POST:
            try:
                results = paypal.refund(contribution.paykey)
            except PaypalError:
                paypal_log.error("Refund failed for: %s" % txn_id, exc_info=True)
                messages.error(request, _("There was an error with " "the refund."))
                return redirect(addon.get_dev_url("refunds"))

            for res in results:
                if res["refundStatus"] == "ALREADY_REVERSED_OR_REFUNDED":
                    paypal_log.debug(
                        "Refund attempt for already-refunded paykey: %s, %s"
                        % (contribution.paykey, res["receiver.email"])
                    )
                    messages.error(request, _("Refund was previously issued; " "no action taken."))
                    return redirect(addon.get_dev_url("refunds"))
                elif res["refundStatus"] == "NO_API_ACCESS_TO_RECEIVER":
                    paypal_log.debug(
                        "Refund attempt for product %s with no "
                        "refund token: %s, %s" % (contribution.addon.pk, contribution.paykey, res["receiver.email"])
                    )
                    messages.error(
                        request,
                        _("A refund can't be issued at this time. We've " "notified an admin; please try again later."),
                    )
                    return redirect(addon.get_dev_url("refunds"))

            contribution.mail_approved()
            refund = contribution.enqueue_refund(amo.REFUND_APPROVED)
            paypal_log.info("Refund %r issued for contribution %r" % (refund.pk, contribution.pk))
            messages.success(request, _("Refund issued."))
        else:
            contribution.mail_declined()
            # TODO: Consider requiring a rejection reason for declined refunds.
            refund = contribution.enqueue_refund(amo.REFUND_DECLINED)
            paypal_log.info("Refund %r declined for contribution %r" % (refund.pk, contribution.pk))
            messages.success(request, _("Refund declined."))
        return redirect(addon.get_dev_url("refunds"))

    return jingo.render(
        request,
        "developers/payments/issue-refund.html",
        {
            "enabled": form_enabled,
            "contribution": contribution,
            "addon": addon,
            "webapp": webapp,
            "transaction_id": txn_id,
        },
    )
Exemple #27
0
 def test_refunded_already(self, opener):
     opener.return_value.text = already_refunded_string
     eq_(paypal.refund('fake-paykey')[0]['refundStatus'],
         'ALREADY_REVERSED_OR_REFUNDED')
Exemple #28
0
 def test_refunded_already(self, opener):
     opener.return_value = StringIO(already_refunded_string)
     eq_(paypal.refund("fake-paykey")[0]["refundStatus"], "ALREADY_REVERSED_OR_REFUNDED")
Exemple #29
0
 def test_refunded_already(self, opener):
     opener.return_value.text = already_refunded_string
     eq_(
         paypal.refund('fake-paykey')[0]['refundStatus'],
         'ALREADY_REVERSED_OR_REFUNDED')
Exemple #30
0
 def test_refund_wrong_status(self, opener):
     opener.return_value.text = error_refund_string
     with self.assertRaises(paypal.PaypalError):
         paypal.refund('fake-paykey')
Exemple #31
0
 def test_refund_processing_failed(self, opener):
     opener.return_value.text = processing_failed_refund_string
     d = paypal.refund('fake-paykey')
     eq_(d[0]['refundStatus'], 'NO_API_ACCESS_TO_RECEIVER')
Exemple #32
0
 def test_refundFailure(self, opener):
     opener.return_value = StringIO(error_refund_string)
     with self.assertRaises(paypal.PaypalError):
         paypal.refund('fake-txnid')
Exemple #33
0
 def test_refund_error(self, _call):
     _call.side_effect = paypal.PaypalError
     with self.assertRaises(paypal.PaypalError):
         paypal.refund('fake-paykey')
Exemple #34
0
 def test_refund_success(self, opener):
     """
     Making refund requests returns the refund info.
     """
     opener.return_value.text = good_refund_string
     eq_(paypal.refund('fake-paykey'), good_refund_data)
Exemple #35
0
 def test_refund_no_refund_token(self, opener):
     opener.return_value = StringIO(no_token_refund_string)
     d = paypal.refund("fake-paykey")
     eq_(d[0]["refundStatus"], "NO_API_ACCESS_TO_RECEIVER")