Aplicaciones construidas en Lisk : proyecto Lisk Ride

diciembre 19, 2020 VICTOR HUGO LAZARTE 0 Comments

 



Introducción

LiskRide es una plataforma comunitaria que conecta a los conductores con asientos libres con los pasajeros que buscan un viaje en la misma dirección. Nuestro objetivo es eliminar a los intermediarios y permitir transacciones directas.

Este proyecto es parte de la economía colaborativa y aprovecha la plataforma de aplicaciones Lisk blockchain, y se ha desarrollado como una aplicación web progresiva basada en Reactjs.

¿Como funciona?

En nuestro ecosistema hay dos tipos de participantes:

  • El conductor
  • El pasajero

En primer lugar, se explican los requisitos previos para el conductor, seguido por el pasajero.

Requisitos previos comunes para pasajeros y conductores

Autenticación

Construyendo el proyecto Lisk Ride

Construyendo el proyecto Lisk Ride

Para usar la aplicación LiskRide, el conductor y el pasajero deberán crear sus cuentas a través de la página de autenticación y mantener la contraseña segura. La contraseña y la dirección se generan automáticamente en la página "Registrarse".

Si los usuarios ya tienen una cuenta, deberán iniciar sesión a través de la página de inicio de sesión.

Transferir

Construyendo el proyecto Lisk Ride

Una vez que haya iniciado sesión, en la página de Transferencia, los usuarios podrán recolectar tokens para usar la plataforma. Cabe señalar que esta transferencia se realiza para el POC.

Aclaración: Pa:ra ver completos los codigos se recomeienda verlos  desde una PC

handleTransfert = ( evento ) => {
esto . setState ( { isLoading : true } )
dejar usuario = JSON . parse ( getUser ( ) ) ;
prueba {
const fundTransaction = new TransferTransaction ( {
activo : {
destinatarioId : usuario . dirección ,
cantidad : utils . convertLSKToBeddows ( "200" ) ,
} ,
networkIdentifier : networkIdentifier ,
} ) ;
fundTransaction . signo ( "XXXX XXXX XXXX XXXX" ) ;
api . transacciones . broadcast ( fundTransaction . toJSON ( ) ) . entonces ( respuesta => {
esto . setState ( { isLoading : false } )
} ) . catch ( err => {
esto . setState ( { isLoading : false } )
consola . log ( JSON . stringify ( err . errores , nulo , 2 ) ) ;
} ) ;
} captura ( error ) {
esto . setState ( { isLoading : false } )
consola . log ( error )
}
} ;
ver crudotransfert.js alojado con ❤ por GitHub

Los requisitos previos del controlador

El conductor requiere los siguientes pasos:

 

Publicación del plan de viaje

Construyendo el proyecto Lisk Ride

El conductor puede agregar su plan de viaje eligiendo la ciudad de destino, la ciudad de salida, la fecha de salida, la tarifa por asiento y la cantidad de asientos disponibles.

applyAsset ( tienda ) {
errores constantes = [ ] ;
viaje constante = tienda . cuenta . getOrDefault ( este . activo . travelId ) ;
conductor constante = tienda . cuenta . obtener ( este . senderId )
const driverTravels = conductor . activo . driverTravels ||  [ ]
if ( ! travel . asset . pickUpDate ) {
const driverTravel = {
carId : esto . activo . carId ,
travelId : esto . activo . travelId ,
senderId : esto . senderId ,
driverAdress : esto . activo . driverAdress ,
pickUpLocation : este . activo . pickUpLocation ,
pickUpDate : esto . activo . pickUpDate ,
availableSeatCount : esto . activo . availableSeatCount ,
pricePerSeat : este . activo . pricePerSeat ,
destino : este . activo . destino
}
driverTravels . empujar ( conductor de viaje )
const updatedTravelAccount = {
... viajar ,
activo : {
carId : esto . activo . carId ,
travelId : esto . activo . travelId ,
senderId : esto . senderId ,
driverAdress : esto . activo . driverAdress ,
pickUpLocation : este . activo . pickUpLocation ,
pickUpDate : esto . activo . pickUpDate ,
availableSeatCount : esto . activo . availableSeatCount ,
pricePerSeat : este . activo . pricePerSeat ,
destino : este . activo . destino
}
} ;
const updatedDriverAccount = {
... conductor ,
activo : {
... conductor . activo ,
driverTravels : driverTravels ,
}
} ;
tienda . cuenta . conjunto ( viajes . dirección , updatedTravelAccount ) ;
tienda . cuenta . set ( este . senderId , updatedDriverAccount ) ;
} más {
errores . empujar (
new TransactionError (
'el viaje ya ha sido registrado' ,
productor . activo . nombre
)
) ;
}
devolver errores ;
}
ver crudoregister-travel.js alojado con ❤ por GitHub

Reserva de pasajeros y aseguramiento de fondos

Construyendo el proyecto Lisk Ride

 

Cuando un pasajero reserva el viaje, la información de contacto se envía al conductor. Además, los fondos se retiran del pasajero y se depositan en la cuenta de viaje para garantizar que el pasajero pueda pagar el viaje.

applyAsset ( tienda ) {
errores constantes = [ ] ;
viaje constante = tienda . cuenta . obtener ( este . activo . travelId ) ;
const pasajero = tienda . cuenta . obtener ( este . activo . Id . de pasajero ) ;
conductor constante = tienda . cuenta . get ( viaje . asset . carId ) ;
const pasajeroViajes = pasajero . activo . viajes de pasajeros ||  [ ]
const foundDriverTravelIndex = controlador . activo . driverTravels . findIndex ( element => element . travelId === this . asset . travelId ) ;
const amountTravel = nuevos utils . BigNum ( viaje . Activo . PricePerSeat ) . mul (
utilidades nuevas . BigNum ( this . Asset . SeatCount )
) ;
const newTravelBalance = nuevos utils . BigNum ( viaje . Saldo ) . añadir (
utilidades nuevas . BigNum ( amountTravel )
) ;
const newPassengerBalance = nuevos utils . BigNum ( pasajero . Saldo ) . sub (
newTravelBalance
) ;
si (
! utils . BigNum ( pasajero . Saldo ) . gt ( "0" ) ||
! utils . BigNum ( pasajero . Saldo ) . gte ( newTravelBalance )
) {
errores . empujar (
new TransactionError (
"cantidad insuficiente para este viaje" ,
esto . activo . travelId
)
) ;
}
si (
! utils . Bignum ( viajes . Activo . AvailableSeatCount ) . gte ( este . activo . seatCount )
) {
errores . empujar (
new TransactionError (
"no hay suficiente asiento para este viaje" ,
esto . activo . travelId
)
) ;
}
si (
pasajero . dirección == conductor . habla a
) {
errores . empujar (
new TransactionError (
"El conductor no puede reservar asiento en su coche" ,
esto . activo . travelId
)
) ;
}
if ( errores . longitud <= 0 ) {
const travelPassengerBalances = viaje . activo . travelPassengerBalances ||  [ ]
const foundTravelPassengerBalanceIndex = travelPassengerBalances . FindIndex ( elemento => elemento . passengerAddress === pasajero . dirección ) ;
const foundTravelPassengerBalance = travelPassengerBalances [ foundTravelPassengerBalanceIndex ] ;
if ( ! foundTravelPassengerBalance ) {
travelPassengerBalances . empujar ( { pasajeroAddress : pasajero . dirección , seatCount : this . asset . seatCount , amountTravel : amountTravel . toString ( ) } )
} más {
travelPassengerBalances [ foundTravelPassengerBalanceIndex ] = { ... foundTravelPassengerBalance , seatCount : utils . BigNum ( foundTravelPassengerBalance . SeatCount ) . añadir ( this . asset . seatCount ) . toString ( ) , amountTravel : utils . BigNum ( foundTravelPassengerBalance . AmountTravel ) . añadir ( utilidades nuevas . BigNum ( amountTravel ) ) . toString ( ) }
}
const restSeatCount = nuevos utils . BigNum (
viajar . activo . availableSeatCount
) . sub ( este . activo . seatCount ) ;
const updatedTravel = {
... viajar ,
activo : {
... viajar . activo ,
travelPassengerBalances : travelPassengerBalances ,
availableSeatCount : restSeatCount . toString ( ) ,
} ,
balance : newTravelBalance . toString ( ) ,
} ;
tienda . cuenta . conjunto ( viajes . dirección , updatedTravel ) ;
viajes de pasajeros . empujar ( viajar )
const updatedPassenger = {
... pasajero ,
activo : {
... pasajero . activo ,
viajes de pasajeros : viajes de pasajeros ,
} ,
balance : newPassengerBalance . toString ( ) ,
} ;
tienda . cuenta . set ( pasajero . dirección , updatedPassenger ) ;
conductor . activo . driverTravels [ foundDriverTravelIndex ] = viaje actualizado . activo
const updatedDriver = {
... conductor ,
activo : {
... conductor . activo ,
driverTravels : conductor . activo . driverTravels ,
}
} ;
tienda . cuenta . conjunto ( conductor . dirección , updatedDriver ) ;
}
devolver errores ;
}
ver crudobook-travel.js alojado con ❤ por GitHub

Viaje

Construyendo el proyecto Lisk Ride

Cuando el conductor se encuentra con el pasajero, debe aceptar comenzar el viaje juntos. Esta acción debe realizarse en presencia de ambas partes. Esto desbloqueará la transferencia de fondos a la cuenta del conductor.

applyAsset ( tienda ) {
errores constantes = [ ] ;
viaje constante = tienda . cuenta . obtener ( este . activo . travelId ) ;
const pasajero = tienda . cuenta . obtener ( este . activo . Id . de pasajero ) ;
const travelDriverBalance = viaje . activo . travelDriverBalance || [ ]
const travelPassengerBalances = viaje . activo . travelPassengerBalances ||  [ ]
const foundTravelPassengerBalance = travelPassengerBalances . encontrar ( elemento => elemento . passengerAddress === pasajeros . dirección ) ;
const foundTravelDriverBalance = travelDriverBalance . encontrar ( elemento => elemento . passengerAddress === pasajeros . dirección ) ;
if ( ! foundTravelDriverBalance ) {
if ( foundTravelPassengerBalance ) {
travelDriverBalance . empujar ( foundTravelPassengerBalance )
}
} más {
errores . empujar (
new TransactionError (
'travelDriverBalance ya se ha configurado para el conductor' ,
esto . activo . travelId
)
) ;
}
const updatedTravelAccount = {
... viajar ,
activo : {
... viajar . activo ,
travelDriverBalance : travelDriverBalance ,
}
} ;
if ( errores . longitud === 0 ) {
tienda . cuenta . conjunto ( viajes . dirección , updatedTravelAccount ) ;
}
devolver errores ;
}
ver crudostart-travel.js alojado con ❤ por GitHub

Recibo de pago

Construyendo el proyecto Lisk Ride

El conductor podrá recuperar los fondos y evaluar al pasajero a través de una interfaz de retiro accesible a través de su lista de viajes. En el futuro, el retiro será posible después de un período de 24 horas.

applyAsset ( tienda ) {
errores constantes = [ ] ;
viaje constante = tienda . cuenta . obtener ( este . activo . travelId ) ;
const pasajero = tienda . cuenta . obtener ( este . activo . Id . de pasajero ) ;
conductor constante = tienda . cuenta . get ( this . asset . carId ) ;
const travelDriverBalance = viaje . activo . travelDriverBalance || [ ] ;
const foundTravelDriverBalanceIndex = travelDriverBalance . findIndex (
( elemento ) => elemento . passAddress === esto . activo . pasajerosId
) ;
si (
! travelDriverBalance [ foundTravelDriverBalanceIndex ] . calificación &&
utilidades nuevas . BigNum (
travelDriverBalance [ foundTravelDriverBalanceIndex ] . cantidad de viaje
) . gt ( 0 )
) {
const amountToWidthdraw = nuevos utils . BigNum (
travelDriverBalance [ foundTravelDriverBalanceIndex ] . cantidad de viaje
) ;
travelDriverBalance [ foundTravelDriverBalanceIndex ] = {
... travelDriverBalance [ foundTravelDriverBalanceIndex ] ,
calificación : esto . activo . calificación ,
amountTravel : "0" ,
} ;
const newTravelBalance = nuevos utils . BigNum ( viaje . Saldo ) . sub (
utilidades nuevas . BigNum ( amountToWidthdraw )
) ;
const newDriverBalance = nuevos utils . BigNum ( conductor . Saldo ) . añadir (
amountToWidthdraw
) ;
const ratings = pasajero . activo . calificaciones || [ ] ;
calificaciones . empujar ( {
calificación : esto . activo . calificación ,
notado por : esto . senderId ,
marca de tiempo : esto . marca de tiempo ,
} ) ;
const updatedTravelAccount = {
... viajar ,
balance : newTravelBalance . toString ( ) ,
activo : {
... viajar . activo ,
travelDriverBalance : travelDriverBalance ,
} ,
} ;
const updatedPassengerAccount = {
... pasajero ,
activo : {
... pasajero . activo ,
calificaciones : calificaciones ,
} ,
} ;
const updatedDriverAccount = {
... conductor ,
balance : newDriverBalance . toString ( ) ,
} ;
tienda . cuenta . conjunto ( viajes . dirección , updatedTravelAccount ) ;
tienda . cuenta . conjunto ( pasajero . dirección , updatedPassengerAccount ) ;
tienda . cuenta . conjunto ( conductor . dirección , updatedDriverAccount ) ;
}
devolver errores ;
}
ver crudoend-travel.js alojado con ❤ por GitHub

Los requisitos previos de los requisitos de los pasajeros

El pasajero requiere los siguientes pasos:

Encontrar un viaje al destino deseado

Construyendo el proyecto Lisk Ride

Para buscar un viaje planificado, el pasajero simplemente tiene que ingresar su destino, su ciudad de salida y la fecha correspondiente. Entonces será posible elegir una ruta a partir de los resultados obtenidos. Si se requiere más información, pueden contactar con el conductor antes de reservar.

La búsqueda fue posible gracias al uso de la API extendida de Moosty:  @ moosty / lisk-extended-api.

estado = {
salida : indefinida ,
pickUpLocation : undefined ,
pickUpDate : nueva fecha ( ) ,
availableSeatCount : 0 ,
pricePerSeat : 0 ,
viajes : [ ] ,
showCalendarModal : falso ,
isLoading : falso
} ;
handleForm = ( ) => {
const {
destino ,
pickUpLocation ,
pickUpDate ,
availableSeatCount ,
} = esto . estado ;
viajes constantes = [ ] ;
esto . setState ( { isLoading : true } )
dejar destinoP = buscar (
`http: // localhost: 2020 / extended-api / accounts? asset = destination & contains = $ { destination } `
) ;
dejar pickUpLocationP = fetch (
`http: // localhost: 2020 / extended-api / accounts? asset = pickUpLocation & contains = $ { pickUpLocation } `
) ;
dejar pickUpDateP = fetch (
`http: // localhost: 2020 / extended-api / accounts? asset = pickUpDate & contains = $ { pickUpDate } `
) ;
var search = {
availableSeatCount ,
pickUpDate ,
pickUpLocation ,
destino
} ;
Promesa . todos ( [
destinoP ,
pickUpLocationP ,
pickUpDateP ,
] )
. entonces ( ( valores ) => {
dejar promesas = [ ] ;
valores . forEach ( ( valor ) => {
promesas . empujar ( valor . json ( ) ) ;
} ) ;
Promesa . todas ( promesas ) . entonces ( ( valores ) => {
valores . forEach ( ( valor ) => {
consola . log ( valor ) ;
valor . datos . forEach ( ( v ) => viaja . empujar ( { travelId : v . id , carId : v . carId , ... v . asset } ) ) ;
} ) ;
filtro constante = { pickUpDate , pickUpLocation , destino }
deja resultados = viajes . filtro ( función ( elemento ) {
para ( clave var en filtro ) {
if ( elemento [ clave ] === indefinido || elemento [ clave ] ! == filtro [ clave ] )
devolver falso ;
}
devuelve verdadero ;
} ) ;
esto . setState ( { isLoading : false } )
deje uniquResult = _ . uniqBy ( resultados , 'travelId' ) ;
esto . apoyos . updateTravels ( { viajes : uniquResult , buscar } )
esto . apoyos . historia . empujar ( "/ inicio / resultados" ) ;
} ) ;
} )
. atrapar ( ( razón ) => {
esto . setState ( { isLoading : false } )
consola . log ( razón ) ;
} ) ;
ver crudosearch.js alojado con ❤ por GitHub

Pago y aseguramiento de fondos

Construyendo el proyecto Lisk Ride

El pasajero reserva su viaje y se le debitará automáticamente el importe correspondiente. Estos fondos se depositarán en la cuenta de viajes.

applyAsset ( tienda ) {
errores constantes = [ ] ;
viaje constante = tienda . cuenta . obtener ( este . activo . travelId ) ;
const pasajero = tienda . cuenta . obtener ( este . activo . Id . de pasajero ) ;
conductor constante = tienda . cuenta . get ( viaje . asset . carId ) ;
const pasajeroViajes = pasajero . activo . viajes de pasajeros ||  [ ]
const foundDriverTravelIndex = controlador . activo . driverTravels . findIndex ( element => element . travelId === this . asset . travelId ) ;
const amountTravel = nuevos utils . BigNum ( viaje . Activo . PricePerSeat ) . mul (
utilidades nuevas . BigNum ( this . Asset . SeatCount )
) ;
const newTravelBalance = nuevos utils . BigNum ( viaje . Saldo ) . añadir (
utilidades nuevas . BigNum ( amountTravel )
) ;
const newPassengerBalance = nuevos utils . BigNum ( pasajero . Saldo ) . sub (
newTravelBalance
) ;
si (
! utils . BigNum ( pasajero . Saldo ) . gt ( "0" ) ||
! utils . BigNum ( pasajero . Saldo ) . gte ( newTravelBalance )
) {
errores . empujar (
new TransactionError (
"cantidad insuficiente para este viaje" ,
esto . activo . travelId
)
) ;
}
si (
! utils . Bignum ( viajes . Activo . AvailableSeatCount ) . gte ( este . activo . seatCount )
) {
errores . empujar (
new TransactionError (
"no hay suficiente asiento para este viaje" ,
esto . activo . travelId
)
) ;
}
si (
pasajero . dirección == conductor . habla a
) {
errores . empujar (
new TransactionError (
"El conductor no puede reservar asiento en su coche" ,
esto . activo . travelId
)
) ;
}
if ( errores . longitud <= 0 ) {
const travelPassengerBalances = viaje . activo . travelPassengerBalances ||  [ ]
const foundTravelPassengerBalanceIndex = travelPassengerBalances . FindIndex ( elemento => elemento . passengerAddress === pasajero . dirección ) ;
const foundTravelPassengerBalance = travelPassengerBalances [ foundTravelPassengerBalanceIndex ] ;
if ( ! foundTravelPassengerBalance ) {
travelPassengerBalances . empujar ( { pasajeroAddress : pasajero . dirección , seatCount : this . asset . seatCount , amountTravel : amountTravel . toString ( ) } )
} más {
travelPassengerBalances [ foundTravelPassengerBalanceIndex ] = { ... foundTravelPassengerBalance , seatCount : utils . BigNum ( foundTravelPassengerBalance . SeatCount ) . añadir ( this . asset . seatCount ) . toString ( ) , amountTravel : utils . BigNum ( foundTravelPassengerBalance . AmountTravel ) . añadir ( utilidades nuevas . BigNum ( amountTravel ) ) . toString ( ) }
}
const restSeatCount = nuevos utils . BigNum (
viajar . activo . availableSeatCount
) . sub ( este . activo . seatCount ) ;
const updatedTravel = {
... viajar ,
activo : {
... viajar . activo ,
travelPassengerBalances : travelPassengerBalances ,
availableSeatCount : restSeatCount . toString ( ) ,
} ,
balance : newTravelBalance . toString ( ) ,
} ;
tienda . cuenta . conjunto ( viajes . dirección , updatedTravel ) ;
viajes de pasajeros . empujar ( viajar )
const updatedPassenger = {
... pasajero ,
activo : {
... pasajero . activo ,
viajes de pasajeros : viajes de pasajeros ,
} ,
balance : newPassengerBalance . toString ( ) ,
} ;
tienda . cuenta . set ( pasajero . dirección , updatedPassenger ) ;
conductor . activo . driverTravels [ foundDriverTravelIndex ] = viaje actualizado . activo
const updatedDriver = {
... conductor ,
activo : {
... conductor . activo ,
driverTravels : conductor . activo . driverTravels ,
}
} ;
tienda . cuenta . conjunto ( conductor . dirección , updatedDriver ) ;
}
devolver errores ;
}
ver crudobook-travel.js alojado con ❤ por GitHub

Viaje

Construyendo el proyecto Lisk Ride

A solicitud del conductor, el pasajero debe aceptar iniciar el viaje. Esta acción debe realizarse en presencia de ambos participantes. Entonces se puede realizar la transferencia de fondos a la cuenta del conductor.

const {
destino ,
pickUpLocation ,
pickUpDate ,
availableSeatCount ,
} = esto . estado ;
viajes constantes = [ ] ;
esto . setState ( { isLoading : true } )
dejar destinoP = buscar (
`http: // localhost: 2020 / extended-api / accounts? asset = destination & contains = $ { destination } `
) ;
dejar pickUpLocationP = fetch (
`http: // localhost: 2020 / extended-api / accounts? asset = pickUpLocation & contains = $ { pickUpLocation } `
) ;
dejar pickUpDateP = fetch (
`http: // localhost: 2020 / extended-api / accounts? asset = pickUpDate & contains = $ { pickUpDate } `
) ;
var search = {
availableSeatCount ,
pickUpDate ,
pickUpLocation ,
destino
} ;
Promesa . todos ( [
destinoP ,
pickUpLocationP ,
pickUpDateP ,
] )
. entonces ( ( valores ) => {
dejar promesas = [ ] ;
valores . forEach ( ( valor ) => {
promesas . empujar ( valor . json ( ) ) ;
} ) ;
Promesa . todas ( promesas ) . entonces ( ( valores ) => {
valores . forEach ( ( valor ) => {
consola . log ( valor ) ;
valor . datos . forEach ( ( v ) => viaja . empujar ( { travelId : v . id , carId : v . carId , ... v . asset } ) ) ;
} ) ;
filtro constante = { pickUpDate , pickUpLocation , destino }
deja resultados = viajes . filtro ( función ( elemento ) {
para ( clave var en filtro ) {
if ( elemento [ clave ] === indefinido || elemento [ clave ] ! == filtro [ clave ] )
devolver falso ;
}
devuelve verdadero ;
} ) ;
esto . setState ( { isLoading : false } )
deje uniquResult = _ . uniqBy ( resultados , 'travelId' ) ;
esto . apoyos . updateTravels ( { viajes : uniquResult , buscar } )
esto . apoyos . historia . empujar ( "/ inicio / resultados" ) ;
} ) ;
} )
. atrapar ( ( razón ) => {
esto . setState ( { isLoading : false } )
consola . log ( razón ) ;
} ) ;
} ;
ver crudosearch.js alojado con ❤ por GitHub

Transacciones personalizadas

LiskRide utiliza las siguientes transacciones personalizadas:

Tipo

Nombre

Descripción

30

AddAccountInfoTransaction

Agregar información complementaria a la cuenta del conductor

31

RegistrarseTravelTransaction

Crea un viaje como cuenta

32

LibroViajeTransacción

Agregar al pasajero a la cuenta de viaje y sus fondos correspondientes

33

StartTravelTransaction

Permita que el conductor califique al pasajero y retire los fondos

34

EndTravelTransaction

Transfiera fondos a la cuenta del conductor, califique al pasajero y elimine al pasajero de la cuenta de viaje

 

Conclusión

Como desarrollador de JavaScript, fue una experiencia agradable trabajar con Lisk SDK . De hecho, el uso de JavaScript se interconecta fácilmente con otras tecnologías web, especialmente con mi interfaz en Reactjs.

No encontré ninguna dificultad en particular tanto con la depuración como con la implementación de la aplicación, lo cual fue muy ventajoso.

También aprecié las transacciones personalizadas que me permitieron concentrarme en el lado comercial de mi proyecto.

Ahora que se desarrolló mi prueba de concepto, los siguientes pasos son los siguientes:

  • Comparte con la comunidad
  • Mejora la lógica empresarial
  • Mejorar la interfaz de usuario para tener en cuenta las experiencias de UX relacionadas con la cadena de bloques Lisk.
  • Mejorar el sistema de búsqueda de viajes
  • Agregar un sistema de gestión de disputas
  • Formar un equipo

En un futuro cercano, LiskRide podría ser una alternativa a las aplicaciones de carpooling existente

Recursos

Demostración web de escritorio:  https://boring-banach-a0e04c.netlify.app

Demostración web móvil: http://lisk-ride.com

Código fuente: https://github.com/blackjmxx/LiskRideApp


 

  Introducción LiskRide es una plataforma comunitaria que conecta a los conductores con asientos libres con los pasajeros que buscan un viaj...