def card_payment_performing(payment_json, amount, order, put_order=True): binding_id = payment_json.get('binding_id') client_id = payment_json.get('client_id') return_url = payment_json.get('return_url') if not binding_id or not client_id or not return_url: return False, u'Карта не найдена' legal = Venue.get(order.venue_id).legal.get() try: success, result = alfa_bank.create_simple(legal.alfa_login, legal.alfa_password, amount, order.key.id(), return_url, client_id) except Exception as e: send_error("Alfa error", "Alfa failure", str(e)) success, result = False, u'Не удалось произвести оплату' if not success: return success, result order.payment_id = result if put_order: order.put() success, error = alfa_bank.hold_and_check(legal.alfa_login, legal.alfa_password, order.payment_id, binding_id) if not success: error = u"Не удалось произвести оплату. %s" % error return success, error
def send_sms(to, text, company_footer=True): if company_footer: text += u'\n%s' % config.APP_NAME data = { 'apikey': SMSPILOT_API_KEY, 'send': [{ 'from': 'Ru-beacon', 'to': phone, 'text': text } for phone in to if phone] } if not data['send']: return try: response = urlfetch.fetch("http://smspilot.ru/api2.php", payload=json.dumps(data), method='POST', headers={ 'Content-Type': 'application/json' }).content logging.info(response) result = json.loads(response) success = "send" in result for message in result.get("send", []): if message["status"] != "0": success = False except Exception as e: success = False response = str(e) if not success: admins.send_error("sms", "SMS failure", response)
def get(self): now = datetime.datetime.now() delta = datetime.timedelta(minutes=MINUTES_INTERVAL) namespace_infos = {} for namespace in metadata.get_namespaces(): namespace_manager.set_namespace(namespace) orders = Order.query(Order.status == CREATING_ORDER, Order.date_created <= now - delta).fetch() if orders: infos = [] for order in orders: if config.APP_KIND == AUTO_APP: info = _handle_auto(order) elif config.APP_KIND == RESTO_APP: info = _handle_resto(order) else: info = [("ERROR", "unknown APP_KIND")] infos.append(info) namespace_infos[namespace] = infos mail_body = "Orders with creating status\n" for namespace in namespace_infos.keys(): mail_body += 'In namespace = %s:\n' % namespace mail_body += "List of orders:\n" + \ "\n\n".join("\n".join("%s: %s" % t for t in info) for info in namespace_infos[namespace]) if namespace_infos: namespace_manager.set_namespace('') admins.send_error("order", "Orders crashed while creating", mail_body)
def get(self): statuses = list(NOT_CANCELED_STATUSES) statuses.remove(READY_ORDER) namespace_orders = {} for namespace in metadata.get_namespaces(): namespace_manager.set_namespace(namespace) resto = RestoCompany.get() if resto: continue orders = Order.query(Order.status.IN(statuses), Order.delivery_time < datetime.utcnow() - timedelta(hours=HOURS_BEFORE)).fetch() if orders: logging.info('-----------------------------') namespace_orders[namespace] = orders for order in orders: logging.info("closing order id=%s, namespace=%s" % (order.key.id(), namespace)) try: done_order(order, namespace, with_push=False) except Exception as e: admins.send_error("closing", "In closing orders occur error!!!", str(e)) mail_body = u"List of orders not closed:\n" for namespace in namespace_orders.keys(): mail_body += u'In namespace = %s:\n' % namespace namespace_manager.set_namespace(namespace) for order in namespace_orders[namespace]: venue = Venue.get_by_id(order.venue_id) venue_title = venue.title if venue else u'Не определено' mail_body += u'%s (%s),\n' % (order.key.id(), venue_title) if namespace_orders: namespace_manager.set_namespace('') admins.send_error("order", "Orders not closed", mail_body)
def fuckup_ios_delivery_types(delivery_types): RESTRICTION = { 'meatme': [DELIVERY], 'sushimarket': [DELIVERY], 'nasushi': [DELIVERY], 'perchiniribaris': [SELF, IN_CAFE], 'perchiniribarislublino': [SELF, IN_CAFE], 'burgerhouse': [SELF, IN_CAFE], 'tykano': [SELF, IN_CAFE], 'magnolia': [SELF, IN_CAFE], 'chikarabar': [SELF, IN_CAFE], 'sushivesla': [DELIVERY], 'pastadeli': [SELF, IN_CAFE] } user = get_temporary_user() if not is_ios_user() or user.get(VERSION) != 2 or RESTRICTION.get( namespace_manager.get_namespace()) == None: return delivery_types send_error = False for delivery_type in delivery_types[:]: if int(delivery_type['id']) in RESTRICTION.get( namespace_manager.get_namespace()): delivery_types.remove(delivery_type) send_error = True if send_error: logging.warning('Cut delivery types: %s' % delivery_types) admins.send_error( 'ios_fuckup', 'Catch Version 2 with 2 delivery types', str({ 'user_agent': user.get(USER_AGENT), 'delivery_types': delivery_types, 'version': user.get(VERSION), 'namespace': namespace_manager.get_namespace() })) return delivery_types
def send_demo_sms(client): text = u'Поздравляем! На Вашу почту поступил тестовый заказ. Хотите боевой?\nhttp://rbcn.mobi/' try: send_sms([client.tel], text, company_footer=False) except Exception as e: error_text = str(e) error_text += u' В демо компании "%s" (%s).' % (config.APP_NAME, namespace_manager.get_namespace()) send_error('sms_error', 'Send sms', error_text)
def post(self): logging.info(self.request.POST) country = self.request.get('FromCountry') region = self.request.get('FromState') phone = self.request.get('From') body = self.request.get('Body') message = u'Страна: %s\nРегион: %s\nТелефон: %s\nСообщение: %s' % ( country, region, phone, body) logging.info(message) admins.send_error('analytics', u'Смс ответ', body=message)
def handle_500(request, response, exception): if config and config.COMPANY_STATUS == COMPANY_IN_PRODUCTION: body = """URL: %s User-Agent: %s Exception: %s Logs: https://appengine.google.com/logs?app_id=s~%s&severity_level_override=0&severity_level=3""" \ % (request.url, request.headers['User-Agent'], exception, CURRENT_APP_ID) admins.send_error("server", "Error 500", body) exc_info = sys.exc_info() raise exc_info[0], exc_info[1], exc_info[2]
def _send_push(self): from models.config.config import config logging.debug( "data:{}, dev_type: {}, parsekey: {}, parserest: {}".format( self.data, self.device_type, config.PARSE_APP_API_KEY, config.PARSE_REST_API_KEY)) if not self.data or self.device_type not in DEVICE_CHOICES or not config.PARSE_APP_API_KEY or not config.PARSE_REST_API_KEY: logging.warning( u'Невозможно послать уведомление, data=%s, device_type=%s' % (self.data, self.device_type)) return payload = { 'channels': self.channels, 'type': DEVICE_TYPE_MAP[self.device_type], 'expiry': timestamp(datetime.datetime.utcnow() + datetime.timedelta(days=365)), 'data': self.data, } headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': config.PARSE_APP_API_KEY, 'X-Parse-REST-API-Key': config.PARSE_REST_API_KEY } logging.debug(headers) try: result = json.loads( urlfetch.fetch('https://api.parse.com/1/push', payload=json.dumps(payload), method='POST', headers=headers, validate_certificate=False, deadline=10).content) logging.info(result) if result and (result.get('code') or result.get('error')): text = u'Namespace = %s\nCode = %s, Error = %s' % ( namespace_manager.get_namespace(), result.get('code'), result.get('error')) send_error('push', u'Ошибка Parse', text) except Exception as e: text = str(e) send_error('push', u'Parse упал', text) result = None return result
def paypal_payment_performing(payment_json, amount, order, client, put_order=True): correlation_id = payment_json['correlation_id'] try: success, info = paypal.authorize(order.key.id(), amount / 100.0, client.paypal_refresh_token, correlation_id) except Exception as e: success, info = False, str(e) if success: order.payment_id = info if put_order: order.put() error = None if not success: error = u'Не удалось произвести оплату' if not isinstance(info, (str, unicode)): info = json.dumps(info) send_error("Paypal error", "Paypal failure", info) return success, error
def get(self): time = datetime.datetime.now() - datetime.timedelta( days=MAX_DAYS_BEFORE_CANCEL) namespace_cancels = {} for namespace in metadata.get_namespaces(): namespace_manager.set_namespace(namespace) unused_gifts = SharedGift.query( SharedGift.status == SharedGift.READY) old_gifts = [gift for gift in unused_gifts if gift.created < time] if old_gifts: for gift in old_gifts: gift.cancel(namespace) namespace_cancels[namespace] = 'Gifts not used within %s days: %s. In company %s\n' % \ (MAX_DAYS_BEFORE_CANCEL, len(old_gifts), config.APP_NAME) if namespace_cancels: namespace_manager.set_namespace('') admins.send_error('order', 'Unused gifts found', ''.join(namespace_cancels.values()))
def post(self): namespace = namespace_manager.get_namespace() order_id = self.request.get_range('order_id') logging.debug('Processing %s/%s', namespace, order_id) order = Order.get_by_id(order_id) if not order: logging.error('Not found order, aborting') return order_url = 'http://auto.rbcn.mobi' + self.uri_for( 'bitrix_order_info', namespace=namespace, order_id=order_id) bm = config.BITRIX_EXT_API_MODULE bitrix_params = {bm.param_name: order_url} bitrix_params.update(bm.constant_params) bitrix_url = "%s?%s" % (bm.url, urllib.urlencode(bitrix_params)) logging.debug('Bitrix URL is %s', bitrix_url) try: result = urlfetch.fetch(bitrix_url, method='POST', deadline=60) if result.status_code >= 500: send_error( 'bitrix', 'Bitrix not responding when importing %s/%s' % (namespace, order_id), '') raise Exception('Bitrix fetch failed with status code %s' % result.status_code) except Exception as e: logging.exception(e) raise else: logging.debug(result.content) content = result.content.lower() if 'error' in content: logging.error('Error!') send_error( 'bitrix', 'Bitrix error when importing %s/%s' % (namespace, order_id), result.content) else: number_match = re.search('\d+', content) if number_match: number = int(number_match.group()) logging.debug('Setting number to %s', number) order.number = int(number) order.put()
def get(self): namespace_hit = {} for namespace in metadata.get_namespaces(): namespace_manager.set_namespace(namespace) if config.HIT_MODULE and config.HIT_MODULE.status == STATUS_AVAILABLE: module = config.HIT_MODULE update_hit_category() items_str = u'\n'.join([ '%s: rating = %s' % (item.get().title, round(item.get().rating, 3)) for item in module.items ]) text = 'In company (%s), hit category (%s) has items: \n%s\n\n' % ( config.APP_NAME, module.title, items_str) namespace_hit[namespace] = text if namespace_hit: namespace_manager.set_namespace('') admins.send_error('hits', 'Hits has been changed', ''.join(namespace_hit.values()))
def post(self, order_id): order = self.user.order_by_id(int(order_id)) if order.status not in [NEW_ORDER, CONFIRM_ORDER]: self.abort(400) if order.status == NEW_ORDER and (order.delivery_type in [DELIVERY, PICKUP] and config.APP_KIND == AUTO_APP): return self.render_error(u'Необходимо сначала подтвердить заказ') try: done_order(order, self.user.namespace) except Exception as e: send_error('Close error', 'Barista error in closing order', str(e)) return self.render_error( u'Непредвиденная ошибка! Повторите позднее! Не отменяйте заказ, если он уже оплачен!' ) self.render_json({ "success": True, "delivery_time": timestamp(order.delivery_time), "actual_delivery_time": timestamp(order.actual_delivery_time) })
def get(self): result_text = u'' for namespace in metadata.get_namespaces(): namespace_manager.set_namespace(namespace) text = u'' for promo in Promo.query().fetch(): if promo.status == STATUS_AVAILABLE: if promo.end and datetime.utcnow() >= promo.end: promo.status = STATUS_UNAVAILABLE promo.put() text += u'\nАкция "%s" была выключена' % promo.title if promo.status == STATUS_UNAVAILABLE: if promo.start and promo.start <= datetime.utcnow(): if promo.end and datetime.utcnow() >= promo.end: continue promo.status = STATUS_AVAILABLE promo.visible = True promo.put() text += u'\nАкция "%s" была включена' % promo.title if text: result_text += u'В компании %s: %s' % (namespace, text) if result_text: namespace_manager.set_namespace('') admins.send_error('Promos', 'Promos has been updated', result_text)
def get(self): now = datetime.datetime.now() since = now - datetime.timedelta(minutes=MINUTES_INTERVAL) namespace_errors = {} for namespace in metadata.get_namespaces(): namespace_manager.set_namespace(namespace) requests = PaymentErrorsStatistics.get_requests(since) if requests: logging.info('-----------------------------') failure_count = sum(int(not r.success) for r in requests) if float(failure_count) / len( requests) > AVAIL_FAILURE_PERCENT / 100.0: failure_info = defaultdict( Counter) # url -> {message -> count} for request in requests: if not request.success: failure_info[request.url][ request.error_message] += 1 error_details = [] for url, messages in failure_info.iteritems(): messages_string = "\n".join( "| %s: %s" % item for item in messages.items()) error_details.append("%s\n%s" % (url, messages_string)) text = u"%s errors out of %s requests\n\n%s" % ( failure_count, len(requests), "\n\n".join(error_details)) namespace_errors[namespace] = text mail_body = u'Errors with AlfaBank within %s minutes and with %s%% of failures:\n' % \ (MINUTES_INTERVAL, AVAIL_FAILURE_PERCENT) for namespace in namespace_errors.keys(): mail_body += u'In namespace = %s:\n' % namespace mail_body += namespace_errors[namespace] if namespace_errors: logging.warning(mail_body) namespace_manager.set_namespace('') admins.send_error("server", "Payment errors", mail_body)
def cancel_order(order, status, namespace, comment=None): success = True if order.has_card_payment: legal = Venue.get(order.venue_id).legal.get() return_result = alfa_bank.reverse(legal.alfa_login, legal.alfa_password, order.payment_id) success = str(return_result['errorCode']) == '0' elif order.has_paypal_payment: success, error = paypal.void(order.payment_id) if success: for gift_detail in order.gift_details: try: empatika_promos.cancel_activation(gift_detail.activation_id) except empatika_promos.EmpatikaPromosError as e: logging.exception(e) admins.send_error("payment", "Cancel activation", str(e)) success = False if success: success_wallet_payment_reverse = False if order.wallet_payment > 0 and config.APP_KIND == AUTO_APP: try: empatika_wallet.reverse(order.client_id, order.key.id()) deferred.defer(get_balance, order.client_id, raise_error=True) # just to update memcache success_wallet_payment_reverse = True except empatika_wallet.EmpatikaWalletError as e: logging.exception(e) admins.send_error("payment", "Wallet reversal failed", str(e)) # main payment reversed -- do not abort for share_gift in order.shared_gift_details: gift = share_gift.gift.get() gift.recover() for performing in order.promo_code_performings: performing = performing.get() performing.recover() if order.subscription_details: subscription = order.subscription_details.subscription.get() subscription.recover(order.subscription_details.amount) if order.geo_push: geo_push = order.geo_push.get() geo_push.recover() if order.left_basket_promo: left_basket_promo = order.left_basket_promo.get() left_basket_promo.recover() order.status = status order.return_datetime = datetime.utcnow() order.return_comment = comment order.email_key_done = None order.email_key_cancel = None order.email_key_postpone = None order.email_key_confirm = None order.put() if order.shared_promo: shared_promo = order.shared_promo.get() if order.client_id == shared_promo.recipient.id(): shared_promo.recipient_promo_success = False elif order.client_id == shared_promo.sender.id(): shared_promo.sender_promo_success = False shared_promo.put() if status == CANCELED_BY_BARISTA_ORDER: client = Client.get(order.client_id) push_text = u"%s, заказ №%s отменен." % (client.name, order.number) if order.has_card_payment: push_text += u" Ваш платеж будет возвращен на карту в течение нескольких минут.\n" if success_wallet_payment_reverse: push_text += u" Бонусные баллы были возвращены на Ваш счет.\n" if comment: push_text += " " + comment OrderPush(push_text, order, namespace).send() elif status == CANCELED_BY_CLIENT_ORDER: message = u"Заказ из мобильного приложения №%s отменен клиентом" % order.key.id( ) venue = Venue.get(order.venue_id) try: sms_pilot.send_sms(venue.phones, message) except: pass for email in venue.emails: if email: deferred.defer(postmark.send_email, EMAIL_FROM, email, message, "<html></html>") return success
def post(self): client_id = self.request.get_range('client_id') or int( self.request.headers.get('Client-Id') or 0) client = Client.get(client_id) if not client: self.abort(400) delivery_type = int(self.request.get('delivery_type')) venue = None delivery_zone = None address = self.request.get('address') if delivery_type in [SELF, IN_CAFE, PICKUP]: venue_id = self.request.get('venue_id') if not venue_id or venue_id == '-1': venue = None else: venue = Venue.get(venue_id) elif delivery_type in [DELIVERY]: if address: address = json.loads(address) address = validate_address(address) venue, delivery_zone = get_venue_and_zone_by_address(address) logging.debug("venue: {0}, delivery zone: {1}".format( venue, delivery_zone)) raw_payment_info = self.request.get('payment') payment_info = None if raw_payment_info: payment_info = json.loads(raw_payment_info) if (not payment_info.get('type_id') and payment_info.get('type_id') != 0) or \ payment_info.get('type_id') == -1: payment_info = None delivery_slot_id = self.request.get('delivery_slot_id') if delivery_slot_id == '-1': self.abort(400) if delivery_slot_id: delivery_slot_id = int(delivery_slot_id) delivery_slot = DeliverySlot.get_by_id(delivery_slot_id) if not delivery_slot: self.abort(400) else: delivery_slot = None delivery_time_minutes = self.request.get( 'delivery_time') # used for old versions todo: remove if delivery_time_minutes: # used for old versions todo: remove send_error( 'minutes', 'delivery_time field in check order', 'The field is invoked in %s' % namespace_manager.get_namespace()) delivery_time_minutes = int( delivery_time_minutes) # used for old versions todo: remove delivery_time_picker = self.request.get('time_picker_value') delivery_time = get_delivery_time(delivery_time_picker, venue, delivery_slot, delivery_time_minutes) items = json.loads(self.request.get('items')) if self.request.get('gifts'): gifts = json.loads(self.request.get('gifts')) else: gifts = [] if self.request.get('order_gifts'): order_gifts = json.loads(self.request.get('order_gifts')) else: order_gifts = [] if self.request.get('cancelled_order_gifts'): cancelled_order_gifts = json.loads( self.request.get('cancelled_order_gifts')) else: cancelled_order_gifts = [] # todo убрать через ~2 месяца - в июне items, gifts = fuckup_move_items_to_gifts(items, gifts) client.save_session(True, bool(items or gifts or order_gifts)) module = config.BASKET_NOTIFICATION_MODULE if module and module.status and items: if client.notif_id: taskqueue.Queue(name='default').delete_tasks_by_name( [client.notif_id]) task = taskqueue.add(url='/task/basket_notification', method='POST', countdown=module.inactivity_duration, params={ 'client_id': client_id, 'namespace': namespace_manager.get_namespace() }) client.notif_id = task.name client.put() extra_fields = json.loads( self.request.get('extra_order_fields', '{}')) # todo: it can be checked in validation if config.APP_KIND == AUTO_APP: result = validate_order(client, items, gifts, order_gifts, cancelled_order_gifts, payment_info, venue, address, delivery_time, delivery_slot, delivery_type, delivery_zone) elif config.APP_KIND == RESTO_APP: result = resto_validate_order(client, items, venue, delivery_time, order_gifts, cancelled_order_gifts, delivery_type) elif config.APP_KIND == DOUBLEB_APP: result = doubleb_validate_order(client, venue, items, payment_info, delivery_time) else: result = {} logging.info('validation result = %s' % result) self.render_json(result)