use getrefundstatus if transaction is marked pending refund (bug 821900)
This commit is contained in:
Родитель
dffb4aeac2
Коммит
656428682f
|
@ -373,13 +373,10 @@ class Contribution(amo.models.ModelBase):
|
|||
limit = datetime.timedelta(seconds=period)
|
||||
return datetime.datetime.now() < (self.created + limit)
|
||||
|
||||
def has_refund(self):
|
||||
from market.models import Refund
|
||||
return (Refund.objects.filter(
|
||||
contribution=self, status__in=[amo.REFUND_PENDING,
|
||||
amo.REFUND_APPROVED,
|
||||
amo.REFUND_APPROVED_INSTANT])
|
||||
.exists())
|
||||
def get_refund_contribs(self):
|
||||
"""Get related set of refund contributions."""
|
||||
return Contribution.objects.filter(
|
||||
related=self, type=amo.CONTRIB_REFUND).order_by('-modified')
|
||||
|
||||
def is_refunded(self):
|
||||
"""
|
||||
|
|
|
@ -330,7 +330,7 @@ h1 {
|
|||
|
||||
.refund-transaction {
|
||||
float: left;
|
||||
padding-top: 13px;
|
||||
padding-top: 15px;
|
||||
width: 50%;
|
||||
label {
|
||||
display: block;
|
||||
|
|
|
@ -1,24 +1,11 @@
|
|||
from tower import ugettext_lazy as _lazy
|
||||
from tower import ugettext as _
|
||||
|
||||
|
||||
STATUS_PENDING = 0 # When the payment has been started.
|
||||
STATUS_COMPLETED = 1 # When the IPN says its ok.
|
||||
STATUS_CHECKED = 2 # When someone calls pay-check on the transaction.
|
||||
# When we we've got a request for a payment, but more work needs to be done
|
||||
# before we can proceed to the next stage, pending.
|
||||
STATUS_RECEIVED = 3
|
||||
# Something went wrong and this transaction failed completely.
|
||||
STATUS_FAILED = 4
|
||||
# Explicit cancel action.
|
||||
STATUS_CANCELLED = 5
|
||||
|
||||
STATUS_DEFAULT = STATUS_PENDING
|
||||
|
||||
|
||||
PROVIDER_PAYPAL = 0
|
||||
PROVIDER_BANGO = 1
|
||||
|
||||
PROVIDERS = {
|
||||
PROVIDER_PAYPAL: _lazy('PayPal'),
|
||||
PROVIDER_BANGO: _lazy('Bango'),
|
||||
PENDING = 'PENDING'
|
||||
COMPLETED = 'OK'
|
||||
FAILED = 'FAILED'
|
||||
REFUND_STATUSES = {
|
||||
PENDING: _('Pending'),
|
||||
COMPLETED: _('Completed'),
|
||||
FAILED: _('Failed'),
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
<dd>
|
||||
<a href="{{ app.get_detail_url() }}">{{ app.name }}</a>
|
||||
</dd>
|
||||
<dt>{{ _('Provider') }}</dt><dd>{{ provider }}</dd>
|
||||
<dt>{{ _('Price Tier') }}</dt><dd>{{ contrib.price_tier }}</dd>
|
||||
{% if related %}
|
||||
<dt>{{ _('Related Transaction') }}</dt>
|
||||
|
@ -43,13 +42,19 @@
|
|||
{{ no_results() }}
|
||||
{% endif %}
|
||||
|
||||
{% if action_allowed('Transaction', 'Refund') and is_refundable %}
|
||||
<form class="refund-transaction c" method="post"
|
||||
action="{{ url('lookup.transaction_refund', uuid) }}">
|
||||
{{ csrf() }}
|
||||
{{ tx_refund_form }}
|
||||
<button>{{ _('Refund this transaction') }}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="refund-transaction c">
|
||||
<dl>
|
||||
<dt>{{ _('Refund Status') }}</dt>
|
||||
<dd>{{ refund_status or _('Unrequested') }}</dd>
|
||||
</dl>
|
||||
{% if action_allowed('Transaction', 'Refund') and is_refundable %}
|
||||
<form method="post"
|
||||
action="{{ url('lookup.transaction_refund', uuid) }}">
|
||||
{{ csrf() }}
|
||||
{{ tx_refund_form }}
|
||||
<button>{{ _('Refund this transaction') }}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.conf import settings
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
from django.core import mail
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.test.client import RequestFactory
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
|
@ -26,8 +25,8 @@ from amo.tests import addon_factory, app_factory, ESTestCase, TestCase
|
|||
from amo.urlresolvers import reverse
|
||||
from devhub.models import ActivityLog
|
||||
from market.models import AddonPaymentData, AddonPremium, Price, Refund
|
||||
from mkt.constants.payments import (STATUS_COMPLETED, STATUS_FAILED,
|
||||
STATUS_PENDING)
|
||||
from mkt.constants.payments import (COMPLETED, FAILED, PENDING,
|
||||
REFUND_STATUSES)
|
||||
from mkt.lookup.views import _transaction_summary, transaction_refund
|
||||
from mkt.site.fixtures import fixture
|
||||
from mkt.webapps.cron import update_weekly_downloads
|
||||
|
@ -279,21 +278,55 @@ class TestTransactionSummary(TestCase):
|
|||
fixtures = fixture('user_support_staff', 'user_999')
|
||||
|
||||
def setUp(self):
|
||||
self.uuid = 45
|
||||
self.transaction_id = 999
|
||||
self.buyer_uuid = 123
|
||||
self.uuid = 'some:uuid'
|
||||
self.transaction_id = 'some:tr'
|
||||
self.seller_uuid = 456
|
||||
self.related_tx_uuid = 789
|
||||
self.user = UserProfile.objects.get(pk=999)
|
||||
|
||||
self.app = addon_factory(type=amo.ADDON_WEBAPP)
|
||||
self.contrib = Contribution.objects.create(
|
||||
addon=addon_factory(type=amo.ADDON_WEBAPP), uuid=self.uuid,
|
||||
user=self.user, transaction_id='some:tr')
|
||||
addon=self.app, uuid=self.uuid, user=self.user,
|
||||
transaction_id=self.transaction_id)
|
||||
|
||||
self.url = reverse('lookup.transaction_summary', args=[self.uuid])
|
||||
self.client.login(username='support-staff@mozilla.com',
|
||||
password='password')
|
||||
|
||||
def create_test_refund(self):
|
||||
refund_contrib = Contribution.objects.create(
|
||||
addon=self.app, related=self.contrib, type=amo.CONTRIB_REFUND,
|
||||
transaction_id='testtransactionid')
|
||||
refund_contrib.enqueue_refund(amo.REFUND_PENDING, self.user)
|
||||
|
||||
def test_transaction_summary(self):
|
||||
data = _transaction_summary(self.uuid)
|
||||
|
||||
eq_(data['is_refundable'], False)
|
||||
eq_(data['contrib'].pk, self.contrib.pk)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_refund_status(self, solitude):
|
||||
solitude.api.bango.refund.status.get.return_value = {'status': PENDING}
|
||||
|
||||
self.create_test_refund()
|
||||
data = _transaction_summary(self.uuid)
|
||||
|
||||
eq_(data['refund_status'], REFUND_STATUSES[PENDING])
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_is_refundable(self, solitude):
|
||||
solitude.api.bango.refund.status.get.return_value = {'status': PENDING}
|
||||
|
||||
self.contrib.update(type=amo.CONTRIB_PURCHASE)
|
||||
data = _transaction_summary(self.uuid)
|
||||
eq_(data['contrib'].pk, self.contrib.pk)
|
||||
eq_(data['is_refundable'], True)
|
||||
|
||||
self.create_test_refund()
|
||||
data = _transaction_summary(self.uuid)
|
||||
eq_(data['is_refundable'], False)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_200(self, solitude):
|
||||
r = self.client.get(self.url)
|
||||
|
@ -305,32 +338,18 @@ class TestTransactionSummary(TestCase):
|
|||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 403)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_no_transaction_404(self, solitude):
|
||||
solitude.api.generic.transaction.get_object_or_404.side_effect = ObjectDoesNotExist
|
||||
def test_no_transaction_404(self):
|
||||
r = self.client.get(reverse('lookup.transaction_summary', args=[999]))
|
||||
eq_(r.status_code, 404)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_transaction_summary(self, solitude):
|
||||
data = _transaction_summary(self.uuid)
|
||||
eq_(data['contrib'].pk, self.contrib.pk)
|
||||
eq_(data['is_refundable'], False)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_is_refundable(self, solitude):
|
||||
self.contrib.update(type=amo.CONTRIB_PURCHASE)
|
||||
data = _transaction_summary(self.uuid)
|
||||
eq_(data['contrib'].pk, self.contrib.pk)
|
||||
eq_(data['is_refundable'], True)
|
||||
|
||||
|
||||
@mock.patch.object(settings, 'TASK_USER_ID', 999)
|
||||
class TestTransactionRefund(TestCase):
|
||||
fixtures = fixture('user_support_staff', 'user_999')
|
||||
|
||||
def setUp(self):
|
||||
self.uuid = 45
|
||||
self.uuid = 'paymentuuid'
|
||||
self.refund_uuid = 'refunduuid'
|
||||
self.summary_url = reverse('lookup.transaction_summary',
|
||||
args=[self.uuid])
|
||||
self.url = reverse('lookup.transaction_refund', args=[self.uuid])
|
||||
|
@ -340,7 +359,7 @@ class TestTransactionRefund(TestCase):
|
|||
|
||||
self.contrib = Contribution.objects.create(
|
||||
addon=self.app, user=self.user, uuid=self.uuid,
|
||||
type=amo.CONTRIB_PURCHASE)
|
||||
type=amo.CONTRIB_PURCHASE, amount=1)
|
||||
|
||||
self.req = RequestFactory().post(self.url, {'refund_reason': 'text'})
|
||||
self.req.user = User.objects.get(username='support_staff')
|
||||
|
@ -352,40 +371,91 @@ class TestTransactionRefund(TestCase):
|
|||
setattr(self.req, '_messages', messages)
|
||||
self.login(self.req.user)
|
||||
|
||||
def bango_ret(self, status):
|
||||
return {
|
||||
'status': status,
|
||||
'transaction': 'transaction_uri'
|
||||
}
|
||||
|
||||
def refund_tx_ret(self):
|
||||
return {'uuid': self.refund_uuid}
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_refund_success(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = ({
|
||||
'status': STATUS_PENDING})
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
|
||||
solitude.get.return_value = self.refund_tx_ret()
|
||||
|
||||
# Do refund.
|
||||
res = transaction_refund(self.req, self.uuid)
|
||||
refund = Refund.objects.filter(contribution__addon=self.app)
|
||||
eq_(refund.count(), 1)
|
||||
refund_contribs = self.contrib.get_refund_contribs()
|
||||
|
||||
# Check Refund created.
|
||||
assert refund.exists()
|
||||
eq_(refund[0].status, amo.REFUND_PENDING)
|
||||
assert self.req.POST['refund_reason'] in refund[0].refund_reason
|
||||
|
||||
# Check refund Contribution created.
|
||||
eq_(refund_contribs.exists(), True)
|
||||
eq_(refund_contribs[0].refund, refund[0])
|
||||
eq_(refund_contribs[0].related, self.contrib)
|
||||
eq_(refund_contribs[0].amount, -self.contrib.amount)
|
||||
|
||||
self.assert3xx(res, self.summary_url)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_refund_failed(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = (
|
||||
{'status': STATUS_FAILED})
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(FAILED)
|
||||
|
||||
res = transaction_refund(self.req, self.uuid)
|
||||
eq_(self.contrib.has_refund(), False)
|
||||
|
||||
# Check no refund Contributions created.
|
||||
assert not self.contrib.get_refund_contribs().exists()
|
||||
self.assert3xx(res, self.summary_url)
|
||||
|
||||
def test_cant_refund(self):
|
||||
self.contrib.update(type=amo.CONTRIB_PENDING)
|
||||
resp = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
eq_(resp.status_code, 404)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_already_refunded(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
|
||||
solitude.get.return_value = self.refund_tx_ret()
|
||||
res = transaction_refund(self.req, self.uuid)
|
||||
refund_count = Contribution.objects.all().count()
|
||||
|
||||
# Check no refund Contributions created.
|
||||
res = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
assert refund_count == Contribution.objects.all().count()
|
||||
self.assert3xx(res, reverse('lookup.transaction_summary',
|
||||
args=[self.uuid]))
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_refund_slumber_error(self, solitude):
|
||||
for exception in (exceptions.HttpClientError,
|
||||
exceptions.HttpServerError):
|
||||
solitude.api.bango.refund.post.side_effect = exception
|
||||
res = transaction_refund(self.req, self.uuid)
|
||||
eq_(self.contrib.has_refund(), False)
|
||||
|
||||
# Check no refund Contributions created.
|
||||
assert not self.contrib.get_refund_contribs().exists()
|
||||
self.assert3xx(res, self.summary_url)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_redirect(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
|
||||
solitude.get.return_value = self.refund_tx_ret()
|
||||
|
||||
res = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
self.assert3xx(res, reverse('lookup.transaction_summary',
|
||||
args=[self.uuid]))
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
@mock.patch.object(settings, 'SEND_REAL_EMAIL', True)
|
||||
def test_refund_pending_email(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = (
|
||||
{'status': STATUS_PENDING})
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
|
||||
solitude.get.return_value = self.refund_tx_ret()
|
||||
|
||||
transaction_refund(self.req, self.uuid)
|
||||
eq_(len(mail.outbox), 1)
|
||||
|
@ -394,39 +464,18 @@ class TestTransactionRefund(TestCase):
|
|||
@mock.patch('mkt.lookup.views.client')
|
||||
@mock.patch.object(settings, 'SEND_REAL_EMAIL', True)
|
||||
def test_refund_completed_email(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = (
|
||||
{'status': STATUS_COMPLETED})
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(COMPLETED)
|
||||
solitude.get.return_value = self.refund_tx_ret()
|
||||
|
||||
transaction_refund(self.req, self.uuid)
|
||||
eq_(len(mail.outbox), 1)
|
||||
assert self.app.name.localized_string in smart_str(mail.outbox[0].body)
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_redirect(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = (
|
||||
{'status': STATUS_PENDING})
|
||||
|
||||
res = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
self.assert3xx(res, reverse('lookup.transaction_summary',
|
||||
args=[self.uuid]))
|
||||
|
||||
def test_cant_refund(self):
|
||||
self.contrib.update(type=amo.CONTRIB_PENDING)
|
||||
resp = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
eq_(resp.status_code, 404)
|
||||
|
||||
def test_already_refunded(self):
|
||||
self.contrib.enqueue_refund(status=amo.REFUND_PENDING,
|
||||
refund_reason='front fell off',
|
||||
user=self.user)
|
||||
res = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
self.assert3xx(res, reverse('lookup.transaction_summary',
|
||||
args=[self.uuid]))
|
||||
|
||||
@mock.patch('mkt.lookup.views.client')
|
||||
def test_403_reg_user(self, solitude):
|
||||
solitude.api.bango.refund.post.return_value = (
|
||||
{'status': STATUS_PENDING})
|
||||
solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
|
||||
solitude.get.return_value = self.refund_tx_ret()
|
||||
|
||||
self.login(self.user)
|
||||
res = self.client.post(self.url, {'refund_reason': 'text'})
|
||||
eq_(res.status_code, 403)
|
||||
|
|
|
@ -36,6 +36,6 @@ urlpatterns = patterns('',
|
|||
url(r'^app_search\.json$', views.app_search,
|
||||
name='lookup.app_search'),
|
||||
(r'^app/(?P<addon_id>[^/]+)/', include(app_patterns)),
|
||||
(r'^transaction/(?P<uuid>[^/]+)/', include(transaction_patterns)),
|
||||
(r'^transaction/(?P<tx_uuid>[^/]+)/', include(transaction_patterns)),
|
||||
(r'^user/(?P<user_id>[^/]+)/', include(user_patterns)),
|
||||
)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from datetime import datetime, timedelta
|
||||
import hashlib
|
||||
import uuid
|
||||
|
||||
from django.db import connection
|
||||
from django.db.models import Sum, Count, Q
|
||||
|
@ -23,8 +25,8 @@ from devhub.models import ActivityLog
|
|||
from elasticutils.contrib.django import S
|
||||
from lib.pay_server import client
|
||||
from market.models import AddonPaymentData, Refund
|
||||
from mkt.constants.payments import (STATUS_COMPLETED, STATUS_FAILED,
|
||||
STATUS_PENDING, PROVIDERS)
|
||||
from mkt.constants.payments import (COMPLETED, FAILED, PENDING,
|
||||
REFUND_STATUSES)
|
||||
from mkt.account.utils import purchase_list
|
||||
from mkt.lookup.forms import TransactionRefundForm, TransactionSearchForm
|
||||
from mkt.lookup.tasks import (email_buyer_refund_approved,
|
||||
|
@ -82,8 +84,8 @@ def user_summary(request, user_id):
|
|||
|
||||
@login_required
|
||||
@permission_required('Transaction', 'View')
|
||||
def transaction_summary(request, uuid):
|
||||
tx_data = _transaction_summary(uuid)
|
||||
def transaction_summary(request, tx_uuid):
|
||||
tx_data = _transaction_summary(tx_uuid)
|
||||
if not tx_data:
|
||||
raise Http404
|
||||
|
||||
|
@ -91,88 +93,105 @@ def transaction_summary(request, uuid):
|
|||
tx_refund_form = TransactionRefundForm()
|
||||
|
||||
return jingo.render(request, 'lookup/transaction_summary.html',
|
||||
dict({'uuid': uuid, 'tx_form': tx_form,
|
||||
dict({'uuid': tx_uuid, 'tx_form': tx_form,
|
||||
'tx_refund_form': tx_refund_form}.items() +
|
||||
tx_data.items()))
|
||||
|
||||
|
||||
def _transaction_summary(uuid):
|
||||
def _transaction_summary(tx_uuid):
|
||||
"""Get transaction details from Solitude API."""
|
||||
contrib = get_object_or_404(Contribution, uuid=uuid)
|
||||
contrib = get_object_or_404(Contribution, uuid=tx_uuid)
|
||||
refund_contribs = contrib.get_refund_contribs()
|
||||
refund_contrib = refund_contribs[0] if refund_contribs.exists() else None
|
||||
|
||||
# If the transaction is pending, we haven't assigned a transaction
|
||||
# uuid to the form yet.
|
||||
solitude = {}
|
||||
if contrib.transaction_id:
|
||||
# We'll probably be pulling more from this later.
|
||||
solitude = (client.api.generic.transaction
|
||||
.get_object_or_404(uuid=contrib.transaction_id))
|
||||
# Get refund status.
|
||||
refund_status = None
|
||||
if refund_contrib and refund_contrib.refund.status == amo.REFUND_PENDING:
|
||||
try:
|
||||
refund_status = REFUND_STATUSES[client.api.bango.refund.status.get(
|
||||
data={'uuid': refund_contrib.transaction_id})['status']]
|
||||
except HttpServerError:
|
||||
refund_status = _('Currently unable to retrieve refund status.')
|
||||
|
||||
return {
|
||||
# There won't be a provider on pending refunds.
|
||||
'provider': PROVIDERS.get(solitude.get('provider', ''), 'None'),
|
||||
# Solitude data.
|
||||
'refund_status': refund_status,
|
||||
|
||||
# Zamboni data.
|
||||
'app': contrib.addon,
|
||||
'contrib': contrib,
|
||||
'related': contrib.related,
|
||||
'type': amo.CONTRIB_TYPES.get(contrib.type, _('Incomplete')),
|
||||
# Whitelist what is refundable.
|
||||
'is_refundable': (contrib.type == amo.CONTRIB_PURCHASE
|
||||
and not contrib.is_refunded()),
|
||||
'related': contrib.related,
|
||||
'is_refundable': ((contrib.type == amo.CONTRIB_PURCHASE)
|
||||
and not refund_contrib),
|
||||
}
|
||||
|
||||
|
||||
@post_required
|
||||
@login_required
|
||||
@permission_required('Transaction', 'Refund')
|
||||
def transaction_refund(request, uuid):
|
||||
contrib = get_object_or_404(Contribution, uuid=uuid,
|
||||
def transaction_refund(request, tx_uuid):
|
||||
contrib = get_object_or_404(Contribution, uuid=tx_uuid,
|
||||
type=amo.CONTRIB_PURCHASE)
|
||||
if contrib.has_refund():
|
||||
refund_contribs = contrib.get_refund_contribs()
|
||||
refund_contrib = refund_contribs[0] if refund_contribs.exists() else None
|
||||
|
||||
if refund_contrib:
|
||||
messages.error(request, _('A refund has already been processed.'))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[uuid]))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))
|
||||
|
||||
form = TransactionRefundForm(request.POST)
|
||||
if not form.is_valid():
|
||||
messages.error(request, str(form.errors))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[uuid]))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))
|
||||
|
||||
try:
|
||||
res = client.api.bango.refund.post({'uuid': contrib.transaction_id})
|
||||
except (HttpClientError, HttpServerError):
|
||||
# Either doing something not supposed to or Solitude had an issue.
|
||||
log.exception('Refund error: %s' % uuid)
|
||||
log.exception('Refund error: %s' % tx_uuid)
|
||||
messages.error(
|
||||
request,
|
||||
_('You cannot make a refund request for this transaction.'))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[uuid]))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))
|
||||
|
||||
if res['status'] == STATUS_PENDING:
|
||||
if res['status'] in [PENDING, COMPLETED]:
|
||||
# Create refund Contribution by cloning the payment Contribution.
|
||||
refund_contrib = Contribution.objects.get(id=contrib.id)
|
||||
refund_contrib.id = None
|
||||
refund_contrib.save()
|
||||
refund_contrib.update(
|
||||
type=amo.CONTRIB_REFUND, related=contrib,
|
||||
uuid=hashlib.md5(str(uuid.uuid4())).hexdigest(),
|
||||
amount=-refund_contrib.amount if refund_contrib.amount else None,
|
||||
transaction_id=client.get(res['transaction'])['uuid'])
|
||||
|
||||
if res['status'] == PENDING:
|
||||
# Create pending Refund.
|
||||
contrib.enqueue_refund(
|
||||
status=amo.REFUND_PENDING,
|
||||
refund_reason=form.cleaned_data['refund_reason'],
|
||||
user=request.amo_user)
|
||||
log.info('Refund pending: %s' % uuid)
|
||||
refund_contrib.enqueue_refund(
|
||||
amo.REFUND_PENDING, request.amo_user,
|
||||
refund_reason=form.cleaned_data['refund_reason'])
|
||||
log.info('Refund pending: %s' % tx_uuid)
|
||||
email_buyer_refund_pending(contrib)
|
||||
messages.success(
|
||||
request, _('Refund for this transaction now pending.'))
|
||||
elif res['status'] == STATUS_COMPLETED:
|
||||
elif res['status'] == COMPLETED:
|
||||
# Create approved Refund.
|
||||
contrib.enqueue_refund(
|
||||
status=amo.REFUND_APPROVED,
|
||||
refund_reason=form.cleaned_data['refund_reason'],
|
||||
user=request.amo_user)
|
||||
log.info('Refund approved: %s' % uuid)
|
||||
refund_contrib.enqueue_refund(
|
||||
amo.REFUND_APPROVED, request.amo_user,
|
||||
refund_reason=form.cleaned_data['refund_reason'])
|
||||
log.info('Refund approved: %s' % tx_uuid)
|
||||
email_buyer_refund_approved(contrib)
|
||||
messages.success(
|
||||
request, _('Refund for this transaction successfully approved.'))
|
||||
elif res['status'] == STATUS_FAILED:
|
||||
elif res['status'] == FAILED:
|
||||
# Bango no like.
|
||||
log.error('Refund failed: %s' % uuid)
|
||||
log.error('Refund failed: %s' % tx_uuid)
|
||||
messages.error(
|
||||
request, _('Refund request for this transaction failed.'))
|
||||
|
||||
return redirect(reverse('lookup.transaction_summary', args=[uuid]))
|
||||
return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
Загрузка…
Ссылка в новой задаче