def post(self, request, *args, **kwargs): form = PrepareWebAppForm(request.DATA) if not form.is_valid(): return Response(form.errors, status=status.HTTP_400_BAD_REQUEST) app = form.cleaned_data['app'] region = getattr(request, 'REGION', None) if region and region.id not in app.get_price_region_ids(): log.info('Region {0} is not in {1}' .format(region.id, app.get_price_region_ids())) return Response('Payments are limited and flag not enabled', status=status.HTTP_403_FORBIDDEN) if app.is_premium() and app.has_purchased(request._request.amo_user): log.info('Already purchased: {0}'.format(app.pk)) return Response({'reason': u'Already purchased app.'}, status=status.HTTP_409_CONFLICT) app_pay_cef.log(request._request, 'Preparing JWT', 'preparing_jwt', 'Preparing JWT for: {0}'.format(app.pk), severity=3) token = get_product_jwt( WebAppProduct(app), client_data=ClientData.get_or_create(request._request), lang=request._request.LANG, region=request._request.REGION, source=request._request.REQUEST.get('src', ''), user=request._request.amo_user, ) return Response(token, status=status.HTTP_201_CREATED)
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) acct = addon.app_payment_account.payment_account seller_uuid = acct.solitude_seller.uuid data = {'amount': str(amount), 'price_point': addon.premium.price.pk, 'id': addon.pk, 'app_name': unicode(addon.name), 'app_description': unicode(addon.summary), 'postback_url': absolutify(reverse('webpay.postback')), 'chargeback_url': absolutify(reverse('webpay.chargeback')), 'seller': addon.pk, 'product_data': urlencode({'contrib_uuid': uuid_, 'seller_uuid': seller_uuid, 'addon_id': addon.pk}), 'typ': 'tu.com/payments/inapp/v1', 'aud': 'tu.com', 'memo': contrib_for} jwt_ = prepare_webpay_pay(data) log.debug('Preparing webpay JWT for addon %s: %s' % (addon, jwt_)) return {'webpayJWT': jwt_, 'contribStatusURL': reverse('webpay.pay_status', args=[addon.app_slug, uuid_])}
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) prices = [{'currency': cur, 'country': 'XX', # This is unused but required! 'amount': str(tier.price)} for cur, tier in addon.premium.price.currencies()] data = {'amount': str(amount), 'prices': prices, 'currency': currency, 'app_name': unicode(addon.name), 'app_description': unicode(addon.description), 'postback_url': absolutify(reverse('webpay.postback')), 'chargeback_url': absolutify(reverse('webpay.chargeback')), 'seller': addon.pk, 'product_data': urlencode({'contrib_uuid': uuid_, 'addon_id': addon.pk}), 'typ': 'tu.com/payments/inapp/v1', 'aud': 'tu.com', 'memo': contrib_for} return {'webpayJWT': prepare_webpay_pay(data), 'contribStatusURL': reverse('webpay.pay_status', args=[addon.app_slug, uuid_])}
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) data = {'amount': str(amount), 'currency': currency, 'app_name': unicode(addon.name), 'app_description': unicode(addon.description), 'postback_url': absolutify(reverse('bluevia.postback')), 'chargeback_url': absolutify(reverse('bluevia.chargeback')), 'seller': addon.pk, 'product_data': urlencode({'contrib_uuid': uuid_, 'addon_id': addon.pk}), 'typ': 'tu.com/payments/inapp/v1', 'aud': 'tu.com', 'memo': contrib_for} if waffle.flag_is_active(request, 'solitude-payments'): bluevia_jwt = client.prepare_bluevia_pay(data) else: bluevia_jwt = prepare_bluevia_pay(data) return {'blueviaJWT': bluevia_jwt, 'contribStatusURL': reverse('bluevia.pay_status', args=[addon.app_slug, uuid_])}
def post(self, request, *args, **kwargs): form = PrepareInAppForm(request.DATA) if not form.is_valid(): app_pay_cef.log( request._request, 'Preparing InApp JWT Failed', 'preparing_inapp_jwt_failed', 'Preparing InApp JWT Failed error: {0}'.format(form.errors), severity=3 ) return Response(form.errors, status=status.HTTP_400_BAD_REQUEST) inapp = form.cleaned_data['inapp'] app_pay_cef.log( request._request, 'Preparing InApp JWT', 'preparing_inapp_jwt', 'Preparing InApp JWT for: {0}'.format(inapp.pk), severity=3 ) token = get_product_jwt( InAppProduct(inapp), client_data=ClientData.get_or_create(request._request), lang=request._request.LANG, source=request._request.REQUEST.get('src', ''), ) return Response(token, status=status.HTTP_201_CREATED)
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) prices = [{'currency': cur, 'amount': str(tier.price)} for cur, tier in addon.premium.price.currencies()] data = {'amount': str(amount), 'prices': prices, 'currency': currency, 'app_name': unicode(addon.name), 'app_description': unicode(addon.description), 'postback_url': absolutify(reverse('bluevia.postback')), 'chargeback_url': absolutify(reverse('bluevia.chargeback')), 'seller': addon.pk, 'product_data': urlencode({'contrib_uuid': uuid_, 'addon_id': addon.pk}), 'typ': 'tu.com/payments/inapp/v1', 'aud': 'tu.com', 'memo': contrib_for} return {'blueviaJWT': prepare_bluevia_pay(data), 'contribStatusURL': reverse('bluevia.pay_status', args=[addon.app_slug, uuid_])}
def _prepare_pay(request, addon): """Prepare a JWT to pass into navigator.pay()""" if addon.is_premium() and addon.has_purchased(request.amo_user): log.info('Already purchased: %d' % addon.pk) raise AlreadyPurchased amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) # Until atob() supports encoded HTML we are stripping all tags. # See bug 831524 app_description = bleach.clean(unicode(addon.description), strip=True, tags=[]) acct = addon.app_payment_account.payment_account seller_uuid = acct.solitude_seller.uuid application_size = addon.current_version.all_files[0].size issued_at = calendar.timegm(time.gmtime()) icons = {} for size in amo.ADDON_ICON_SIZES: icons[str(size)] = absolutify(addon.get_icon_url(size)) req = { 'iss': settings.APP_PURCHASE_KEY, 'typ': settings.APP_PURCHASE_TYP, 'aud': settings.APP_PURCHASE_AUD, 'iat': issued_at, 'exp': issued_at + 3600, # expires in 1 hour 'request': { 'name': unicode(addon.name), 'description': app_description, 'pricePoint': addon.premium.price.name, 'id': make_ext_id(addon.pk), 'postbackURL': absolutify(reverse('webpay.postback')), 'chargebackURL': absolutify(reverse('webpay.chargeback')), 'productData': urlencode({'contrib_uuid': uuid_, 'seller_uuid': seller_uuid, 'addon_id': addon.pk, 'application_size': application_size}), 'icons': icons, } } jwt_ = sign_webpay_jwt(req) log.debug('Preparing webpay JWT for addon %s: %s' % (addon, jwt_)) app_pay_cef.log(request, 'Preparing JWT', 'preparing_jwt', 'Preparing JWT for: %s' % (addon.pk), severity=3) if request.API: url = reverse('webpay-status', kwargs={'uuid': uuid_}) else: url = reverse('webpay.pay_status', args=[addon.app_slug, uuid_]) return {'webpayJWT': jwt_, 'contribStatusURL': url}
def prepare_pay(request, addon): if addon.is_premium() and addon.has_purchased(request.amo_user): log.info('Already purchased: %d' % addon.pk) raise AlreadyPurchased app_pay_cef.log(request, 'Preparing JWT', 'preparing_jwt', 'Preparing JWT for: %s' % (addon.pk), severity=3) return get_product_jwt( WebAppProduct(addon), user=request.amo_user, region=request.REGION, source=request.REQUEST.get('src', ''), lang=request.LANG, client_data=ClientData.get_or_create(request), )
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) # Until atob() supports encoded HTML we are stripping all tags. # See bug 831524 app_summary = bleach.clean(unicode(addon.summary), strip=True, tags=[]) acct = addon.app_payment_account.payment_account seller_uuid = acct.solitude_seller.uuid issued_at = calendar.timegm(time.gmtime()) req = { 'iss': settings.APP_PURCHASE_KEY, 'typ': settings.APP_PURCHASE_TYP, 'aud': settings.APP_PURCHASE_AUD, 'iat': issued_at, 'exp': issued_at + 3600, # expires in 1 hour 'request': { 'name': unicode(addon.name), 'description': app_summary, 'pricePoint': addon.premium.price.pk, 'id': make_ext_id(addon.pk), 'postbackURL': absolutify(reverse('webpay.postback')), 'chargebackURL': absolutify(reverse('webpay.chargeback')), 'productData': urlencode({'contrib_uuid': uuid_, 'seller_uuid': seller_uuid, 'addon_id': addon.pk}), } } jwt_ = sign_webpay_jwt(req) log.debug('Preparing webpay JWT for addon %s: %s' % (addon, jwt_)) app_pay_cef.log(request, 'Preparing JWT', 'preparing_jwt', 'Preparing JWT for: %s' % (addon.pk), severity=3) return {'webpayJWT': jwt_, 'contribStatusURL': reverse('webpay.pay_status', args=[addon.app_slug, uuid_])}
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug("Storing contrib for uuid: %s" % uuid_) Contribution.objects.create( addon_id=addon.id, amount=amount, source=request.REQUEST.get("src", ""), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request), ) # Until atob() supports encoded HTML we are stripping all tags. # See bug 831524 app_summary = bleach.clean(unicode(addon.summary), strip=True, tags=[]) acct = addon.app_payment_account.payment_account seller_uuid = acct.solitude_seller.uuid issued_at = calendar.timegm(time.gmtime()) req = { "iss": settings.APP_PURCHASE_KEY, "typ": settings.APP_PURCHASE_TYP, "aud": settings.APP_PURCHASE_AUD, "iat": issued_at, "exp": issued_at + 3600, # expires in 1 hour "request": { "name": unicode(addon.name), "description": app_summary, "pricePoint": addon.premium.price.pk, "id": make_ext_id(addon.pk), "postbackURL": absolutify(reverse("webpay.postback")), "chargebackURL": absolutify(reverse("webpay.chargeback")), "productData": urlencode({"contrib_uuid": uuid_, "seller_uuid": seller_uuid, "addon_id": addon.pk}), }, } jwt_ = sign_webpay_jwt(req) log.debug("Preparing webpay JWT for addon %s: %s" % (addon, jwt_)) app_pay_cef.log(request, "Preparing JWT", "preparing_jwt", "Preparing JWT for: %s" % (addon.pk), severity=3) return {"webpayJWT": jwt_, "contribStatusURL": reverse("webpay.pay_status", args=[addon.app_slug, uuid_])}
def test_get_or_create(self): download_source = DownloadSource.objects.create(name='mkt-home') device_type = 'desktop' user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0)' client = RequestFactory() request = client.post('/somewhere', data={'src': download_source.name, 'device_type': device_type, 'is_chromeless': False}, **{'HTTP_USER_AGENT': user_agent}) cli = ClientData.get_or_create(request) eq_(cli.download_source, download_source) eq_(cli.device_type, device_type) eq_(cli.user_agent, user_agent) eq_(cli.is_chromeless, False) eq_(cli.language, 'en-us') eq_(cli.region, None)
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) # Until atob() supports encoded HTML we are stripping all tags. # See bug 831524 app_summary = bleach.clean(unicode(addon.summary), strip=True, tags=[]) acct = addon.app_payment_account.payment_account seller_uuid = acct.solitude_seller.uuid issued_at = calendar.timegm(time.gmtime()) req = { 'iss': settings.APP_PURCHASE_KEY, 'typ': settings.APP_PURCHASE_TYP, 'aud': settings.APP_PURCHASE_AUD, 'iat': issued_at, 'exp': issued_at + 3600, # expires in 1 hour 'request': { 'name': unicode(addon.name), 'description': app_summary, 'pricePoint': addon.premium.price.pk, 'id': make_ext_id(addon.pk), 'postbackURL': absolutify(reverse('webpay.postback')), 'chargebackURL': absolutify(reverse('webpay.chargeback')), 'productData': urlencode({'contrib_uuid': uuid_, 'seller_uuid': seller_uuid, 'addon_id': addon.pk}), } } jwt_ = sign_webpay_jwt(req) log.debug('Preparing webpay JWT for addon %s: %s' % (addon, jwt_)) return {'webpayJWT': jwt_, 'contribStatusURL': reverse('webpay.pay_status', args=[addon.app_slug, uuid_])}
def test_get_or_create(self): download_source = DownloadSource.objects.create(name='mkt-home') device_type = 'desktop' user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0)' client = RequestFactory() request = client.post('/somewhere', data={ 'src': download_source.name, 'device_type': device_type, 'is_chromeless': False }, **{'HTTP_USER_AGENT': user_agent}) cli = ClientData.get_or_create(request) eq_(cli.download_source, download_source) eq_(cli.device_type, device_type) eq_(cli.user_agent, user_agent) eq_(cli.is_chromeless, False) eq_(cli.language, 'en-us') eq_(cli.region, None)
def debug(request, addon): if not settings.DEBUG: raise http.Http404 data = { 'urls': {'es': '%s/apps/webapp/%s' % (settings.ES_URLS[0], addon.pk)}, 'pay_request': '' } if addon.is_premium(): data['pay_request'] = get_product_jwt( WebAppProduct(addon), user=request.amo_user, region=request.REGION, source=request.REQUEST.get('src', ''), lang=request.LANG, client_data=ClientData.get_or_create(request), )['webpayJWT'] return render(request, 'developers/debug.html', {'app': addon, 'data': data})
def prepare_pay(request, addon): """Prepare a BlueVia JWT to pass into navigator.pay()""" amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) data = {'amount': amount, 'currency': currency, 'app_name': unicode(addon.name), 'app_description': unicode(addon.description), 'postback_url': absolutify(reverse('bluevia.postback')), 'chargeback_url': absolutify(reverse('bluevia.chargeback')), 'seller': addon, 'contrib_uuid': uuid_, 'memo': contrib_for} return {'bluevia_jwt': client.prepare_bluevia_pay(data), 'contrib_uuid': uuid_}
def debug(request, addon): if not settings.DEBUG: raise http.Http404 data = { 'urls': { 'es': '%s/apps/webapp/%s' % (settings.ES_URLS[0], addon.pk) }, 'pay_request': '' } if addon.is_premium(): data['pay_request'] = get_product_jwt( WebAppProduct(addon), user=request.amo_user, region=request.REGION, source=request.REQUEST.get('src', ''), lang=request.LANG, client_data=ClientData.get_or_create(request), )['webpayJWT'] return render(request, 'developers/debug.html', { 'app': addon, 'data': data })
def purchase(request, addon): amount, currency, uuid_, contrib_for = start_purchase(request, addon) if not amount: # We won't write a contribution row for this because there # will not be a valid Paypal transaction. But we have to write the # Purchase row, something that writing to the contribution normally # does for us. AddonPurchase.objects.safer_get_or_create(addon=addon, user=request.amo_user) return http.HttpResponse(json.dumps({ 'url': '', 'paykey': '', 'error': '', 'status': 'COMPLETED' }), content_type='application/json') paykey, status, error = '', '', '' # TODO(solitude): remove this, pre-approval and currency will be # stored in solitude. preapproval = None if (not waffle.flag_is_active(request, 'solitude-payments') and request.amo_user): preapproval = request.amo_user.get_preapproval() # User the users default currency. if currency == 'USD' and preapproval and preapproval.currency: currency = preapproval.currency if waffle.flag_is_active(request, 'solitude-payments'): # Now call the client. result = {} try: result = client.pay({ 'amount': amount, 'currency': currency, 'buyer': request.amo_user, 'seller': addon, 'memo': contrib_for }) except client.Error as error: # Note that by assigning this to error, it will go into the return # value for the json. General solitude errors will then be # reported back to the user. paypal.paypal_log_cef(request, addon, uuid_, 'PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error('Error getting paykey: %s' % addon.pk, exc_info=True) # TODO(solitude): just use the dictionary when solitude is live. paykey = result.get('pay_key', '') status = result.get('status', '') uuid_ = result.get('uuid', '') else: # TODO(solitude): remove this when solitude goes live. try: paykey, status = paypal.get_paykey( dict(amount=amount, chains=settings.PAYPAL_CHAINS, currency=currency, email=addon.paypal_id, ip=request.META.get('REMOTE_ADDR'), memo=contrib_for, pattern='purchase.done', preapproval=preapproval, qs={'realurl': request.POST.get('realurl')}, slug=addon.app_slug, uuid=uuid_)) except paypal.PaypalError as error: paypal.paypal_log_cef(request, addon, uuid_, 'PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error('Error getting paykey, purchase of addon: %s' % addon.pk, exc_info=True) if paykey: # TODO(solitude): at some point we'll have to see what to do with # contributions. download_source = request.REQUEST.get('src', '') contrib = Contribution(addon_id=addon.id, amount=amount, source=download_source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) log.debug('Storing contrib for uuid: %s' % uuid_) # If this was a pre-approval, it's completed already, we'll # double check this with PayPal, just to be sure nothing went wrong. if status == 'COMPLETED': paypal.paypal_log_cef(request, addon, uuid_, 'Purchase', 'PURCHASE', 'A user purchased using pre-approval') log.debug('Status completed for uuid: %s' % uuid_) if waffle.flag_is_active(request, 'solitude-payments'): result = client.post_pay_check(data={'pay_key': paykey}) if result['status'] == 'COMPLETED': contrib.type = amo.CONTRIB_PURCHASE else: log.error('Check purchase failed on uuid: %s' % uuid_) status = 'NOT-COMPLETED' else: #TODO(solitude): remove this when solitude goes live. if paypal.check_purchase(paykey) == 'COMPLETED': log.debug('Check purchase completed for uuid: %s' % uuid_) contrib.type = amo.CONTRIB_PURCHASE else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error('Check purchase failed on uuid: %s' % uuid_) status = 'NOT-COMPLETED' contrib.save() else: log.error('No paykey present for uuid: %s' % uuid_) log.debug('Got paykey for addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey) if request.POST.get('result_type') == 'json' or request.is_ajax(): return http.HttpResponse(json.dumps({ 'url': url, 'paykey': paykey, 'error': str(error), 'status': status }), content_type='application/json') # This is the non-Ajax fallback. if status != 'COMPLETED': return http.HttpResponseRedirect(url) messages.success(request, _('Purchase complete')) return http.HttpResponseRedirect(addon.get_detail_url())
def purchase(request, addon): amount, currency, uuid_, contrib_for = start_purchase(request, addon) if not amount: # We won't write a contribution row for this because there # will not be a valid Paypal transaction. But we have to write the # Purchase row, something that writing to the contribution normally # does for us. AddonPurchase.objects.safer_get_or_create(addon=addon, user=request.amo_user) return http.HttpResponse(json.dumps({'url': '', 'paykey': '', 'error': '', 'status': 'COMPLETED'}), content_type='application/json') paykey, status, error = '', '', '' # TODO(solitude): remove this, pre-approval and currency will be # stored in solitude. preapproval = None if (not waffle.flag_is_active(request, 'solitude-payments') and request.amo_user): preapproval = request.amo_user.get_preapproval() # User the users default currency. if currency == 'USD' and preapproval and preapproval.currency: currency = preapproval.currency if waffle.flag_is_active(request, 'solitude-payments'): # Now call the client. result = {} try: result = client.pay({'amount': amount, 'currency': currency, 'buyer': request.amo_user, 'seller': addon, 'memo': contrib_for}) except client.Error as error: # Note that by assigning this to error, it will go into the return # value for the json. General solitude errors will then be # reported back to the user. paypal.paypal_log_cef(request, addon, uuid_, 'PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error('Error getting paykey: %s' % addon.pk, exc_info=True) # TODO(solitude): just use the dictionary when solitude is live. paykey = result.get('pay_key', '') status = result.get('status', '') uuid_ = result.get('uuid', '') else: # TODO(solitude): remove this when solitude goes live. try: paykey, status = paypal.get_paykey(dict( amount=amount, chains=settings.PAYPAL_CHAINS, currency=currency, email=addon.paypal_id, ip=request.META.get('REMOTE_ADDR'), memo=contrib_for, pattern='purchase.done', preapproval=preapproval, qs={'realurl': request.POST.get('realurl')}, slug=addon.app_slug, uuid=uuid_ )) except paypal.PaypalError as error: paypal.paypal_log_cef(request, addon, uuid_, 'PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error('Error getting paykey, purchase of addon: %s' % addon.pk, exc_info=True) if paykey: # TODO(solitude): at some point we'll have to see what to do with # contributions. download_source = request.REQUEST.get('src', '') contrib = Contribution(addon_id=addon.id, amount=amount, source=download_source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) log.debug('Storing contrib for uuid: %s' % uuid_) # If this was a pre-approval, it's completed already, we'll # double check this with PayPal, just to be sure nothing went wrong. if status == 'COMPLETED': paypal.paypal_log_cef(request, addon, uuid_, 'Purchase', 'PURCHASE', 'A user purchased using pre-approval') log.debug('Status completed for uuid: %s' % uuid_) if waffle.flag_is_active(request, 'solitude-payments'): result = client.post_pay_check(data={'pay_key': paykey}) if result['status'] == 'COMPLETED': contrib.type = amo.CONTRIB_PURCHASE else: log.error('Check purchase failed on uuid: %s' % uuid_) status = 'NOT-COMPLETED' else: #TODO(solitude): remove this when solitude goes live. if paypal.check_purchase(paykey) == 'COMPLETED': log.debug('Check purchase completed for uuid: %s' % uuid_) contrib.type = amo.CONTRIB_PURCHASE else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error('Check purchase failed on uuid: %s' % uuid_) status = 'NOT-COMPLETED' contrib.save() else: log.error('No paykey present for uuid: %s' % uuid_) log.debug('Got paykey for addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey) if request.POST.get('result_type') == 'json' or request.is_ajax(): return http.HttpResponse(json.dumps({'url': url, 'paykey': paykey, 'error': str(error), 'status': status}), content_type='application/json') # This is the non-Ajax fallback. if status != 'COMPLETED': return http.HttpResponseRedirect(url) messages.success(request, _('Purchase complete')) return http.HttpResponseRedirect(addon.get_detail_url())
def _prepare_pay(request, addon): """Prepare a JWT to pass into navigator.pay()""" if addon.is_premium() and addon.has_purchased(request.amo_user): log.info('Already purchased: %d' % addon.pk) raise AlreadyPurchased amount, currency, uuid_, contrib_for = start_purchase(request, addon) log.debug('Storing contrib for uuid: %s' % uuid_) Contribution.objects.create(addon_id=addon.id, amount=amount, source=request.REQUEST.get('src', ''), source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=None, user=request.amo_user, price_tier=addon.premium.price, client_data=ClientData.get_or_create(request)) # Until atob() supports encoded HTML we are stripping all tags. # See bug 831524 app_description = bleach.clean(unicode(addon.description), strip=True, tags=[]) acct = addon.app_payment_account.payment_account seller_uuid = acct.solitude_seller.uuid application_size = addon.current_version.all_files[0].size issued_at = calendar.timegm(time.gmtime()) icons = {} for size in amo.ADDON_ICON_SIZES: icons[str(size)] = absolutify(addon.get_icon_url(size)) req = { 'iss': settings.APP_PURCHASE_KEY, 'typ': settings.APP_PURCHASE_TYP, 'aud': settings.APP_PURCHASE_AUD, 'iat': issued_at, 'exp': issued_at + 3600, # expires in 1 hour 'request': { 'name': unicode(addon.name), 'description': app_description, 'pricePoint': addon.premium.price.name, 'id': make_ext_id(addon.pk), 'postbackURL': absolutify(reverse('webpay.postback')), 'chargebackURL': absolutify(reverse('webpay.chargeback')), 'productData': urlencode({ 'contrib_uuid': uuid_, 'seller_uuid': seller_uuid, 'addon_id': addon.pk, 'application_size': application_size }), 'icons': icons, } } jwt_ = sign_webpay_jwt(req) log.debug('Preparing webpay JWT for addon %s: %s' % (addon, jwt_)) app_pay_cef.log(request, 'Preparing JWT', 'preparing_jwt', 'Preparing JWT for: %s' % (addon.pk), severity=3) if request.API: url = reverse('api_dispatch_detail', kwargs={ 'resource_name': 'status', 'api_name': 'webpay', 'uuid': uuid_ }) else: url = reverse('webpay.pay_status', args=[addon.app_slug, uuid_]) return {'webpayJWT': jwt_, 'contribStatusURL': url}