Exemple #1
0
    def prepare_notice(self, request):
        qs = request.GET
        if request.GET:
            raw_qs = request.get_full_path().split('?')[1]
        else:
            raw_qs = ''

        trans_id = self.provider.transaction_from_notice(qs)
        session_trans_id = request.session.get('trans_id')

        if not trans_id:
            log.info('Provider={pr} did not provide a transaction ID '
                     'on the query string'.format(pr=self.name))
            raise msg.DevMessage(msg.TRANS_MISSING)
        if trans_id != session_trans_id:
            log.info('Provider={pr} transaction {tr} is not in the '
                     'active session'.format(pr=self.name, tr=trans_id))
            raise msg.DevMessage(msg.NO_ACTIVE_TRANS)

        try:
            response = self.provider.get_notice_result(qs, raw_qs)
        except HttpClientError, err:
            log.error('post to reference payment notice for transaction '
                      'ID {trans} failed: {err}'.format(trans=trans_id,
                                                        err=err))
            raise msg.DevMessage(msg.NOTICE_EXCEPTION)
Exemple #2
0
    def server_notification(self, request):
        """
        Handles the server to server notification that is sent after
        a transaction is completed.
        """
        # Get the notification data from the incoming request.
        data = self.provider.get_notification_data(request)

        try:
            # Post the result to solitude.
            self.provider.get_notification_result(data)
        except HttpClientError, err:
            log.error('Provider={pr} post failed: {err}'.format(pr=self.name,
                                                                err=err))
            raise msg.DevMessage(msg.NOTICE_ERROR)
Exemple #3
0
class ProviderHelper:
    """
    A common interface to all payment providers.
    """
    def __init__(self, name, slumber=None):
        self.slumber = slumber or client.slumber
        ProviderClass = provider_cls(name)
        self.provider = ProviderClass(self.slumber)
        self.name = self.provider.name

    @classmethod
    def choose(cls, mcc=None, mnc=None):
        """
        Given the user's mobile network (when available) return a suitable
        provider helper object.

        Keyword arguments:

        **mcc**
            The user's mobile carrier code, if known.

        **mnc**
            The user's mobile network code, if known.
        """
        # Switch to Boku when on one of their networks.
        if (mcc, mnc) in BokuProvider.network_data:
            provider_name = BokuProvider.name
        else:
            provider_name = settings.PAYMENT_PROVIDER

        log.info(
            'choosing payment provider "{pr}" for mcc {mcc}, mnc {mnc}'.format(
                pr=provider_name, mcc=mcc, mnc=mnc))

        # TODO: fall back to a payment provider *supported* by the
        # seller. bug 993691.

        return cls(provider_name)

    def start_transaction(self,
                          transaction_uuid,
                          seller_uuid,
                          product_id,
                          product_name,
                          prices,
                          icon_url,
                          user_uuid,
                          application_size,
                          source='unknown',
                          mcc=None,
                          mnc=None):
        """
        Start a payment provider transaction to begin the purchase flow.
        """
        try:
            seller = self.slumber.generic.seller.get_object_or_404(
                uuid=seller_uuid)
        except ObjectDoesNotExist:
            raise SellerNotConfigured(
                '{pr}: Seller with uuid {u} does not exist'.format(
                    u=seller_uuid, pr=self.provider.name))
        seller_id = seller['resource_pk']
        log.info('{pr}: transaction {tr}: seller: {sel}'.format(
            tr=transaction_uuid, sel=seller_id, pr=self.provider.name))
        log.info('{pr}: get product for seller_uuid={uuid} external_id={ext}'.
                 format(pr=self.provider.name,
                        uuid=seller_uuid,
                        ext=product_id))

        product = None
        try:
            product = self.slumber.generic.product.get_object_or_404(
                external_id=product_id,
                seller=seller_id,
            )
            log.info('{pr}: found generic product {prod}'.format(
                pr=self.provider.name, prod=product))
            provider_product = self.provider.get_product(seller, product)
            log.info('{pr}: found provider product {prod}'.format(
                prod=provider_product, pr=self.provider.name))
        except ObjectDoesNotExist:
            product, provider_product = self.create_product(
                product_id, product_name, seller, generic_product=product)

        trans_token, pay_url = self.provider.create_transaction(
            generic_seller=seller,
            generic_product=product,
            provider_product=provider_product,
            product_name=product_name,
            transaction_uuid=transaction_uuid,
            prices=prices,
            user_uuid=user_uuid,
            application_size=application_size,
            source=source,
            icon_url=icon_url,
            mcc=mcc,
            mnc=mnc,
        )
        log.info('{pr}: made provider trans {trans}'.format(
            trans=trans_token, pr=self.provider.name))

        return trans_token, pay_url, seller_id

    def create_product(self,
                       external_id,
                       product_name,
                       generic_seller,
                       generic_product=None):
        """
        Creates a generic product and provider product on the fly.

        This is for scenarios like adhoc in-app payments where the
        system might be selling a product for the first time.
        """
        log.info('{pr}: creating product with name: {name}, '
                 'external_id: {ext_id}, seller: {seller}'.format(
                     name=product_name,
                     ext_id=external_id,
                     seller=generic_seller,
                     pr=self.provider.name))

        if not generic_product:
            generic_product = self.slumber.generic.product.post({
                'external_id':
                external_id,
                'seller':
                generic_seller['resource_uri'],
                'public_id':
                str(uuid.uuid4()),
                'access':
                solitude_const.ACCESS_PURCHASE,
            })
            log.info('{pr}: created generic product {prod}'.format(
                prod=generic_product, pr=self.provider.name))

        # If there is no provider seller it means the billing account has
        # not yet been set up in Devhub. Thus, we're not caching an exception
        # here.
        provider_seller = self.provider.get_seller(generic_seller)

        provider_product = self.provider.create_product(
            generic_product, provider_seller, external_id, product_name)
        log.info('{pr}: created provider product {prod}'.format(
            prod=provider_product, pr=self.provider.name))

        return generic_product, provider_product

    def is_callback_token_valid(self, querystring):
        try:
            response = self.provider.create_notice(querystring)
        except HttpClientError as e:
            log.error('{pr}: Notice creation with querystring {querystring} '
                      'failed: {error}'.format(querystring=querystring,
                                               error=e,
                                               pr=self.provider.name))
            return False
        log.info('{pr}: validation of the token against the provider '
                 '{response}'.format(response=response, pr=self.provider.name))
        return response['result'] == 'OK'

    def prepare_notice(self, request):
        qs = request.GET
        if request.GET:
            raw_qs = request.get_full_path().split('?')[1]
        else:
            raw_qs = ''

        trans_id = self.provider.transaction_from_notice(qs)
        session_trans_id = request.session.get('trans_id')

        if not trans_id:
            log.info('Provider={pr} did not provide a transaction ID '
                     'on the query string'.format(pr=self.name))
            raise msg.DevMessage(msg.TRANS_MISSING)
        if trans_id != session_trans_id:
            log.info('Provider={pr} transaction {tr} is not in the '
                     'active session'.format(pr=self.name, tr=trans_id))
            raise msg.DevMessage(msg.NO_ACTIVE_TRANS)

        try:
            response = self.provider.get_notice_result(qs, raw_qs)
        except HttpClientError, err:
            log.error('post to reference payment notice for transaction '
                      'ID {trans} failed: {err}'.format(trans=trans_id,
                                                        err=err))
            raise msg.DevMessage(msg.NOTICE_EXCEPTION)

        log.info('reference payment notice check result={result}; '
                 'trans_id={trans}'.format(trans=trans_id,
                                           result=response['result']))

        if response['result'] != 'OK':
            raise msg.DevMessage(msg.NOTICE_ERROR)

        return trans_id