def login_user(request, service='google', save_redirect=None, token=None, is_force=None): if service and len(service) > 32: # Probably some injection spam -_- raise httpexceptions.HTTPBadRequest(detail="invalid service") user_id = get_user_id(request) if user_id and not is_force: return user_id if token and request.features.get('token_login'): # Only enabled during testing u = get(token=token) if u: login_user_id(request, u.id) return u.id request.session['next'] = save_redirect request.session.save() oauth = service_registry[service or 'google'](request) next, state = oauth.auth_url() # is_force=is_force request.session['oauth_state'] = state raise httpexceptions.HTTPSeeOther(next)
def handle_stripe(request, data): if not data['livemode']: return if data['type'] != "invoice.payment_succeeded": return if not data['data']['object']['total']: # Shortcut for empty totals (free accounts) return remote_id = data['user_id'] accounts = model.Session.query(model.Account).filter_by(remote_id=remote_id, service='stripe') if not accounts: raise httpexceptions.HTTPNotFound('Account handler not found.') has_funnels = any(a.config.get('ga_funnels') for a in accounts) if not has_funnels: return # Validate webhook stripe_query = api.account.query_service(request, accounts[0]) event_data = stripe_query.validate_webhook(data) if not event_data: raise httpexceptions.HTTPBadRequest('Webhook failed to validate: %s' % data['id']) count = 0 for a in accounts: for ga_tracking_id in a.config.get('ga_funnels', []): count += 1 tasks.service.stripe_webhook.delay(ga_tracking_id, a.id, event_data) log.info('stripe webhook: Queued %d funnels for account: %s' % (count, a.id))
def index(request): """ The only app-routed view which delegates the rest of the API-related functionality. Collates the API result into the final payload and response object. This is bypassed if the API method is processed through a @handle_api view. """ data = { 'status': 'ok', 'code': 200, 'messages': [], 'result': {}, } format = request.params.get('format', 'json') if format not in ('json', 'redirect'): return httpexceptions.HTTPBadRequest('Invalid format requested: %s' % format) encode_settings = {'cls': SchemaEncoder} if request.params.get('pretty'): encode_settings['sort_keys'] = True encode_settings['indent'] = 4 next = request.params.get('next') or request.referer try: r = api_controller(request) if r is not None: data['result'] = r except APIControllerError as e: data['messages'] += [e.message] data['code'] = e.code data['status'] = 'error' except (APIControllerError, LoginRequired) as e: data['messages'] += [e.message] data['code'] = e.code data['status'] = 'error' if isinstance(e, LoginRequired): next = e.next_url(request, next=next) data['messages'] += request.pop_flash() if format == 'redirect': for message in data['messages']: # Copy request-level messages to session storage to be displayed on # redirect. request.session.flash(message) return httpexceptions.HTTPSeeOther(next or '/') body = json.dumps(data, **encode_settings).encode() return Response(body, content_type='application/json', status=data['code'])
def _namecheap_subscription_alter(request, data): nc_api = service_registry['namecheap'].instance event_id = data['event']['id'] return_uri = data['event'].get('returnURI') remote_id = data['event']['user']['username'] order = data['event']['order'] q = model.Session.query(model.Account).filter_by(service='namecheap', remote_id=remote_id) q = q.options(orm.joinedload('user')) account = q.first() if not account: raise httpexceptions.HTTPBadRequest('Invalid remote id.') user = account.user old_plan = user.plan api.account.set_plan(user, order['pricing_plan_sku'], update_subscription=False) amount = 0 if not user.num_remaining: amount = user.payment.prorate(old_plan=old_plan, new_plan=user.plan) if amount: try: user.payment.invoice(amount=amount, description='Briefmetrics: %s (Prorated)' % user.plan.option_str) except: log.error('Namecheap prorate invoice failed, reverting plan to %s: %s' % (old_plan.id, user)) api.account.set_plan(user, old_plan.id) raise log.info('namecheap webhook: Altered %s' % user) ack = { 'type': 'subscription_alter_resp', 'id': event_id, 'response': { 'state': 'Active', 'message': 'Changed plan to %s' % user.plan.option_str, } } if return_uri: # Confirm event, activate subscription r = nc_api.session.request('PUT', return_uri, json=ack) # Bypass our wrapper assert_response(r) return ack
def handle_namecheap(request, data): if isinstance(data, list): # Stupid hack for Namecheap's broken QA code. data = data[0] event_token = data.get('event_token') if not event_token: return nc_api = service_registry['namecheap'].instance # Get event details try: r = nc_api.request('GET', '/v1/saas/saas/event/{token}'.format(token=event_token)) except Exception as e: log.error('namecheap webhook: Failed token lookup: %s' % event_token) raise data = r.json() fn = { 'subscription_create': _namecheap_subscription_create, 'subscription_alter': _namecheap_subscription_alter, 'subscription_cancel': _namecheap_subscription_cancel, }.get(data['type']) if not fn: raise httpexceptions.HTTPBadRequest('Invalid event type: %s' % data['type']) log.debug("namecheap webhook: Processing %s: %s" % (data['type'], data['event'])) try: return fn(request, data) except Exception as e: if event_token in fail_cache: log.error('namecheap webhook: Failed %s (%s): %s (again, silenced)' % (data['type'], e, event_token)) return log.error('namecheap webhook: Failed %s (%s): %s' % (data['type'], e, event_token)) fail_cache.add(event_token) raise
def _namecheap_subscription_cancel(request, data): nc_api = service_registry['namecheap'].instance event_id = data['event']['id'] return_uri = data['event'].get('returnURI') remote_id = data['event']['user']['username'] q = model.Session.query(model.Account).filter_by(service='namecheap', remote_id=remote_id) q = q.options(orm.joinedload('user')) account = q.first() if not account: raise httpexceptions.HTTPBadRequest('Invalid remote id.') user = account.user amount = 0 if not user.num_remaining: amount = user.payment and user.payment.prorate() if amount: user.payment.invoice(amount=amount, description='Briefmetrics: Prorated refund') api.account.delete_payments(user) log.info('namecheap webhook: Cancelled %s' % user) ack = { 'type': 'subscription_cancel_resp', 'id': event_id, 'response': { 'state': 'Inactive', } } if return_uri: # Confirm event, activate subscription r = nc_api.session.request('PUT', return_uri, json=ack) # Bypass our wrapper assert_response(r) return ack
def connect_oauth(request, oauth, user_required=False): user, account = get_account(request, service=oauth.id, user_required=user_required) code = request.params.get('code') if not code: raise httpexceptions.HTTPBadRequest('Missing code.') error = request.params.get('error') if error: raise APIError('Failed to connect: %s' % error) url = request.current_route_url().replace( 'http://', 'https://') # We lie, because honeybadger. try: token = oauth.auth_token(url) except InvalidGrantError: # Try again. raise httpexceptions.HTTPSeeOther( request.route_path('account_login', service=oauth.id)) except OAuth2Error as e: raise APIError( "Unexpected authentication error, please try again: %s" % e.description) except ValueError as e: raise APIError("Unexpected response error, please try again: %s" % e) remote_id, email, display_name, remote_data = oauth.query_user() if not user: # New user user = get_or_create( email=email, service=oauth.id, token=token, display_name=display_name, remote_id=remote_id, remote_data=remote_data, ) account = user.accounts[0] elif not account: # New account account = model.Account.create( display_name=display_name or user.display_name, user=user, oauth_token=token, service=oauth.id, remote_id=remote_id, remote_data=remote_data, ) else: # Update account account.oauth_token = token account.remote_id = remote_id account.remote_data = remote_data force_skip = oauth.is_autocreate and Session.query( model.Report).filter_by(account_id=account.id).limit(1).count() if force_skip: # Already exists, skip autocreate. oauth.is_autocreate = False Session.commit() return account