Skip to content
This repository has been archived by the owner on Jan 25, 2018. It is now read-only.

Commit

Permalink
Merge pull request #293 from kumar303/errors
Browse files Browse the repository at this point in the history
Bubble up error codes to devs (bug 846818)
  • Loading branch information
kumar303 committed Sep 19, 2013
2 parents 5f6620c + e2f7c9e commit 8b2e199
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 53 deletions.
6 changes: 3 additions & 3 deletions media/js/pay/cancel.js
Expand Up @@ -17,13 +17,13 @@ require(['cli'], function(cli) {
window.setTimeout(callPayFailure, 500);
} else {
console.log('[pay] payment failed, closing window');
// This string is used to determine the message on the marketplace
// change it at your peril.
paymentFailed('cancelled');
paymentFailed(cli.bodyData.errorCode || cli.bodyData.cancelCode);
}
}

if (cli.bodyData.cancelflow === true) {
// Automatically cancel (close the window) for cases like when a user
// clicks the cancel button on Bango's hosted flow.
callPayFailure();
}

Expand Down
24 changes: 9 additions & 15 deletions webpay/bango/views.py
Expand Up @@ -7,18 +7,14 @@

from django_paranoia.decorators import require_GET, require_POST
from slumber.exceptions import HttpClientError
from tower import ugettext_lazy

from lib.solitude.api import client
from webpay.bango.auth import basic, NoHeader, WrongHeader
from webpay.base import dev_messages as msg
from webpay.base.logger import getLogger
from webpay.base.utils import _error
from webpay.base.utils import system_error
from webpay.pay import tasks

user_error_msg = ugettext_lazy(
u'There was an internal error processing the payment. '
u'Try again or contact Mozilla if it persists.')
log = getLogger('w.bango')
RECORDED_OK = 'RECORDED_OK'

Expand Down Expand Up @@ -104,12 +100,11 @@ def success(request):
if request.GET.get('ResponseCode') != 'OK':
log.error('in success(): Invalid Bango response code: {code}'
.format(code=request.GET.get('ResponseCode')))
return _error(request, external=user_error_msg,
code=msg.BAD_BANGO_CODE)
return system_error(request, code=msg.BAD_BANGO_CODE)

result = _record(request)
if result is not RECORDED_OK:
return _error(request, external=user_error_msg, code=result)
return system_error(request, code=result)

# Signature verification was successful; fulfill the payment.
tasks.payment_notify.delay(request.GET.get('MerchantTransactionId'))
Expand All @@ -124,23 +119,22 @@ def error(request):
if request.GET.get('ResponseCode') == 'OK':
log.error('in error(): Invalid Bango response code: {code}'
.format(code=request.GET.get('ResponseCode')))
return _error(request, external=user_error_msg,
code=msg.BAD_BANGO_CODE)
return system_error(request, code=msg.BAD_BANGO_CODE)

result = _record(request)
if result is not RECORDED_OK:
return _error(request, external=user_error_msg, code=result)
return system_error(request, code=result)

if request.GET.get('ResponseCode') == 'CANCEL':
return render(request, 'bango/cancel.html')
return render(request, 'bango/cancel.html',
{'error_code': msg.USER_CANCELLED})

if request.GET.get('ResponseCode') == 'NOT_SUPPORTED':
# This is a credit card or price point / region mismatch.
# In theory users should never trigger this.
return _error(request, external=user_error_msg,
code=msg.UNSUPPORTED_PAY)
return system_error(request, code=msg.UNSUPPORTED_PAY)

return _error(request, external=user_error_msg, code=msg.BANGO_ERROR)
return system_error(request, code=msg.BANGO_ERROR)


@csrf_exempt
Expand Down
5 changes: 4 additions & 1 deletion webpay/base/context_processors.py
@@ -1,8 +1,11 @@
from django.conf import settings
from webpay.pay.constants import PIN_ERROR_CODES

from . import dev_messages


def defaults(request):
return {'session': request.session,
'STATIC_URL': settings.STATIC_URL,
'PIN_ERROR_CODES': PIN_ERROR_CODES}
'PIN_ERROR_CODES': PIN_ERROR_CODES,
'dev_messages': dev_messages}
12 changes: 12 additions & 0 deletions webpay/base/dev_messages.py
Expand Up @@ -9,6 +9,7 @@
BAD_JWT_ISSUER = 'BAD_JWT_ISSUER'
BAD_ICON_KEY = 'BAD_ICON_KEY'
BAD_PRICE_POINT = 'BAD_PRICE_POINT'
BAD_REQUEST = 'BAD_REQUEST'
BAD_SIM_RESULT = 'BAD_SIM_RESULT'
BANGO_ERROR = 'BANGO_ERROR'
EXPIRED_JWT = 'EXPIRED_JWT'
Expand All @@ -20,9 +21,14 @@
NO_DEFAULT_LOC = 'NO_DEFAULT_LOC'
NO_SIM_REASON = 'NO_SIM_REASON'
NOTICE_ERROR = 'NOTICE_ERROR'
PAY_DISABLED = 'PAY_DISABLED'
UNSUPPORTED_PAY = 'UNSUPPORTED_PAY'
SIM_DISABLED = 'SIM_DISABLED'
SIM_ONLY_KEY = 'SIM_ONLY_KEY'
TRANS_ENDED = 'TRANS_ENDED'
# This string is used to determine the message on Marketplace;
# change it at your peril.
USER_CANCELLED = 'USER_CANCELLED'

SHORT_FIELDS = ('chargebackURL',
'defaultLocale',
Expand Down Expand Up @@ -71,6 +77,7 @@ def _build_legend():
# localized.
BAD_JWT_ISSUER: _('No one has been registered for this JWT issuer.'),
BAD_PRICE_POINT: _('The price point is unknown or invalid.'),
BAD_REQUEST: _('The request to begin payment was invalid.'),
BAD_SIM_RESULT:
_('The requested payment simulation result is not supported.'),
BANGO_ERROR:
Expand Down Expand Up @@ -103,12 +110,17 @@ def _build_legend():
"the key '{0}'.").format('reason'),
NOTICE_ERROR: _('The notification service responded with an '
'error while verifying the payment notice'),
PAY_DISABLED: _('Payments are temporarily disabled'),
SIM_DISABLED: _('Payment simulations are disabled at this time.'),
SIM_ONLY_KEY:
_('This payment key can only be used to simulate purchases.'),
TRANS_ENDED:
_('The purchase cannot be completed because the current '
'transaction has already ended.'),
UNSUPPORTED_PAY:
_('The payment method or price point is not supported for this '
'region or operator.'),
USER_CANCELLED: _('The user cancelled the payment.'),
}

# Define all short field too long errors.
Expand Down
2 changes: 2 additions & 0 deletions webpay/base/templates/base.html
Expand Up @@ -34,6 +34,8 @@
data-cancelled-msg="{{ _('Payment cancelled.') }}"
data-logout-timeout="{{ settings.LOGOUT_TIMEOUT }}"
data-settings="{{ settings.JS_SETTINGS|json }}"
data-error-code="{{ error_code }}"
data-cancel-code="{{ dev_messages.USER_CANCELLED }}"
>
<section class="pay">
<div id="content">
Expand Down
4 changes: 2 additions & 2 deletions webpay/base/templates/error.html
Expand Up @@ -8,8 +8,8 @@
<div class="msg">
<h2>{{ _('Error') }}</h2>
<p>{{ error }}</p>
{% if code %}
<p>{{ code }}</p>
{% if error_code %}
<p>{{ error_code }}</p>
{% endif %}
</div>
<footer>
Expand Down
33 changes: 13 additions & 20 deletions webpay/base/utils.py
Expand Up @@ -32,26 +32,19 @@ def log_cef_meta(msg, meta, full_path, **kw):
_log_cef(msg, severity, meta, **cef_kw)


def _error(request, msg='', exception=None, display=False,
code=None, external=None):
if not external:
# This is the default catch-all user error message.
external = _('There was an error setting up the payment. '
def app_error(request, **kw):
user_message = _('There was an error setting up the payment. '
'Try again or contact the app if it persists.')
if msg:
log.error('Error handler: %s' % msg)

# Checking code here is is a short-term hack until all things send
# error codes.
if not code and (settings.VERBOSE_LOGGING or display):
if exception:
msg = u'%s: %s' % (exception.__class__.__name__, exception)
if msg:
external = msg
if not exception and not msg:
# This should never happen but it might be happening.
# See bug 864306
log.error('No detailed error message/exception in handler')
return custom_error(request, user_message, **kw)


def system_error(request, **kw):
user_message = _('There was an internal error processing the '
'payment. Try again or contact Mozilla if it '
'persists.')
return custom_error(request, user_message, **kw)


def custom_error(request, user_message, code=None, status=400):
return render(request, 'error.html',
{'error': external, 'code': code}, status=400)
{'error': user_message, 'error_code': code}, status=status)
6 changes: 4 additions & 2 deletions webpay/pay/tests/test_views.py
Expand Up @@ -11,6 +11,7 @@
import mock
from mozpay.exc import RequestExpired
from nose.tools import eq_, ok_
from pyquery import PyQuery as pq

from lib.marketplace.api import UnknownPricePoint
from lib.solitude import constants
Expand Down Expand Up @@ -180,6 +181,8 @@ def test_missing_url(self):
def test_non_https_url(self):
res = self.get(self.request())
self.assertContains(res, msg.MALFORMED_URL, status_code=400)
doc = pq(res.content)
eq_(doc('body').attr('data-error-code'), msg.MALFORMED_URL)

@mock.patch.object(settings, 'ALLOWED_CALLBACK_SCHEMES', ['https'])
def test_non_https_url_ok_for_simulation(self):
Expand Down Expand Up @@ -403,8 +406,7 @@ def wait_ended_transaction(self, get_transaction, status):
'uid_pay': 123,
}
res = self.client.get(self.wait)
self.assertContains(res,
'Transaction has already ended.',
self.assertContains(res, msg.TRANS_ENDED,
status_code=400)

def test_wait_ended_transaction(self, get_transaction):
Expand Down
22 changes: 12 additions & 10 deletions webpay/pay/views.py
Expand Up @@ -18,7 +18,7 @@
from webpay.base import dev_messages as msg
from webpay.base.decorators import json_view
from webpay.base.logger import getLogger
from webpay.base.utils import _error
from webpay.base.utils import app_error, custom_error, system_error
from webpay.pin.forms import VerifyPinForm
from webpay.pin.utils import check_pin_status

Expand All @@ -39,15 +39,17 @@ def process_pay_req(request):
codes = []
for erlist in form.errors.values():
codes.extend(erlist)
if len(codes) > 1:
# This will probably break something, like maybe paymentFailed().
log.error('multiple error codes: {codes}'.format(codes=codes))
codes = ', '.join(codes)
return _error(request, code=codes)
return app_error(request, code=codes)

if settings.ONLY_SIMULATIONS and not form.is_simulation:
# Real payments are currently disabled.
# Only simulated payments are allowed.
return render(request, 'error.html',
{'error': _('Payments are temporarily disabled.')},
status=503)
return custom_error(request, _('Payments are temporarily disabled.'),
code=msg.PAY_DISABLED, status=503)

exc = er = None
try:
Expand All @@ -68,7 +70,7 @@ def process_pay_req(request):

if exc:
log.exception('calling verify_jwt')
return _error(request, code=er)
return app_error(request, code=er)

icon_urls = []
if pay_req['request'].get('icons'):
Expand All @@ -83,14 +85,14 @@ def process_pay_req(request):
check_postbacks=False)
except ValueError, exc:
log.exception('invalid URLs')
return _error(request, code=msg.MALFORMED_URL)
return app_error(request, code=msg.MALFORMED_URL)

# Assert pricePoint is valid.
try:
marketplace.get_price(pay_req['request']['pricePoint'])
except UnknownPricePoint, exc:
log.exception('calling get price_price()')
return _error(request, code=msg.BAD_PRICE_POINT)
return app_error(request, code=msg.BAD_PRICE_POINT)

_trim_pay_request(pay_req)

Expand Down Expand Up @@ -127,7 +129,7 @@ def lobby(request):
if sess.get('trans_id'):
log.info('Attempted to restart non-existent transaction {0}'
.format(sess.get('trans_id')))
return _error(request, msg='req is required')
return system_error(request, code=msg.BAD_REQUEST)

pin_form = VerifyPinForm()

Expand Down Expand Up @@ -260,7 +262,7 @@ def wait_to_start(request):
statsd.incr('purchase.payment_time.failure')
log.exception('Attempt to restart finished transaction {0} '
'with status {1}'.format(trans_id, trans['status']))
return _error(request, msg=_('Transaction has already ended.'))
return system_error(request, code=msg.TRANS_ENDED)

if trans['status'] == constants.STATUS_PENDING:
statsd.incr('purchase.payment_time.success')
Expand Down

0 comments on commit 8b2e199

Please sign in to comment.