Created
March 15, 2016 19:13
-
-
Save Verurteilt/253662b56df9f1e9b635 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
import sys | |
### INTEGRACION Django | |
try: | |
import imp | |
imp.find_module('settings') # Assumed to be in the same directory. | |
except ImportError: | |
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) | |
sys.exit(1) | |
from settings import setup_environ | |
import settings | |
parser, options, args = setup_environ(settings, usage=u'%prog [options] start|stop|restart') | |
### | |
import logging | |
import logging.handlers | |
import os | |
import time | |
from django import db | |
from django.conf import settings | |
from django.core.management.color import color_style | |
from django.db import transaction | |
from General.utils.daemon import Daemon | |
# =-=-=-=-=-=-=-=-=-=-=-=-=-=- COLORES | |
os.environ['DJANGO_COLORS'] = settings.DJANGO_COLORS | |
style = color_style() | |
# =-=-=-=-=-=-=-=-=-=-=-=-=-=- | |
ARCHIVO_LOG = 'ipn.log' | |
# =-=-=-=-=-=-=-=-=-=-=-=-=-=- MENSAJES | |
MSJ_INICIANDO_SERV = style.HTTP_INFO('Iniciando servicio de validacion IPN.') | |
MSJ_TERMINANDO_SERV = style.HTTP_INFO('Terminando servicio de validacion IPN.') | |
MSJ_ERROR_SERV = style.ERROR('Ocurrio un error en el servicio: %s.') | |
MSJ_INICIANDO_VERIFICACIONPP = 'Iniciando verificacion con PayPal...' | |
MSJ_RESPUESTA_VERIFICACIONPP = 'Respuesta de PayPal: %s' | |
MSJ_CONEXION_MONGODB = 'Realizando conexion con MongoDB...' | |
MSJ_IPN_SIN_VERIFICAR = 'IPN %s se encuentra sin verificar (%s)' | |
MSJ_TRANSACCION_YA_PROCESADA = 'Transaccion %s ya se encuentra registrada y procesada en transacciones' | |
MSJ_IPN_A_VERIFICAR = 'Verificando IPN %s' | |
MSJ_IDENTIFICANDO_PAGO = 'Identificando pago %s...' | |
MSJ_IPN_VERIFICADAS = 'Se han terminado de verificar IPNs' | |
MSJ_VERIFICANDO_INFO_IPN = 'Verificando informacion IPN...' | |
MSJ_PROCESANDO_TRANSACCIONPP = 'Procesando transaccion PayPal...' | |
MSJ_TRANSACCION_EXITOSA = 'Se proceso la transaccion %s exitosamente' | |
MSJ_NO_CUSTOM = style.NOTICE('\'custom\'') + ' no encontrado, intentando identificar el pago mediante \'item_number\' (%s) e \'item_name\' (%s)...' | |
MSJ_OBTENIENDO_CUENTA_GRAL = 'No se tiene la informacion necesaria para identificar el pago, obteniendo una cuenta general para asignarlo...' | |
MSJ_IDENTIFICANDO_FORMA_PAGO = 'Identificando forma de pago PayPal...' | |
MSJ_TRANSACCION_NO_VALIDA = 'La transaccion no cumple con los requerimientos minimos para ser procesada, '#payment_status %s y mc_currency %s' | |
MSJ_TRANSACCION_NO_VALIDA_PAYMENT = MSJ_TRANSACCION_NO_VALIDA + 'payment_status %s y mc_currency %s' | |
#MSJ_TRANSACCION_NO_VALIDA_SUBSCR = MSJ_TRANSACCION_NO_VALIDA + 'referencia %s y operador %s' | |
MSJ_TRANSACCION_NO_PROCESADA = 'No se pudo procesar la transaccion %s exitosamente' | |
MSJ_ERROR_OBTENCION_CUENTAPP = 'No se pudo determinar la cuenta PayPal para verificar el pago' | |
MSJ_ERROR_OBTENCION_CARGO = 'El cargo %s no existe en MiUTEL' | |
MSJ_ERROR_PARAMETROS_INSUFICIENTES = 'No se tiene la informacion necesaria para verificar, identificar y procesar el pago: %s' | |
MSJ_ERROR_OBTENCION_CUENTA_GRAL = 'No se puede determinar una cuenta/usuario en donde aplicar el pago' | |
MSJ_ERROR_GRAL = 'Ocurrio un error al procesar IPN: %s' | |
MSJ_SUSCRIPCION_YA_PROCESADA = 'Suscripcion %s ya se encuentra registrada y procesada en suscripciones' | |
MSJ_ERROR_OBTENCION_OPERADOR = 'El operador que invito a domiciliar, no existe en MiUTEL, se registrara operacion sin operador: %s' | |
MSJ_ERROR_OBTENCION_OPERADOR2 = 'No se tiene informacion del operador que invito a domiciliar, se registrara operacion sin operador' | |
MSJ_SUBSCR_PAYMENT_NO_PROCESADO = 'Pago por suscripcion no se procesa hasta tener informacion de la suscripcion %s' | |
# =-=-=-=-=-=-=-=-=-=-=-=-=-=- LOGGER | |
logger = logging.getLogger('%s.%s' % ('validaripn', __name__)) | |
handler = logging.handlers.RotatingFileHandler(ARCHIVO_LOG, maxBytes=10000000, backupCount=20) | |
formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s - %(message)s') | |
logger.setLevel(logging.INFO) | |
handler.setFormatter(formatter) | |
logger.addHandler(handler) | |
class IPN(Daemon): | |
def __init__(self, pidfile, tiempo_ejecucion=60): | |
self.tiempo_ejecucion = tiempo_ejecucion | |
super(IPN, self).__init__(pidfile) | |
def verificar_ipn(self): | |
import re | |
from bson import ObjectId | |
#from django.contrib.auth.models import User | |
from General.mongodb import cliente_mongodb | |
from pagos.interpretes.base import buscar_concepto_banco | |
from Simoba.paypal import fechahorapp_a_fechahoralocal | |
from Simoba.paypal.transaccion import Transaccion | |
from Simoba.paypal.models import TransaccionPayPal, CuentaPaypal, SubscriptionPayPal, CatTipoSuscripcionPayPal | |
#from registro.models import Cargo, Moneda | |
from General.utils import desencripta_rsa | |
logger.info(MSJ_CONEXION_MONGODB) | |
#cliente_mongodb = MongoDB(host='mongodb://%s:%s@%s/%s' % (settings.MONGO_USER, urlquote(settings.MONGO_PWD), settings.MONGO_HOST, settings.MONGO_DB)) | |
#cliente_mongodb.collection = cliente_mongodb[settings.MONGO_DB].paypal_ipn | |
#cliente_mongodb = MongoDB(settings.MONGO_USER, settings.MONGO_PWD, host=settings.MONGO_HOST, source=settings.MONGO_DB) | |
#cliente_mongodb.collection = cliente_mongodb.db.paypal_ipn | |
ipn_recibido_q = {'estado': TransaccionPayPal.TRANS_IPN_RECIBIDO} | |
ipn_procesado_q = dict(payment_status='Completed', **ipn_recibido_q) | |
ipn_verificado_q = {'$set': {'estado': TransaccionPayPal.TRANS_IPN_VERIFICADA}} | |
#ipn_recibidos = cliente_mongodb.collection.find({ | |
ipn_recibidos = cliente_mongodb.PaypalIpn.find({ | |
'$or': [ | |
{'$and': [ipn_recibido_q, {'payment_status': 'Completed'}]}, | |
{'$and': [ipn_recibido_q, {'txn_type': 'subscr_signup'}]} | |
] | |
}) | |
_es_subscription = lambda txn_type: txn_type.lower() == TransaccionPayPal.TRANS_SUBSCR_SIGNUP.lower() | |
_es_subscr_payment = lambda txn_type: txn_type.lower() == TransaccionPayPal.TRANS_SUBSCR_PAYMENT.lower() | |
for ipn in ipn_recibidos: | |
try: | |
transaccion_pp = None | |
subscription_pp = None | |
es_subscription = _es_subscription(ipn['txn_type']) | |
es_subscr_payment = _es_subscr_payment(ipn['txn_type']) | |
logger.info(MSJ_IPN_A_VERIFICAR % style.HTTP_INFO( | |
es_subscription and ipn['subscr_id'] or ipn['txn_id'])) | |
# hasta el momento solo se manejan suscripciones y pagos | |
if es_subscription or es_subscr_payment: | |
try: | |
subscription_pp = SubscriptionPayPal.objects.select_related('cuentapaypal').get(subscriptionid=ipn['subscr_id']) | |
# continuar con el siguiente IPN solo si se | |
# trata de una suscripcion (subscr_signup) | |
if es_subscription: | |
cliente_mongodb.PaypalIpn.update(dict(_id=ObjectId(ipn['_id']), subscr_id=ipn['subscr_id'], **ipn_recibido_q), ipn_verificado_q) | |
logger.info(MSJ_SUSCRIPCION_YA_PROCESADA % ipn['subscr_id']) | |
# no debe procesarse IPN | |
continue | |
except SubscriptionPayPal.DoesNotExist: | |
subscription_pp = SubscriptionPayPal(subscriptionid=ipn['subscr_id']) | |
if es_subscr_payment: | |
logger.warning(MSJ_SUBSCR_PAYMENT_NO_PROCESADO % style.NOTICE(ipn['subscr_id'])) | |
continue | |
if not es_subscription: | |
try: | |
transaccion_pp = TransaccionPayPal.objects.select_related('cuentapaypal').get(transactionid=ipn['txn_id']) | |
if transaccion_pp.cat_estado_id == TransaccionPayPal.TRANS_PROCESADA: | |
cliente_mongodb.PaypalIpn.update(dict(_id=ObjectId(ipn['_id']), txn_id=ipn['txn_id'], **ipn_procesado_q), ipn_verificado_q) | |
logger.info(MSJ_TRANSACCION_YA_PROCESADA % ipn['txn_id']) | |
# no debe procesarse IPN | |
continue | |
except TransaccionPayPal.DoesNotExist: | |
pass | |
logger.info(MSJ_IPN_SIN_VERIFICAR % ( | |
style.HTTP_INFO(es_subscription and ipn['subscr_id'] or ipn['txn_id']), | |
style.HTTP_INFO(ipn['txn_type']) | |
)) | |
# los siguientes valores deben existir para | |
# verificar, identificar y registrar el pago | |
# o la suscripcion | |
url = ipn['urlencode'] | |
mc_currency = ipn['mc_currency'] | |
mc_gross = not es_subscription and ipn['mc_gross'] or '?' | |
payment_status = not es_subscription and ipn['payment_status'] or '?' | |
payment_date = not es_subscription and ipn['payment_date'] or '?' | |
subscr_date = es_subscription and ipn['subscr_date'] or '?' | |
receiver_email = ipn['receiver_email'] | |
# los siguientes valores pueden o no estar disponibles | |
custom = ipn.get('custom') | |
item_number = ipn.get('item_number') | |
item_name = ipn.get('item_name') | |
# concepto_banco = None | |
# referencia = None | |
# operador = None | |
# subscr_licenciatura_id = None | |
cuenta_paypal = getattr(transaccion_pp, 'cuentapaypal', None) or CuentaPaypal.objects.obtener_cuenta_paypal(receiver_email=receiver_email) | |
#if cargo_id is None: | |
# si el tipo de transaccion es pago por/o suscripcion PayPal, intentar | |
# buscar la referencia del usuario en 'item_number' o 'item_name' | |
if es_subscr_payment or es_subscription: | |
logger.warning(MSJ_NO_CUSTOM % (style.NOTICE(item_number or '-'), style.NOTICE(item_name or '-'))) | |
# # obtener la licenciatura en caso de que | |
# # haya sido especificada en la suscripcion: | |
# # esto nos permitira realizar una consulta | |
# # mas especifica en la obtencion de una referencia | |
# if ipn.get('option_selection1'): | |
# match_licenciatura = re.match(r'^.+ ([0-9]+)$', ipn['option_selection1']) | |
# subscr_licenciatura_id = match_licenciatura and match_licenciatura.group(1) | |
# # primero intentar con 'item_number' | |
# if item_number is not None: | |
# referencia = self.obtener_referencia(item_number.strip(), cuenta_paypal.cuenta_banco_id, licenciatura_id=subscr_licenciatura_id) | |
# match = None | |
# # a traves de 'item_name' puede obtenerse la referencia, en | |
# # pagos por suscripcion (subscr_payment), o el operador que | |
# # realizo una invitacion para suscripcion, en caso de | |
# # suscripcion (subscr_signup) | |
# if item_name is not None: | |
# match = re.match(r'^[a-zA-Z ]+ ?(\d+)?$', item_name) | |
# # si no se encontro referencia con 'item_number', | |
# # intentar con 'item_name' | |
# if referencia is None and match and match.group(1) is not None: | |
# referencia = self.obtener_referencia(match.group(1).strip(), cuenta_paypal.cuenta_banco_id, licenciatura_id=subscr_licenciatura_id) | |
# # el id del operador que envio la invitacion | |
# # puede estar incluido en 'item_name' | |
# if es_subscription: | |
# if match and match.group(1): | |
# try: | |
# operador = User.objects.get(pk=match.group(1)) | |
# except User.DoesNotExist, e: | |
# logger.error(MSJ_ERROR_OBTENCION_OPERADOR % ' | '.join((unicode(e), 'pk=%s' % match.group(1)))) | |
# else: | |
# logger.warning(MSJ_ERROR_OBTENCION_OPERADOR2) | |
# # solo se muestran estos mensajes y se | |
# # realizan busquedas adicionales en caso de que | |
# # la transaccion sea un pago (subscr_payment) | |
# else: | |
# if referencia is None: | |
# logger.warning(style.NOTICE(MSJ_OBTENIENDO_CUENTA_GRAL)) | |
# else: | |
# logger.info(MSJ_IDENTIFICANDO_FORMA_PAGO) | |
# try: | |
# concepto_banco = buscar_concepto_banco(cuenta_paypal.cuenta_banco_id, ipn) | |
# except IndexError: | |
# raise Exception(MSJ_ERROR_OBTENCION_CUENTA_GRAL) | |
logger.info(MSJ_INICIANDO_VERIFICACIONPP) | |
transaccion_paypal = Transaccion( | |
cuenta_paypal.apiusername, | |
desencripta_rsa(cuenta_paypal.apipassword), | |
desencripta_rsa(cuenta_paypal.apisignature), | |
cuenta_paypal.cat_apipaypal.url_paypal + '/cgi-bin/webscr', | |
cuenta_paypal.cat_apipaypal.version | |
) | |
respuestapp = transaccion_paypal.validate_ipn(url).read() | |
transaccion_paypal.close() # cerrar transaccion PayPal | |
logger.info(MSJ_RESPUESTA_VERIFICACIONPP % style.HTTP_INFO(respuestapp)) | |
if respuestapp == 'VERIFIED': | |
# iniciar verificacion, identificacion y registro de pago | |
logger.info(MSJ_VERIFICANDO_INFO_IPN) | |
# parametros para actualizacion en caso de error | |
params_update = {'_id': ObjectId(ipn['_id'])} | |
if not es_subscription: | |
params_update.update(dict(txn_id=ipn['txn_id'], **ipn_procesado_q)) | |
if payment_status == 'Completed' #and mc_currency in Moneda.habilitados.values_list('clave', flat=True): | |
logger.info(MSJ_IDENTIFICANDO_PAGO % style.HTTP_INFO(str(custom))) | |
#cargo = getattr(transaccion_pp, 'cargo', None) or (cargo_id is not None and Cargo.objects.get(pk=cargo_id) or None) | |
logger.info(MSJ_PROCESANDO_TRANSACCIONPP) | |
respuesta_paypal = { | |
'paymentinfo_0_transactionid': ipn['txn_id'], | |
'paymentinfo_0_amt': mc_gross, | |
'paymentinfo_0_paymentstatus': payment_status, | |
} | |
respuesta_paypal.update(ipn) | |
transaccion_paypal = self.procesar_transaccion(cuenta_paypal, concepto_banco, respuesta_paypal, cargo, | |
referencia=referencia, es_subscr_payment=es_subscr_payment, subscription_pp=subscription_pp) | |
if transaccion_paypal.es_procesada(): | |
cliente_mongodb.PaypalIpn.update(params_update, ipn_verificado_q) | |
logger.info(MSJ_TRANSACCION_EXITOSA % style.HTTP_SUCCESS(transaccion_paypal.transactionid)) | |
continue # procesar otra transaccion | |
logger.error(MSJ_TRANSACCION_NO_PROCESADA % style.ERROR(ipn['txn_id'])) | |
else: | |
logger.error(MSJ_TRANSACCION_NO_VALIDA_PAYMENT % (style.ERROR(payment_status), style.ERROR(mc_currency))) | |
else: | |
params_update.update(dict(subscr_id=ipn['subscr_id'], **ipn_recibido_q)) | |
#if referencia is not None: | |
logger.info(MSJ_PROCESANDO_TRANSACCIONPP) | |
subscription_pp.usuario_id = referencia and referencia.usuario_id | |
subscription_pp.operador = operador | |
subscription_pp.fecha_suscripcion = fechahorapp_a_fechahoralocal(subscr_date) | |
subscription_pp.cuentapaypal = cuenta_paypal | |
subscription_pp.licenciatura_id = subscr_licenciatura_id | |
subscription_pp.tipo_suscripcion = CatTipoSuscripcionPayPal.habilitados.get(clave=ipn['txn_type']) | |
subscription_pp.num_pagos = ipn.get('recur_times', 0) | |
subscription_pp.monto = ipn['mc_amount3'] | |
subscription_pp.save() | |
cliente_mongodb.PaypalIpn.update(params_update, ipn_verificado_q) | |
logger.info(MSJ_TRANSACCION_EXITOSA % style.HTTP_SUCCESS(subscription_pp.subscriptionid)) | |
continue # procesar otra transaccion | |
#else: | |
#logger.error(MSJ_TRANSACCION_NO_VALIDA_SUBSCR % (style.ERROR(unicode(referencia)), style.ERROR(unicode(operador)))) | |
# si se llega a este punto, la transaccion | |
# no pudo procesarse exitosamente | |
if params_update: | |
cliente_mongodb.PaypalIpn.update(params_update, {'$set': {'estado': TransaccionPayPal.TRANS_IPN_INVALIDO}}) | |
except CuentaPaypal.DoesNotExist: | |
logger.error(MSJ_ERROR_OBTENCION_CUENTAPP) | |
except Cargo.DoesNotExist: | |
logger.error(MSJ_ERROR_OBTENCION_CARGO % style.ERROR(str(cargo_id))) | |
except KeyError, e: | |
logger.error(MSJ_ERROR_PARAMETROS_INSUFICIENTES % style.NOTICE(', '.join(e.args))) | |
except Exception, e: | |
logger.error(MSJ_ERROR_GRAL % style.ERROR(unicode(e))) | |
#cliente_mongodb.desconectar() | |
logger.info(MSJ_IPN_VERIFICADAS) | |
@transaction.commit_on_success | |
def procesar_transaccion(self, cuenta_paypal, concepto_banco, rpp, cargo, referencia=None, **kwargs): | |
from decimal import Decimal | |
from conciliacion import registrar_deposito_operacion | |
from paypal import fechahorapp_a_fechahoralocal | |
from paypal.helper import procesar_transaccion_paypal | |
from paypal.models import SubscriptionTransaccionPayPal | |
fechapago_deposito = fechahorapp_a_fechahoralocal(rpp['payment_date']) | |
params_f = dict(params_deposito=dict(fecha_pago=fechapago_deposito, cuenta_id=cuenta_paypal.cuenta_banco_id)) | |
es_subscr_payment = kwargs.pop('es_subscr_payment', False) | |
subscription_pp = kwargs.pop('subscription_pp', None) | |
#if cargo is None: | |
if es_subscr_payment: | |
referencia = referencia or concepto_banco.referencia | |
usuario = referencia.usuario | |
deposito = registrar_deposito_operacion( | |
usuario, | |
#usuario.obtener_cuenta(referencia.licenciatura_id, cuenta_paypal.cuenta_banco_id), | |
usuario.obtener_cuenta(cuenta_banco=cuenta_paypal.cuenta_banco_id), | |
Decimal(rpp['mc_gross']), | |
dict( | |
cat_forma_pago_id=concepto_banco.forma_pago_id, | |
folio_pago=rpp['txn_id'], | |
conciliador_id=2294, # por el momento [email protected] es el conciliador | |
referencia_unica=referencia.referencia, | |
**params_f['params_deposito'] | |
) | |
) | |
params_f.update(dict(usuario=usuario, deposito=deposito, licenciatura_id=referencia.licenciatura_id)) | |
else: | |
params_f.update(dict(cargo=cargo)) | |
transaccion_paypal = procesar_transaccion_paypal(rpp, cuenta_paypal.pk, **params_f) | |
# en caso de ser un pago por suscripcion, | |
# relacionarlo con la suscripcion | |
if es_subscr_payment: | |
SubscriptionTransaccionPayPal.objects.create(subscriptionid=subscription_pp, transactionid=transaccion_paypal) | |
return transaccion_paypal | |
def obtener_referencia(self, referencia, cuenta_banco_id, licenciatura_id=None): | |
from pagos.interpretes.base import obtener_referencia_con_cargos_mas_antiguos | |
from registro.models import Referencia | |
try: | |
referencias = Referencia.habilitados.filter(referencia__exact=referencia, cuenta_banco=cuenta_banco_id) | |
if licenciatura_id is not None: | |
referencias = referencias.filter(licenciatura=licenciatura_id) | |
return obtener_referencia_con_cargos_mas_antiguos(referencias) | |
except Referencia.DoesNotExist: | |
pass | |
return None | |
def run(self): | |
while True: | |
try: | |
self.verificar_ipn() | |
except Exception, e: | |
logger.error(style.ERROR(unicode(e))) | |
db.reset_queries() # limpia querysets | |
db.close_connection() # cierra conexiones a BD | |
time.sleep(self.tiempo_ejecucion) | |
if __name__=='__main__': | |
try: | |
demonio = IPN('/tmp/ipn.pid') | |
#demonio = IPN('/var/run/ipn.pid', tiempo_ejecucion=1800) | |
if len(args) == 1: | |
if args[0] == 'start': | |
logger.info(MSJ_INICIANDO_SERV) | |
demonio.start() | |
elif args[0] == 'stop': | |
logger.info(MSJ_TERMINANDO_SERV) | |
demonio.stop() | |
elif args[0] == 'restart': | |
demonio.restart() | |
else: | |
print style.ERROR('Opción no válida') | |
sys.exit(2) | |
sys.exit(0) | |
else: | |
print style.HTTP_SUCCESS(parser.get_usage()) | |
sys.exit(2) | |
except Exception, e: | |
logger.error(MSJ_ERROR_SERV % unicode(e)) | |
sys.exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment