allow refunds, just not instant ones for in-app (bug 746417)
This commit is contained in:
Родитель
d0b25d4021
Коммит
9509f4bebe
|
@ -340,15 +340,8 @@ class Contribution(amo.models.ModelBase):
|
||||||
def get_absolute_refund_url(self):
|
def get_absolute_refund_url(self):
|
||||||
return absolutify(self.get_refund_url())
|
return absolutify(self.get_refund_url())
|
||||||
|
|
||||||
def can_we_refund(self):
|
|
||||||
"""
|
|
||||||
We can only refund a purchase.
|
|
||||||
We cannot refund in-app payments; PayPal can however.
|
|
||||||
"""
|
|
||||||
return self.type == amo.CONTRIB_PURCHASE
|
|
||||||
|
|
||||||
def is_instant_refund(self):
|
def is_instant_refund(self):
|
||||||
if not self.can_we_refund():
|
if self.type != amo.CONTRIB_PURCHASE:
|
||||||
return False
|
return False
|
||||||
limit = datetime.timedelta(seconds=settings.PAYPAL_REFUND_INSTANT)
|
limit = datetime.timedelta(seconds=settings.PAYPAL_REFUND_INSTANT)
|
||||||
return datetime.datetime.now() < (self.created + limit)
|
return datetime.datetime.now() < (self.created + limit)
|
||||||
|
|
|
@ -57,20 +57,17 @@ class TestContributionModel(amo.tests.TestCase):
|
||||||
|
|
||||||
def test_instant_refund(self):
|
def test_instant_refund(self):
|
||||||
self.con.update(created=datetime.now())
|
self.con.update(created=datetime.now())
|
||||||
assert self.con.can_we_refund(), 'Refund on purchases'
|
|
||||||
assert self.con.is_instant_refund(), 'Refund should be instant'
|
assert self.con.is_instant_refund(), 'Refund should be instant'
|
||||||
|
|
||||||
def test_not_instant_refund(self):
|
def test_not_instant_refund(self):
|
||||||
diff = timedelta(seconds=settings.PAYPAL_REFUND_INSTANT + 10)
|
diff = timedelta(seconds=settings.PAYPAL_REFUND_INSTANT + 10)
|
||||||
self.con.update(created=datetime.now() - diff)
|
self.con.update(created=datetime.now() - diff)
|
||||||
assert self.con.can_we_refund(), 'Refund on purchases'
|
|
||||||
assert not self.con.is_instant_refund(), "Refund shouldn't be instant"
|
assert not self.con.is_instant_refund(), "Refund shouldn't be instant"
|
||||||
|
|
||||||
|
|
||||||
def test_refund_inapp_instant(self):
|
def test_refund_inapp_instant(self):
|
||||||
for ctype in ('CONTRIB_INAPP', 'CONTRIB_INAPP_PENDING'):
|
for ctype in ('CONTRIB_INAPP', 'CONTRIB_INAPP_PENDING'):
|
||||||
self.con.update(created=datetime.now(), type=getattr(amo, ctype))
|
self.con.update(created=datetime.now(), type=getattr(amo, ctype))
|
||||||
assert not self.con.can_we_refund(), (
|
|
||||||
'No refund on %s inapp' % ctype)
|
|
||||||
assert not self.con.is_instant_refund(), (
|
assert not self.con.is_instant_refund(), (
|
||||||
'No refund on %s inapp' % ctype)
|
'No refund on %s inapp' % ctype)
|
||||||
|
|
||||||
|
@ -79,12 +76,9 @@ class TestContributionModel(amo.tests.TestCase):
|
||||||
for ctype in ('CONTRIB_INAPP', 'CONTRIB_INAPP_PENDING'):
|
for ctype in ('CONTRIB_INAPP', 'CONTRIB_INAPP_PENDING'):
|
||||||
self.con.update(created=datetime.now() - diff,
|
self.con.update(created=datetime.now() - diff,
|
||||||
type=getattr(amo, ctype))
|
type=getattr(amo, ctype))
|
||||||
assert not self.con.can_we_refund(), (
|
|
||||||
'No refund on %s inapp' % ctype)
|
|
||||||
assert not self.con.is_instant_refund(), (
|
assert not self.con.is_instant_refund(), (
|
||||||
'No refund on %s inapp' % ctype)
|
'No refund on %s inapp' % ctype)
|
||||||
|
|
||||||
|
|
||||||
class TestEmail(amo.tests.TestCase):
|
class TestEmail(amo.tests.TestCase):
|
||||||
fixtures = ['base/users', 'base/addon_3615']
|
fixtures = ['base/users', 'base/addon_3615']
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,24 @@
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{ csrf() }}
|
{{ csrf() }}
|
||||||
<p>
|
<p>
|
||||||
{% trans support_url=url('support', contribution.pk, 'author') %}
|
{% if contribution.type == amo.CONTRIB_INAPP %}
|
||||||
If you are unsatisfied with your purchase, you may request a
|
{% trans support_url=url('support', contribution.pk, 'author') %}
|
||||||
refund from the developer for up to 60 days. Requests within 30
|
If you are unsatisfied with your purchase, you may request a
|
||||||
minutes after purchase are automatically granted. If you are
|
refund from the developer for up to 60 days. If you are
|
||||||
having difficulty using the app, we encourage you to
|
having difficulty using the app, we encourage you to
|
||||||
<a href="{{ support_url }}">ask the developer for support</a> before
|
<a href="{{ support_url }}">ask the developer for support</a> before
|
||||||
requesting a refund.
|
requesting a refund.
|
||||||
{% endtrans %}
|
{% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
{% trans support_url=url('support', contribution.pk, 'author') %}
|
||||||
|
If you are unsatisfied with your purchase, you may request a
|
||||||
|
refund from the developer for up to 60 days. Requests within 30
|
||||||
|
minutes after purchase are automatically granted. If you are
|
||||||
|
having difficulty using the app, we encourage you to
|
||||||
|
<a href="{{ support_url }}">ask the developer for support</a> before
|
||||||
|
requesting a refund.
|
||||||
|
{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<p class="form-footer">
|
<p class="form-footer">
|
||||||
<button type="submit">{{ _('Continue') }}</button> {{ _('or') }}
|
<button type="submit">{{ _('Continue') }}</button> {{ _('or') }}
|
||||||
|
|
|
@ -8,12 +8,8 @@
|
||||||
<li><a href="{{ url('support', contribution.pk, 'resources') }}">
|
<li><a href="{{ url('support', contribution.pk, 'resources') }}">
|
||||||
{{ _('I have billing or payment concerns') }} »</a></li>
|
{{ _('I have billing or payment concerns') }} »</a></li>
|
||||||
{% if waffle.switch('allow-refund') %}
|
{% if waffle.switch('allow-refund') %}
|
||||||
{% if contribution.can_we_refund() %}
|
<li><a href="{{ url('support', contribution.pk, 'request') }}">
|
||||||
<li><a href="{{ url('support', contribution.pk, 'request') }}">
|
{{ _('I want to request a refund') }} »</a></li>
|
||||||
{{ _('I want to request a refund') }} »</a></li>
|
|
||||||
{% elif contribution.type == amo.CONTRIB_INAPP %}
|
|
||||||
<li>{{ _('In-app purchases cannot be refunded through the Marketplace.') }}</li>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{# No L10n or even `loc` on purpose. #}
|
{# No L10n or even `loc` on purpose. #}
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -31,13 +31,6 @@ class TestRequestSupport(PurchaseBase):
|
||||||
eq_(doc('#support-start').find('a').eq(0).attr('href'),
|
eq_(doc('#support-start').find('a').eq(0).attr('href'),
|
||||||
self.get_support_url('author'))
|
self.get_support_url('author'))
|
||||||
|
|
||||||
def test_start_no_inapp_refund(self):
|
|
||||||
self.con.update(type=amo.CONTRIB_INAPP)
|
|
||||||
r = self.client.get(self.get_support_url())
|
|
||||||
content = r.content.decode('utf-8')
|
|
||||||
eq_(pq(content)('#support-start').find('a').length, 3)
|
|
||||||
assert self.get_support_url('request') not in content
|
|
||||||
|
|
||||||
def test_support_page_external_link(self):
|
def test_support_page_external_link(self):
|
||||||
self.app.support_url = 'http://omg.org/yes'
|
self.app.support_url = 'http://omg.org/yes'
|
||||||
self.app.save()
|
self.app.save()
|
||||||
|
@ -94,10 +87,10 @@ class TestRequestSupport(PurchaseBase):
|
||||||
res = self.client.post(self.get_support_url('mozilla'), {'b': 'c'})
|
res = self.client.post(self.get_support_url('mozilla'), {'b': 'c'})
|
||||||
assert 'text' in res.context['form'].errors
|
assert 'text' in res.context['form'].errors
|
||||||
|
|
||||||
def test_no_request_inapp(self):
|
def test_request_inapp(self):
|
||||||
self.con.update(type=amo.CONTRIB_INAPP)
|
self.con.update(type=amo.CONTRIB_INAPP)
|
||||||
res = self.client.post(self.get_support_url('request'))
|
res = self.client.post(self.get_support_url('request'))
|
||||||
eq_(res.status_code, 403)
|
eq_(res.status_code, 302)
|
||||||
|
|
||||||
def test_refund_remove(self):
|
def test_refund_remove(self):
|
||||||
res = self.client.post(self.get_support_url('request'), {'remove': 1})
|
res = self.client.post(self.get_support_url('request'), {'remove': 1})
|
||||||
|
@ -127,11 +120,6 @@ class TestRequestSupport(PurchaseBase):
|
||||||
eq_(len(mail.outbox), 0)
|
eq_(len(mail.outbox), 0)
|
||||||
self.assertRedirects(res, reverse('account.purchases'), 302)
|
self.assertRedirects(res, reverse('account.purchases'), 302)
|
||||||
|
|
||||||
def test_no_reason_inapp(self):
|
|
||||||
self.con.update(type=amo.CONTRIB_INAPP)
|
|
||||||
res = self.client.post(self.get_support_url('reason'))
|
|
||||||
eq_(res.status_code, 403)
|
|
||||||
|
|
||||||
@mock.patch('stats.models.Contribution.is_instant_refund')
|
@mock.patch('stats.models.Contribution.is_instant_refund')
|
||||||
def test_request_mails(self, is_instant_refund):
|
def test_request_mails(self, is_instant_refund):
|
||||||
is_instant_refund.return_value = False
|
is_instant_refund.return_value = False
|
||||||
|
|
|
@ -99,9 +99,6 @@ def support_mozilla(request, contribution, wizard):
|
||||||
|
|
||||||
@waffle_switch('allow-refund')
|
@waffle_switch('allow-refund')
|
||||||
def refund_request(request, contribution, wizard):
|
def refund_request(request, contribution, wizard):
|
||||||
if not contribution.can_we_refund():
|
|
||||||
return http.HttpResponseForbidden()
|
|
||||||
|
|
||||||
addon = contribution.addon
|
addon = contribution.addon
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
return redirect('support', contribution.pk, 'reason')
|
return redirect('support', contribution.pk, 'reason')
|
||||||
|
@ -112,9 +109,6 @@ def refund_request(request, contribution, wizard):
|
||||||
|
|
||||||
@waffle_switch('allow-refund')
|
@waffle_switch('allow-refund')
|
||||||
def refund_reason(request, contribution, wizard):
|
def refund_reason(request, contribution, wizard):
|
||||||
if not contribution.can_we_refund():
|
|
||||||
return http.HttpResponseForbidden()
|
|
||||||
|
|
||||||
addon = contribution.addon
|
addon = contribution.addon
|
||||||
if not 'request' in wizard.get_progress():
|
if not 'request' in wizard.get_progress():
|
||||||
return redirect('support', contribution.pk, 'request')
|
return redirect('support', contribution.pk, 'request')
|
||||||
|
|
Загрузка…
Ссылка в новой задаче