Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:mozilla/webpay

  • Loading branch information...
commit e20d3965ae54aeeba16a7cd33997c7bec7062882 2 parents 572f3d3 + c86f93d
@potch potch authored
View
57 lib/solitude/api.py
@@ -57,6 +57,18 @@ def safe_run(self, command, *args, **kwargs):
return {'errors': res}
return res
+ def buyer_has_pin(self, uuid):
+ """Returns True if the existing buyer has a PIN.
+
+ :param uuid: String to identify the buyer by.
+ :rtype: boolean
+ """
+ res = self.safe_run(self.slumber.generic.buyer.get, **{'uuid': uuid})
+ if res['meta']['total_count'] == 0:
+ return False
+ else:
+ return res['objects'][0]['pin']
+
def create_buyer(self, uuid, pin=None):
"""Creates a buyer with an optional PIN in solitude.
@@ -138,20 +150,14 @@ def configure_product_for_billing(self, webpay_trans_id,
seller_product__external_id=product_id
)
if res['meta']['total_count'] == 0:
- # TODO(Kumar) create products on the fly. bug 820164
- raise NotImplementedError(
- 'this product does not exist and must be created')
-
- # Create the product on the fly.
- # This case exists for in-app purchases where the
- # seller is selling a new item for the first time.
-
+ bango_product_uri = self.create_product(product_id,
+ product_name, currency, amount, res['objects'][0])
else:
bango_product_uri = res['objects'][0]['resource_uri']
log.info('transaction %s: bango product: %s'
% (webpay_trans_id, bango_product_uri))
- res = self.slumber.bango('create-billing').post({
+ res = self.slumber.bango.billing.post({
'pageTitle': product_name,
'price_currency': currency,
'price_amount': str(amount),
@@ -162,6 +168,39 @@ def configure_product_for_billing(self, webpay_trans_id,
% (webpay_trans_id, bill_id))
return bill_id
+ def create_product(self, external_id, product_name, currency, amount, seller):
+ """
+ Creates a product and a Bango ID on the fly in solitude.
+ """
+ if not seller['bango']:
+ raise ValueError('No bango account set up for %s' %
+ seller['resource_pk'])
+
+ product = self.slumber.generic.product.post({
+ 'external_id': external_id,
+ 'seller': seller['bango']['seller']
+ })
+ bango = self.slumber.bango.product.post({
+ 'seller_bango': seller['bango']['resource_uri'],
+ 'seller_product': product['resource_uri'],
+ 'name': product_name,
+ 'categoryId': 1,
+ 'secret': 'n' # This is likely going to be removed.
+ })
+ self.slumber.bango.premium.post({
+ 'price': amount,
+ 'currencyIso': currency,
+ 'seller_product_bango': bango['resource_uri']
+ })
+
+ self.slumber.bango.rating.post({
+ 'rating': 'UNIVERSAL',
+ 'ratingScheme': 'GLOBAL',
+ 'seller_product_bango': bango['resource_uri']
+ })
+ return bango['resource_uri']
+
+
if getattr(settings, 'SOLITUDE_URL', False):
client = SolitudeAPI(settings.SOLITUDE_URL)
View
22 lib/solitude/tests.py
@@ -1,6 +1,7 @@
from django.conf import settings
from django.test import TestCase
+import mock
from nose.exc import SkipTest
from nose.tools import eq_
@@ -100,3 +101,24 @@ def test_verify_without_confirm_and_good_pin(self):
def test_verify_alpha_pin(self):
assert not client.verify_pin(self.uuid, 'lame')
+
+
+class CreateBangoTest(TestCase):
+ uuid = 'some:pin'
+
+ def test_create_no_bango(self):
+ with self.assertRaises(ValueError):
+ client.create_product('ext:id', None, None, None,
+ {'bango': None, 'resource_pk': 'foo'})
+
+ @mock.patch('lib.solitude.api.client.slumber')
+ def test_create_bango(self, slumber):
+ # Temporary mocking. Remove when this is mocked properly.
+ slumber.bango.generic.post.return_value = {'product': 'some:uri'}
+ slumber.bango.product.post.return_value = {'resource_uri': 'some:uri'}
+ assert client.create_product('ext:id', 'product:name', 'CAD', 1,
+ {'bango': {'seller': 's', 'resource_uri': 'r'},
+ 'resource_pk': 'foo'})
+ assert slumber.generic.product.post.called
+ assert slumber.bango.rating.post.called
+ assert slumber.bango.premium.post.called
View
13 media/js/pay/pay.js
@@ -32,11 +32,14 @@ $(function() {
$.post(verifyUrl, {assertion: assertion})
.success(function(data, textStatus, jqXHR) {
console.log('login success');
- $('.message').hide();
- $('#enter-pin').fadeIn();
- console.log($('#pin [name="pin"]')[0]);
- $('#pin [name="pin"]')[0].focus();
- console.log('foo');
+ if (!data.has_pin) {
+ window.location = data.pin_create;
+ } else {
+ $('.message').hide();
+ $('#enter-pin').fadeIn();
+ console.log($('#pin [name="pin"]')[0]);
+ $('#pin [name="pin"]')[0].focus();
+ }
})
.error(function() {
console.log('login error');
View
32 media/js/pay/wait.js
@@ -0,0 +1,32 @@
+$(function() {
+ "use strict";
+
+ var startUrl;
+ var timeout;
+
+ if ($('body').data('waitflow')) {
+ startUrl = $('body').data('trans-start-url');
+ poll();
+ }
+
+ function poll() {
+ $.get(startUrl)
+ .success(function(data, textStatus, jqXHR) {
+ if (data.url) {
+ if (timeout) {
+ window.clearTimeout(timeout);
+ }
+ window.location = data.url;
+ } else {
+ // The transaction is pending or it failed.
+ // TODO(Kumar) check for failed transactions here.
+ console.log('transaction state: ' + data.state)
+ timeout = window.setTimeout(poll, 1000);
+ }
+ })
+ .error(function() {
+ console.log('error checking transaction');
+ });
+ }
+
+});
View
1  requirements/prod.txt
@@ -1,3 +1,4 @@
+amqplib==1.0.2
Babel==0.9.6
Django==1.4.3
anyjson==0.3.3
View
2  scripts/update/commander_settings.py.dist
@@ -10,6 +10,8 @@ UPDATE_REF = "origin/master"
UPDATE_VENDOR_REF = "origin/master"
WEB_HOSTGROUP="addons-dev"
+CELERY_HOSTGROUP = ''
+CELERY_SERVICE = ''
SSH_KEY="/root/keys/addons-updater"
View
10 scripts/update/update.py
@@ -41,6 +41,15 @@ def update_code(ctx, ref='origin/master'):
ctx.local("git submodule foreach 'git submodule sync --quiet'")
ctx.local("git submodule foreach 'git submodule update --init --recursive'")
+
+@task
+@hostgroups(settings.CELERY_HOSTGROUP, remote_kwargs={'ssh_key': settings.SSH_KEY})
+def update_celery(ctx):
+ ctx.remote(settings.REMOTE_UPDATE_SCRIPT)
+ if getattr(settings, 'CELERY_SERVICE', False):
+ ctx.remote("/sbin/service %s restart" % settings.CELERY_SERVICE)
+
+
@task
def compress_assets(ctx, arg=''):
with ctx.lcd(settings.SRC_DIR):
@@ -77,6 +86,7 @@ def deploy_app(ctx):
def deploy(ctx):
checkin_changes()
deploy_app()
+ update_celery()
@task
def pre_update(ctx, ref=settings.UPDATE_REF):
View
1  settings_test.py
@@ -13,3 +13,4 @@
# We want to act as if we are hitting Solitude APIs even though it will
# be intercepted by mock objects.
FAKE_PAYMENTS = False
+FAKE_PAY_COMPLETE = FAKE_PAYMENTS
View
45 webpay/auth/tests/test.py
@@ -1,3 +1,5 @@
+import json
+
from django import test
from django.conf import settings
from django.core.urlresolvers import reverse
@@ -7,6 +9,10 @@
import mock
from nose.tools import eq_
+from webpay.auth.utils import client
+from webpay.auth import views as auth_views
+
+
good_assertion = {u'status': u'okay',
u'audience': u'http://some.site',
u'expires': 1351707833170,
@@ -48,6 +54,7 @@ def unverify(self):
del self.client.cookies[settings.SESSION_COOKIE_NAME]
+@mock.patch.object(client, 'buyer_has_pin', lambda *args: False)
@mock.patch.object(settings, 'DOMAIN', 'web.pay')
class TestAuth(SessionTestCase):
@@ -76,3 +83,41 @@ def test_session_cleaned(self, verify_assertion):
verify_assertion.return_value = False
eq_(self.client.post(self.url, {'assertion': 'bad'}).status_code, 400)
eq_(self.client.session.get('uuid'), None)
+
+
+@mock.patch.object(auth_views, 'verify_assertion', lambda *a: good_assertion)
+class TestBuyerHasPin(SessionTestCase):
+
+ def do_auth(self):
+ res = self.client.post(reverse('auth.verify'), {'assertion': 'good'})
+ eq_(res.status_code, 200, res)
+ return json.loads(res.content)
+
+ @mock.patch('lib.solitude.api.client.slumber')
+ def test_no_user(self, slumber):
+ slumber.generic.buyer.get.return_value = {
+ 'meta': {'total_count': 0}
+ }
+ data = self.do_auth()
+ eq_(self.client.session.get('uuid_has_pin'), False)
+ eq_(data['has_pin'], False)
+
+ @mock.patch('lib.solitude.api.client.slumber')
+ def test_user_no_pin(self, slumber):
+ slumber.generic.buyer.get.return_value = {
+ 'meta': {'total_count': 1},
+ 'objects': [{'pin': False}]
+ }
+ self.do_auth()
+ eq_(self.client.session.get('uuid_has_pin'), False)
+
+ @mock.patch('lib.solitude.api.client.slumber')
+ def test_user_with_pin(self, slumber):
+ slumber.generic.buyer.get.return_value = {
+ 'meta': {'total_count': 1},
+ 'objects': [{'pin': True}]
+ }
+ data = self.do_auth()
+ eq_(self.client.session.get('uuid_has_pin'), True)
+ eq_(data['has_pin'], True)
+ eq_(data['pin_create'], reverse('pin.create'))
View
11 webpay/auth/utils.py
@@ -2,6 +2,8 @@
from django.conf import settings
+from lib.solitude.api import client
+
def get_uuid(email):
"""
@@ -26,5 +28,12 @@ def get_user(request):
raise KeyError('Attempt to access user without it being set, '
'did you use the user_verified decorator?')
+
def set_user(request, email):
- request.session['uuid'] = get_uuid(email)
+ uuid = get_uuid(email)
+ request.session['uuid'] = uuid
+ set_user_has_pin(request, client.buyer_has_pin(uuid))
+
+
+def set_user_has_pin(request, has_pin):
+ request.session['uuid_has_pin'] = has_pin
View
6 webpay/auth/views.py
@@ -1,4 +1,5 @@
from django import http
+from django.core.urlresolvers import reverse
from django.views.decorators.http import require_POST
import commonware.log
@@ -6,6 +7,7 @@
from django_browserid.forms import BrowserIDForm
from session_csrf import anonymous_csrf_exempt
+from webpay.base.decorators import json_view
from utils import set_user
log = commonware.log.getLogger('w.auth')
@@ -13,6 +15,7 @@
@anonymous_csrf_exempt
@require_POST
+@json_view
def verify(request):
form = BrowserIDForm(data=request.POST)
if form.is_valid():
@@ -22,7 +25,8 @@ def verify(request):
if result:
log.info('assertion ok: %s' % result)
set_user(request, result['email'])
- return http.HttpResponse('ok')
+ return {'has_pin': request.session['uuid_has_pin'],
+ 'pin_create': reverse('pin.create')}
request.session.clear()
return http.HttpResponseBadRequest()
View
23 webpay/base/decorators.py
@@ -0,0 +1,23 @@
+import functools
+import json
+
+from django import http
+
+
+def json_view(f=None, status_code=200):
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kw):
+ response = func(*args, **kw)
+ if isinstance(response, http.HttpResponse):
+ return response
+ else:
+ return http.HttpResponse(
+ json.dumps(response),
+ content_type='application/json',
+ status=status_code)
+ return wrapper
+ if f:
+ return decorator(f)
+ else:
+ return decorator
View
14 webpay/pay/templates/pay/fake-bango-url.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block content %}
+ <div class="message">
+ <p>
+ Pretend this is a Bango payment screen for configuration ID {{ bill_config_id }}
+ </p>
+ <p>
+ <form action="{{ url('pay.complete') }}" method="post">
+ <input type="submit" value="Complete the payment">
+ </form>
+ </p>
+ </div>
+{% endblock %}
View
12 webpay/pay/templates/pay/wait-to-start.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% block body_attrs -%}
+ data-waitflow="true"
+ data-trans-start-url="{{ url('pay.trans_start_url') }}"
+{%- endblock %}
+
+{% block content %}
+ <div id="begin" class="message">
+ Waiting for payment to complete ...
+ </div>
+{% endblock %}
View
7 webpay/pay/tests/test_tasks.py
@@ -284,15 +284,12 @@ def update(self, **kw):
self.trans = Transaction.objects.get(pk=self.trans.pk)
def set_billing_id(self, slumber, num):
- # Set up a call to /bango/billing-config
- billing = mock.Mock()
- slumber.bango.return_value = billing
- billing.post.return_value = {
+ slumber.bango.billing.post.return_value = {
'resource_pk': '3333',
'billingConfigurationId': num,
'responseMessage': 'Success',
'responseCode': 'OK',
- 'resource_uri': '/bango/create-billing/3333/'
+ 'resource_uri': '/bango/billing/3333/'
}
@raises(tasks.TransactionOutOfSync)
View
70 webpay/pay/tests/test_views.py
@@ -11,7 +11,8 @@
from webpay.pay.forms import VerifyForm
from webpay.pay.models import (Issuer, ISSUER_ACTIVE, ISSUER_INACTIVE,
- Transaction, TRANS_STATE_PENDING)
+ Transaction, TRANS_STATE_PENDING,
+ TRANS_STATE_READY)
from webpay.pay.samples import JWTtester
sample = os.path.join(os.path.dirname(__file__), 'sample.key')
@@ -131,8 +132,8 @@ def test_debug(self):
eq_(res.status_code, 400)
# Output should show exception message.
self.assertContains(res,
- 'InvalidJWT: Signature verification failed',
- status_code=400)
+ 'InvalidJWT: Signature verification failed',
+ status_code=400)
def test_not_debug(self):
with self.settings(VERBOSE_LOGGING=False):
@@ -202,3 +203,66 @@ def test_valid_purchase(self):
assert form.is_valid()
eq_(form.key, settings.KEY)
eq_(form.secret, settings.SECRET)
+
+
+class TestWaitToStart(Base):
+
+ def setUp(self):
+ super(TestWaitToStart, self).setUp()
+ self.wait = reverse('pay.wait_to_start')
+ self.start = reverse('pay.trans_start_url')
+ self.trans = Transaction.create(
+ state=TRANS_STATE_PENDING,
+ issuer_key='some app',
+ amount='0.99',
+ currency='BRL',
+ name='Virtual Eagle',
+ description='you know, just, an eagle',
+ json_request='{}')
+
+ # Set up a session for this client because the session code in
+ # Django's docs isn't working.
+ engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
+ self.session = engine.SessionStore()
+ self.session.create()
+ session_key = self.session.session_key
+
+ # Log in.
+ self.session['uuid'] = 'verified-user'
+ # Start a payment.
+ self.session['trans_id'] = self.trans.pk
+ self.session.save()
+
+ self.client = test.Client()
+ self.client.cookies[settings.SESSION_COOKIE_NAME] = session_key
+
+ def update(self, **kw):
+ Transaction.objects.filter(pk=self.trans.pk).update(**kw)
+ self.trans = Transaction.objects.get(pk=self.trans.pk)
+
+ @mock.patch.object(settings, 'BANGO_PAY_URL', 'http://bango/pay?bcid=%s')
+ def test_redirect_when_ready(self):
+ self.update(state=TRANS_STATE_READY, bango_config_id=123)
+ res = self.client.get(self.wait)
+ eq_(res['Location'], settings.BANGO_PAY_URL % 123)
+
+ @mock.patch.object(settings, 'BANGO_PAY_URL', 'http://bango/pay?bcid=%s')
+ def test_start_ready(self):
+ self.update(state=TRANS_STATE_READY, bango_config_id=123)
+ res = self.client.get(self.start)
+ eq_(res.status_code, 200, res.content)
+ data = json.loads(res.content)
+ eq_(data['url'], settings.BANGO_PAY_URL % 123)
+ eq_(data['state'], self.trans.state)
+
+ def test_start_not_ready(self):
+ res = self.client.get(self.start)
+ eq_(res.status_code, 200, res.content)
+ data = json.loads(res.content)
+ eq_(data['url'], None)
+ eq_(data['state'], self.trans.state)
+
+ def test_wait(self):
+ res = self.client.get(self.wait)
+ eq_(res.status_code, 200)
+ self.assertContains(res, 'Waiting')
View
6 webpay/pay/urls.py
@@ -6,7 +6,11 @@
url(r'^$', views.lobby, name='pay.lobby'),
url(r'^complete$', views.complete, name='pay.complete'),
url(r'^fakepay$', views.fakepay, name='pay.fakepay'),
+ # Be careful if you change this because it could be hard
+ # coded into settings. See BANGO_PAY_URL.
+ url(r'^fake-bango-url$', views.fake_bango_url,
+ name='pay.fake_bango_url'),
url(r'^wait_to_start$', views.wait_to_start, name='pay.wait_to_start'),
- url(r'^trans_start_url/([\d]+)$', views.trans_start_url,
+ url(r'^trans_start_url$', views.trans_start_url,
name='pay.trans_start_url'),
)
View
44 webpay/pay/views.py
@@ -13,11 +13,12 @@
from tower import ugettext as _
from webpay.auth.decorators import user_verified
+from webpay.base.decorators import json_view
from webpay.pin.forms import VerifyPinForm
from . import tasks
from .forms import VerifyForm
-from .models import (Issuer, Transaction, TRANS_STATE_PENDING,
- TRANS_STATE_COMPLETED)
+from .models import (Issuer, Transaction, TRANS_STATE_COMPLETED,
+ TRANS_STATE_PENDING, TRANS_STATE_READY)
log = commonware.log.getLogger('w.pay')
@@ -91,8 +92,9 @@ def complete(request):
if 'trans_id' not in request.session:
return http.HttpResponseBadRequest()
# Simulate app purchase!
- # TODO(Kumar): fixme
- if settings.FAKE_PAYMENTS:
+ # TODO(Kumar): fixme. See bug 795143
+ if settings.FAKE_PAY_COMPLETE:
+ log.warning('Completing fake transaction without checking signature')
trans = Transaction.objects.get(pk=request.session['trans_id'])
trans.state = TRANS_STATE_COMPLETED
trans.save()
@@ -110,6 +112,13 @@ def fakepay(request):
@user_verified
@require_GET
+def fake_bango_url(request):
+ return render(request, 'pay/fake-bango-url.html',
+ {'bill_config_id': request.GET['bcid']})
+
+
+@user_verified
+@require_GET
def wait_to_start(request):
"""
Wait until the transaction is in a ready state.
@@ -118,24 +127,23 @@ def wait_to_start(request):
When ready, redirect to the Bango payment URL using
the generated billing configuration ID.
"""
- # TODO(Kumar) Create view to poll transaction state and redirect
- # to bango URL based on billing config ID. Bug 820192.
- return http.HttpResponse('view not yet implemented')
- #return render(request, 'pay/wait_to_start.html')
+ trans = Transaction.objects.get(pk=request.session['trans_id'])
+ if trans.state == TRANS_STATE_READY:
+ # The transaction is ready; no need to wait for it.
+ return http.HttpResponseRedirect(
+ settings.BANGO_PAY_URL % trans.bango_config_id)
+ return render(request, 'pay/wait-to-start.html')
@user_verified
+@json_view
@require_GET
-def trans_start_url(request, trans_id):
+def trans_start_url(request):
"""
JSON handler to get the Bango payment URL to start a transaction.
"""
- # TODO(Kumar) Poll transaction for URL. Bug 820192.
- # TODO(Kumar) 403 if the transaction was not started by the logged in user.
- raise NotImplementedError('view does not exist')
- #trans = Transaction.objects.get(pk=trans_id)
- #data = {'url': None, 'state': trans.state}
- #if trans.state == TRANS_STATE_READY:
- # data['url'] = ('http://mozilla.test.bango.org/mozpayments/?bcid=%s'
- # % trans.bango_config_id)
- #return data
+ trans = Transaction.objects.get(pk=request.session['trans_id'])
+ data = {'url': None, 'state': trans.state}
+ if trans.state == TRANS_STATE_READY:
+ data['url'] = settings.BANGO_PAY_URL % trans.bango_config_id
+ return data
View
11 webpay/pin/templates/pin/confirm.html
@@ -1,15 +1,18 @@
{% extends "base.html" %}
{% block content %}
- <p>Confirm your PIN:</p>
+ <h2>Confirm your PIN:</h2>
<p>
<form id="pin" action="{{ url('pin.confirm') }}" method="post">
{{ csrf() }}
{{ form.non_field_errors() }}
- {{ form.pin }}
+ <div class="pinbox">
+ {{ form.pin }}
+ </div>
<footer>
- <a class="button" href="{{ url('pin.change') }}">Forgot PIN</a>
- <button type="submit">Continue</button></footer>
+ <a class="button" href="{{ url('pin.change') }}">Forgot PIN</a>
+ <button type="submit">Continue</button>
+ </footer>
</form>
</p>
View
23 webpay/pin/tests/test_views.py
@@ -1,6 +1,6 @@
from django.core.urlresolvers import reverse
-from mock import patch
+from mock import ANY, patch
from nose.tools import eq_
from lib.solitude.api import client
@@ -26,12 +26,15 @@ def test_unauth(self):
@patch('lib.solitude.api.client.create_buyer', auto_spec=True)
@patch('lib.solitude.api.client.change_pin', auto_spec=True)
+ @patch('webpay.pin.views.set_user_has_pin', auto_spec=True)
@patch.object(client, 'get_buyer', lambda x: {})
- def test_buyer_does_not_exist(self, change_pin, create_buyer):
+ def test_buyer_does_not_exist(self, set_user_has_pin, change_pin,
+ create_buyer):
res = self.client.post(self.url, data={'pin': '1234'})
assert create_buyer.called
assert not change_pin.called
- self.assertRedirects(res, reverse('pin.confirm'))
+ set_user_has_pin.assert_called_with(ANY, True)
+ assert res['Location'].endswith(reverse('pin.confirm'))
@patch('lib.solitude.api.client.create_buyer', auto_spec=True)
@patch('lib.solitude.api.client.change_pin', auto_spec=True)
@@ -40,7 +43,7 @@ def test_buyer_does_exist_with_no_pin(self, change_pin, create_buyer):
res = self.client.post(self.url, data={'pin': '1234'})
assert not create_buyer.called
assert change_pin.called
- self.assertRedirects(res, reverse('pin.confirm'))
+ assert res['Location'].endswith(reverse('pin.confirm'))
@patch('lib.solitude.api.client.create_buyer', auto_spec=True)
@patch('lib.solitude.api.client.change_pin', auto_spec=True)
@@ -61,7 +64,7 @@ def test_buyer_does_exist_with_pin(self, change_pin, create_buyer):
def test_buyer_does_exist_with_short_pin(self, create_buyer):
res = self.client.post(self.url, data={'pin': '123'})
assert not create_buyer.called
- form = res.context[0].get('form')
+ form = res.context['form']
eq_(form.errors.get('pin'),
[ERROR_STRINGS['PIN must be exactly 4 numbers long']])
@@ -73,7 +76,7 @@ def test_buyer_does_exist_with_short_pin(self, create_buyer):
def test_buyer_does_exist_with_alpha_pin(self, create_buyer):
res = self.client.post(self.url, data={'pin': '1234'})
assert not create_buyer.called
- form = res.context[0].get('form')
+ form = res.context['form']
eq_(form.errors.get('pin'),
[ERROR_STRINGS['PIN may only consists of numbers']])
@@ -88,7 +91,7 @@ def test_unauth(self):
@patch.object(client, 'verify_pin', lambda x, y: True)
def test_good_pin(self):
res = self.client.post(self.url, data={'pin': '1234'})
- self.assertRedirects(res, get_payment_url())
+ assert res['Location'].endswith(get_payment_url())
@patch.object(client, 'verify_pin', lambda x, y: False)
def test_bad_pin(self):
@@ -112,7 +115,7 @@ def test_unauth(self):
@patch.object(client, 'confirm_pin', lambda x, y: True)
def test_good_pin(self):
res = self.client.post(self.url, data={'pin': '1234'})
- self.assertRedirects(res, get_payment_url())
+ assert res['Location'].endswith(get_payment_url())
@patch.object(client, 'confirm_pin', lambda x, y: False)
def test_bad_pin(self):
@@ -150,7 +153,7 @@ def test_good_pin(self, change_pin):
def test_alpha_pin(self):
res = self.client.post(self.url, data={'old_pin': '1234',
'pin': '4321'})
- form = res.context[0].get('form')
+ form = res.context['form']
eq_(form.errors.get('pin'),
[ERROR_STRINGS['PIN may only consists of numbers']])
self.assertTemplateUsed(res, 'pin/change.html')
@@ -164,7 +167,7 @@ def test_alpha_pin(self):
def test_short_pin(self):
res = self.client.post(self.url, data={'old_pin': '1234',
'pin': '432'})
- form = res.context[0].get('form')
+ form = res.context['form']
eq_(form.errors.get('pin'),
[ERROR_STRINGS['PIN must be exactly 4 numbers long']])
self.assertTemplateUsed(res, 'pin/change.html')
View
3  webpay/pin/views.py
@@ -6,7 +6,7 @@
from lib.solitude.api import client
from webpay.auth.decorators import user_verified
-from webpay.auth.utils import get_user
+from webpay.auth.utils import get_user, set_user_has_pin
from webpay.pay import get_payment_url
from . import forms
@@ -24,6 +24,7 @@ def create(request):
else:
res = client.create_buyer(form.uuid, form.cleaned_data['pin'])
if form.handle_client_errors(res):
+ set_user_has_pin(request, True)
return http.HttpResponseRedirect(reverse('pin.confirm'))
return render(request, 'pin/create.html', {'form': form})
View
10 webpay/settings/base.py
@@ -36,6 +36,7 @@
'js/lib/format.js',
'js/css3.js',
'js/pay/pay.js',
+ 'js/pay/wait.js',
'js/pin/pin.js',
),
}
@@ -180,6 +181,11 @@
# for the Bango flow when True.
FAKE_PAYMENTS = True
+# Instead of relying on Bango's postback and signature verification
+# just pretend everything is ok and send post notifications to the app.
+# This is temporary and should go away in bug 795143
+FAKE_PAY_COMPLETE = FAKE_PAYMENTS
+
# Control which Persona server you use for logins.
# This is useful for switching to a development Persona server.
@@ -190,3 +196,7 @@
# Living on the bleeding B2G edge!
BROWSERID_VERIFICATION_URL = 'https://notoriousb2g.personatest.org/verify'
BROWSERID_JS_URL = 'https://notoriousb2g.personatest.org/include.js'
+
+# This is the URL for the bango payment screen.
+# It will receive one string substitution: the billing configuration ID.
+BANGO_PAY_URL = 'http://mozilla.test.bango.org/mozpayments/?bcid=%s'
View
4 webpay/settings/local.py-dist
@@ -116,3 +116,7 @@ SOLITUDE_URL = 'http://localhost:9000'
# If you want to test the pay flow without making a real Bango
# payment, set this to True.
#FAKE_PAYMENTS = True
+
+# Uncomment this to redirect to a fake Bango payments screen
+# when FAKE_PAYMENTS is False.
+#BANGO_PAY_URL = '/mozpay/fake-bango-url?bcid=%s'
Please sign in to comment.
Something went wrong with that request. Please try again.