def charge(self, session, payment_id=None, stripeToken=None, stripeEmail='ignored', **ignored): log.debug('PAYMENT: payment_id={}, stripeToken={}', payment_id or 'NONE', stripeToken or 'NONE') if ignored: log.debug('PAYMENT: received unexpected stripe parameters: {}', ignored) try: try: return func(self, session=session, payment_id=payment_id, stripeToken=stripeToken) except HTTPRedirect: # Paranoia: we want to try commiting while we're INSIDE of the # @credit_card decorator to ensure that we catch any database # errors (like unique constraint violations). We have to wrap # this try-except inside another try-except because we want # to re-raise the HTTPRedirect, and also have unexpected DB # exceptions caught by the outermost exception handler. session.commit() raise except HTTPRedirect: raise except Exception: error_text = \ 'Got an error while calling charge' \ '(self, payment_id={!r}, stripeToken={!r}, ignored={}):\n{}\n\n' \ 'IMPORTANT: This could have resulted in an attendee paying and not being ' \ 'marked as paid in the database. Definitely double check this.'\ .format(payment_id, stripeToken, ignored, traceback.format_exc()) report_critical_exception(msg=error_text, subject='ERROR: MAGFest Stripe error (Automated Message)') return traceback.format_exc()
def charge_cc(self, session, token): try: log.debug( 'PAYMENT: !!! attempting to charge stripeToken {} {} cents for {}', token, self.amount, self.description) self.response = stripe.Charge.create( card=token, currency='usd', amount=self.amount, description=self.description, receipt_email=self.receipt_email) log.info( 'PAYMENT: !!! SUCCESS: charged stripeToken {} {} cents for {}, responseID={}', token, self.amount, self.description, getattr(self.response, 'id', None)) except stripe.CardError as e: msg = 'Your card was declined with the following error from our processor: ' + str( e) log.error('PAYMENT: !!! FAIL: {}', msg) return msg except stripe.StripeError as e: error_txt = 'Got an error while calling charge_cc(self, token={!r})'.format( token) report_critical_exception( msg=error_txt, subject='ERROR: MAGFest Stripe invalid request error') return 'An unexpected problem occurred while processing your card: ' + str( e) else: if self.models: session.add(self.stripe_transaction_from_charge())
def create_stripe_intent(self, session): log.debug('PAYMENT: !!! attempting to charge {} cents for {}', self.amount, self.description) try: stripe_intent = stripe.PaymentIntent.create( payment_method_types=['card'], amount=self.amount, currency='usd', description=self.description, ) if self.models: self._stripe_transaction = self.stripe_transaction_from_charge( stripe_intent.id) session.add(self._stripe_transaction) for model in self.models: multi = len(self.models) > 1 session.add( self.stripe_transaction_for_model( model, self._stripe_transaction, multi)) return stripe_intent except Exception as e: error_txt = 'Got an error while calling create_stripe_intent()' report_critical_exception( msg=error_txt, subject='ERROR: MAGFest Stripe invalid request error') return 'An unexpected problem occurred while processing your card: ' + str( e)
def with_timed(*args, **kwargs): before = datetime.now() try: return func(*args, **kwargs) finally: log.debug('{}{}.{} loaded in {} seconds'.format( prepend_text, func.__module__, func.__name__, (datetime.now() - before).total_seconds()))
def _jsonrpc_handler(self=None): id = None def error(status, code, message): response = {'jsonrpc': '2.0', 'id': id, 'error': {'code': code, 'message': message}} log.debug('Returning error message: {}', repr(response).encode('utf-8')) cherrypy.response.status = status return response def success(result): response = {'jsonrpc': '2.0', 'id': id, 'result': result} log.debug('Returning success message: {}', { 'jsonrpc': '2.0', 'id': id, 'result': len(result) if is_listy(result) else str(result).encode('utf-8')}) cherrypy.response.status = 200 return response request_body = cherrypy.request.json if not isinstance(request_body, dict): return error(400, ERR_INVALID_JSON, 'Invalid json input: {!r}'.format(request_body)) log.debug('jsonrpc request body: {}', repr(request_body).encode('utf-8')) id, params = request_body.get('id'), request_body.get('params', []) if 'method' not in request_body: return error(400, ERR_INVALID_RPC, '"method" field required for jsonrpc request') method = request_body['method'] if method.count('.') != 1: return error(404, ERR_MISSING_FUNC, 'Invalid method ' + method) module, function = method.split('.') if module not in services: return error(404, ERR_MISSING_FUNC, 'No module ' + module) service = services[module] if not hasattr(service, function): return error(404, ERR_MISSING_FUNC, 'No function ' + method) if not isinstance(params, (list, dict)): return error(400, ERR_INVALID_PARAMS, 'Invalid parameter list: {!r}'.format(params)) args, kwargs = (params, {}) if isinstance(params, list) else ([], params) precall(request_body) try: return success(getattr(service, function)(*args, **kwargs)) except HTTPError as http_error: return error(http_error.code, ERR_FUNC_EXCEPTION, http_error._message) except Exception as e: log.error('Unexpected error', exc_info=True) message = 'Unexpected error: {}'.format(e) if debug: message += '\n' + traceback.format_exc() return error(500, ERR_FUNC_EXCEPTION, message) finally: trigger_delayed_notifications()
def _is_invalid_url(url): if c.MIVS_SKIP_URL_VALIDATION: return False try: log.debug("_is_invalid_url() is fetching '%s' to check if it's reachable." % url) with urlopen(url, timeout=30) as f: f.read() except Exception: return True
def prereg_payment(self, session, payment_id=None, stripeToken=None): if not payment_id or not stripeToken or c.HTTP_METHOD != 'POST': message = 'The payment was interrupted. Please check below to ensure you received your badge.' raise HTTPRedirect('paid_preregistrations?message={}', message) charge = Charge.get(payment_id) if not charge.total_cost: message = 'Your total cost was $0. Your credit card has not been charged.' elif charge.amount != charge.total_cost: message = 'Our preregistration price has gone up; ' \ 'please fill out the payment form again at the higher price' else: message = charge.charge_cc(session, stripeToken) if message: raise HTTPRedirect('index?message={}', message) # from this point on, the credit card has actually been charged but we haven't marked anything as charged yet. # be ultra-careful until the attendees/groups are marked paid and written to the DB or we could end up in a # situation where we took the payment, but didn't mark the cards charged for attendee in charge.attendees: attendee.paid = c.HAS_PAID attendee.amount_paid = attendee.total_cost attendee_name = 'PLACEHOLDER' if attendee.is_unassigned else attendee.full_name log.info("PAYMENT: marked attendee id={} ({}) as paid", attendee.id, attendee_name) session.add(attendee) for group in charge.groups: group.amount_paid = group.default_cost log.info("PAYMENT: marked group id={} ({}) as paid", group.id, group.name) for attendee in group.attendees: attendee.amount_paid = attendee.total_cost - attendee.badge_cost attendee_name = 'UNASSIGNED PLACEHOLDER' if attendee.is_unassigned else attendee.full_name log.info("PAYMENT: marked group member id={} ({}) as paid", attendee.id, attendee_name) session.add(group) session.commit( ) # paranoia: really make sure we lock in marking taking payments in the database Charge.unpaid_preregs.clear() Charge.paid_preregs.extend(charge.targets) log.debug( 'PAYMENT: prereg payment actual charging process FINISHED for stripeToken={}', stripeToken) raise HTTPRedirect('paid_preregistrations?payment_received={}', charge.dollar_amount)
def error(status, code, message): response = { 'jsonrpc': '2.0', 'id': id, 'error': { 'code': code, 'message': message } } log.debug('Returning error message: {}', repr(response).encode('utf-8')) cherrypy.response.status = status return response
def success(result): response = {'jsonrpc': '2.0', 'id': id, 'result': result} log.debug( 'Returning success message: {}', { 'jsonrpc': '2.0', 'id': id, 'result': len(result) if is_listy(result) else str(result).encode('utf-8') }) cherrypy.response.status = 200 return response
def with_run_threaded(*args, **kwargs): if lock: @wraps(func) def locked_func(*a, **kw): if lock.acquire(blocking=blocking, timeout=timeout): try: return func(*a, **kw) finally: lock.release() else: log.warn("Can't acquire lock, skipping background thread: {}".format(name)) thread = threading.Thread(target=locked_func, *args, **kwargs) else: thread = threading.Thread(target=func, *args, **kwargs) thread.name = name log.debug('Starting background thread: {}'.format(name)) thread.start()
def prereg_payment(self, session, payment_id=None, stripeToken=None, message=''): if not payment_id or not stripeToken or c.HTTP_METHOD != 'POST': message = 'The payment was interrupted. Please check below to ensure you received your badge.' raise HTTPRedirect('paid_preregistrations?message={}', message) charge = Charge.get(payment_id) if not charge.total_cost: message = 'Your total cost was $0. Your credit card has not been charged.' elif charge.amount != charge.total_cost: message = 'Our preregistration price has gone up; ' \ 'please fill out the payment form again at the higher price' else: for attendee in charge.attendees: if not message and attendee.promo_code_id: message = check_prereg_promo_code(session, attendee) if not message: message = charge.charge_cc(session, stripeToken) if message: raise HTTPRedirect('index?message={}', message) # from this point on, the credit card has actually been charged but we haven't marked anything as charged yet. # be ultra-careful until the attendees/groups are marked paid and written to the DB or we could end up in a # situation where we took the payment, but didn't mark the cards charged for attendee in charge.attendees: attendee.paid = c.HAS_PAID attendee.amount_paid_override = attendee.total_cost attendee_name = 'PLACEHOLDER' if attendee.is_unassigned else attendee.full_name log.info("PAYMENT: marked attendee id={} ({}) as paid", attendee.id, attendee_name) session.add(attendee) if attendee.badges: pc_group = session.create_promo_code_group(attendee, attendee.name, int(attendee.badges) - 1) session.add(pc_group) session.commit() # save PromoCodeGroup to the database to generate receipt items correctly for attendee in charge.attendees: session.add_receipt_items_by_model(charge, attendee) Charge.unpaid_preregs.clear() Charge.paid_preregs.extend(charge.targets) log.debug('PAYMENT: prereg payment actual charging process FINISHED for stripeToken={}', stripeToken) raise HTTPRedirect('paid_preregistrations?payment_received={}', charge.dollar_amount)
def check_missed_stripe_payments(): pending_ids = [] with Session() as session: pending_payments = session.query(StripeTransaction).filter_by( type=c.PENDING) for payment in pending_payments: pending_ids.append(payment.stripe_id) events = stripe.Event.list( type='payment_intent.succeeded', created={ # Check for events created in the last hour. 'gte': int(time.time() - 60 * 60), }) for event in events.auto_paging_iter(): payment_intent = event.data.object log.debug('Processing Payment Intent ID {}', payment_intent.id) if payment_intent.id in pending_ids: log.debug('Charge is pending, intent ID is {}', payment_intent.id) Charge.mark_paid_from_stripe_id(payment_intent.id)
def prereg_payment(self, session, payment_id=None, stripeToken=None): if not payment_id or not stripeToken or c.HTTP_METHOD != 'POST': message = 'The payment was interrupted. Please check below to ensure you received your badge.' raise HTTPRedirect('paid_preregistrations?message={}', message) charge = Charge.get(payment_id) if not charge.total_cost: message = 'Your total cost was $0. Your credit card has not been charged.' elif charge.amount != charge.total_cost: message = 'Our preregistration price has gone up; ' \ 'please fill out the payment form again at the higher price' else: message = charge.charge_cc(session, stripeToken) if message: raise HTTPRedirect('index?message={}', message) # from this point on, the credit card has actually been charged but we haven't marked anything as charged yet. # be ultra-careful until the attendees/groups are marked paid and written to the DB or we could end up in a # situation where we took the payment, but didn't mark the cards charged for attendee in charge.attendees: attendee.paid = c.HAS_PAID attendee_name = 'PLACEHOLDER' if attendee.is_unassigned else attendee.full_name log.info("PAYMENT: marked attendee id={} ({}) as paid", attendee.id, attendee_name) session.add(attendee) if attendee.badges: pc_group = session.create_promo_code_group(attendee, attendee.name, int(attendee.badges) - 1) session.add(pc_group) session.commit() attendee.amount_paid = attendee.total_cost session.commit() # paranoia: really make sure we lock in marking taking payments in the database Charge.unpaid_preregs.clear() Charge.paid_preregs.extend(charge.targets) log.debug('PAYMENT: prereg payment actual charging process FINISHED for stripeToken={}', stripeToken) raise HTTPRedirect('paid_preregistrations?payment_received={}', charge.dollar_amount)
def _collect_models(cls, query): models = set() for d in listify(query): try: model = Session.resolve_model(d['_model']) except Exception: log.debug('unable to resolve model {} in query {}', d.get('_model'), d) else: models.add(model) for attr_name in _collect_fields(d): curr_model = model for prop_name in attr_name.split('.'): if hasattr(curr_model, prop_name): prop = getattr(curr_model, prop_name) if isinstance( prop, InstrumentedAttribute) and hasattr( prop.property, 'mapper'): curr_model = prop.property.mapper.class_ models.update([curr_model]) if prop_name in d: subquery = deepcopy(d[prop_name]) if isinstance(subquery, (list, set, tuple)) \ and not filter(lambda x: isinstance(x, dict), subquery): subquery = { i: True for i in subquery } elif isinstance( subquery, six.string_types): subquery = {subquery: True} if isinstance(subquery, dict): subquery[ '_model'] = curr_model.__name__ models.update( cls._collect_models(subquery)) else: break return models
def create_stripe_intent(self, session): log.debug('Creating Stripe Intent to charge {} cents for {}', self.amount, self.description) try: customer = None if self.receipt_email: customer_list = stripe.Customer.list( email=self.receipt_email, limit=1, ) if customer_list: customer = customer_list.data[0] else: customer = stripe.Customer.create( description=self.receipt_email, email=self.receipt_email, ) stripe_intent = stripe.PaymentIntent.create( payment_method_types=['card'], amount=self.amount, currency='usd', description=self.description, receipt_email=customer.email if self.receipt_email else None, customer=customer.id if customer else None, ) if self.models: self._stripe_transaction = self.stripe_transaction_from_charge(stripe_intent.id) session.add(self._stripe_transaction) for model in self.models: multi = len(self.models) > 1 session.add(self.stripe_transaction_for_model(model, self._stripe_transaction, multi)) return stripe_intent except Exception as e: error_txt = 'Got an error while calling create_stripe_intent()' report_critical_exception(msg=error_txt, subject='ERROR: MAGFest Stripe invalid request error') return 'An unexpected problem occurred while setting up payment: ' + str(e)
def test_eager_formatting_adapter(log_stream): log = AutoLogger(EagerFormattingAdapter) log.log(0, 'suppressed') log.debug('a %(a)d b %(b)s', {'a': 1, 'b': 2}) log.trace('TEST NO INTERPOLATION') log.trace('TEST %s', 'MSG') log.debug('TEST %s', 'MSG') log.info('TEST %s%s%s', 'M', 'S', 'G') log.warn('TEST %s', 'MSG') log.warning('TEST %s', 'MSG') log.error('TEST %s', 'MSG') try: assert False except Exception: log.exception('TEST %s', 'MSG') log.critical('TEST %s', 'MSG') log.fatal('TEST %s', 'MSG') result = log_stream.getvalue() assert result.startswith("""\ [DEBUG] tests.test_logging: a 1 b 2 [TRACE] tests.test_logging: TEST NO INTERPOLATION [TRACE] tests.test_logging: TEST MSG [DEBUG] tests.test_logging: TEST MSG [INFO] tests.test_logging: TEST MSG [WARNING] tests.test_logging: TEST MSG [WARNING] tests.test_logging: TEST MSG [ERROR] tests.test_logging: TEST MSG [ERROR] tests.test_logging: TEST MSG Traceback (most recent call last): """) assert result.endswith("""\ AssertionError: assert False [CRITICAL] tests.test_logging: TEST MSG [CRITICAL] tests.test_logging: TEST MSG """)
def success(result): response = {'jsonrpc': '2.0', 'id': id, 'result': result} log.debug('Returning success message: {}', { 'jsonrpc': '2.0', 'id': id, 'result': len(result) if is_listy(result) else str(result).encode('utf-8')}) cherrypy.response.status = 200 return response
def _create_or_fetch(cls, session, value, **backref_mapping): """ Fetch an existing or create a new instance of this class. Fetching uses the values from the value positional argument (the id if available, or if any keys that correspond to unique constraints are present). In both cases the instance will still need to be updated using whatever new values you want. Args: cls (class): The class object we're going to fetch or create session (Session): the session object value (any): the dictionary value to fetch with **backref_mapping: the backref key name and value of the "parent" object of the object you're fetching or about to create. If the backref value of a fetched instance is not the same as the value of what's passed in, we will instead create a new instance. This is because we want to prevent "stealing" an existing object in a one-to-one relationship unless an id is explicitly passed. Returns: A previously existing or newly created (and added to the session) model instance. """ assert len( backref_mapping ) <= 1, 'only one backref key is allowed at this time: {}'.format( backref_mapping) if backref_mapping: backref_name = list(backref_mapping.keys())[0] parent_id = backref_mapping[backref_name] else: backref_name, parent_id = None, None id = None if isinstance(value, Mapping): id = value.get('id', None) elif isinstance(value, six.string_types): id = value instance = None if id is not None: try: instance = session.query(cls).filter(cls.id == id).first() except Exception: log.error('Unable to fetch instance based on id value {!r}', value, exc_info=True) raise TypeError( 'Invalid instance ID type for relation: {0.__name__} (value: {1})' .format(cls, value)) elif isinstance(value, Mapping): # if there's no id, check to see if we're provided a dictionary # that includes all of the columns associated with a UniqueConstraint. for column_names in cls.unique_constraint_column_names: if all( (name in value and value[name]) for name in column_names): # all those column names are provided, # use that to query by chaining together all the necessary # filters to construct that query q = session.query(cls) filter_kwargs = { name: value[name] for name in column_names } try: instance = q.filter_by(**filter_kwargs).one() except NoResultFound: continue except MultipleResultsFound: log.error( 'multiple results found for {} unique constraint: {}', cls.__name__, column_names) raise else: break else: log.debug( 'unable to search using unique constraints: {} with {}', column_names, value) if instance and id is None and backref_mapping and getattr( instance, backref_name, None) != parent_id: log.warning( 'Attempting to change the owner of {} without an explicitly passed id; ' 'a new {} instance will be used instead', instance, cls.__name__) instance = None if not instance: log.debug('creating new: {} with id {}', cls.__name__, id) if id is None: instance = cls() else: instance = cls(id=id) session.add(instance) return instance
'tabletop_check_notification_replies', 'tabletop_send_notifications' ] TASK_INTERVAL = 180 # Check every three minutes twilio_client = None if c.SEND_SMS: try: twilio_sid = c.TABLETOP_TWILIO_SID twilio_token = c.TABLETOP_TWILIO_TOKEN if twilio_sid and twilio_token: twilio_client = TwilioRestClient(twilio_sid, twilio_token) else: log.debug( 'Tabletop twilio SID and/or TOKEN is not in INI, not going to ' 'try to start twilio for tabletop SMS messaging') except Exception: log.error('Twilio: unable to initialize twilio REST client', exc_info=True) twilio_client = None else: log.info('SMS DISABLED for tabletop') def send_sms(to, body, from_=c.TABLETOP_TWILIO_NUMBER): to = normalize_phone(to, c.TABLETOP_PHONE_COUNTRY or 'US') if not twilio_client: log.error('no twilio client configured') elif c.DEV_BOX and to not in c.TESTING_PHONE_NUMBERS: log.info('We are in dev box mode, so we are not sending {!r} to {!r}',
TASK_INTERVAL = 180 # Check every three minutes TEXT_TEMPLATE = 'Checkin for {signup.event.name} {checkin}, {signup.event.location_room_name}. Reply N to drop out' twilio_client = None if c.SEND_SMS: try: twilio_sid = c.PANELS_TWILIO_SID twilio_token = c.PANELS_TWILIO_TOKEN if twilio_sid and twilio_token: twilio_client = TwilioRestClient(twilio_sid, twilio_token) else: log.debug( 'Panels twilio SID and/or TOKEN is not in INI, not going to ' 'try to start twilio for panels SMS notifications') except Exception: log.error('Twilio: unable to initialize twilio REST client', exc_info=True) twilio_client = None else: log.info('SMS DISABLED for panels') def send_sms(to, body, from_=c.PANELS_TWILIO_NUMBER): message = None sid = 'Unable to send sms' try: to = normalize_phone(to) if not twilio_client:
def error(status, code, message): response = {'jsonrpc': '2.0', 'id': id, 'error': {'code': code, 'message': message}} log.debug('Returning error message: {}', repr(response).encode('utf-8')) cherrypy.response.status = status return response