Example #1
0
def paypal_completed(request, transaction_id, serialize=None, amount=None):
    # Make sure transaction has not yet been processed.
    if Contribution.objects.filter(transaction_id=transaction_id).exists():
        paypal_log.info('Completed IPN already processed')
        return http.HttpResponse('Transaction already processed')

    # Note that when this completes the uuid is moved over to transaction_id.
    try:
        original = Contribution.objects.get(uuid=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    paypal_log.info('Completed IPN received: %s' % transaction_id)
    data = StatsDictField().to_python(php.serialize(serialize))
    update = {'transaction_id': transaction_id,
              'uuid': None, 'post_data': data}

    if amount:
        update['amount'] = _parse_currency(amount)['amount']

    original.update(**update)
    # Send thankyou email.
    try:
        original.mail_thankyou(request)
    except ContributionError as e:
        # A failed thankyou email is not a show stopper, but is good to know.
        paypal_log.error('Thankyou note email failed with error: %s' % e)

    paypal_log_cef(request, original.addon, transaction_id,
                   'Contribution', 'CONTRIBUTION',
                   'A user contributed to an addon')
    paypal_log.info('Completed successfully processed')
    return http.HttpResponse('Success!')
Example #2
0
def paypal_reversal(request, post, transaction):
    try:
        original = Contribution.objects.get(transaction_id=post['txn_id'])
    except Contribution.DoesNotExist:
        return _log_unmatched(post)

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    paypal_log.info('Reversal IPN received: %s' % post['txn_id'])
    amount = _parse_currency(transaction['amount'])
    refund = Contribution.objects.create(
        addon=original.addon, related=original,
        user=original.user, type=amo.CONTRIB_CHARGEBACK,
        amount=-amount['amount'], currency=amount['currency'],
        post_data=php.serialize(post)
    )
    refund.mail_chargeback()

    paypal_log_cef(request, original.addon, post['txn_id'],
                   'Chargeback', 'CHARGEBACK',
                   'A paypal chargeback was processed')

    return http.HttpResponse('Success!')
Example #3
0
def paypal_refunded(request, post, transaction):
    try:
        original = Contribution.objects.get(transaction_id=post['txn_id'])
    except Contribution.DoesNotExist:
        return _log_unmatched(post)

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    original.handle_chargeback('refund')
    paypal_log.info('Refund IPN received: %s' % post['txn_id'])
    amount = _parse_currency(transaction['amount'])

    Contribution.objects.create(addon=original.addon,
                                related=original,
                                user=original.user,
                                type=amo.CONTRIB_REFUND,
                                amount=-amount['amount'],
                                currency=amount['currency'],
                                post_data=php.serialize(post))
    paypal_log.info('Refund successfully processed')

    paypal_log_cef(request, original.addon, post['txn_id'], 'Refund', 'REFUND',
                   'A paypal refund was processed')

    return http.HttpResponse('Success!')
Example #4
0
def paypal_reversal(request, transaction_id, serialize=None, amount=None):
    try:
        original = Contribution.objects.get(transaction_id=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    original.handle_chargeback('reversal')
    paypal_log.info('Reversal IPN received: %s' % transaction_id)
    amount = _parse_currency(amount)
    refund = Contribution.objects.create(addon=original.addon,
                                         related=original,
                                         user=original.user,
                                         type=amo.CONTRIB_CHARGEBACK,
                                         amount=-amount['amount'],
                                         currency=amount['currency'],
                                         post_data=php.serialize(serialize))
    refund.mail_chargeback()

    paypal_log_cef(request, original.addon, transaction_id, 'Chargeback',
                   'CHARGEBACK', 'A paypal chargeback was processed')

    return http.HttpResponse('Success!')
Example #5
0
def paypal_reversal(request, transaction_id, serialize=None, amount=None):
    try:
        original = Contribution.objects.get(transaction_id=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    original.handle_chargeback('reversal')
    paypal_log.info('Reversal IPN received: %s' % transaction_id)
    amount = _parse_currency(amount)
    refund = Contribution.objects.create(
        addon=original.addon, related=original,
        user=original.user, type=amo.CONTRIB_CHARGEBACK,
        amount=-amount['amount'], currency=amount['currency'],
        post_data=php.serialize(serialize)
    )
    refund.mail_chargeback()

    paypal_log_cef(request, original.addon, transaction_id,
                   'Chargeback', 'CHARGEBACK',
                   'A paypal chargeback was processed')

    return http.HttpResponse('Success!')
Example #6
0
def paypal_refunded(request, post, transaction):
    try:
        original = Contribution.objects.get(transaction_id=post['txn_id'])
    except Contribution.DoesNotExist:
        return None

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    paypal_log.info('Refund IPN received: %s' % post['txn_id'])
    amount = _parse_currency(transaction['amount'])

    Contribution.objects.create(
        addon=original.addon, related=original,
        user=original.user, type=amo.CONTRIB_REFUND,
        amount=-amount['amount'], currency=amount['currency'],
        post_data=php.serialize(post)
    )
    paypal_log.info('Refund successfully processed')

    paypal_log_cef(request, original.addon, post['txn_id'],
                   'Refund', 'REFUND',
                   'A paypal refund was processed')

    return http.HttpResponse('Success!')
Example #7
0
def paypal_reversal(request, post, transaction):
    try:
        original = Contribution.objects.get(transaction_id=post['txn_id'])
    except Contribution.DoesNotExist:
        return None

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    paypal_log.info('Reversal IPN received: %s' % post['txn_id'])
    amount = _parse_currency(transaction['amount'])
    refund = Contribution.objects.create(addon=original.addon,
                                         related=original,
                                         user=original.user,
                                         type=amo.CONTRIB_CHARGEBACK,
                                         amount=-amount['amount'],
                                         currency=amount['currency'],
                                         post_data=php.serialize(post))
    refund.mail_chargeback()

    paypal_log_cef(request, original.addon, post['txn_id'], 'Chargeback',
                   'CHARGEBACK', 'A paypal chargeback was processed')

    return http.HttpResponse('Success!')
Example #8
0
def purchase_complete(request, addon, status):
    result = ''
    if status == 'complete':
        uuid_ = request.GET.get('uuid')
        log.debug('Looking up contrib for uuid: %s' % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING)
                  | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE))
        con = get_object_or_404(Contribution, lookup)

        log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' %
                  (addon.pk, request.amo_user.pk, con.paykey[:10]))
        try:
            result = paypal.check_purchase(con.paykey)
            if result == 'ERROR':
                paypal.paypal_log_cef(
                    request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL',
                    'Checking purchase state returned error')
                raise
        except:
            paypal.paypal_log_cef(
                request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL',
                'There was an error checking purchase state')
            log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' %
                      (addon.pk, request.amo_user.pk, con.paykey[:10]),
                      exc_info=True)
            result = 'ERROR'
            status = 'error'

        log.debug('Paypal returned: %s for paykey: %s' %
                  (result, con.paykey[:10]))
        if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING:
            con.update(type=amo.CONTRIB_PURCHASE)

    context = {
        'realurl': request.GET.get('realurl', ''),
        'status': status,
        'result': result
    }

    # For mobile, bounce back to the details page.
    if request.MOBILE:
        url = urlparams(shared_url('detail', addon), **context)
        return http.HttpResponseRedirect(url)

    context.update({'addon': addon})
    response = jingo.render(request, 'addons/paypal_result.html', context)
    response['x-frame-options'] = 'allow'
    return response
Example #9
0
def purchase_complete(request, addon, status):
    result = ""
    if status == "complete":
        uuid_ = request.GET.get("uuid")
        log.debug("Looking up contrib for uuid: %s" % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        lookup = Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)
        con = get_object_or_404(Contribution, lookup)

        log.debug(
            "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10])
        )
        try:
            result = paypal.check_purchase(con.paykey)
            if result == "ERROR":
                paypal.paypal_log_cef(
                    request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "Checking purchase state returned error"
                )
                raise
        except:
            paypal.paypal_log_cef(
                request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "There was an error checking purchase state"
            )
            log.error(
                "Check purchase paypal addon: %s, user: %s, paykey: %s"
                % (addon.pk, request.amo_user.pk, con.paykey[:10]),
                exc_info=True,
            )
            result = "ERROR"
            status = "error"

        log.debug("Paypal returned: %s for paykey: %s" % (result, con.paykey[:10]))
        if result == "COMPLETED" and con.type == amo.CONTRIB_PENDING:
            con.update(type=amo.CONTRIB_PURCHASE)

    response = jingo.render(
        request,
        "addons/paypal_result.html",
        {
            "addon": addon,
            "realurl": request.GET.get("realurl", ""),
            # What the client claimed they did.
            "status": status,
            # And what paypal thought of that claim.
            "result": result,
        },
    )
    response["x-frame-options"] = "allow"
    return response
Example #10
0
def purchase_complete(request, addon, status):
    result = ''
    if status == 'complete':
        uuid_ = request.GET.get('uuid')
        log.debug('Looking up contrib for uuid: %s' % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING)
                  | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE))
        con = get_object_or_404(Contribution, lookup)

        log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s' %
                  (addon.pk, request.amo_user.pk, con.paykey[:10]))
        try:
            result = paypal.check_purchase(con.paykey)
            if result == 'ERROR':
                paypal.paypal_log_cef(
                    request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL',
                    'Checking purchase state returned error')
                raise
        except:
            paypal.paypal_log_cef(
                request, addon, uuid_, 'Purchase Fail', 'PURCHASEFAIL',
                'There was an error checking purchase state')
            log.error('Check purchase paypal addon: %s, user: %s, paykey: %s' %
                      (addon.pk, request.amo_user.pk, con.paykey[:10]),
                      exc_info=True)
            result = 'ERROR'
            status = 'error'

        log.debug('Paypal returned: %s for paykey: %s' %
                  (result, con.paykey[:10]))
        if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING:
            con.update(type=amo.CONTRIB_PURCHASE)

    response = jingo.render(
        request,
        'addons/paypal_result.html',
        {
            'addon': addon,
            'realurl': request.GET.get('realurl', ''),
            # What the client claimed they did.
            'status': status,
            # And what paypal thought of that claim.
            'result': result
        })
    response['x-frame-options'] = 'allow'
    return response
Example #11
0
def purchase_complete(request, addon, status):
    result = ""
    if status == "complete":
        uuid_ = request.GET.get("uuid")
        log.debug("Looking up contrib for uuid: %s" % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        lookup = Q(uuid=uuid_, type=amo.CONTRIB_PENDING) | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE)
        con = get_object_or_404(Contribution, lookup)

        log.debug(
            "Check purchase paypal addon: %s, user: %s, paykey: %s" % (addon.pk, request.amo_user.pk, con.paykey[:10])
        )
        try:
            result = paypal.check_purchase(con.paykey)
            if result == "ERROR":
                paypal.paypal_log_cef(
                    request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "Checking purchase state returned error"
                )
                raise
        except:
            paypal.paypal_log_cef(
                request, addon, uuid_, "Purchase Fail", "PURCHASEFAIL", "There was an error checking purchase state"
            )
            log.error(
                "Check purchase paypal addon: %s, user: %s, paykey: %s"
                % (addon.pk, request.amo_user.pk, con.paykey[:10]),
                exc_info=True,
            )
            result = "ERROR"
            status = "error"

        log.debug("Paypal returned: %s for paykey: %s" % (result, con.paykey[:10]))
        if result == "COMPLETED" and con.type == amo.CONTRIB_PENDING:
            con.update(type=amo.CONTRIB_PURCHASE)

    context = {"realurl": request.GET.get("realurl", ""), "status": status, "result": result}

    # For mobile, bounce back to the details page.
    if request.MOBILE:
        url = urlparams(shared_url("detail", addon), **context)
        return redirect(url)

    context.update({"addon": addon})
    response = jingo.render(request, "addons/paypal_result.html", context)
    response["x-frame-options"] = "allow"
    return response
Example #12
0
def purchase_complete(request, addon, status):
    result = ''
    if status == 'complete':
        uuid_ = request.GET.get('uuid')
        log.debug('Looking up contrib for uuid: %s' % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) |
                  Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE))
        con = get_object_or_404(Contribution, lookup)

        log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s'
                  % (addon.pk, request.amo_user.pk, con.paykey[:10]))
        try:
            result = paypal.check_purchase(con.paykey)
            if result == 'ERROR':
                paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail',
                                      'PURCHASEFAIL',
                                      'Checking purchase state returned error')
                raise
        except:
            paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail',
                                  'PURCHASEFAIL',
                                  'There was an error checking purchase state')
            log.error('Check purchase paypal addon: %s, user: %s, paykey: %s'
                      % (addon.pk, request.amo_user.pk, con.paykey[:10]),
                      exc_info=True)
            result = 'ERROR'
            status = 'error'

        log.debug('Paypal returned: %s for paykey: %s'
                  % (result, con.paykey[:10]))
        if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING:
            con.update(type=amo.CONTRIB_PURCHASE)

    context = {'realurl': request.GET.get('realurl', ''),
               'status': status, 'result': result}

    # For mobile, bounce back to the details page.
    if request.MOBILE:
        url = urlparams(shared_url('detail', addon), **context)
        return http.HttpResponseRedirect(url)

    context.update({'addon': addon})
    response = jingo.render(request, 'addons/paypal_result.html', context)
    response['x-frame-options'] = 'allow'
    return response
Example #13
0
def purchase_complete(request, addon, status):
    result = ''
    if status == 'complete':
        uuid_ = request.GET.get('uuid')
        log.debug('Looking up contrib for uuid: %s' % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) |
                  Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE))
        con = get_object_or_404(Contribution, lookup)

        log.debug('Check purchase paypal addon: %s, user: %s, paykey: %s'
                  % (addon.pk, request.amo_user.pk, con.paykey[:10]))
        try:
            result = paypal.check_purchase(con.paykey)
            if result == 'ERROR':
                paypal.paypal_log_cef(request, addon, uuid_,
                              'Purchase Fail', 'PURCHASEFAIL',
                              'Checking purchase state returned error')
                raise
        except:
            paypal.paypal_log_cef(request, addon, uuid_,
                              'Purchase Fail', 'PURCHASEFAIL',
                              'There was an error checking purchase state')
            log.error('Check purchase paypal addon: %s, user: %s, paykey: %s'
                      % (addon.pk, request.amo_user.pk, con.paykey[:10]),
                      exc_info=True)
            result = 'ERROR'
            status = 'error'

        log.debug('Paypal returned: %s for paykey: %s'
                  % (result, con.paykey[:10]))
        if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING:
            con.update(type=amo.CONTRIB_PURCHASE)

    response = jingo.render(request, 'addons/paypal_result.html',
                            {'addon': addon,
                             'realurl': request.GET.get('realurl', ''),
                             # What the client claimed they did.
                             'status': status,
                             # And what paypal thought of that claim.
                             'result': result})
    response['x-frame-options'] = 'allow'
    return response
Example #14
0
def paypal_completed(request, transaction_id, serialize=None, amount=None):
    # Make sure transaction has not yet been processed.
    if Contribution.objects.filter(transaction_id=transaction_id).exists():
        paypal_log.info('Completed IPN already processed')
        return http.HttpResponse('Transaction already processed')

    # Note that when this completes the uuid is moved over to transaction_id.
    try:
        original = Contribution.objects.get(uuid=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    paypal_log.info('Completed IPN received: %s' % transaction_id)
    data = StatsDictField().to_python(php.serialize(serialize))
    update = {
        'transaction_id': transaction_id,
        'uuid': None,
        'post_data': data
    }

    if original.type == amo.CONTRIB_PENDING:
        # This is a purchase that has failed to hit the completed page.
        # But this ok, this IPN means that it all went through.
        update['type'] = amo.CONTRIB_PURCHASE
        # If they failed to hit the completed page, they also failed
        # to get it logged properly. This will add the log in.
        amo.log(amo.LOG.PURCHASE_ADDON, original.addon)

    if amount:
        update['amount'] = _parse_currency(amount)['amount']

    original.update(**update)
    # Send thankyou email.
    try:
        original.mail_thankyou(request)
    except ContributionError as e:
        # A failed thankyou email is not a show stopper, but is good to know.
        paypal_log.error('Thankyou note email failed with error: %s' % e)

    paypal_log_cef(request, original.addon, transaction_id, 'Purchase',
                   'PURCHASE', 'A user purchased or contributed to an addon')
    paypal_log.info('Completed successfully processed')
    return http.HttpResponse('Success!')
Example #15
0
def paypal_completed(request, transaction_id, serialize=None, amount=None):
    # Make sure transaction has not yet been processed.
    if Contribution.objects.filter(transaction_id=transaction_id).exists():
        paypal_log.info('Completed IPN already processed')
        return http.HttpResponse('Transaction already processed')

    # Note that when this completes the uuid is moved over to transaction_id.
    try:
        original = Contribution.objects.get(uuid=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    paypal_log.info('Completed IPN received: %s' % transaction_id)
    data = StatsDictField().to_python(php.serialize(serialize))
    update = {'transaction_id': transaction_id,
              'uuid': None, 'post_data': data}

    if original.type == amo.CONTRIB_PENDING:
        # This is a purchase that has failed to hit the completed page.
        # But this ok, this IPN means that it all went through.
        update['type'] = amo.CONTRIB_PURCHASE
        # If they failed to hit the completed page, they also failed
        # to get it logged properly. This will add the log in.
        amo.log(amo.LOG.PURCHASE_ADDON, original.addon)

    if amount:
        update['amount'] = _parse_currency(amount)['amount']

    original.update(**update)
    # Send thankyou email.
    try:
        original.mail_thankyou(request)
    except ContributionError as e:
        # A failed thankyou email is not a show stopper, but is good to know.
        paypal_log.error('Thankyou note email failed with error: %s' % e)

    paypal_log_cef(request, original.addon, transaction_id,
                   'Purchase', 'PURCHASE',
                   'A user purchased or contributed to an addon')
    paypal_log.info('Completed successfully processed')
    return http.HttpResponse('Success!')
Example #16
0
def pay(request, signed_req, pay_req):
    amount = pay_req['request']['price']
    currency = pay_req['request']['currency']
    source = request.POST.get('source', '')
    product = pay_req['_config'].addon
    # L10n: {0} is the product name. {1} is the application name.
    contrib_for = (
        _(u'Mozilla Marketplace in-app payment for {0} to {1}').format(
            pay_req['request']['name'], product.name))
    uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()

    paykey, status = '', ''
    preapproval = None
    if request.amo_user:
        preapproval = request.amo_user.get_preapproval()

    try:
        paykey, status = paypal.get_paykey(
            dict(
                amount=amount,
                chains=settings.PAYPAL_CHAINS,
                currency=currency,
                email=product.paypal_id,
                ip=request.META.get('REMOTE_ADDR'),
                memo=contrib_for,
                pattern='inapp_pay.pay_status',
                preapproval=preapproval,
                qs={'realurl': request.POST.get('realurl')},
                slug=pay_req['_config'].
                pk,  # passed to pay_done() via reverse()
                uuid=uuid_))
    except paypal.PaypalError, exc:
        paypal.paypal_log_cef(request, product, uuid_, 'in-app PayKey Failure',
                              'PAYKEYFAIL',
                              'There was an error getting the paykey')
        log.error(u'Error getting paykey, in-app payment: %s' %
                  pay_req['_config'].pk,
                  exc_info=True)
        InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config'])
        return render_error(request, exc)
Example #17
0
def paypal_completed(request, post, transaction):
    # Make sure transaction has not yet been processed.
    if Contribution.objects.filter(transaction_id=post['txn_id']).exists():
        paypal_log.info('Completed IPN already processed')
        return http.HttpResponse('Transaction already processed')

    # Note that when this completes the uuid is moved over to transaction_id.
    try:
        original = Contribution.objects.get(uuid=post['txn_id'])
    except Contribution.DoesNotExist:
        return None

    paypal_log.info('Completed IPN received: %s' % post['txn_id'])
    data = StatsDictField().to_python(php.serialize(post))
    update = {
        'transaction_id': post['txn_id'],
        'uuid': None,
        'post_data': data
    }

    if original.type == amo.CONTRIB_PENDING:
        # This is a purchase that has failed to hit the completed page.
        # But this ok, this IPN means that it all went through.
        update['type'] = amo.CONTRIB_PURCHASE

    if 'mc_gross' in post:
        update['amount'] = post['mc_gross']

    original.update(**update)
    # Send thankyou email.
    try:
        original.mail_thankyou(request)
    except ContributionError as e:
        # A failed thankyou email is not a show stopper, but is good to know.
        paypal_log.error('Thankyou note email failed with error: %s' % e)

    paypal_log_cef(request, original.addon, post['txn_id'], 'Purchase',
                   'PURCHASE', 'A user purchased or contributed to an addon')
    paypal_log.info('Completed successfully processed')
    return http.HttpResponse('Success!')
Example #18
0
def pay(request, signed_req, pay_req):
    amount = pay_req["request"]["price"]
    currency = pay_req["request"]["currency"]
    source = request.POST.get("source", "")
    addon = pay_req["_config"].addon
    # L10n: {0} is the product name. {1} is the application name
    contrib_for = _(u"In-app payment for {0} to {1}").format(
        jinja2.escape(pay_req["request"]["name"]), jinja2.escape(addon.name)
    )
    uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()

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

    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="inapp_pay.pay_done",
                preapproval=preapproval,
                qs={"realurl": request.POST.get("realurl")},
                slug=pay_req["_config"].pk,  # passed to pay_done() via reverse()
                uuid=uuid_,
            )
        )
    except paypal.PaypalError, exc:
        paypal.paypal_log_cef(
            request, addon, uuid_, "in-app PayKey Failure", "PAYKEYFAIL", "There was an error getting the paykey"
        )
        log.error(u"Error getting paykey, in-app payment: %s" % pay_req["_config"].pk, exc_info=True)
        InappPayLog.log(request, "PAY_ERROR", config=pay_req["_config"])
        return render_error(request, exc)
Example #19
0
def pay(request, signed_req, pay_req):
    paykey, status = '', ''
    preapproval = None
    if request.amo_user:
        preapproval = request.amo_user.get_preapproval()
    tier, price, currency = _get_price(pay_req, preapproval=preapproval)

    source = request.POST.get('source', '')
    product = pay_req['_config'].addon
    # L10n: {0} is the product name. {1} is the application name.
    contrib_for = (_(u'Mozilla Marketplace in-app payment for {0} to {1}')
                   .format(pay_req['request']['name'], product.name))
    uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()


    try:
        paykey, status = paypal.get_paykey(dict(
            amount=price,
            chains=settings.PAYPAL_CHAINS,
            currency=currency,
            email=product.paypal_id,
            ip=request.META.get('REMOTE_ADDR'),
            memo=contrib_for,
            pattern='inapp_pay.pay_status',
            preapproval=preapproval,
            qs={'realurl': request.POST.get('realurl')},
            slug=pay_req['_config'].pk,  # passed to pay_done() via reverse()
            uuid=uuid_
        ))
    except paypal.PaypalError, exc:
        paypal.paypal_log_cef(request, product, uuid_,
                              'in-app PayKey Failure', 'PAYKEYFAIL',
                              'There was an error getting the paykey')
        log.error(u'Error getting paykey, in-app payment: %s'
                  % pay_req['_config'].pk,
                  exc_info=True)
        InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config'])
        return render_error(request, exc)
Example #20
0
def paypal_completed(request, post, transaction):
    # Make sure transaction has not yet been processed.
    if Contribution.objects.filter(transaction_id=post['txn_id']).exists():
        paypal_log.info('Completed IPN already processed')
        return http.HttpResponse('Transaction already processed')

    # Note that when this completes the uuid is moved over to transaction_id.
    try:
        original = Contribution.objects.get(uuid=post['txn_id'])
    except Contribution.DoesNotExist:
        return _log_unmatched(post)

    paypal_log.info('Completed IPN received: %s' % post['txn_id'])
    data = StatsDictField().to_python(php.serialize(post))
    update = {'transaction_id': post['txn_id'],
              'uuid': None, 'post_data': data}

    if original.type == amo.CONTRIB_PENDING:
        # This is a purchase that has failed to hit the completed page.
        # But this ok, this IPN means that it all went through.
        update['type'] = amo.CONTRIB_PURCHASE

    if 'mc_gross' in post:
        update['amount'] = post['mc_gross']

    original.update(**update)
    # Send thankyou email.
    try:
        original.mail_thankyou(request)
    except ContributionError as e:
        # A failed thankyou email is not a show stopper, but is good to know.
        paypal_log.error('Thankyou note email failed with error: %s' % e)

    paypal_log_cef(request, original.addon, post['txn_id'],
                   'Purchase', 'PURCHASE',
                   'A user purchased or contributed to an addon')
    paypal_log.info('Completed successfully processed')
    return http.HttpResponse('Success!')
Example #21
0
def paypal_refunded(request, transaction_id, serialize=None, amount=None):
    try:
        original = Contribution.objects.get(transaction_id=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    original.handle_chargeback('refund')
    paypal_log.info('Refund IPN received: %s' % transaction_id)
    price_currency = _parse_currency(amount)
    amount = price_currency['amount']
    currency = price_currency['currency']

    # Contribution with negative amount for refunds.
    Contribution.objects.create(
        addon=original.addon, related=original,
        user=original.user, type=amo.CONTRIB_REFUND,
        amount=-amount, currency=currency,
        price_tier=original.price_tier,
        post_data=php.serialize(serialize)
    )
    paypal_log.info('Refund successfully processed')

    paypal_log_cef(request, original.addon, transaction_id,
                   'Refund', 'REFUND',
                   'A paypal refund was processed')

    return http.HttpResponse('Success!')
Example #22
0
def paypal_refunded(request, transaction_id, serialize=None, amount=None):
    try:
        original = Contribution.objects.get(transaction_id=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    # If the contribution has a related contribution we've processed it.
    try:
        original = Contribution.objects.get(related=original)
        paypal_log.info('Related contribution, state: %s, pk: %s' %
                        (original.related.type, original.related.pk))
        return http.HttpResponse('Transaction already processed')
    except Contribution.DoesNotExist:
        pass

    original.handle_chargeback('refund')
    paypal_log.info('Refund IPN received: %s' % transaction_id)
    price_currency = _parse_currency(amount)
    amount = price_currency['amount']
    currency = price_currency['currency']

    # Contribution with negative amount for refunds.
    Contribution.objects.create(addon=original.addon,
                                related=original,
                                user=original.user,
                                type=amo.CONTRIB_REFUND,
                                amount=-amount,
                                currency=currency,
                                price_tier=original.price_tier,
                                post_data=php.serialize(serialize))
    paypal_log.info('Refund successfully processed')

    paypal_log_cef(request, original.addon, transaction_id, 'Refund', 'REFUND',
                   'A paypal refund was processed')

    return http.HttpResponse('Success!')
Example #23
0
def paypal_completed(request, transaction_id, serialize=None, amount=None):
    # Make sure transaction has not yet been processed.
    if Contribution.objects.filter(transaction_id=transaction_id).exists():
        paypal_log.info('Completed IPN already processed')
        return http.HttpResponse('Transaction already processed')

    # Note that when this completes the uuid is moved over to transaction_id.
    try:
        original = Contribution.objects.get(uuid=transaction_id)
    except Contribution.DoesNotExist:
        paypal_log.info('Ignoring transaction: %s' % transaction_id)
        return http.HttpResponse('Transaction not found; skipping.')

    paypal_log.info('Completed IPN received: %s' % transaction_id)
    data = StatsDictField().to_python(php.serialize(serialize))
    update = {
        'transaction_id': transaction_id,
        'uuid': None,
        'post_data': data
    }

    if amount:
        update['amount'] = _parse_currency(amount)['amount']

    original.update(**update)
    # Send thankyou email.
    try:
        original.mail_thankyou(request)
    except ContributionError as e:
        # A failed thankyou email is not a show stopper, but is good to know.
        paypal_log.error('Thankyou note email failed with error: %s' % e)

    paypal_log_cef(request, original.addon, transaction_id, 'Contribution',
                   'CONTRIBUTION', 'A user contributed to an addon')
    paypal_log.info('Completed successfully processed')
    return http.HttpResponse('Success!')
Example #24
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 #25
0
def pay(request, signed_req, pay_req):
    paykey, status = '', ''
    preapproval = None
    if request.amo_user:
        preapproval = request.amo_user.get_preapproval()
    tier, price, currency = _get_price(pay_req, preapproval=preapproval)

    source = request.POST.get('source', '')
    product = pay_req['_config'].addon
    # L10n: {0} is the product name. {1} is the application name.
    contrib_for = (_(u'Firefox Marketplace in-app payment for {0} to {1}')
                   .format(pay_req['request']['name'], product.name))
    # TODO(solitude): solitude lib will create these for us.
    uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if waffle.flag_is_active(request, 'solitude-payments'):
        # TODO(solitude): when the migration of data is completed, we
        # will be able to remove this. Seller data is populated in solitude
        # on submission or devhub changes. If those don't occur, you won't be
        # able to sell at all.
        client.create_seller_for_pay(product)

        complete = reverse('inapp_pay.pay_status',
                           args=[pay_req['_config'].pk, 'complete'])
        cancel = reverse('inapp_pay.pay_status',
                         args=[pay_req['_config'].pk, 'cancel'])

        # TODO(bug 748137): remove retry is False.
        try:
            result = client.pay({'amount': price, 'currency': currency,
                                 'buyer': request.amo_user, 'seller': product,
                                 'memo': contrib_for, 'complete': complete,
                                 'cancel': cancel}, retry=False)
        except client.Error as exc:
            paypal.paypal_log_cef(request, product, uuid_,
                                  'in-app PayKey Failure', 'PAYKEYFAIL',
                                  'There was an error getting the paykey')
            log.error(u'Error getting paykey, in-app payment: %s'
                      % pay_req['_config'].pk,
                      exc_info=True)
            InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config'])
            return render_error(request, exc)

        #TODO(solitude): just use the dictionary when solitude is live.
        paykey = result.get('pay_key', '')
        status = result.get('status', '')
        uuid_ = result.get('uuid', '')

    else:
        try:
            paykey, status = paypal.get_paykey(dict(
                amount=price,
                chains=settings.PAYPAL_CHAINS,
                currency=currency,
                email=product.paypal_id,
                ip=request.META.get('REMOTE_ADDR'),
                memo=contrib_for,
                pattern='inapp_pay.pay_status',
                preapproval=preapproval,
                qs={'realurl': request.POST.get('realurl')},
                slug=pay_req['_config'].pk,  # passed to pay_done()
                                             # via reverse()
                uuid=uuid_
            ))
        except paypal.PaypalError, exc:
            paypal.paypal_log_cef(request, product, uuid_,
                                  'in-app PayKey Failure', 'PAYKEYFAIL',
                                  'There was an error getting the paykey')
            log.error(u'Error getting paykey, in-app payment: %s'
                      % pay_req['_config'].pk,
                      exc_info=True)
            InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config'])
            return render_error(request, exc)
Example #26
0
def purchase_done(request, addon, status):
    result = ''
    if status == 'complete':
        uuid_ = request.GET.get('uuid')
        log.debug('Looking up contrib for uuid: %s' % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        #
        # TODO(solitude): this will change when we figure out what to do
        # with Contributions.
        lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING)
                  | Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE))
        con = get_object_or_404(Contribution, lookup)

        log.debug('Check purchase addon: %s, user: %s, paykey: %s' %
                  (addon.pk, request.amo_user.pk, con.paykey[:10]))

        if waffle.flag_is_active(request, 'solitude-payments'):
            try:
                res = client.post_pay_check(data={'uuid': uuid_})
            except client.Error:
                paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail',
                                      'PURCHASEFAIL',
                                      'Checking purchase error')
                raise

            result = res['status']

        # TODO(solitude): can be removed once solitude is live.
        else:
            try:
                result = paypal.check_purchase(con.paykey)
                if result == 'ERROR':
                    paypal.paypal_log_cef(request, addon, uuid_,
                                          'Purchase Fail', 'PURCHASEFAIL',
                                          'Checking purchase error')
                    raise
            except:
                paypal.paypal_log_cef(request, addon, uuid_, 'Purchase Fail',
                                      'PURCHASEFAIL',
                                      'There was an error checking purchase')
                log.error('Check purchase addon: %s, user: %s, paykey: %s' %
                          (addon.pk, request.amo_user.pk, con.paykey[:10]),
                          exc_info=True)
                result = 'ERROR'
                status = 'error'

        log.debug('Paypal returned: %s for paykey: %s' %
                  (result, con.paykey[:10]))
        if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING:
            amo.log(amo.LOG.PURCHASE_ADDON, addon)
            con.update(type=amo.CONTRIB_PURCHASE)

    context = {
        'realurl': request.GET.get('realurl', ''),
        'status': status,
        'result': result,
        'product': addon,
        'error': amo.PAYMENT_DETAILS_ERROR.get(result, '')
    }

    response = jingo.render(request, 'purchase/done.html', context)
    response['x-frame-options'] = 'allow'
    return response
Example #27
0
                               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':
                    log.debug(
                        'Check in-app payment is completed for uuid: %s' %
                        uuid_)
                    contrib.type = amo.CONTRIB_INAPP
                else:
                    # In this case PayPal disagreed, we should not be trusting
                    # what get_paykey said. Which is a worry.
                    log.error('Check in-app payment failed on uuid: %s' %
                              uuid_)
Example #28
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 #29
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 #30
0
            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":
                    log.debug("Check in-app payment is completed for uuid: %s" % uuid_)
                    contrib.type = amo.CONTRIB_INAPP
                else:
                    # In this case PayPal disagreed, we should not be trusting
                    # what get_paykey said. Which is a worry.
                    log.error("Check in-app payment failed on uuid: %s" % uuid_)
                    status = "NOT-COMPLETED"
            else:
                # TODO(solitude): remove this when solitude goes live.
Example #31
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 #32
0
def pay(request, signed_req, pay_req):
    paykey, status = "", ""
    preapproval = None
    if request.amo_user:
        preapproval = request.amo_user.get_preapproval()
    tier, price, currency = _get_price(pay_req, preapproval=preapproval)

    source = request.POST.get("source", "")
    product = pay_req["_config"].addon
    # L10n: {0} is the product name. {1} is the application name.
    contrib_for = _(u"Firefox Marketplace in-app payment for {0} to {1}").format(
        pay_req["request"]["name"], product.name
    )
    # TODO(solitude): solitude lib will create these for us.
    uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if waffle.flag_is_active(request, "solitude-payments"):
        # TODO(solitude): when the migration of data is completed, we
        # will be able to remove this. Seller data is populated in solitude
        # on submission or devhub changes. If those don't occur, you won't be
        # able to sell at all.
        client.create_seller_for_pay(product)

        complete = reverse("inapp_pay.pay_status", args=[pay_req["_config"].pk, "complete"])
        cancel = reverse("inapp_pay.pay_status", args=[pay_req["_config"].pk, "cancel"])

        # TODO(bug 748137): remove retry is False.
        try:
            result = client.pay(
                {
                    "amount": price,
                    "currency": currency,
                    "buyer": request.amo_user,
                    "seller": product,
                    "memo": contrib_for,
                    "complete": complete,
                    "cancel": cancel,
                },
                retry=False,
            )
        except client.Error as exc:
            paypal.paypal_log_cef(
                request, product, uuid_, "in-app PayKey Failure", "PAYKEYFAIL", "There was an error getting the paykey"
            )
            log.error(u"Error getting paykey, in-app payment: %s" % pay_req["_config"].pk, exc_info=True)
            InappPayLog.log(request, "PAY_ERROR", config=pay_req["_config"])
            return render_error(request, exc)

        # TODO(solitude): just use the dictionary when solitude is live.
        paykey = result.get("pay_key", "")
        status = result.get("status", "")
        uuid_ = result.get("uuid", "")

    else:
        try:
            paykey, status = paypal.get_paykey(
                dict(
                    amount=price,
                    chains=settings.PAYPAL_CHAINS,
                    currency=currency,
                    email=product.paypal_id,
                    ip=request.META.get("REMOTE_ADDR"),
                    memo=contrib_for,
                    pattern="inapp_pay.pay_status",
                    preapproval=preapproval,
                    qs={"realurl": request.POST.get("realurl")},
                    slug=pay_req["_config"].pk,  # passed to pay_done()
                    # via reverse()
                    uuid=uuid_,
                )
            )
        except paypal.PaypalError, exc:
            paypal.paypal_log_cef(
                request, product, uuid_, "in-app PayKey Failure", "PAYKEYFAIL", "There was an error getting the paykey"
            )
            log.error(u"Error getting paykey, in-app payment: %s" % pay_req["_config"].pk, exc_info=True)
            InappPayLog.log(request, "PAY_ERROR", config=pay_req["_config"])
            return render_error(request, exc)
Example #33
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 #34
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 #35
0
def purchase_done(request, addon, status):
    result = ''
    if status == 'complete':
        uuid_ = request.GET.get('uuid')
        log.debug('Looking up contrib for uuid: %s' % uuid_)

        # The IPN may, or may not have come through. Which means looking for
        # a for pre or post IPN contributions. If both fail, then we've not
        # got a matching contribution.
        #
        # TODO(solitude): this will change when we figure out what to do
        # with Contributions.
        lookup = (Q(uuid=uuid_, type=amo.CONTRIB_PENDING) |
                  Q(transaction_id=uuid_, type=amo.CONTRIB_PURCHASE))
        con = get_object_or_404(Contribution, lookup)

        log.debug('Check purchase addon: %s, user: %s, paykey: %s'
                  % (addon.pk, request.amo_user.pk, con.paykey[:10]))

        if waffle.flag_is_active(request, 'solitude-payments'):
            try:
                res = client.post_pay_check(data={'uuid': uuid_})
            except client.Error:
                paypal.paypal_log_cef(request, addon, uuid_,
                                      'Purchase Fail', 'PURCHASEFAIL',
                                      'Checking purchase error')
                raise

            result = res['status']

        # TODO(solitude): can be removed once solitude is live.
        else:
            try:
                result = paypal.check_purchase(con.paykey)
                if result == 'ERROR':
                    paypal.paypal_log_cef(request, addon, uuid_,
                                          'Purchase Fail', 'PURCHASEFAIL',
                                          'Checking purchase error')
                    raise
            except:
                paypal.paypal_log_cef(request, addon, uuid_,
                                      'Purchase Fail', 'PURCHASEFAIL',
                                      'There was an error checking purchase')
                log.error('Check purchase addon: %s, user: %s, paykey: %s'
                          % (addon.pk, request.amo_user.pk, con.paykey[:10]),
                          exc_info=True)
                result = 'ERROR'
                status = 'error'

        log.debug('Paypal returned: %s for paykey: %s'
                  % (result, con.paykey[:10]))
        if result == 'COMPLETED' and con.type == amo.CONTRIB_PENDING:
            amo.log(amo.LOG.PURCHASE_ADDON, addon)
            con.update(type=amo.CONTRIB_PURCHASE)

    context = {'realurl': request.GET.get('realurl', ''),
               'status': status, 'result': result, 'product': addon,
               'error': amo.PAYMENT_DETAILS_ERROR.get(result, '')}

    response = jingo.render(request, 'purchase/done.html', context)
    response['x-frame-options'] = 'allow'
    return response
Example #36
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 #37
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 #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, 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 #39
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 #40
0
            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':
                    log.debug('Check in-app payment is completed for uuid: %s'
                          % uuid_)
                    contrib.type = amo.CONTRIB_INAPP
                else:
                    # In this case PayPal disagreed, we should not be trusting
                    # what get_paykey said. Which is a worry.
                    log.error('Check in-app payment failed on uuid: %s'
                              % uuid_)
                    status = 'NOT-COMPLETED'
Example #41
0
def pay(request, signed_req, pay_req):
    paykey, status = '', ''
    preapproval = None
    if request.amo_user:
        preapproval = request.amo_user.get_preapproval()
    tier, price, currency = _get_price(pay_req, preapproval=preapproval)

    source = request.POST.get('source', '')
    product = pay_req['_config'].addon
    # L10n: {0} is the product name. {1} is the application name.
    contrib_for = (
        _(u'Firefox Marketplace in-app payment for {0} to {1}').format(
            pay_req['request']['name'], product.name))
    # TODO(solitude): solitude lib will create these for us.
    uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()

    if waffle.flag_is_active(request, 'solitude-payments'):
        # TODO(solitude): when the migration of data is completed, we
        # will be able to remove this. Seller data is populated in solitude
        # on submission or devhub changes. If those don't occur, you won't be
        # able to sell at all.
        client.create_seller_for_pay(product)

        complete = reverse('inapp_pay.pay_status',
                           args=[pay_req['_config'].pk, 'complete'])
        cancel = reverse('inapp_pay.pay_status',
                         args=[pay_req['_config'].pk, 'cancel'])

        # TODO(bug 748137): remove retry is False.
        try:
            result = client.pay(
                {
                    'amount': price,
                    'currency': currency,
                    'buyer': request.amo_user,
                    'seller': product,
                    'memo': contrib_for,
                    'complete': complete,
                    'cancel': cancel
                },
                retry=False)
        except client.Error as exc:
            paypal.paypal_log_cef(request, product, uuid_,
                                  'in-app PayKey Failure', 'PAYKEYFAIL',
                                  'There was an error getting the paykey')
            log.error(u'Error getting paykey, in-app payment: %s' %
                      pay_req['_config'].pk,
                      exc_info=True)
            InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config'])
            return render_error(request, exc)

        #TODO(solitude): just use the dictionary when solitude is live.
        paykey = result.get('pay_key', '')
        status = result.get('status', '')
        uuid_ = result.get('uuid', '')

    else:
        try:
            paykey, status = paypal.get_paykey(
                dict(
                    amount=price,
                    chains=settings.PAYPAL_CHAINS,
                    currency=currency,
                    email=product.paypal_id,
                    ip=request.META.get('REMOTE_ADDR'),
                    memo=contrib_for,
                    pattern='inapp_pay.pay_status',
                    preapproval=preapproval,
                    qs={'realurl': request.POST.get('realurl')},
                    slug=pay_req['_config'].pk,  # passed to pay_done()
                    # via reverse()
                    uuid=uuid_))
        except paypal.PaypalError, exc:
            paypal.paypal_log_cef(request, product, uuid_,
                                  'in-app PayKey Failure', 'PAYKEYFAIL',
                                  'There was an error getting the paykey')
            log.error(u'Error getting paykey, in-app payment: %s' %
                      pay_req['_config'].pk,
                      exc_info=True)
            InappPayLog.log(request, 'PAY_ERROR', config=pay_req['_config'])
            return render_error(request, exc)
Example #42
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())