Example #1
0
def index_finance_total_by_src(addons, **kw):
    """
    Bug 758059
    Total finance stats, source breakdown.
    """
    es = elasticutils.get_es()
    log.info('Indexing total financial stats by source for %s apps.' %
              len(addons))

    for addon in addons:
        # Get all contributions for given add-on.
        qs = Contribution.objects.filter(addon=addon, uuid=None)
        if not qs.exists():
            continue

        # Get list of distinct sources.
        sources = set(qs.values_list('source', flat=True))

        for source in sources:
            try:
                key = ord_word('src' + str(addon) + str(source))
                data = search.get_finance_total_by_src(qs, addon, source)
                if not already_indexed(Contribution, data):
                    Contribution.index(data, bulk=True, id=key)
                es.flush_bulk(forced=True)
            except Exception, exc:
                index_finance_total_by_src.retry(args=[addons], exc=exc)
                raise
Example #2
0
File: tasks.py Project: vdt/zamboni
def index_finance_total_by_currency(addons, **kw):
    """
    Bug 757581
    Total finance stats, currency breakdown.
    """
    es = elasticutils.get_es()
    log.info('Indexing total financial stats by currency for %s apps.' %
             len(addons))

    for addon in addons:
        # Get all contributions for given add-on.
        qs = Contribution.objects.filter(addon=addon, uuid=None)
        if not qs.exists():
            continue

        # Get list of distinct currencies.
        currencies = set(qs.values_list('currency', flat=True))

        for currency in currencies:
            try:
                key = ord_word('cur' + str(addon) + currency.lower())
                data = search.get_finance_total_by_currency(
                    qs, addon, currency)
                if not already_indexed(Contribution, data):
                    Contribution.index(data, bulk=True, id=key)
                es.flush_bulk(forced=True)
            except Exception, exc:
                index_finance_total_by_currency.retry(args=[addons], exc=exc)
                raise
Example #3
0
def index_finance_total_by_src(addons, **kw):
    """
    Bug 758059
    Total finance stats, source breakdown.
    """
    index = kw.get('index', Contribution._get_index())
    es = amo.search.get_es()
    log.info('Indexing total financial stats by source for %s apps.' %
              len(addons))

    for addon in addons:
        # Get all contributions for given add-on.
        qs = Contribution.objects.filter(addon=addon, uuid=None)
        if not qs.exists():
            continue

        # Get list of distinct sources.
        sources = set(qs.values_list('source', flat=True))

        for source in sources:
            try:
                key = ord_word('src' + str(addon) + str(source))
                data = search.get_finance_total(qs, addon, 'source',
                                                source=source)
                for index in get_indices(index):
                    if not already_indexed(Contribution, data, index):
                        Contribution.index(data, bulk=True, id=key,
                                           index=index)
                es.flush_bulk(forced=True)
            except Exception, exc:
                index_finance_total_by_src.retry(args=[addons], exc=exc, **kw)
                raise
Example #4
0
def index_contribution_counts(ids, **kw):
    """
    Contribution stats by addon-date unique pair Uses a nested
    dictionary to not index duplicate contribution with same addon/date
    pairs. For each addon-date, it stores the addon in the dict as a top
    level key with a dict as its value. And it stores the date in the
    addon's dict as a second level key. To check if an addon-date pair has
    been already index, it looks up the dict[addon][date] to see if the
    key exists.
    """
    es = elasticutils.get_es()
    qs = (Contribution.objects.filter(id__in=ids)
          .order_by('created').values('addon', 'created'))

    try:
        addons_dates = defaultdict(lambda: defaultdict(dict))
        for contribution in qs:
            addon = contribution['addon']
            date = contribution['created'].strftime('%Y%m%d')

            # date for addon not processed, index it and give it key
            if not date in addons_dates[addon]:
                key = '%s-%s' % (addon, date)
                data = search.extract_contribution_counts(contribution)
                Contribution.index(data, bulk=True, id=key)
                addons_dates[addon][date] = 0

        if qs:
            log.info('Indexed %s addons/apps for contribution stats: %s' %
                     (len(addons_dates), qs[0]['created']))
        es.flush_bulk(forced=True)
    except Exception, exc:
        index_contribution_counts.retry(args=[ids], exc=exc)
        raise
Example #5
0
def index_finance_total_by_currency(addons, **kw):
    """
    Bug 757581
    Total finance stats, currency breakdown.
    """
    index = kw.get('index', Contribution._get_index())
    es = amo.search.get_es()
    log.info('Indexing total financial stats by currency for %s apps.' %
              len(addons))

    for addon in addons:
        # Get all contributions for given add-on.
        qs = Contribution.objects.filter(addon=addon, uuid=None)
        if not qs.exists():
            continue

        # Get list of distinct currencies.
        currencies = set(qs.values_list('currency', flat=True))

        for currency in currencies:
            try:
                key = ord_word('cur' + str(addon) + currency.lower())
                data = search.get_finance_total(
                    qs, addon, 'currency', currency=currency)
                for index in get_indices(index):
                    if not already_indexed(Contribution, data, index):
                        Contribution.index(data, bulk=True, id=key,
                                           index=index)
                es.flush_bulk(forced=True)
            except Exception, exc:
                index_finance_total_by_currency.retry(args=[addons], exc=exc, **kw)
                raise
Example #6
0
def index_contribution_counts(ids, **kw):
    """
    Contribution stats by addon-date unique pair Uses a nested
    dictionary to not index duplicate contribution with same addon/date
    pairs. For each addon-date, it stores the addon in the dict as a top
    level key with a dict as its value. And it stores the date in the
    addon's dict as a second level key. To check if an addon-date pair has
    been already index, it looks up the dict[addon][date] to see if the
    key exists.
    """
    es = elasticutils.get_es()
    qs = (Contribution.objects.filter(id__in=ids).order_by('created').values(
        'addon', 'created'))

    try:
        addons_dates = defaultdict(lambda: defaultdict(dict))
        for contribution in qs:
            addon = contribution['addon']
            date = contribution['created'].strftime('%Y%m%d')

            # date for addon not processed, index it and give it key
            if not date in addons_dates[addon]:
                key = '%s-%s' % (addon, date)
                data = search.extract_contribution_counts(contribution)
                Contribution.index(data, bulk=True, id=key)
                addons_dates[addon][date] = 0

        if qs:
            log.info('Indexed %s addons/apps for contribution stats: %s' %
                     (len(addons_dates), qs[0]['created']))
        es.flush_bulk(forced=True)
    except Exception, exc:
        index_contribution_counts.retry(args=[ids], exc=exc)
        raise
Example #7
0
def contribute(request, addon):
    contrib_type = request.GET.get('type', 'suggested')
    is_suggested = contrib_type == 'suggested'
    source = request.GET.get('source', '')
    comment = request.GET.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.GET.get('onetime-amount', '')}.get(contrib_type, '')
    if not amount:
        amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if addon.charity:
        name, paypal_id = ('%s: %s' % (addon.name, addon.charity.name),
                           addon.charity.paypal)
    else:
        name, paypal_id = addon.name, addon.paypal_id
    # l10n: {0} is the addon name
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))

    paykey, error = '', ''
    try:
        paykey = paypal.get_paykey(dict(uuid=contribution_uuid,
                    slug=addon.slug, amount=amount, email=paypal_id,
                    memo=contrib_for, ip=request.META.get('REMOTE_ADDR'),
                    pattern='addons.paypal'))
    except:
        log.error('Error getting paykey, contribution for addon: %s'
                  % addon.pk, exc_info=True)
        error = _('There was an error communicating with PayPal.')

    if paykey:
        contrib = Contribution(addon_id=addon.id,
                           charity_id=addon.charity_id,
                           amount=amount,
                           source=source,
                           source_locale=request.LANG,
                           annoying=addon.annoying,
                           uuid=str(contribution_uuid),
                           is_suggested=is_suggested,
                           suggested_amount=addon.suggested_amount,
                           comment=comment,
                           paykey=paykey)
        contrib.save()

    assert settings.PAYPAL_FLOW_URL, 'settings.PAYPAL_FLOW_URL is not defined'

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({'url': url,
                                             'paykey': paykey,
                                             'error': error}),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #8
0
def contribute(request, addon):
    contrib_type = request.GET.get('type', 'suggested')
    is_suggested = contrib_type == 'suggested'
    source = request.GET.get('source', '')
    comment = request.GET.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.GET.get('onetime-amount', '')}.get(contrib_type, '')
    if not amount:
        amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if addon.charity:
        name, paypal_id = ('%s: %s' % (addon.name, addon.charity.name),
                           addon.charity.paypal)
    else:
        name, paypal_id = addon.name, addon.paypal_id
    # l10n: {0} is the addon name
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))

    paykey, error = '', ''
    try:
        paykey = paypal.get_paykey(dict(uuid=contribution_uuid,
                    slug=addon.slug, amount=amount, email=paypal_id,
                    memo=contrib_for, ip=request.META.get('REMOTE_ADDR'),
                    pattern='addons.paypal'))
    except:
        log.error('Error getting paykey, contribution for addon: %s'
                  % addon.pk, exc_info=True)
        error = _('There was an error communicating with PayPal.')

    if paykey:
        contrib = Contribution(addon_id=addon.id,
                           charity_id=addon.charity_id,
                           amount=amount,
                           source=source,
                           source_locale=request.LANG,
                           annoying=addon.annoying,
                           uuid=str(contribution_uuid),
                           is_suggested=is_suggested,
                           suggested_amount=addon.suggested_amount,
                           comment=comment,
                           paykey=paykey)
        contrib.save()

    assert settings.PAYPAL_FLOW_URL, 'settings.PAYPAL_FLOW_URL is not defined'

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({'url': url,
                                             'paykey': paykey,
                                             'error': error}),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #9
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.GET.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))

    paykey, error = '', ''
    try:
        pattern = 'addons.purchase.finished'
        slug = addon.slug
        if addon.is_webapp():
            pattern = 'apps.purchase.finished'
            slug = addon.app_slug

        paykey = paypal.get_paykey(
            dict(uuid=uuid_,
                 slug=slug,
                 amount=amount,
                 memo=contrib_for,
                 email=addon.paypal_id,
                 ip=request.META.get('REMOTE_ADDR'),
                 pattern=pattern,
                 qs={'realurl': request.GET.get('realurl')},
                 chains=settings.PAYPAL_CHAINS))
    except:
        log.error('Error getting paykey, purchase of addon: %s' % addon.pk,
                  exc_info=True)
        error = _('There was an error communicating with PayPal.')

    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_)
        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.GET.get('result_type') == 'json' or request.is_ajax():
        return http.HttpResponse(json.dumps({
            'url': url,
            'paykey': paykey,
            'error': error
        }),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #10
0
def index_latest_mkt_stats(index=None, aliased=True):
    raise_if_reindex_in_progress()
    yesterday = datetime.date.today() - datetime.timedelta(days=1)

    try:
        latest = Contribution.search(index).order_by('-date').values_dict()
        latest_contribution = latest and latest[0]['date'] or yesterday
    except pyes.exceptions.SearchPhaseExecutionException:
        latest_contribution = yesterday

    try:
        latest = Installed.search(index).order_by('-date').values_dict()
        latest_install = latest and latest[0]['date'] or yesterday
    except pyes.exceptions.SearchPhaseExecutionException:
        latest_install = yesterday

    latest = min(latest_contribution, latest_install)

    fmt = lambda d: d.strftime('%Y-%m-%d')
    date_range = '%s:%s' % (fmt(latest), fmt(datetime.date.today()))
    cron_log.info('index_mkt_stats --date=%s' % date_range)
    call_command('index_mkt_stats',
                 addons=None,
                 date=date_range,
                 index=index,
                 aliased=True)
Example #11
0
    def test_index(self):
        tasks.index_finance_total_by_currency([self.app.pk])
        self.refresh(timesleep=1)
        raise SkipTest('Test is unreliable and causes intermittent failures.')

        # Grab document for each source breakdown and compare.
        for currency in self.currencies:
            # For some reason, query fails if uppercase letter in filter.
            document = (Contribution.search().filter(addon=self.app.pk,
                        currency=currency.lower()).values_dict('currency',
                        'revenue', 'count', 'refunds',
                        'revenue_non_normalized')[0])
            document = {
                'count': document['count'],
                'revenue': int(document['revenue']),
                'refunds': document['refunds'],
                'revenue_non_normalized':
                    int(document['revenue_non_normalized'])}
            self.expected[currency]['revenue'] = (
                int(self.expected[currency]['revenue'])
            )
            self.expected[currency]['revenue_non_normalized'] = (
                int(self.expected[currency]['revenue_non_normalized'])
            )
            eq_(document, self.expected[currency])
Example #12
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.GET.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))

    paykey, error = "", ""
    try:
        paykey = paypal.get_paykey(
            dict(
                uuid=uuid_,
                slug=addon.slug,
                amount=amount,
                memo=contrib_for,
                email=addon.paypal_id,
                ip=request.META.get("REMOTE_ADDR"),
                pattern="addons.purchase.finished",
                qs={"realurl": request.GET.get("realurl")},
                ipn=False,
            )
        )
    except:
        log.error("Error getting paykey, purchase of addon: %s" % addon.pk, exc_info=True)
        error = _("There was an error communicating with PayPal.")

    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,
        )
        contrib.save()

    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.GET.get("result_type") == "json" or request.is_ajax():
        return http.HttpResponse(
            json.dumps({"url": url, "paykey": paykey, "error": error}), content_type="application/json"
        )
    return http.HttpResponseRedirect(url)
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.GET.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))

    paykey, error = '', ''
    try:
        pattern = 'addons.purchase.finished'
        slug = addon.slug
        if addon.is_webapp():
            pattern = 'apps.purchase.finished'
            slug = addon.app_slug

        paykey = paypal.get_paykey(dict(uuid=uuid_, slug=slug,
                    amount=amount, memo=contrib_for, email=addon.paypal_id,
                    ip=request.META.get('REMOTE_ADDR'),
                    pattern=pattern,
                    qs={'realurl': request.GET.get('realurl')},
                    chains=settings.PAYPAL_CHAINS))
    except:
        log.error('Error getting paykey, purchase of addon: %s' % addon.pk,
                  exc_info=True)
        error = _('There was an error communicating with PayPal.')

    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_)
        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.GET.get('result_type') == 'json' or request.is_ajax():
        return http.HttpResponse(json.dumps({'url': url,
                                             'paykey': paykey,
                                             'error': error}),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #14
0
def index_finance_daily(ids, **kw):
    """
    Bug 748015
    Takes a list of Contribution ids and uses its addon and date fields to
    index stats for that day.

    Contribution stats by addon-date unique pair. Uses a nested
    dictionary to not index duplicate contribution with same addon/date
    pairs. For each addon-date, it stores the addon in the dict as a top
    level key with a dict as its value. And it stores the date in the
    add-on's dict as a second level key. To check if an addon-date pair has
    been already index, it looks up the dict[addon][date] to see if the
    key exists. This adds some speed up when batch processing.

    ids -- ids of apps.stats.Contribution objects
    """
    index = kw.get('index', Contribution._get_index())
    es = amo.search.get_es()

    # Get contributions.
    qs = (Contribution.objects.filter(id__in=ids).order_by('created').values(
        'addon', 'created'))
    log.info('[%s] Indexing %s contributions for daily stats.' %
             (qs[0]['created'], len(ids)))

    addons_dates = defaultdict(lambda: defaultdict(dict))
    for contribution in qs:
        addon = contribution['addon']
        date = contribution['created'].strftime('%Y%m%d')

        try:
            # Date for add-on not processed, index it and give it key.
            if not date in addons_dates[addon]:
                key = ord_word('fin' + str(addon) + str(date))
                data = search.get_finance_daily(contribution)
                for index in get_indices(index):
                    if not already_indexed(Contribution, data, index):
                        Contribution.index(data,
                                           bulk=True,
                                           id=key,
                                           index=index)
                addons_dates[addon][date] = 0
            es.flush_bulk(forced=True)
        except Exception, exc:
            index_finance_daily.retry(args=[ids], exc=exc, **kw)
            raise
Example #15
0
def old_contribute(request, addon):
    contrib_type = request.GET.get('type', '')
    is_suggested = contrib_type == 'suggested'
    source = request.GET.get('source', '')
    comment = request.GET.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.GET.get('onetime-amount', ''),
        'monthly': request.GET.get('monthly-amount', '')}.get(contrib_type, '')

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    contrib = Contribution(addon_id=addon.id,
                           charity_id=addon.charity_id,
                           amount=amount,
                           source=source,
                           source_locale=request.LANG,
                           annoying=addon.annoying,
                           uuid=str(contribution_uuid),
                           is_suggested=is_suggested,
                           suggested_amount=addon.suggested_amount,
                           comment=comment)
    contrib.save()

    return_url = "%s?%s" % (reverse('addons.thanks', args=[addon.slug]),
                            urllib.urlencode({'uuid': contribution_uuid}))
    # L10n: {0} is an add-on name.
    if addon.charity:
        name, paypal = addon.charity.name, addon.charity.paypal
    else:
        name, paypal = addon.name, addon.paypal_id
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))
    redirect_url_params = contribute_url_params(
                            paypal,
                            addon.id,
                            contrib_for,
                            absolutify(return_url),
                            amount,
                            contribution_uuid,
                            contrib_type == 'monthly',
                            comment)

    return http.HttpResponseRedirect(settings.PAYPAL_CGI_URL
                                     + '?'
                                     + urllib.urlencode(redirect_url_params))
Example #16
0
def contribute(request, addon_id):

    addon = get_object_or_404(Addon.objects.valid(), id=addon_id)

    contrib_type = request.GET.get('type', '')
    is_suggested = contrib_type == 'suggested'
    source = request.GET.get('source', '')
    comment = request.GET.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.GET.get('onetime-amount', ''),
        'monthly': request.GET.get('monthly-amount', '')}.get(contrib_type, '')

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    contrib = Contribution(addon_id=addon.id,
                           amount=amount,
                           source=source,
                           source_locale=request.LANG,
                           annoying=addon.annoying,
                           uuid=str(contribution_uuid),
                           is_suggested=is_suggested,
                           suggested_amount=addon.suggested_amount,
                           comment=comment)
    contrib.save()

    return_url = "%s?%s" % (reverse('addons.thanks', args=[addon.id]),
                            urllib.urlencode({'uuid': contribution_uuid}))
    """ L10n: Phrase that will appear on the paypal
        site specifying who the contribution is for"""
    contrib_for = _('Contribution for {addon_name}').format(addon_name=addon.name)
    redirect_url_params = contribute_url_params(
                            addon.paypal_id,
                            addon.id,
                            contrib_for,
                            absolutify(return_url),
                            amount,
                            contribution_uuid,
                            contrib_type == 'monthly',
                            comment)

    return http.HttpResponseRedirect(settings.PAYPAL_CGI_URL
                                     + '?'
                                     + urllib.urlencode(redirect_url_params))
Example #17
0
def index_finance_daily(ids, **kw):
    """
    Bug 748015
    Takes a list of Contribution ids and uses its addon and date fields to
    index stats for that day.

    Contribution stats by addon-date unique pair. Uses a nested
    dictionary to not index duplicate contribution with same addon/date
    pairs. For each addon-date, it stores the addon in the dict as a top
    level key with a dict as its value. And it stores the date in the
    add-on's dict as a second level key. To check if an addon-date pair has
    been already index, it looks up the dict[addon][date] to see if the
    key exists. This adds some speed up when batch processing.

    ids -- ids of apps.stats.Contribution objects
    """
    index = kw.get('index', Contribution._get_index())
    es = amo.search.get_es()

    # Get contributions.
    qs = (Contribution.objects.filter(id__in=ids)
          .order_by('created').values('addon', 'created'))
    log.info('[%s] Indexing %s contributions for daily stats.' %
             (qs[0]['created'], len(ids)))

    addons_dates = defaultdict(lambda: defaultdict(dict))
    for contribution in qs:
        addon = contribution['addon']
        date = contribution['created'].strftime('%Y%m%d')

        try:
            # Date for add-on not processed, index it and give it key.
            if not date in addons_dates[addon]:
                key = ord_word('fin' + str(addon) + str(date))
                data = search.get_finance_daily(contribution)
                for index in get_indices(index):
                    if not already_indexed(Contribution, data, index):
                        Contribution.index(data, bulk=True, id=key,
                                           index=index)
                addons_dates[addon][date] = 0
            es.flush_bulk(forced=True)
        except Exception, exc:
            index_finance_daily.retry(args=[ids], exc=exc, **kw)
            raise
Example #18
0
    def test_index(self):
        tasks.index_finance_total([self.app.pk])
        self.refresh(timesleep=1)

        document = Contribution.search().filter(addon=self.app.pk).values_dict("revenue", "count", "refunds")[0]

        document = {"count": document["count"], "revenue": int(document["revenue"]), "refunds": document["refunds"]}
        self.expected["revenue"] = int(self.expected["revenue"])

        eq_(document, self.expected)
Example #19
0
    def test_total_contributions(self):

        c = Contribution()
        c.addon_id = 3615
        c.amount = "9.99"
        c.save()

        tasks.addon_total_contributions(3615)
        a = Addon.objects.no_cache().get(pk=3615)
        eq_(float(a.total_contributions), 9.99)

        c = Contribution()
        c.addon_id = 3615
        c.amount = "10.00"
        c.save()

        tasks.addon_total_contributions(3615)
        a = Addon.objects.no_cache().get(pk=3615)
        eq_(float(a.total_contributions), 19.99)
Example #20
0
def old_contribute(request, addon):
    contrib_type = request.GET.get('type', '')
    is_suggested = contrib_type == 'suggested'
    source = request.GET.get('source', '')
    comment = request.GET.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.GET.get('onetime-amount', ''),
        'monthly': request.GET.get('monthly-amount', '')
    }.get(contrib_type, '')

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    contrib = Contribution(addon_id=addon.id,
                           charity_id=addon.charity_id,
                           amount=amount,
                           source=source,
                           source_locale=request.LANG,
                           annoying=addon.annoying,
                           uuid=str(contribution_uuid),
                           is_suggested=is_suggested,
                           suggested_amount=addon.suggested_amount,
                           comment=comment)
    contrib.save()

    return_url = "%s?%s" % (reverse('addons.thanks', args=[addon.slug]),
                            urllib.urlencode({'uuid': contribution_uuid}))
    # L10n: {0} is an add-on name.
    if addon.charity:
        name, paypal = addon.charity.name, addon.charity.paypal
    else:
        name, paypal = addon.name, addon.paypal_id
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))
    redirect_url_params = contribute_url_params(paypal, addon.id, contrib_for,
                                                absolutify(return_url), amount,
                                                contribution_uuid,
                                                contrib_type == 'monthly',
                                                comment)

    return http.HttpResponseRedirect(settings.PAYPAL_CGI_URL + '?' +
                                     urllib.urlencode(redirect_url_params))
Example #21
0
def index_addon_aggregate_contributions(addons, **kw):
    """
    Aggregates stats from all of the contributions for a given addon
    """
    es = elasticutils.get_es()
    log.info('Aggregating total contribution stats for %s addons' %
             len(addons))
    try:
        for addon in addons:
            # Only count uuid=None; those are verified transactions.
            qs = Contribution.objects.filter(addon__in=addons, uuid=None)

            # Create lists of annotated dicts [{'addon':1, 'revenue':5}...]
            revenues = qs.values('addon').annotate(revenue=Sum('amount'))
            sales = qs.values('addon').annotate(sales=Count('id'))
            refunds = (qs.filter(
                refund__isnull=False).values('addon').annotate(
                    refunds=Count('id')))

            # Loop over revenue, sales, refunds.
            data_dict = defaultdict(lambda: defaultdict(dict))
            for revenue in revenues:
                data_dict[str(
                    revenue['addon'])]['revenue'] = revenue['revenue']
            for sale in sales:
                data_dict[str(sale['addon'])]['sales'] = sale['sales']
            for refund in refunds:
                data_dict[str(refund['addon'])]['refunds'] = refund['refunds']
            for addon, addon_dict in data_dict.iteritems():
                data = {
                    'addon': addon,
                    'count': addon_dict['sales'],
                    'revenue': addon_dict['revenue'],
                    'refunds': addon_dict['refunds'],
                }
                Contribution.index(data, bulk=True, id=addon)

            es.flush_bulk(forced=True)
    except Exception, exc:
        index_addon_aggregate_contributions.retry(args=[addons], exc=exc)
        raise
Example #22
0
File: tasks.py Project: vdt/zamboni
def index_finance_total(addons, **kw):
    """
    Aggregates financial stats from all of the contributions for a given app.
    """
    es = elasticutils.get_es()
    log.info('Indexing total financial stats for %s apps.' % len(addons))

    for addon in addons:
        # Get all contributions for given add-on.
        qs = Contribution.objects.filter(addon=addon, uuid=None)
        if not qs.exists():
            continue
        try:
            key = ord_word('tot' + str(addon))
            data = search.get_finance_total(qs, addon)
            if not already_indexed(Contribution, data):
                Contribution.index(data, bulk=True, id=key)
            es.flush_bulk(forced=True)
        except Exception, exc:
            index_finance_total.retry(args=[addons], exc=exc)
            raise
Example #23
0
File: cron.py Project: vdt/zamboni
def index_latest_mkt_stats():
    latest_contribution = Contribution.search().order_by(
        '-date').values_dict()[0]['date']
    latest_install = Installed.search().order_by(
        '-date').values_dict()[0]['date']

    latest = min(latest_contribution, latest_install)

    fmt = lambda d: d.strftime('%Y-%m-%d')
    date_range = '%s:%s' % (fmt(latest), fmt(datetime.date.today()))
    cron_log.info('index_mkt_stats --date=%s' % date_range)
    call_command('index_mkt_stats', addons=None, date=date_range)
Example #24
0
def index_latest_mkt_stats():
    latest_contribution = Contribution.search().order_by('-date'
        ).values_dict()[0]['date']
    latest_install = Installed.search().order_by('-date'
        ).values_dict()[0]['date']

    latest = min(latest_contribution, latest_install)

    fmt = lambda d: d.strftime('%Y-%m-%d')
    date_range = '%s:%s' % (fmt(latest), fmt(datetime.date.today()))
    cron_log.info('index_mkt_stats --date=%s' % date_range)
    call_command('index_mkt_stats', addons=None, date=date_range)
Example #25
0
def index_addon_aggregate_contributions(addons, **kw):
    """
    Aggregates stats from all of the contributions for a given addon
    """
    es = elasticutils.get_es()
    log.info('Aggregating total contribution stats for %s addons' %
        len(addons))
    try:
        for addon in addons:
            # Only count uuid=None; those are verified transactions.
            qs = Contribution.objects.filter(addon__in=addons, uuid=None)

            # Create lists of annotated dicts [{'addon':1, 'revenue':5}...]
            revenues = qs.values('addon').annotate(revenue=Sum('amount'))
            sales = qs.values('addon').annotate(sales=Count('id'))
            refunds = (qs.filter(refund__isnull=False).
                values('addon').annotate(refunds=Count('id')))

            # Loop over revenue, sales, refunds.
            data_dict = defaultdict(lambda: defaultdict(dict))
            for revenue in revenues:
                data_dict[str(
                    revenue['addon'])]['revenue'] = revenue['revenue']
            for sale in sales:
                data_dict[str(sale['addon'])]['sales'] = sale['sales']
            for refund in refunds:
                data_dict[str(refund['addon'])]['refunds'] = refund['refunds']
            for addon, addon_dict in data_dict.iteritems():
                data = {
                    'addon': addon,
                    'count': addon_dict['sales'],
                    'revenue': addon_dict['revenue'],
                    'refunds': addon_dict['refunds'],
                }
                Contribution.index(data, bulk=True, id=addon)

            es.flush_bulk(forced=True)
    except Exception, exc:
        index_addon_aggregate_contributions.retry(args=[addons], exc=exc)
        raise
Example #26
0
    def test_index(self):
        tasks.index_finance_total([self.app.pk])
        self.refresh(timesleep=1)

        document = Contribution.search().filter(addon=self.app.pk
            ).values_dict('revenue', 'count', 'refunds')[0]

        document = {'count': document['count'],
                    'revenue': int(document['revenue']),
                    'refunds': document['refunds']}
        self.expected['revenue'] = int(self.expected['revenue'])

        eq_(document, self.expected)
Example #27
0
def index_finance_total(addons, **kw):
    """
    Aggregates financial stats from all of the contributions for a given app.
    """
    es = elasticutils.get_es()
    log.info('Indexing total financial stats for %s apps.' %
              len(addons))

    for addon in addons:
        # Get all contributions for given add-on.
        qs = Contribution.objects.filter(addon=addon, uuid=None)
        if not qs.exists():
            continue
        try:
            key = ord_word('tot' + str(addon))
            data = search.get_finance_total(qs, addon)
            if not already_indexed(Contribution, data):
                Contribution.index(data, bulk=True, id=key)
            es.flush_bulk(forced=True)
        except Exception, exc:
            index_finance_total.retry(args=[addons], exc=exc)
            raise
Example #28
0
    def test_index(self):
        tasks.index_finance_total_by_src([self.app.pk])
        self.refresh(timesleep=1)

        # Grab document for each source breakdown and compare.
        for source in self.sources:
            # For some reason, query fails if uppercase letter in filter.
            document = (
                Contribution.search()
                .filter(addon=self.app.pk, source=source.lower())
                .values_dict("source", "revenue", "count", "refunds")[0]
            )
            document = {"count": document["count"], "revenue": int(document["revenue"]), "refunds": document["refunds"]}
            self.expected[source]["revenue"] = int(self.expected[source]["revenue"])
            eq_(document, self.expected[source])
Example #29
0
    def test_index(self):
        tasks.index_finance_total([self.app.pk])
        self.refresh(timesleep=1)

        document = Contribution.search().filter(addon=self.app.pk).values_dict(
            'revenue', 'count', 'refunds')[0]

        document = {
            'count': document['count'],
            'revenue': int(document['revenue']),
            'refunds': document['refunds']
        }
        self.expected['revenue'] = int(self.expected['revenue'])

        eq_(document, self.expected)
Example #30
0
    def test_index(self):
        tasks.index_finance_daily.delay(self.ids)
        self.refresh(timesleep=1)

        document = Contribution.search().filter(addon=self.app.pk).values_dict("date", "revenue", "count", "refunds")[0]

        date = document["date"]
        ex_date = self.expected["date"]
        eq_((date.year, date.month, date.day), (ex_date.year, ex_date.month, ex_date.day))

        document = {"count": document["count"], "revenue": int(document["revenue"]), "refunds": document["refunds"]}
        del (self.expected["date"])

        self.expected["revenue"] = int(self.expected["revenue"])
        eq_(document, self.expected)
Example #31
0
    def test_index(self):
        tasks.index_finance_total_by_src([self.app.pk])
        self.refresh(timesleep=1)

        # Grab document for each source breakdown and compare.
        for source in self.sources:
            # For some reason, query fails if uppercase letter in filter.
            document = (Contribution.search().filter(addon=self.app.pk,
                        source=source.lower()).values_dict('source', 'revenue',
                        'count', 'refunds')[0])
            document = {'count': document['count'],
                        'revenue': int(document['revenue']),
                        'refunds': document['refunds']}
            self.expected[source]['revenue'] = (
                int(self.expected[source]['revenue'])
            )
            eq_(document, self.expected[source])
Example #32
0
    def test_index(self):
        tasks.index_finance_total_by_src([self.app.pk])
        self.refresh(timesleep=1)

        # Grab document for each source breakdown and compare.
        for source in self.sources:
            # For some reason, query fails if uppercase letter in filter.
            document = (Contribution.search().filter(
                addon=self.app.pk,
                source=source.lower()).values_dict('source', 'revenue',
                                                   'count', 'refunds')[0])
            document = {
                'count': document['count'],
                'revenue': int(document['revenue']),
                'refunds': document['refunds']
            }
            self.expected[source]['revenue'] = (int(
                self.expected[source]['revenue']))
            eq_(document, self.expected[source])
Example #33
0
    def test_index(self):
        tasks.index_finance_daily.delay(self.ids)
        self.refresh(timesleep=1)

        document = Contribution.search().filter(addon=self.app.pk
            ).values_dict('date', 'revenue', 'count', 'refunds')[0]

        date = document['date']
        ex_date = self.expected['date']
        eq_((date.year, date.month, date.day),
            (ex_date.year, ex_date.month, ex_date.day))

        document = {'count': document['count'],
                    'revenue': int(document['revenue']),
                    'refunds': document['refunds']}
        del(self.expected['date'])

        self.expected['revenue'] = int(self.expected['revenue'])
        eq_(document, self.expected)
Example #34
0
    def test_index(self):
        tasks.index_finance_total_by_currency([self.app.pk])
        self.refresh(timesleep=1)

        # Grab document for each source breakdown and compare.
        for currency in self.currencies:
            # For some reason, query fails if uppercase letter in filter.
            document = (
                Contribution.search()
                .filter(addon=self.app.pk, currency=currency.lower())
                .values_dict("currency", "revenue", "count", "refunds", "revenue_non_normalized")[0]
            )
            document = {
                "count": document["count"],
                "revenue": int(document["revenue"]),
                "refunds": document["refunds"],
                "revenue_non_normalized": int(document["revenue_non_normalized"]),
            }
            self.expected[currency]["revenue"] = int(self.expected[currency]["revenue"])
            self.expected[currency]["revenue_non_normalized"] = int(self.expected[currency]["revenue_non_normalized"])
            eq_(document, self.expected[currency])
Example #35
0
    def test_index(self):
        tasks.index_finance_daily.delay(self.ids)
        self.refresh(timesleep=1)

        document = Contribution.search().filter(addon=self.app.pk).values_dict(
            'date', 'revenue', 'count', 'refunds')[0]

        date = document['date']
        ex_date = self.expected['date']
        eq_((date.year, date.month, date.day),
            (ex_date.year, ex_date.month, ex_date.day))

        document = {
            'count': document['count'],
            'revenue': int(document['revenue']),
            'refunds': document['refunds']
        }
        del (self.expected['date'])

        self.expected['revenue'] = int(self.expected['revenue'])
        eq_(document, self.expected)
Example #36
0
def index_latest_mkt_stats(index=None, aliased=True):
    raise_if_reindex_in_progress()
    yesterday = datetime.date.today() - datetime.timedelta(days=1)

    try:
        latest = Contribution.search(index).order_by('-date').values_dict()
        latest_contribution = latest and latest[0]['date'] or yesterday
    except pyes.exceptions.SearchPhaseExecutionException:
        latest_contribution = yesterday

    try:
        latest = Installed.search(index).order_by('-date').values_dict()
        latest_install = latest and latest[0]['date'] or yesterday
    except pyes.exceptions.SearchPhaseExecutionException:
        latest_install = yesterday

    latest = min(latest_contribution, latest_install)

    fmt = lambda d: d.strftime('%Y-%m-%d')
    date_range = '%s:%s' % (fmt(latest), fmt(datetime.date.today()))
    cron_log.info('index_mkt_stats --date=%s' % date_range)
    call_command('index_mkt_stats', addons=None, date=date_range, index=index,
                 aliased=True)
Example #37
0
    def test_index(self):
        tasks.index_finance_total_by_currency([self.app.pk])
        self.refresh(timesleep=1)

        # Grab document for each source breakdown and compare.
        for currency in self.currencies:
            # For some reason, query fails if uppercase letter in filter.
            document = (Contribution.search().filter(
                addon=self.app.pk, currency=currency.lower()).values_dict(
                    'currency', 'revenue', 'count', 'refunds',
                    'revenue_non_normalized')[0])
            document = {
                'count': document['count'],
                'revenue': int(document['revenue']),
                'refunds': document['refunds'],
                'revenue_non_normalized':
                int(document['revenue_non_normalized'])
            }
            self.expected[currency]['revenue'] = (int(
                self.expected[currency]['revenue']))
            self.expected[currency]['revenue_non_normalized'] = (int(
                self.expected[currency]['revenue_non_normalized']))
            eq_(document, self.expected[currency])
Example #38
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"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))
Example #39
0
        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)

    with transaction.commit_on_success():
        contrib = Contribution(addon_id=product.id,
                               amount=price,
                               source=source,
                               source_locale=request.LANG,
                               currency=currency,
                               uuid=str(uuid_),
                               price_tier=tier,
                               type=amo.CONTRIB_INAPP_PENDING,
                               paykey=paykey,
                               user=request.amo_user)
        log.debug('Storing in-app payment 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, product, uuid_, 'Purchase',
                                  'PURCHASE',
                                  'A user purchased using pre-approval')

            log.debug('Status is completed for uuid: %s' % uuid_)
            if waffle.flag_is_active(request, 'solitude-payments'):
Example #40
0
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())
Example #41
0
def contribute(request, addon):
    webapp = addon.is_webapp()
    contrib_type = request.GET.get('type', 'suggested')
    is_suggested = contrib_type == 'suggested'
    source = request.GET.get('source', '')
    comment = request.GET.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.GET.get('onetime-amount', '')
    }.get(contrib_type, '')
    if not amount:
        amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if addon.charity:
        # TODO(andym): Figure out how to get this in the addon authors
        # locale, rather than the contributors locale.
        name, paypal_id = (u'%s: %s' % (addon.name, addon.charity.name),
                           addon.charity.paypal)
    else:
        name, paypal_id = addon.name, addon.paypal_id
    # l10n: {0} is the addon name
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))

    preapproval = None
    if waffle.flag_is_active(request, 'allow-pre-auth') and request.amo_user:
        preapproval = request.amo_user.get_preapproval()

    paykey, error, status = '', '', ''
    try:
        paykey, status = paypal.get_paykey(
            dict(amount=amount,
                 email=paypal_id,
                 ip=request.META.get('REMOTE_ADDR'),
                 memo=contrib_for,
                 pattern='%s.paypal' % ('apps' if webapp else 'addons'),
                 preapproval=preapproval,
                 slug=addon.slug,
                 uuid=contribution_uuid))
    except:
        paypal.paypal_log_cef(request, addon, contribution_uuid,
                              'PayKey Failure', 'PAYKEYFAIL',
                              'There was an error getting the paykey')
        log.error('Error getting paykey, contribution for addon: %s' %
                  addon.pk,
                  exc_info=True)
        error = _('There was an error communicating with PayPal.')

    if paykey:
        contrib = Contribution(addon_id=addon.id,
                               charity_id=addon.charity_id,
                               amount=amount,
                               source=source,
                               source_locale=request.LANG,
                               annoying=addon.annoying,
                               uuid=str(contribution_uuid),
                               is_suggested=is_suggested,
                               suggested_amount=addon.suggested_amount,
                               comment=comment,
                               paykey=paykey)
        contrib.save()

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({
            'url': url,
            'paykey': paykey,
            'error': error,
            'status': status
        }),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #42
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 = '', '', ''
    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())
Example #43
0
def contribute(request, addon):
    webapp = addon.is_webapp()
    contrib_type = request.POST.get('type', 'suggested')
    is_suggested = contrib_type == 'suggested'
    source = request.POST.get('source', '')
    comment = request.POST.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.POST.get('onetime-amount', '')
    }.get(contrib_type, '')
    if not amount:
        amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION

    # This is all going to get shoved into solitude. Temporary.
    form = ContributionForm({'amount': amount})
    if not form.is_valid():
        return http.HttpResponse(json.dumps({'error': 'Invalid data.',
                                             'status': '', 'url': '',
                                             'paykey': ''}),
                                 content_type='application/json')

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if addon.charity:
        # TODO(andym): Figure out how to get this in the addon authors
        # locale, rather than the contributors locale.
        name, paypal_id = (u'%s: %s' % (addon.name, addon.charity.name),
                           addon.charity.paypal)
    else:
        name, paypal_id = addon.name, addon.paypal_id
    # l10n: {0} is the addon name
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))

    preapproval = None
    if waffle.flag_is_active(request, 'allow-pre-auth') and request.amo_user:
        preapproval = request.amo_user.get_preapproval()

    paykey, error, status = '', '', ''
    try:
        paykey, status = paypal.get_paykey(
            dict(amount=amount,
                 email=paypal_id,
                 ip=request.META.get('REMOTE_ADDR'),
                 memo=contrib_for,
                 pattern='%s.paypal' % ('apps' if webapp else 'addons'),
                 preapproval=preapproval,
                 slug=addon.slug,
                 uuid=contribution_uuid))
    except paypal.PaypalError as error:
        paypal.paypal_log_cef(request, addon, contribution_uuid,
                              'PayKey Failure', 'PAYKEYFAIL',
                              'There was an error getting the paykey')
        log.error('Error getting paykey, contribution for addon: %s'
                  % addon.pk, exc_info=True)

    if paykey:
        contrib = Contribution(addon_id=addon.id, charity_id=addon.charity_id,
                               amount=amount, source=source,
                               source_locale=request.LANG,
                               annoying=addon.annoying,
                               uuid=str(contribution_uuid),
                               is_suggested=is_suggested,
                               suggested_amount=addon.suggested_amount,
                               comment=comment, paykey=paykey)
        contrib.save()

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({'url': url,
                                             'paykey': paykey,
                                             'error': str(error),
                                             'status': status}),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #44
0
def contribute(request, addon):
    commentlimit = 255  # Enforce paypal-imposed comment length limit

    contrib_type = request.POST.get('type', 'suggested')
    is_suggested = contrib_type == 'suggested'
    source = request.POST.get('source', '')
    comment = request.POST.get('comment', '')

    amount = {
        'suggested': addon.suggested_amount,
        'onetime': request.POST.get('onetime-amount', '')
    }.get(contrib_type, '')
    if not amount:
        amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION

    form = ContributionForm({'amount': amount})
    if len(comment) > commentlimit or not form.is_valid():
        return http.HttpResponse(json.dumps({'error': 'Invalid data.',
                                             'status': '', 'url': '',
                                             'paykey': ''}),
                                 content_type='application/json')

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if addon.charity:
        # TODO(andym): Figure out how to get this in the addon authors
        # locale, rather than the contributors locale.
        name, paypal_id = (u'%s: %s' % (addon.name, addon.charity.name),
                           addon.charity.paypal)
    else:
        name, paypal_id = addon.name, addon.paypal_id
    # l10n: {0} is the addon name
    contrib_for = _(u'Contribution for {0}').format(jinja2.escape(name))

    paykey, error, status = '', '', ''
    try:
        paykey, status = paypal.get_paykey(
            dict(amount=amount,
                 email=paypal_id,
                 ip=request.META.get('REMOTE_ADDR'),
                 memo=contrib_for,
                 pattern='addons.paypal',
                 slug=addon.slug,
                 uuid=contribution_uuid))
    except paypal.PaypalError as error:
        paypal.paypal_log_cef(request, addon, contribution_uuid,
                              'PayKey Failure', 'PAYKEYFAIL',
                              'There was an error getting the paykey')
        log.error('Error getting paykey, contribution for addon: %s'
                  % addon.pk, exc_info=True)

    if paykey:
        contrib = Contribution(addon_id=addon.id, charity_id=addon.charity_id,
                               amount=amount, source=source,
                               source_locale=request.LANG,
                               annoying=addon.annoying,
                               uuid=str(contribution_uuid),
                               is_suggested=is_suggested,
                               suggested_amount=addon.suggested_amount,
                               comment=comment, paykey=paykey)
        contrib.save()

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({'url': url,
                                             'paykey': paykey,
                                             'error': str(error),
                                             'status': status}),
                                 content_type='application/json')
    return http.HttpResponseRedirect(url)
Example #45
0
            'amount': str(amount),
            'email': paypal_id,
            'ip': request.META.get('REMOTE_ADDR'),
            'memo': contrib_for})
    except paypal.AuthError, error:
        paypal_log.error('Authentication error: %s' % error)
    except Exception, error:
        paypal_log.error('Error: %s' % error)

    if paykey:
        contrib = Contribution(addon_id=addon.id,
                           charity_id=addon.charity_id,
                           amount=amount,
                           source=source,
                           source_locale=request.LANG,
                           annoying=addon.annoying,
                           uuid=str(contribution_uuid),
                           is_suggested=is_suggested,
                           suggested_amount=addon.suggested_amount,
                           comment=comment,
                           paykey=paykey)
        contrib.save()

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({'url': url, 'paykey': paykey}),
                                 content_type='application/json')
    elif paykey is None:
        # If there was an error getting the paykey, raise this.
Example #46
0
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())
Example #47
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 = '', '', ''
    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())
Example #48
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'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))
Example #49
0
                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)

    with transaction.commit_on_success():
        contrib = Contribution(addon_id=product.id, amount=price,
                               source=source, source_locale=request.LANG,
                               currency=currency, uuid=str(uuid_),
                               price_tier=tier,
                               type=amo.CONTRIB_INAPP_PENDING,
                               paykey=paykey, user=request.amo_user)
        log.debug('Storing in-app payment 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, product, uuid_,
                                  'Purchase', 'PURCHASE',
                                  'A user purchased using pre-approval')

            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':
Example #50
0
    def test_total_contributions(self):

        c = Contribution()
        c.addon_id = 3615
        c.amount = '9.99'
        c.save()

        tasks.addon_total_contributions(3615)
        a = Addon.objects.no_cache().get(pk=3615)
        eq_(float(a.total_contributions), 9.99)

        c = Contribution()
        c.addon_id = 3615
        c.amount = '10.00'
        c.save()

        tasks.addon_total_contributions(3615)
        a = Addon.objects.no_cache().get(pk=3615)
        eq_(float(a.total_contributions), 19.99)
Example #51
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'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))
Example #52
0
def contribute(request, addon):
    contrib_type = request.POST.get("type", "suggested")
    is_suggested = contrib_type == "suggested"
    source = request.POST.get("source", "")
    comment = request.POST.get("comment", "")

    amount = {"suggested": addon.suggested_amount, "onetime": request.POST.get("onetime-amount", "")}.get(
        contrib_type, ""
    )
    if not amount:
        amount = settings.DEFAULT_SUGGESTED_CONTRIBUTION

    form = ContributionForm({"amount": amount})
    if not form.is_valid():
        return http.HttpResponse(
            json.dumps({"error": "Invalid data.", "status": "", "url": "", "paykey": ""}),
            content_type="application/json",
        )

    contribution_uuid = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if addon.charity:
        # TODO(andym): Figure out how to get this in the addon authors
        # locale, rather than the contributors locale.
        name, paypal_id = (u"%s: %s" % (addon.name, addon.charity.name), addon.charity.paypal)
    else:
        name, paypal_id = addon.name, addon.paypal_id
    # l10n: {0} is the addon name
    contrib_for = _(u"Contribution for {0}").format(jinja2.escape(name))

    paykey, error, status = "", "", ""
    try:
        paykey, status = paypal.get_paykey(
            dict(
                amount=amount,
                email=paypal_id,
                ip=request.META.get("REMOTE_ADDR"),
                memo=contrib_for,
                pattern="addons.paypal",
                slug=addon.slug,
                uuid=contribution_uuid,
            )
        )
    except paypal.PaypalError as error:
        paypal.paypal_log_cef(
            request, addon, contribution_uuid, "PayKey Failure", "PAYKEYFAIL", "There was an error getting the paykey"
        )
        log.error("Error getting paykey, contribution for addon: %s" % addon.pk, exc_info=True)

    if paykey:
        contrib = Contribution(
            addon_id=addon.id,
            charity_id=addon.charity_id,
            amount=amount,
            source=source,
            source_locale=request.LANG,
            annoying=addon.annoying,
            uuid=str(contribution_uuid),
            is_suggested=is_suggested,
            suggested_amount=addon.suggested_amount,
            comment=comment,
            paykey=paykey,
        )
        contrib.save()

    url = "%s?paykey=%s" % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get("result_type") == "json" or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(
            json.dumps({"url": url, "paykey": paykey, "error": str(error), "status": status}),
            content_type="application/json",
        )
    return http.HttpResponseRedirect(url)
Example #53
0
            contrib_for
        })
    except paypal.AuthError, error:
        paypal_log.error('Authentication error: %s' % error)
        nice_error = _('There was a problem communicating with Paypal.')
    except Exception, error:
        paypal_log.error('Error: %s' % error)
        nice_error = _('There was a problem with that contribution.')

    if paykey:
        contrib = Contribution(addon_id=addon.id,
                               charity_id=addon.charity_id,
                               amount=amount,
                               source=source,
                               source_locale=request.LANG,
                               annoying=addon.annoying,
                               uuid=str(contribution_uuid),
                               is_suggested=is_suggested,
                               suggested_amount=addon.suggested_amount,
                               comment=comment,
                               paykey=paykey)
        contrib.save()

    assert settings.PAYPAL_FLOW_URL, 'settings.PAYPAL_FLOW_URL is not defined'

    url = '%s?paykey=%s' % (settings.PAYPAL_FLOW_URL, paykey)
    if request.GET.get('result_type') == 'json' or request.is_ajax():
        # If there was an error getting the paykey, then JSON will
        # not have a paykey and the JS can cope appropriately.
        return http.HttpResponse(json.dumps({
            'url': url,