def purchase_complete(request, addon, status): result = '' if status == 'complete': con = Contribution.objects.get(uuid=request.GET.get('uuid'), type=amo.CONTRIB_PENDING) log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) try: result = paypal.check_purchase(con.paykey) except: log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED': # Markup the contribution suitably con.type = amo.CONTRIB_PURCHASE con.uuid = None con.save() response = jingo.render(request, 'addons/paypal_result.html', {'addon': addon, 'realurl': request.GET.get('realurl', ''), # What the client claimed they did. 'status': status, # And what paypal thought of that claim. 'result': result}) response['x-frame-options'] = 'allow' return response
def purchase_complete(request, addon, status): result = '' if status == 'complete': uuid_ = request.GET.get('uuid') log.debug('Looking up contrib for uuid: %s' % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)) con = get_object_or_404(Contribution, lookup) log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) try: result = paypal.check_purchase(con.paykey) if result == 'ERROR': paypal.paypal_log_cef( request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase state returned error') raise except: paypal.paypal_log_cef( request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'There was an error checking purchase state') log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING: con.update(type=amo.CONTRIB_PURCHASE) context = { 'realurl': request.GET.get('realurl', ''), 'status': status, 'result': result } # For mobile, bounce back to the details page. if request.MOBILE: url = urlparams(shared_url('detail', addon), **context) return http.HttpResponseRedirect(url) context.update({'addon': addon}) response = jingo.render(request, 'addons/paypal_result.html', context) response['x-frame-options'] = 'allow' return response
def purchase_complete(request, addon, status): result = "" if status == "complete": uuid_ = request.GET.get("uuid") log.debug("Looking up contrib for uuid: %s" % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. lookup = Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE) con = get_object_or_404(Contribution, lookup) log.debug( "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10]) ) try: result = paypal.check_purchase(con.paykey) if result == "ERROR": paypal.paypal_log_cef( request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "Checking purchase state returned error" ) raise except: paypal.paypal_log_cef( request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "There was an error checking purchase state" ) log.error( "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True, ) result = "ERROR" status = "error" log.debug("Paypal returned: %s for paykey: %s" % (result, con.paykey[:10])) if result == "COMPLETED" and con.type == amo.CONTRIB_PENDING: con.update(type=amo.CONTRIB_PURCHASE) response = jingo.render( request, "addons/paypal_result.html", { "addon": addon, "realurl": request.GET.get("realurl", ""), # What the client claimed they did. "status": status, # And what paypal thought of that claim. "result": result, }, ) response["x-frame-options"] = "allow" return response
def purchase_complete(request, addon, status): result = '' if status == 'complete': uuid_ = request.GET.get('uuid') log.debug('Looking up contrib for uuid: %s' % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)) con = get_object_or_404(Contribution, lookup) log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) try: result = paypal.check_purchase(con.paykey) if result == 'ERROR': paypal.paypal_log_cef( request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase state returned error') raise except: paypal.paypal_log_cef( request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'There was an error checking purchase state') log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING: con.update(type=amo.CONTRIB_PURCHASE) response = jingo.render( request, 'addons/paypal_result.html', { 'addon': addon, 'realurl': request.GET.get('realurl', ''), # What the client claimed they did. 'status': status, # And what paypal thought of that claim. 'result': result }) response['x-frame-options'] = 'allow' return response
def purchase_complete(request, addon, status): result = "" if status == "complete": uuid_ = request.GET.get("uuid") log.debug("Looking up contrib for uuid: %s" % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. lookup = Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE) con = get_object_or_404(Contribution, lookup) log.debug( "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10]) ) try: result = paypal.check_purchase(con.paykey) if result == "ERROR": paypal.paypal_log_cef( request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "Checking purchase state returned error" ) raise except: paypal.paypal_log_cef( request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "There was an error checking purchase state" ) log.error( "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True, ) result = "ERROR" status = "error" log.debug("Paypal returned: %s for paykey: %s" % (result, con.paykey[:10])) if result == "COMPLETED" and con.type == amo.CONTRIB_PENDING: con.update(type=amo.CONTRIB_PURCHASE) context = {"realurl": request.GET.get("realurl", ""), "status": status, "result": result} # For mobile, bounce back to the details page. if request.MOBILE: url = urlparams(shared_url("detail", addon), **context) return redirect(url) context.update({"addon": addon}) response = jingo.render(request, "addons/paypal_result.html", context) response["x-frame-options"] = "allow" return response
def purchase_complete(request, addon, status): result = '' if status == 'complete': uuid_ = request.GET.get('uuid') log.debug('Looking up contrib for uuid: %s' % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)) con = get_object_or_404(Contribution, lookup) log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) try: result = paypal.check_purchase(con.paykey) if result == 'ERROR': paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase state returned error') raise except: paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'There was an error checking purchase state') log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING: con.update(type=amo.CONTRIB_PURCHASE) context = {'realurl': request.GET.get('realurl', ''), 'status': status, 'result': result} # For mobile, bounce back to the details page. if request.MOBILE: url = urlparams(shared_url('detail', addon), **context) return http.HttpResponseRedirect(url) context.update({'addon': addon}) response = jingo.render(request, 'addons/paypal_result.html', context) response['x-frame-options'] = 'allow' return response
def purchase_complete(request, addon, status): result = '' if status == 'complete': uuid_ = request.GET.get('uuid') log.debug('Looking up contrib for uuid: %s' % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)) con = get_object_or_404(Contribution, lookup) log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) try: result = paypal.check_purchase(con.paykey) if result == 'ERROR': paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase state returned error') raise except: paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'There was an error checking purchase state') log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING: con.update(type=amo.CONTRIB_PURCHASE) response = jingo.render(request, 'addons/paypal_result.html', {'addon': addon, 'realurl': request.GET.get('realurl', ''), # What the client claimed they did. 'status': status, # And what paypal thought of that claim. 'result': result}) response['x-frame-options'] = 'allow' return response
def purchase_complete(request, addon, status): result = "" if status == "complete": con = Contribution.objects.get(uuid=request.GET.get("uuid"), type=amo.CONTRIB_PENDING) log.debug( "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10]) ) try: result = paypal.check_purchase(con.paykey) except: log.error( "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True, ) result = "ERROR" status = "error" log.debug("Paypal returned: %s for paykey: %s" % (result, con.paykey[:10])) if result == "COMPLETED": # Markup the contribution suitably con.type = amo.CONTRIB_PURCHASE con.uuid = None con.save() response = jingo.render( request, "addons/paypal_result.html", { "addon": addon, "realurl": request.GET.get("realurl", ""), # What the client claimed they did. "status": status, # And what paypal thought of that claim. "result": result, }, ) response["x-frame-options"] = "allow" return response
def test_check_purchase_fails(self, opener): opener.return_value = StringIO(other_error) eq_(paypal.check_purchase("some-paykey"), False)
def test_check_purchase(self, opener): opener.return_value = StringIO(good_check_purchase) eq_(paypal.check_purchase("some-paykey"), "CREATED")
def _test_check_purchase_no_mock(self): # Remove _ and run if you'd like to try this unmocked. key = paypal.get_paykey(self.data) eq_(paypal.check_purchase(key), "CREATED")
log.debug('Status is 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': log.debug('Check in-app payment is completed for uuid: %s' % uuid_) contrib.type = amo.CONTRIB_INAPP else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error('Check in-app payment 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 in-app payment is completed for uuid: %s' % uuid_) contrib.type = amo.CONTRIB_INAPP else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error('Check in-app payment failed on uuid: %s' % uuid_) status = 'NOT-COMPLETED' contrib.save() payment = InappPayment.objects.create( config=pay_req['_config'], contribution=contrib,
def purchase(request, addon): log.debug("Starting purchase of addon: %s by user: %s" % (addon.pk, request.amo_user.pk)) amount = addon.premium.get_price() source = request.POST.get("source", "") uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() # l10n: {0} is the addon name contrib_for = _(u"Purchase of {0}").format(jinja2.escape(addon.name)) # Default is USD. amount, currency = addon.premium.get_price(), "USD" # If tier is specified, then let's look it up. form = PriceCurrencyForm(data=request.POST, price=addon.premium.price) if form.is_valid(): tier = form.get_tier() if tier: amount, currency = tier.price, tier.currency paykey, status, error = "", "", "" preapproval = None if waffle.flag_is_active(request, "allow-pre-auth") and request.amo_user: preapproval = request.amo_user.get_preapproval() try: pattern = "addons.purchase.finished" slug = addon.slug if addon.is_webapp(): pattern = "apps.purchase.finished" slug = addon.app_slug 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=pattern, preapproval=preapproval, qs={"realurl": request.POST.get("realurl")}, slug=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: contrib = Contribution( addon_id=addon.id, amount=amount, source=source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user, ) 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 is completed for uuid: %s" % uuid_) if paypal.check_purchase(paykey) == "COMPLETED": log.debug("Check purchase is 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 redirect(url) messages.success(request, _("Purchase complete")) return redirect(shared_url("addons.detail", addon))
def test_check_purchase_fails(self, opener): opener.return_value = StringIO(other_error) eq_(paypal.check_purchase('some-paykey'), False)
def _test_check_purchase_no_mock(self): # Remove _ and run if you'd like to try this unmocked. key = paypal.get_paykey(self.data) eq_(paypal.check_purchase(key), 'CREATED')
def purchase(request, addon): log.debug('Starting purchase of addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) amount = addon.premium.get_price() source = request.POST.get('source', '') uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() # L10n: {0} is the addon name. contrib_for = (_(u'Mozilla Marketplace purchase of {0}').format( addon.name)) # Default is USD. amount, currency = addon.premium.get_price(), 'USD' # If tier is specified, then let's look it up. if waffle.switch_is_active('currencies'): form = PriceCurrencyForm(data=request.POST, addon=addon) if form.is_valid(): tier = form.get_tier() if tier: amount, currency = tier.price, tier.currency 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 = '', '', '' preapproval = None if 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 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: contrib = Contribution(addon_id=addon.id, amount=amount, source=source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user) 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 is completed for uuid: %s' % uuid_) if paypal.check_purchase(paykey) == 'COMPLETED': log.debug('Check purchase is 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 redirect(url) messages.success(request, _('Purchase complete')) return redirect(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 test_check_purchase_fails(self, opener): opener.return_value.text = other_error eq_(paypal.check_purchase('some-paykey'), False)
def test_check_purchase(self, opener): opener.return_value.text = good_check_purchase eq_(paypal.check_purchase('some-paykey'), 'CREATED')
) log.debug("Status is 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": log.debug("Check in-app payment is completed for uuid: %s" % uuid_) contrib.type = amo.CONTRIB_INAPP else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error("Check in-app payment 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 in-app payment is completed for uuid: %s" % uuid_) contrib.type = amo.CONTRIB_INAPP else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error("Check in-app payment failed on uuid: %s" % uuid_) status = "NOT-COMPLETED" contrib.save() payment = InappPayment.objects.create( config=pay_req["_config"], contribution=contrib, name=pay_req["request"]["name"], description=pay_req["request"]["description"],
def purchase_done(request, addon, status): result = '' if status == 'complete': uuid_ = request.GET.get('uuid') log.debug('Looking up contrib for uuid: %s' % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. # # TODO(solitude): this will change when we figure out what to do # with Contributions. lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)) con = get_object_or_404(Contribution, lookup) log.debug('Check purchase addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) if waffle.flag_is_active(request, 'solitude-payments'): try: res = client.post_pay_check(data={'uuid': uuid_}) except client.Error: paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase error') raise result = res['status'] # TODO(solitude): can be removed once solitude is live. else: try: result = paypal.check_purchase(con.paykey) if result == 'ERROR': paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase error') raise except: paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'There was an error checking purchase') log.error('Check purchase addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING: amo.log(amo.LOG.PURCHASE_ADDON, addon) con.update(type=amo.CONTRIB_PURCHASE) context = { 'realurl': request.GET.get('realurl', ''), 'status': status, 'result': result, 'product': addon, 'error': amo.PAYMENT_DETAILS_ERROR.get(result, '') } response = jingo.render(request, 'purchase/done.html', context) response['x-frame-options'] = 'allow' return response
def purchase(request, addon): log.debug('Starting purchase of addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) amount = addon.premium.get_price() source = request.POST.get('source', '') uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() # l10n: {0} is the addon name contrib_for = _(u'Purchase of {0}').format(jinja2.escape(addon.name)) # Default is USD. amount, currency = addon.premium.get_price(), 'USD' # If tier is specified, then let's look it up. form = PriceCurrencyForm(data=request.POST, addon=addon) if form.is_valid(): tier = form.get_tier() if tier: amount, currency = tier.price, tier.currency paykey, status, error = '', '', '' preapproval = None if waffle.flag_is_active(request, 'allow-pre-auth') and request.amo_user: preapproval = request.amo_user.get_preapproval() try: pattern = 'addons.purchase.finished' slug = addon.slug if addon.is_webapp(): pattern = 'apps.purchase.finished' slug = addon.app_slug 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=pattern, preapproval=preapproval, qs={'realurl': request.POST.get('realurl')}, slug=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: contrib = Contribution(addon_id=addon.id, amount=amount, source=source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user) 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 is completed for uuid: %s' % uuid_) if paypal.check_purchase(paykey) == 'COMPLETED': log.debug('Check purchase is 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(shared_url('addons.detail', addon))
def purchase(request, addon): log.debug('Starting purchase of addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) amount = addon.premium.get_price() source = request.POST.get('source', '') uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() # l10n: {0} is the addon name contrib_for = _(u'Purchase of {0}').format(jinja2.escape(addon.name)) # Default is USD. amount, currency = addon.premium.get_price(), 'USD' # If tier is specified, then let's look it up. form = PriceCurrencyForm(data=request.POST, addon=addon) if form.is_valid(): tier = form.get_tier() if tier: amount, currency = tier.price, tier.currency paykey, status, error = '', '', '' preapproval = None if waffle.flag_is_active(request, 'allow-pre-auth') and request.amo_user: preapproval = request.amo_user.get_preapproval() try: pattern = 'addons.purchase.finished' slug = addon.slug if addon.is_webapp(): pattern = 'apps.purchase.finished' slug = addon.app_slug 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=pattern, preapproval=preapproval, qs={'realurl': request.POST.get('realurl')}, slug=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: contrib = Contribution(addon_id=addon.id, amount=amount, source=source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user) 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 is completed for uuid: %s' % uuid_) if paypal.check_purchase(paykey) == 'COMPLETED': log.debug('Check purchase is 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(shared_url('addons.detail', addon))
def test_check_purchase(self, opener): opener.return_value = StringIO(good_check_purchase) eq_(paypal.check_purchase('some-paykey'), 'CREATED')
def purchase(request, addon): log.debug('Starting purchase of addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) amount = addon.premium.get_price() source = request.POST.get('source', '') uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() # L10n: {0} is the addon name. contrib_for = (_(u'Mozilla Marketplace purchase of {0}') .format(addon.name)) # Default is USD. amount, currency = addon.premium.get_price(), 'USD' # If tier is specified, then let's look it up. if waffle.switch_is_active('currencies'): form = PriceCurrencyForm(data=request.POST, addon=addon) if form.is_valid(): tier = form.get_tier() if tier: amount, currency = tier.price, tier.currency 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 = '', '', '' preapproval = None if 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 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: contrib = Contribution(addon_id=addon.id, amount=amount, source=source, source_locale=request.LANG, uuid=str(uuid_), type=amo.CONTRIB_PENDING, paykey=paykey, user=request.amo_user) 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 is completed for uuid: %s' % uuid_) if paypal.check_purchase(paykey) == 'COMPLETED': log.debug('Check purchase is 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 redirect(url) messages.success(request, _('Purchase complete')) return redirect(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())
if waffle.flag_is_active(request, 'solitude-payments'): result = client.post_pay_check(data={'pay_key': paykey}) if result['status'] == 'COMPLETED': log.debug( 'Check in-app payment is completed for uuid: %s' % uuid_) contrib.type = amo.CONTRIB_INAPP else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error('Check in-app payment 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 in-app payment is completed for uuid: %s' % uuid_) contrib.type = amo.CONTRIB_INAPP else: # In this case PayPal disagreed, we should not be trusting # what get_paykey said. Which is a worry. log.error('Check in-app payment failed on uuid: %s' % uuid_) status = 'NOT-COMPLETED' contrib.save() payment = InappPayment.objects.create( config=pay_req['_config'],
def purchase_done(request, addon, status): result = '' if status == 'complete': uuid_ = request.GET.get('uuid') log.debug('Looking up contrib for uuid: %s' % uuid_) # The IPN may, or may not have come through. Which means looking for # a for pre or post IPN contributions. If both fail, then we've not # got a matching contribution. # # TODO(solitude): this will change when we figure out what to do # with Contributions. lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)) con = get_object_or_404(Contribution, lookup) log.debug('Check purchase addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10])) if waffle.flag_is_active(request, 'solitude-payments'): try: res = client.post_pay_check(data={'uuid': uuid_}) except client.Error: paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase error') raise result = res['status'] # TODO(solitude): can be removed once solitude is live. else: try: result = paypal.check_purchase(con.paykey) if result == 'ERROR': paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'Checking purchase error') raise except: paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL', 'There was an error checking purchase') log.error('Check purchase addon: %s, user: %s, paykey: %s' % (addon.pk, request.amo_user.pk, con.paykey[:10]), exc_info=True) result = 'ERROR' status = 'error' log.debug('Paypal returned: %s for paykey: %s' % (result, con.paykey[:10])) if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING: amo.log(amo.LOG.PURCHASE_ADDON, addon) con.update(type=amo.CONTRIB_PURCHASE) context = {'realurl': request.GET.get('realurl', ''), 'status': status, 'result': result, 'product': addon, 'error': amo.PAYMENT_DETAILS_ERROR.get(result, '')} response = jingo.render(request, 'purchase/done.html', context) response['x-frame-options'] = 'allow' return response