Пример #1
0
    def obj_create(self, bundle, request, **kwargs):
        form = CreateBillingConfigurationForm(bundle.data)
        if not form.is_valid():
            raise self.form_errors(form)

        client = get_client()
        billing = client.client('billing')
        data = form.bango_data

        types = billing.factory.create('ArrayOfString')
        for f in PAYMENT_TYPES:
            types.string.append(f)
        data['typeFilter'] = types

        price_list = billing.factory.create('ArrayOfPrice')
        for item in form.cleaned_data['prices']:
            price = billing.factory.create('Price')
            price.amount = item.cleaned_data['amount']
            price.currency = item.cleaned_data['currency']
            price_list.Price.append(price)

        data['priceList'] = price_list

        config = billing.factory.create('ArrayOfBillingConfigurationOption')
        configs = {
            'APPLICATION_CATEGORY_ID': '18',
            'APPLICATION_SIZE_KB': 2,
            'BILLING_CONFIGURATION_TIME_OUT': 120,
            'REDIRECT_URL_ONSUCCESS': data.pop('redirect_url_onsuccess'),
            'REDIRECT_URL_ONERROR': data.pop('redirect_url_onerror'),
            'REQUEST_SIGNATURE': sign(data['externalTransactionId']),
        }
        for k, v in configs.items():
            opt = billing.factory.create('BillingConfigurationOption')
            opt.configurationOptionName = k
            opt.configurationOptionValue = v
            config.BillingConfigurationOption.append(opt)

        data['configurationOptions'] = config
        resp = get_client().CreateBillingConfiguration(data)
        bundle.data = {'responseCode': resp.responseCode,
                       'responseMessage': resp.responseMessage,
                       'billingConfigurationId': resp.billingConfigurationId}

        create_data = data.copy()
        create_data['transaction_uuid'] = data.pop('externalTransactionId')
        create.send(sender=self, bundle=bundle, data=create_data, form=form)
        return bundle
Пример #2
0
    def _check_for_tampering(self, tok, cleaned_data):
        """
        Use the token service to see if any data has been tampered with.
        """
        cli = get_client().client('token_checker')
        with statsd.timer('solitude.bango.request.checktoken'):
            true_data = cli.service.CheckToken(token=tok)
        if true_data.ResponseCode is None:
            # Any None field means the token was invalid.
            # This might happen if someone tampered with Token= itself in the
            # query string or if Bango's server was messed up.
            statsd.incr('solitude.bango.response.checktoken_fail')
            msg = 'Invalid Bango token: {0}'.format(tok)
            log.error(msg)
            raise forms.ValidationError(msg)

        for form_fld, true_attr in (
            ('moz_signature', 'Signature'),
            ('moz_transaction', 'MerchantTransactionId'),
            ('bango_response_code', 'ResponseCode'),
            ('bango_response_message', 'ResponseMessage'),
            ('bango_trans_id', 'BangoTransactionId'),
        ):
            true_val = getattr(true_data, true_attr)
            # Make sure the true value is a str() just like it is on the query
            # string.
            true_val = str(true_val)
            form_val = cleaned_data.get(form_fld)
            # Since moz_transaction is an object, get the real value.
            if form_val and form_fld == 'moz_transaction':
                form_val = form_val.uuid

            if form_val and form_val != true_val:
                msg = ('Bango query string tampered with: field: {field}; '
                       'fake: {fake}; true: {true}'.format(field=form_fld,
                                                           fake=form_val,
                                                           true=true_val))
                log_cef(msg, self._request, severity=3)
                log.info(msg)
                log.info('token check response: {true_data}'.format(
                    true_data=true_data))
                # Completely reject the form since it was tampered with.
                raise forms.ValidationError(
                    'Form field {0} has been tampered with. '
                    'True: {1}; fake: {2}'.format(form_fld, true_val,
                                                  form_val))
Пример #3
0
    def _check_for_tampering(self, tok, cleaned_data):
        """
        Use the token service to see if any data has been tampered with.
        """
        cli = get_client().client('token_checker')
        with statsd.timer('solitude.bango.request.checktoken'):
            true_data = cli.service.CheckToken(token=tok)
        if true_data.ResponseCode is None:
            # Any None field means the token was invalid.
            # This might happen if someone tampered with Token= itself in the
            # query string or if Bango's server was messed up.
            statsd.incr('solitude.bango.response.checktoken_fail')
            msg = 'Invalid Bango token: {0}'.format(tok)
            log.error(msg)
            raise forms.ValidationError(msg)

        for form_fld, true_attr in (
                ('moz_signature', 'Signature'),
                ('moz_transaction', 'MerchantTransactionId'),
                ('bango_response_code', 'ResponseCode'),
                ('bango_response_message', 'ResponseMessage'),
                ('bango_trans_id', 'BangoTransactionId'),):
            true_val = getattr(true_data, true_attr)
            # Make sure the true value is a str() just like it is on the query
            # string.
            true_val = str(true_val)
            form_val = cleaned_data.get(form_fld)
            # Since moz_transaction is an object, get the real value.
            if form_val and form_fld == 'moz_transaction':
                form_val = form_val.uuid

            if form_val and form_val != true_val:
                msg = ('Bango query string tampered with: field: {field}; '
                       'fake: {fake}; true: {true}'
                       .format(field=form_fld, fake=form_val, true=true_val))
                log_cef(msg, self._request, severity=3)
                log.info(msg)
                log.info('token check response: {true_data}'
                         .format(true_data=true_data))
                # Completely reject the form since it was tampered with.
                raise forms.ValidationError(
                    'Form field {0} has been tampered with. '
                    'True: {1}; fake: {2}'.format(
                        form_fld, true_val, form_val))
Пример #4
0
    def client(self, method, data, raise_on=None, client=None):
        """
        Client to call the bango client and process errors in a way that
        is relevant to the form. If you pass in a list of errors, these will
        be treated as errors the callee is going to deal with and will not
        be returning ImmediateHttpResponses. Instead the callee will have to
        cope with these BangoAnticipatedErrors as appropriate.

        You can optionally pass in a client to override the default.
        """
        raise_on = raise_on or []
        try:
            return getattr(client or get_client(), method)(data)
        except BangoUnanticipatedError, exc:
            # It was requested that the error that was passed in
            # was actually anticipated, so let's raise that type of error.
            if exc.id in raise_on:
                raise BangoAnticipatedError(exc.id, exc.message)

            res = self.client_errors(exc)
            raise BangoImmediateError(format_form_errors(res))
Пример #5
0
    def client(self, method, data, raise_on=None, client=None):
        """
        Client to call the bango client and process errors in a way that
        is relevant to the form. If you pass in a list of errors, these will
        be treated as errors the callee is going to deal with and will not
        be returning ImmediateHttpResponses. Instead the callee will have to
        cope with these BangoAnticipatedErrors as appropriate.

        You can optionally pass in a client to override the default.
        """
        raise_on = raise_on or []
        try:
            return getattr(client or get_client(), method)(data)
        except BangoUnanticipatedError, exc:
            # It was requested that the error that was passed in
            # was actually anticipated, so let's raise that type of error.
            if exc.id in raise_on:
                raise BangoAnticipatedError(exc.id, exc.message)

            res = self.client_errors(exc)
            raise BangoImmediateError(format_form_errors(res))
Пример #6
0
def prepare(form, bango):
    data = form.bango_data
    # Add in the Bango number from the serializer.
    data['bango'] = bango

    # Used to create the approprate data structure.
    client = get_client()
    billing = client.client('billing')
    price_list = billing.factory.create('ArrayOfPrice')
    price_types = set()

    for item in form.cleaned_data['prices']:
        price = billing.factory.create('Price')
        price.amount = item.cleaned_data['price']
        price.currency = item.cleaned_data['currency']
        price_types.add(item.cleaned_data['method'])

        # TODO: remove this.
        # Very temporary and very fragile hack to fix bug 882183.
        # Bango cannot accept regions with price info so if there
        # are two USD values for different regions it triggers a 500 error.
        append = True
        for existing in price_list.Price:
            if existing.currency == price.currency:
                log.info('Skipping %s:%s because we already have %s:%s'
                         % (price.currency, price.amount,
                            existing.currency, existing.amount))
                append = False
                break

        if append:
            price_list.Price.append(price)

    data['priceList'] = price_list

    # More workarounds for bug 882321, ideally we'd send one type per
    # region, price, combination. If all the prices say operator, then
    # we'll set it to that. Otherwise its all.
    type_filters = PAYMENT_TYPES
    if price_types == set([str(PAYMENT_METHOD_OPERATOR)]):
        type_filters = MICRO_PAYMENT_TYPES

    types = billing.factory.create('ArrayOfString')
    for f in type_filters:
        types.string.append(f)
    data['typeFilter'] = types

    config = billing.factory.create('ArrayOfBillingConfigurationOption')
    configs = {
        'APPLICATION_CATEGORY_ID': '18',
        'APPLICATION_SIZE_KB': data.pop('application_size'),
        # Tell Bango to use our same transaction expiry logic.
        # However, we pad it by 60 seconds to show a prettier Mozilla user
        # error in the case of a real timeout.
        'BILLING_CONFIGURATION_TIME_OUT': settings.TRANSACTION_EXPIRY + 60,
        'REDIRECT_URL_ONSUCCESS': data.pop('redirect_url_onsuccess'),
        'REDIRECT_URL_ONERROR': data.pop('redirect_url_onerror'),
        'REQUEST_SIGNATURE': sign(data['externalTransactionId']),
    }
    user_uuid = data.pop('user_uuid')
    if settings.SEND_USER_ID_TO_BANGO:
        configs['MOZ_USER_ID'] = user_uuid
        log.info('Sending MOZ_USER_ID {uuid} for transaction {tr}'
                 .format(uuid=user_uuid, tr=data['externalTransactionId']))
    if settings.BANGO_ICON_URLS:
        icon_url = data.pop('icon_url', None)
        if icon_url:
            configs['APPLICATION_LOGO_URL'] = icon_url

    for k, v in configs.items():
        opt = billing.factory.create('BillingConfigurationOption')
        opt.configurationOptionName = k
        opt.configurationOptionValue = v
        config.BillingConfigurationOption.append(opt)

    data['configurationOptions'] = config
    return data
Пример #7
0
    def obj_create(self, bundle, request, **kwargs):
        form = CreateBillingConfigurationForm(bundle.data)
        if not form.is_valid():
            raise self.form_errors(form)

        client = get_client()
        billing = client.client('billing')
        data = form.bango_data

        usd_price = None
        price_list = billing.factory.create('ArrayOfPrice')
        for item in form.cleaned_data['prices']:
            price = billing.factory.create('Price')
            price.amount = item.cleaned_data['amount']
            price.currency = item.cleaned_data['currency']
            if price.currency == 'USD':
                usd_price = Decimal(price.amount)
            price_list.Price.append(price)

        data['priceList'] = price_list

        if not usd_price:
            # This should never happen because USD is always part of the list.
            raise ValueError('Purchase for %r did not contain a USD price'
                             % data.get('externalTransactionId'))
        if usd_price < settings.BANGO_MAX_MICRO_AMOUNT:
            type_filters = MICRO_PAYMENT_TYPES
        else:
            type_filters = PAYMENT_TYPES

        types = billing.factory.create('ArrayOfString')
        for f in type_filters:
            types.string.append(f)
        data['typeFilter'] = types

        config = billing.factory.create('ArrayOfBillingConfigurationOption')
        configs = {
            'APPLICATION_CATEGORY_ID': '18',
            'APPLICATION_SIZE_KB': 2,
            'BILLING_CONFIGURATION_TIME_OUT': 120,
            'REDIRECT_URL_ONSUCCESS': data.pop('redirect_url_onsuccess'),
            'REDIRECT_URL_ONERROR': data.pop('redirect_url_onerror'),
            'REQUEST_SIGNATURE': sign(data['externalTransactionId']),
        }
        if settings.BANGO_ICON_URLS:
            icon_url = data.pop('icon_url', None)
            if icon_url:
                configs['APPLICATION_LOGO_URL'] = icon_url

        for k, v in configs.items():
            opt = billing.factory.create('BillingConfigurationOption')
            opt.configurationOptionName = k
            opt.configurationOptionValue = v
            config.BillingConfigurationOption.append(opt)

        data['configurationOptions'] = config
        resp = self.client('CreateBillingConfiguration', data)
        bundle.data = {'responseCode': resp.responseCode,
                       'responseMessage': resp.responseMessage,
                       'billingConfigurationId': resp.billingConfigurationId}

        create_data = data.copy()
        create_data['transaction_uuid'] = data.pop('externalTransactionId')
        create.send(sender=self, bundle=bundle, data=create_data, form=form)
        return bundle
Пример #8
0
    def call(self, form):
        data = form.bango_data
        client = get_client()
        billing = client.client('billing')
        usd_price = None
        price_list = billing.factory.create('ArrayOfPrice')
        for item in form.cleaned_data['prices']:
            price = billing.factory.create('Price')
            price.amount = item.cleaned_data['price']
            price.currency = item.cleaned_data['currency']
            if price.currency == 'USD':
                usd_price = Decimal(price.amount)

            # TODO: remove this.
            # Very temporary and very fragile hack to fix bug 882183.
            # Bango cannot accept regions with price info so if there
            # are two USD values for different regions it triggers a 500 error.
            append = True
            for existing in price_list.Price:
                if existing.currency == price.currency:
                    log.info('Skipping %s:%s because we already have %s:%s'
                             % (price.currency, price.amount,
                                existing.currency, existing.amount))
                    append = False
                    break

            if append:
                price_list.Price.append(price)

        data['priceList'] = price_list

        if not usd_price:
            # This should never happen because USD is always part of the list.
            raise ValueError('Purchase for %r did not contain a USD price'
                             % data.get('externalTransactionId'))
        if usd_price < settings.BANGO_MAX_MICRO_AMOUNT:
            type_filters = MICRO_PAYMENT_TYPES
        else:
            type_filters = PAYMENT_TYPES

        types = billing.factory.create('ArrayOfString')
        for f in type_filters:
            types.string.append(f)
        data['typeFilter'] = types

        config = billing.factory.create('ArrayOfBillingConfigurationOption')
        configs = {
            'APPLICATION_CATEGORY_ID': '18',
            'APPLICATION_SIZE_KB': data.pop('application_size'),
            # Tell Bango to use our same transaction expiry logic.
            # However, we pad it by 60 seconds to show a prettier Mozilla user
            # error in the case of a real timeout.
            'BILLING_CONFIGURATION_TIME_OUT': settings.TRANSACTION_EXPIRY + 60,
            'REDIRECT_URL_ONSUCCESS': data.pop('redirect_url_onsuccess'),
            'REDIRECT_URL_ONERROR': data.pop('redirect_url_onerror'),
            'REQUEST_SIGNATURE': sign(data['externalTransactionId']),
        }
        user_uuid = data.pop('user_uuid')
        if settings.SEND_USER_ID_TO_BANGO:
            configs['MOZ_USER_ID'] = user_uuid
        if settings.BANGO_ICON_URLS:
            icon_url = data.pop('icon_url', None)
            if icon_url:
                configs['APPLICATION_LOGO_URL'] = icon_url

        for k, v in configs.items():
            opt = billing.factory.create('BillingConfigurationOption')
            opt.configurationOptionName = k
            opt.configurationOptionValue = v
            config.BillingConfigurationOption.append(opt)

        data['configurationOptions'] = config
        return self.client('CreateBillingConfiguration', data)
Пример #9
0
 def client(self, method, data, client=None):
     return getattr(client or get_client(), method)(data)
Пример #10
0
 def client(self, method, data, client=None):
     return getattr(client or get_client(), method)(data)
Пример #11
0
def prepare(form, bango):
    data = form.bango_data
    # Add in the Bango number from the serializer.
    data['bango'] = bango

    # Used to create the approprate data structure.
    client = get_client()
    billing = client.client('billing')
    price_list = billing.factory.create('ArrayOfPrice')
    price_types = set()

    for item in form.cleaned_data['prices']:
        price = billing.factory.create('Price')
        price.amount = item.cleaned_data['price']
        price.currency = item.cleaned_data['currency']
        price_types.add(item.cleaned_data['method'])

        # TODO: remove this.
        # Very temporary and very fragile hack to fix bug 882183.
        # Bango cannot accept regions with price info so if there
        # are two USD values for different regions it triggers a 500 error.
        append = True
        for existing in price_list.Price:
            if existing.currency == price.currency:
                log.info('Skipping %s:%s because we already have %s:%s' %
                         (price.currency, price.amount, existing.currency,
                          existing.amount))
                append = False
                break

        if append:
            price_list.Price.append(price)

    data['priceList'] = price_list

    # More workarounds for bug 882321, ideally we'd send one type per
    # region, price, combination. If all the prices say operator, then
    # we'll set it to that. Otherwise its all.
    type_filters = PAYMENT_TYPES
    if price_types == set([str(PAYMENT_METHOD_OPERATOR)]):
        type_filters = MICRO_PAYMENT_TYPES

    types = billing.factory.create('ArrayOfString')
    for f in type_filters:
        types.string.append(f)
    data['typeFilter'] = types

    config = billing.factory.create('ArrayOfBillingConfigurationOption')
    configs = {
        'APPLICATION_CATEGORY_ID': '18',
        'APPLICATION_SIZE_KB': data.pop('application_size'),
        # Tell Bango to use our same transaction expiry logic.
        # However, we pad it by 60 seconds to show a prettier Mozilla user
        # error in the case of a real timeout.
        'BILLING_CONFIGURATION_TIME_OUT': settings.TRANSACTION_EXPIRY + 60,
        'REDIRECT_URL_ONSUCCESS': data.pop('redirect_url_onsuccess'),
        'REDIRECT_URL_ONERROR': data.pop('redirect_url_onerror'),
        'REQUEST_SIGNATURE': sign(data['externalTransactionId']),
    }
    user_uuid = data.pop('user_uuid')
    if settings.SEND_USER_ID_TO_BANGO:
        configs['MOZ_USER_ID'] = user_uuid
        log.info('Sending MOZ_USER_ID {uuid} for transaction {tr}'.format(
            uuid=user_uuid, tr=data['externalTransactionId']))
    if settings.BANGO_ICON_URLS:
        icon_url = data.pop('icon_url', None)
        if icon_url:
            configs['APPLICATION_LOGO_URL'] = icon_url

    for k, v in configs.items():
        opt = billing.factory.create('BillingConfigurationOption')
        opt.configurationOptionName = k
        opt.configurationOptionValue = v
        config.BillingConfigurationOption.append(opt)

    data['configurationOptions'] = config
    return data