Example #1
0
 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")
Example #2
0
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
Example #3
0
 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
Example #4
0
 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')
Example #5
0
 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')
Example #6
0
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
                        })
Example #7
0
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})
Example #8
0
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)
Example #9
0
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)
Example #10
0
 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
Example #11
0
 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
Example #12
0
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)
Example #13
0
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())