def test_transaction_constraints(): transaction_data = dict( concepto_pago='PRUEBA', institucion_ordenante='646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='072', monto=1020, nombre_beneficiario='Rogelio Lopez', nombre_ordenante='BANCO', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='speid_id', fecha_operacion=datetime.today(), clave_rastreo='abc123', ) transaction = Transaction(**transaction_data) transaction.save() assert transaction.id is not None assert transaction.compound_key is not None # Unique-Spare Index skip documents that not contains compound_key # and not allow saving duplicated values second_transaction = Transaction(**transaction_data) with pytest.raises(NotUniqueError): second_transaction.save() assert second_transaction.compound_key == transaction.compound_key assert second_transaction.id is None transaction.delete()
def create_orden(): transaction = Transaction() try: external_transaction = StpTransaction(**request.json) transaction = external_transaction.transform() if CLABES_BLOCKED: clabes = CLABES_BLOCKED.split(',') if transaction.cuenta_beneficiario in clabes: capture_message('Transacción retenida') raise Exception previous_trx = Transaction.objects( clave_rastreo=transaction.clave_rastreo, cuenta_ordenante=transaction.cuenta_ordenante, cuenta_beneficiario=transaction.cuenta_beneficiario, monto=transaction.monto) assert len(previous_trx) == 0 transaction.confirm_callback_transaction() transaction.save() r = request.json r['estado'] = Estado.convert_to_stp_state(transaction.estado) except Exception as exc: r = dict(estado='LIQUIDACION') transaction.estado = Estado.error transaction.save() capture_exception(exc) return 201, r
def test_send_order(create_account): transaction = Transaction( concepto_pago='PRUEBA', institucion_ordenante='90646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='40072', monto=1020, nombre_beneficiario='Ricardo Sánchez Castillo de la Mancha S.A. de CV', nombre_ordenante=' Ricardo Sánchez Castillo de la Mancha S.A. de CV', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='speid_id', tipo_cuenta_beneficiario=40, ) order = transaction.create_order() assert order.institucionOperante == transaction.institucion_ordenante assert order.institucionContraparte == transaction.institucion_beneficiaria assert order.monto == 10.20 assert transaction.clave_rastreo == order.claveRastreo assert transaction.tipo_cuenta_beneficiario == order.tipoCuentaBeneficiario assert transaction.rfc_curp_beneficiario == order.rfcCurpBeneficiario assert transaction.referencia_numerica == order.referenciaNumerica assert order.nombreBeneficiario == ('Ricardo Sanchez Castillo de la Mancha' ' S') assert order.nombreOrdenante == 'Ricardo Sanchez Castillo de la Mancha S' assert len(order.nombreBeneficiario) == 39 assert len(order.nombreOrdenante) == 39 order = transaction.create_order() assert transaction.stp_id == order.id transaction.delete()
def test_get_order(): transaction = Transaction( concepto_pago='PRUEBA', institucion_ordenante='646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='072', monto=1020, nombre_beneficiario='Ricardo Sánchez Castillo de la Mancha S.A. de CV', nombre_ordenante='Ricardo Sánchez Castillo de la Mancha S.A. de CV', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='speid_id', ) order = transaction.get_order() assert order.fechaOperacion is None assert order.institucionOperante == transaction.institucion_ordenante assert order.institucionContraparte == transaction.institucion_beneficiaria assert transaction.clave_rastreo == order.claveRastreo assert transaction.tipo_cuenta_beneficiario == order.tipoCuentaBeneficiario assert transaction.rfc_curp_beneficiario == order.rfcCurpBeneficiario assert transaction.referencia_numerica == order.referenciaNumerica assert transaction.empresa == order.empresa assert order.nombreBeneficiario == 'Ricardo Sánchez Castillo de la Mancha' assert order.nombreOrdenante == 'Ricardo Sánchez Castillo de la Mancha' assert len(order.nombreBeneficiario) == 37 assert len(order.nombreOrdenante) == 37
def test_send_transaction(mock_send_transaction: MagicMock): params = dict( fecha_operacion=datetime.now(), institucion_ordenante='40012', institucion_beneficiaria='90646', clave_rastreo="PRUEBATAMIZI1", monto=100.0, nombre_ordenante="BANCO", tipo_cuenta_ordenante=40, cuenta_ordenante="846180000500000008", rfc_curp_ordenante="ND", nombre_beneficiario="TAMIZI", tipo_cuenta_beneficiario=40, cuenta_beneficiario="646180157000000004", rfc_curp_beneficiario="ND", concepto_pago="PRUEBA", referencia_numerica=2423, empresa="TAMIZI", ) transaction = Transaction(**params) send_transaction(transaction.to_dict()) task_params = mock_send_transaction.call_args[1]['kwargs']['transaction'] for param, value in params.items(): if param == 'fecha_operacion': continue assert value == task_params[param] assert (params['fecha_operacion'].isoformat() == task_params['fecha_operacion'])
def test_create_order_event_failed_twice(mock_callback_api, client, default_outcome_transaction): trx = Transaction(**default_outcome_transaction) trx.stp_id = DEFAULT_ORDEN_ID trx.save() id_trx = trx.id data = dict(id=DEFAULT_ORDEN_ID, Estado='DEVOLUCION', Detalle="0") resp = client.post('/orden_events', json=data) assert resp.status_code == 200 assert resp.data == "got it!".encode() trx = Transaction.objects.get(id=id_trx) assert trx.estado is Estado.failed num_events = len(trx.events) data = dict(id=DEFAULT_ORDEN_ID, Estado='DEVOLUCION', Detalle="0") resp = client.post('/orden_events', json=data) assert resp.status_code == 200 assert resp.data == "got it!".encode() trx = Transaction.objects.get(id=id_trx) assert trx.estado is Estado.failed assert len(trx.events) == num_events trx.delete() trx.delete()
def process_incoming_transaction(incoming_transaction: dict) -> dict: transaction = Transaction() try: external_tx = StpTransaction(**incoming_transaction) # type: ignore transaction = external_tx.transform() if CLABES_BLOCKED: clabes = CLABES_BLOCKED.split(',') if transaction.cuenta_beneficiario in clabes or ( transaction.cuenta_ordenante in clabes): capture_message('Transacción retenida') raise Exception transaction.confirm_callback_transaction() transaction.save() r = incoming_transaction r['estado'] = Estado.convert_to_stp_state(transaction.estado) except (NotUniqueError, TypeError) as e: r = dict(estado='LIQUIDACION') capture_exception(e) except Exception as e: r = dict(estado='LIQUIDACION') transaction.estado = Estado.error transaction.events.append(Event(type=EventType.error, metadata=str(e))) transaction.save() capture_exception(e) return r
def execute(order_val: dict): # Get version version = 0 if "version" in order_val: version = order_val['version'] if time_in_range(START_DOWNTIME, STOP_DOWNTIME, datetime.utcnow().time()): raise ScheduleError transaction = Transaction() try: input = factory.create(version, **order_val) transaction = input.transform() if not clabe.validate_clabe(transaction.cuenta_beneficiario) and ( not luhnmod10.valid(transaction.cuenta_beneficiario)): raise MalformedOrderException() except (MalformedOrderException, TypeError, ValueError): transaction.set_state(Estado.error) transaction.save() raise MalformedOrderException() try: prev_trx = Transaction.objects.get(speid_id=transaction.speid_id) # Si la transacción ya esta como succeeded termina # Puede suceder cuando se corre la misma tarea tiempo después # Y la transacción ya fue confirmada por stp assert prev_trx.estado != Estado.succeeded transaction = prev_trx transaction.events.append(Event(type=EventType.retry)) except DoesNotExist: transaction.events.append(Event(type=EventType.created)) transaction.save() pass except AssertionError: # Para evitar que se vuelva a mandar o regresar se manda la excepción raise ResendSuccessOrderException() if transaction.monto > MAX_AMOUNT: transaction.events.append(Event(type=EventType.error)) transaction.save() raise MalformedOrderException() now = datetime.utcnow() try: # Return transaction after 2 hours of creation assert (now - transaction.created_at) < timedelta(hours=2) transaction.create_order() except ( AssertionError, InvalidAccountType, InvalidAmount, InvalidInstitution, InvalidTrackingKey, PldRejected, ValidationError, ): transaction.set_state(Estado.failed) transaction.save()
def migrate_from_csv(transactions, events, requests): """ Hace la migración de una base de datos Postgres a MongoDB, los datos deben ser exportados a CSV antes de ser ejecutado, se puede utilizar el archivo scripts/migrate_postgres_mongo.sh para ejecutar la tarea completa :param transactions: Archivo CSV con los datos de las transacciones :param events: Archivo CSV con los eventos relacionados a las transacciones :param requests: Archivo CSV con los requests :return: """ transactions_list = pandas.read_csv( transactions, converters=dict(institucion_ordenante=lambda x: str(x), institucion_beneficiaria=lambda x: str(x), cuenta_ordenante=lambda x: str(x), cuenta_beneficiario=lambda x: str(x))) transactions_list = transactions_list.where( (pandas.notnull(transactions_list)), None) events_list = pandas.read_csv(events) events_list = events_list.where((pandas.notnull(events_list)), None) transactions = [] for _, t in transactions_list.iterrows(): t['stp_id'] = t['orden_id'] del t['orden_id'] if t['estado'] in ['ND', 'success', 'liquidacion']: t['estado'] = 'succeeded' t['institucion_beneficiaria'] = str(t['institucion_beneficiaria']) t['cuenta_ordenante'] = str(t['cuenta_ordenante']) t['cuenta_beneficiario'] = str(t['cuenta_beneficiario']) transaction = Transaction(**t) transaction_events = events_list[events_list.transaction_id == t['id']] for _, e in transaction_events.iterrows(): transaction.events.append( Event(type=EventType[e['type']], metadata=e['meta'], created_at=datetime.strptime(e['created_at'], '%Y-%m-%d %H:%M:%S.%f'))) transaction.id = None transactions.append(transaction) requests_list = pandas.read_csv(requests) requests_list = requests_list.where((pandas.notnull(requests_list)), None) requests = [] for _, r in requests_list.iterrows(): r['method'] = r['method'].upper() request = Request(**r) request.id = None requests.append(request) Request.objects.insert(requests) for transaction in transactions: transaction.save()
def test_event(): received = Event(type=EventType.received) completed = Event(type=EventType.completed) transaction = Transaction(events=[received, completed]) transaction.save() id_trx = transaction.id transaction = Transaction.objects.get(id=id_trx) assert len(transaction.events) == 2 assert transaction.events[0].type == received.type assert transaction.events[1].type == completed.type transaction.delete()
def transform(self) -> Transaction: trans_dict = { camel_to_snake(k): v for k, v in self.__dict__.items() if not k.startswith('_') } trans_dict['stp_id'] = trans_dict.pop('clave', None) trans_dict['monto'] = round(trans_dict['monto'] * 100) transaction = Transaction(**trans_dict) transaction.speid_id = base62_uuid('SR')() transaction.fecha_operacion = datetime.strptime( str(transaction.fecha_operacion), '%Y%m%d' ).date() return transaction
def test_invalid_id_order_event(client, default_outcome_transaction): trx = Transaction(**default_outcome_transaction) trx.stp_id = DEFAULT_ORDEN_ID trx.save() id_trx = trx.id data = dict(id='9', Estado='LIQUIDACION', Detalle="0") resp = client.post('/orden_events', json=data) assert resp.status_code == 200 assert resp.data == "got it!".encode() trx = Transaction.objects.get(id=id_trx) assert trx.estado is Estado.submitted trx.delete()
def execute(order_val): # Get version version = 0 if "version" in order_val: version = order_val['version'] transaction = Transaction() try: input = factory.create(version, **order_val) transaction = input.transform() if not clabe.validate_clabe(transaction.cuenta_beneficiario) and ( not luhnmod10.valid(transaction.cuenta_beneficiario)): raise MalformedOrderException() except (MalformedOrderException, TypeError, ValueError): transaction.set_state(Estado.error) transaction.save() raise MalformedOrderException() try: prev_trx = Transaction.objects.get(speid_id=transaction.speid_id) transaction = prev_trx transaction.events.append(Event(type=EventType.retry)) except DoesNotExist: transaction.events.append(Event(type=EventType.created)) pass if transaction.monto > MAX_AMOUNT: transaction.events.append(Event(type=EventType.error)) transaction.save() raise MalformedOrderException() order = transaction.get_order() transaction.save() # Send order to STP order.monto = order.monto / 100 res = order.registra() if res is not None and res.id > 0: transaction.stp_id = res.id transaction.events.append( Event(type=EventType.completed, metadata=str(res))) else: transaction.events.append( Event(type=EventType.error, metadata=str(res))) transaction.save()
def transaction(): transaction = Transaction( concepto_pago='PRUEBA', institucion_ordenante='90646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='40072', monto=1020, nombre_beneficiario='Ricardo Sánchez', nombre_ordenante='BANCO', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='speid_id', ) transaction.save() yield transaction transaction.delete()
def test_process_transaction(client, default_outcome_transaction): trx = Transaction(**default_outcome_transaction) trx.stp_id = DEFAULT_ORDEN_ID trx.save() assert trx.estado is Estado.submitted resp = client.get('/transactions?' 'status=submitted&prefix_ordenante=6461801570') assert resp.status_code == 200 resp = client.patch(f'/transactions/{resp.json[0]["_id"]["$oid"]}/process') assert resp.status_code == 201 trx = Transaction.objects.get(id=resp.json["_id"]["$oid"]) assert trx.estado is Estado.submitted assert trx.stp_id is not None trx.delete()
def test_reverse_transaction(client, default_outcome_transaction, mock_callback_api): trx = Transaction(**default_outcome_transaction) trx.stp_id = DEFAULT_ORDEN_ID trx.save() assert trx.estado is Estado.submitted resp = client.get('/transactions?' 'status=submitted&prefix_ordenante=6461801570') assert resp.status_code == 200 resp = client.patch(f'/transactions/{resp.json[0]["_id"]["$oid"]}/reverse') assert resp.status_code == 201 trx = Transaction.objects.get(id=resp.json["_id"]["$oid"]) assert trx.estado is Estado.failed trx.delete()
def outcome_transaction() -> Generator[Transaction, None, None]: transaction = Transaction( stp_id=2456305, concepto_pago='PRUEBA', institucion_ordenante='90646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='40072', monto=2511, nombre_beneficiario='Ricardo Sánchez', tipo_cuenta_beneficiario=40, nombre_ordenante='BANCO', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='go' + datetime.now().strftime('%m%d%H%M%S'), version=1, ) transaction.save() yield transaction transaction.delete()
def test_order_event_duplicated(client, default_outcome_transaction, mock_callback_api): trx = Transaction(**default_outcome_transaction) trx.stp_id = DEFAULT_ORDEN_ID trx.save() id_trx = trx.id data = dict(id=DEFAULT_ORDEN_ID, Estado='LIQUIDACION', Detalle="0") resp = client.post('/orden_events', json=data) assert resp.status_code == 200 assert resp.data == "got it!".encode() data = dict(id=DEFAULT_ORDEN_ID, Estado='DEVOLUCION', Detalle="0") resp = client.post('/orden_events', json=data) assert resp.status_code == 200 assert resp.data == "got it!".encode() trx = Transaction.objects.get(id=id_trx) assert trx.estado is Estado.failed trx.delete()
def test_send_transaction(mock_callback_api): transaction = Transaction( fecha_operacion=datetime.today(), institucion_ordenante=40012, institucion_beneficiaria=90646, clave_rastreo="PRUEBATAMIZI1", monto=100.0, nombre_ordenante="BANCO", tipo_cuenta_ordenante=40, cuenta_ordenante="846180000500000008", rfc_curp_ordenante="ND", nombre_beneficiario="TAMIZI", tipo_cuenta_beneficiario=40, cuenta_beneficiario="646180157000000004", rfc_curp_beneficiario="ND", concepto_pago="PRUEBA", referencia_numerica=2423, empresa="TAMIZI", ) res = send_transaction(transaction.to_dict()) assert res['status'] == 'succeeded'
def test_fail_send_order_no_account_registered(): transaction = Transaction( concepto_pago='PRUEBA', institucion_ordenante='90646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='40072', monto=1020, nombre_beneficiario='Ricardo Sánchez Castillo de la Mancha S.A. de CV', nombre_ordenante=' Ricardo Sánchez Castillo de la Mancha S.A. de CV', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='speid_id', tipo_cuenta_beneficiario=40, ) transaction_id = transaction.save().id with pytest.raises(MalformedOrderException): transaction.create_order() transaction = Transaction.objects.get(id=transaction_id) assert transaction.estado is Estado.error transaction.delete()
def test_get_transactions(client, default_income_transaction, default_outcome_transaction, mock_callback_api): resp = client.post('/ordenes', json=default_income_transaction) assert resp.status_code == 201 trx_in = Transaction.objects.order_by('-created_at').first() trx_out = Transaction(**default_outcome_transaction) trx_out.stp_id = DEFAULT_ORDEN_ID trx_out.save() resp = client.get('/transactions?' 'status=submitted&prefix_ordenante=6461801570') assert resp.status_code == 200 assert str(trx_out.id) == resp.json[0]['_id']['$oid'] resp = client.get('/transactions?' 'status=submitted&prefix_beneficiario=6461801570') assert resp.status_code == 200 assert str(trx_in.id) == resp.json[0]['_id']['$oid'] resp = client.get('/transactions') assert resp.status_code == 200 assert len(resp.json) == 2
def test_transaction(): transaction = Transaction( concepto_pago='PRUEBA', institucion_ordenante='646', cuenta_beneficiario='072691004495711499', institucion_beneficiaria='072', monto=1020, nombre_beneficiario='Ricardo Sánchez', nombre_ordenante='BANCO', cuenta_ordenante='646180157000000004', rfc_curp_ordenante='ND', speid_id='speid_id', ) transaction.events.append(Event(type=EventType.created)) transaction.events.append(Event(type=EventType.received)) transaction.events.append(Event(type=EventType.retry)) transaction.events.append(Event(type=EventType.retry)) transaction.events.append(Event(type=EventType.error)) transaction.events.append(Event(type=EventType.completed)) transaction.save() trx_saved = Transaction.objects.get(id=transaction.id) assert transaction.concepto_pago == trx_saved.concepto_pago assert transaction.institucion_beneficiaria == ( trx_saved.institucion_beneficiaria) assert transaction.cuenta_beneficiario == trx_saved.cuenta_beneficiario assert transaction.institucion_beneficiaria == ( trx_saved.institucion_beneficiaria) assert transaction.monto == trx_saved.monto assert transaction.nombre_beneficiario == trx_saved.nombre_beneficiario assert transaction.nombre_ordenante == trx_saved.nombre_ordenante assert transaction.cuenta_ordenante == trx_saved.cuenta_ordenante assert transaction.rfc_curp_ordenante == trx_saved.rfc_curp_ordenante assert transaction.speid_id == trx_saved.speid_id assert len(trx_saved.events) == 6 transaction.delete()
def transform(self) -> Transaction: transaction = Transaction(**self.to_dict()) return transaction
def transform(self): transaction = Transaction(**self.to_dict()) transaction.fecha_operacion = datetime.today() transaction.estado = Estado.submitted return transaction