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)
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)
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