def test_seller_not(self, patch_seller_paypal, create_seller_paypal): self.addon.update(paypal_id="foo") create_seller_paypal.return_value = {"paypal_id": None, "resource_pk": 1} client.create_seller_for_pay(self.addon) kwargs = patch_seller_paypal.call_args[1] eq_(kwargs["pk"], 1) eq_(kwargs["data"]["paypal_id"], "foo")
def start_purchase(request, addon): log.debug('Starting purchase of addon: %s by user: %s' % (addon.pk, request.amo_user.pk)) amount = addon.premium.get_price() uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() # L10n: {0} is the addon name. contrib_for = (_(u'Firefox 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 waffle.flag_is_active(request, 'solitude-payments'): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. Seller data is populated in solitude # on submission or devhub changes. If those don't occur, you won't be # able to sell at all. client.create_seller_for_pay(addon) return amount, currency, uuid_, contrib_for
def test_seller_there(self, patch_seller_paypal, create_seller_paypal): create_seller_paypal.return_value = { 'paypal_id': 'asd', 'resource_pk': 1 } client.create_seller_for_pay(None) assert not patch_seller_paypal.called
def test_seller_not(self, patch_seller_paypal, create_seller_paypal): self.addon.update(paypal_id='foo') create_seller_paypal.return_value = {'paypal_id': None, 'resource_pk': 1} client.create_seller_for_pay(self.addon) kwargs = patch_seller_paypal.call_args[1] eq_(kwargs['pk'], 1) eq_(kwargs['data']['paypal_id'], 'foo')
def test_seller_not(self, patch_seller_paypal, create_seller_paypal): self.addon.update(paypal_id='foo') create_seller_paypal.return_value = { 'paypal_id': None, 'resource_pk': 1 } client.create_seller_for_pay(self.addon) kwargs = patch_seller_paypal.call_args[1] eq_(kwargs['pk'], 1) eq_(kwargs['data']['paypal_id'], 'foo')
def payments_confirm(request, addon_id, addon): data = {} # TODO(solitude): remove all references to AddonPaymentData. if waffle.flag_is_active(request, 'solitude-payments'): data = client.get_seller_paypal_if_exists(addon) or {} adp, created = AddonPaymentData.objects.safer_get_or_create(addon=addon) if not data: data = model_to_dict(adp) form = PaypalPaymentData(request.POST or data) if request.method == 'POST' and form.is_valid(): if waffle.flag_is_active(request, 'solitude-payments'): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. pk = client.create_seller_for_pay(addon) client.patch_seller_paypal(pk=pk, data=form.cleaned_data) # TODO(solitude): remove this. adp.update(**form.cleaned_data) AppSubmissionChecklist.objects.get(addon=addon).update(payments=True) addon.mark_done() return redirect('submit.app.done', addon.app_slug) return jingo.render(request, 'submit/payments-confirm.html', { 'step': 'payments', 'addon': addon, 'form': form })
def paypal_setup_confirm(request, addon_id, addon, webapp, source='paypal'): # If you bounce through paypal as you do permissions changes set the # source to paypal. if source == 'paypal': msg = _('PayPal set up complete.') title = _('Confirm Details') button = _('Continue') # If you just hit this page from the Manage Paypal, show some less # wizardy stuff. else: msg = _('Changes saved.') title = _('Contact Details') button = _('Save') data = {} if waffle.flag_is_active(request, 'solitude-payments'): data = client.get_seller_paypal_if_exists(addon) or {} # TODO(solitude): remove this bit. # If it's not in solitude, use the local version adp, created = (AddonPaymentData.objects .safer_get_or_create(addon=addon)) if not data: data = model_to_dict(adp) form = forms.PaypalPaymentData(request.POST or data) if request.method == 'POST' and form.is_valid(): if waffle.flag_is_active(request, 'solitude-payments'): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. pk = client.create_seller_for_pay(addon) client.patch_seller_paypal(pk=pk, data=form.cleaned_data) # TODO(solitude): remove this. adp.update(**form.cleaned_data) messages.success(request, msg) if source == 'paypal' and addon.is_incomplete() and addon.paypal_id: addon.mark_done() return redirect(addon.get_dev_url('paypal_setup')) return jingo.render(request, 'developers/payments/paypal-details-confirm.html', {'addon': addon, 'button': button, 'form': form, 'title': title})
def pay(request, signed_req, pay_req): paykey, status = '', '' preapproval = None if request.amo_user: preapproval = request.amo_user.get_preapproval() tier, price, currency = _get_price(pay_req, preapproval=preapproval) source = request.POST.get('source', '') product = pay_req['_config'].addon # L10n: {0} is the product name. {1} is the application name. contrib_for = ( _(u'Firefox Marketplace in-app payment for {0} to {1}').format( pay_req['request']['name'], product.name)) # TODO(solitude): solitude lib will create these for us. uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() if waffle.flag_is_active(request, 'solitude-payments'): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. Seller data is populated in solitude # on submission or devhub changes. If those don't occur, you won't be # able to sell at all. client.create_seller_for_pay(product) complete = reverse('inapp_pay.pay_status', args=[pay_req['_config'].pk, 'complete']) cancel = reverse('inapp_pay.pay_status', args=[pay_req['_config'].pk, 'cancel']) # TODO(bug 748137): remove retry is False. try: result = client.pay( { 'amount': price, 'currency': currency, 'buyer': request.amo_user, 'seller': product, 'memo': contrib_for, 'complete': complete, 'cancel': cancel }, retry=False) except client.Error as exc: paypal.paypal_log_cef(request, product, uuid_, 'in-app PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error(u'Error getting paykey, in-app payment: %s' % pay_req['_config'].pk, exc_info=True) InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config']) return render_error(request, exc) #TODO(solitude): just use the dictionary when solitude is live. paykey = result.get('pay_key', '') status = result.get('status', '') uuid_ = result.get('uuid', '') else: try: paykey, status = paypal.get_paykey( dict( amount=price, chains=settings.PAYPAL_CHAINS, currency=currency, email=product.paypal_id, ip=request.META.get('REMOTE_ADDR'), memo=contrib_for, pattern='inapp_pay.pay_status', preapproval=preapproval, qs={'realurl': request.POST.get('realurl')}, slug=pay_req['_config'].pk, # passed to pay_done() # via reverse() uuid=uuid_)) except paypal.PaypalError, exc: paypal.paypal_log_cef(request, product, uuid_, 'in-app PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error(u'Error getting paykey, in-app payment: %s' % pay_req['_config'].pk, exc_info=True) InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config']) return render_error(request, exc)
def pay(request, signed_req, pay_req): paykey, status = '', '' preapproval = None if request.amo_user: preapproval = request.amo_user.get_preapproval() tier, price, currency = _get_price(pay_req, preapproval=preapproval) source = request.POST.get('source', '') product = pay_req['_config'].addon # L10n: {0} is the product name. {1} is the application name. contrib_for = (_(u'Firefox Marketplace in-app payment for {0} to {1}') .format(pay_req['request']['name'], product.name)) # TODO(solitude): solitude lib will create these for us. uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() if waffle.flag_is_active(request, 'solitude-payments'): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. Seller data is populated in solitude # on submission or devhub changes. If those don't occur, you won't be # able to sell at all. client.create_seller_for_pay(product) complete = reverse('inapp_pay.pay_status', args=[pay_req['_config'].pk, 'complete']) cancel = reverse('inapp_pay.pay_status', args=[pay_req['_config'].pk, 'cancel']) # TODO(bug 748137): remove retry is False. try: result = client.pay({'amount': price, 'currency': currency, 'buyer': request.amo_user, 'seller': product, 'memo': contrib_for, 'complete': complete, 'cancel': cancel}, retry=False) except client.Error as exc: paypal.paypal_log_cef(request, product, uuid_, 'in-app PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error(u'Error getting paykey, in-app payment: %s' % pay_req['_config'].pk, exc_info=True) InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config']) return render_error(request, exc) #TODO(solitude): just use the dictionary when solitude is live. paykey = result.get('pay_key', '') status = result.get('status', '') uuid_ = result.get('uuid', '') else: try: paykey, status = paypal.get_paykey(dict( amount=price, chains=settings.PAYPAL_CHAINS, currency=currency, email=product.paypal_id, ip=request.META.get('REMOTE_ADDR'), memo=contrib_for, pattern='inapp_pay.pay_status', preapproval=preapproval, qs={'realurl': request.POST.get('realurl')}, slug=pay_req['_config'].pk, # passed to pay_done() # via reverse() uuid=uuid_ )) except paypal.PaypalError, exc: paypal.paypal_log_cef(request, product, uuid_, 'in-app PayKey Failure', 'PAYKEYFAIL', 'There was an error getting the paykey') log.error(u'Error getting paykey, in-app payment: %s' % pay_req['_config'].pk, exc_info=True) InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config']) return render_error(request, exc)
def test_seller_there(self, patch_seller_paypal, create_seller_paypal): create_seller_paypal.return_value = {'paypal_id': 'asd', 'resource_pk': 1} client.create_seller_for_pay(None) assert not patch_seller_paypal.called
def test_seller_there(self, patch_seller_paypal, create_seller_paypal): create_seller_paypal.return_value = {"paypal_id": "asd", "resource_pk": 1} client.create_seller_for_pay(None) assert not patch_seller_paypal.called
def pay(request, signed_req, pay_req): paykey, status = "", "" preapproval = None if request.amo_user: preapproval = request.amo_user.get_preapproval() tier, price, currency = _get_price(pay_req, preapproval=preapproval) source = request.POST.get("source", "") product = pay_req["_config"].addon # L10n: {0} is the product name. {1} is the application name. contrib_for = _(u"Firefox Marketplace in-app payment for {0} to {1}").format( pay_req["request"]["name"], product.name ) # TODO(solitude): solitude lib will create these for us. uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest() if waffle.flag_is_active(request, "solitude-payments"): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. Seller data is populated in solitude # on submission or devhub changes. If those don't occur, you won't be # able to sell at all. client.create_seller_for_pay(product) complete = reverse("inapp_pay.pay_status", args=[pay_req["_config"].pk, "complete"]) cancel = reverse("inapp_pay.pay_status", args=[pay_req["_config"].pk, "cancel"]) # TODO(bug 748137): remove retry is False. try: result = client.pay( { "amount": price, "currency": currency, "buyer": request.amo_user, "seller": product, "memo": contrib_for, "complete": complete, "cancel": cancel, }, retry=False, ) except client.Error as exc: paypal.paypal_log_cef( request, product, uuid_, "in-app PayKey Failure", "PAYKEYFAIL", "There was an error getting the paykey" ) log.error(u"Error getting paykey, in-app payment: %s" % pay_req["_config"].pk, exc_info=True) InappPayLog.log(request, "PAY_ERROR", config=pay_req["_config"]) return render_error(request, exc) # TODO(solitude): just use the dictionary when solitude is live. paykey = result.get("pay_key", "") status = result.get("status", "") uuid_ = result.get("uuid", "") else: try: paykey, status = paypal.get_paykey( dict( amount=price, chains=settings.PAYPAL_CHAINS, currency=currency, email=product.paypal_id, ip=request.META.get("REMOTE_ADDR"), memo=contrib_for, pattern="inapp_pay.pay_status", preapproval=preapproval, qs={"realurl": request.POST.get("realurl")}, slug=pay_req["_config"].pk, # passed to pay_done() # via reverse() uuid=uuid_, ) ) except paypal.PaypalError, exc: paypal.paypal_log_cef( request, product, uuid_, "in-app PayKey Failure", "PAYKEYFAIL", "There was an error getting the paykey" ) log.error(u"Error getting paykey, in-app payment: %s" % pay_req["_config"].pk, exc_info=True) InappPayLog.log(request, "PAY_ERROR", config=pay_req["_config"]) return render_error(request, exc)
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 = '', '', '' # 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'): # TODO(solitude): when the migration of data is completed, we # will be able to remove this. Seller data is populated in solitude # on submission or devhub changes. If those don't occur, you won't be # able to sell at all. client.create_seller_for_pay(addon) # 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: 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. 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 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 redirect(url) messages.success(request, _('Purchase complete')) return redirect(addon.get_detail_url())