Skip to main content

Aseguradora Digital

Vamos a ponernos en el papel de una aseguradora digital que previo a emitir una poliza, necesita realizar una inspeccion para poder verificar los productos y reducir el porcentaje de fraude.

Esta aseguradora digital venderá unicamente pólizas de hogar, por lo que necesitara integrar inspecciones de tipo goods

Caso de uso

El flujo que vamos a tratar de implementar es el siguiente:

Diseño de la base de datos

La aseguradora tendrá en su base de datos las siguientes entidades:

  • Available Goods: Los bienes que se van a poder cubrir en una póliza
const mongoose = require('mongoose')

const schema = new mongoose.Schema({
make: String,
model: String,
category: String,
type: String,
price: Number,
serialNumber: String,
})

export default mongoose.model('availableGoods', schema)
  • Available Policies: Las pólizas disponibles para contratar
const mongoose = require('mongoose')

const schema = new mongoose.Schema({
name: String,
coverages: [
{
type: String,
},
],
})

export default mongoose.model('availablePolicy', schema)
  • Customer: El cliente que va a contratar una poliza
const mongoose = require('mongoose')

const schema = new mongoose.Schema({
occupation: String,
name: string,
})

export default mongoose.model('customer', schema)
  • Policies: Las pólizas contratadas por un customer asociadas a un availablePolicy
const mongoose = require('mongoose')

const schema = new mongoose.Schema({
status: String,
startDate: Date,
endDate: Date,
availablePolicyId: mongoose.Types.ObjectId,
customerId: mongoose.Types.ObjectId,
goods: [{ type: mongoose.Types.ObjectId, ref: 'availableGood' }],
})

export default mongoose.model('policy', schema)

Plantilla personalizada

En esta implementación vamos a crear un template para el tipo de inspection goods. Para ello, debemos seguir los siguientes pasos:

1. Dirígite al dashboard de Autoinspector
2. Haz click en el ícono de tu perfil y luego haz click en Configuración
3. Dirígite a la sección de inspecciones
4. Haz click sobre Administrar Plantillas
5. Haz click sobre el botón Crear Template y selecciona Bienes
6. Agrega las imágenes que deseas validar para los bienes
7. Guarda los cambios y copia el identificador único de la plantilla

Una vez que ya hayas creado tu plantilla personalizada, asegúrate de copiar su identificador único y guardalo en tu aplicación backend ya sea como una variable de entorno o constante literal

Creación de la póliza e inspección

Vamos a crear nuestro primer endpoint, el cual recibirá las peticiones de creación de póliza. Al momento de crear la póliza en nuestro base de datos, procederemos con la creación de la inspección de tipo goods.

EJEMPLO SIMPLIFICADO

Si vas a realizar la implementación en producción, asegúrate de verificar toda la información que recibas en la petición como asi también el manejo de errores. Nosotros obviamos estos aspectos para centrarnos en lo que realmente importa de la implementación.

app.post('/policy/:availablePolicyId', async (req, res) => {
const availablePolicy = await availablePolicyEntity.findOne({
_id: req.params.availablePolicyId,
})

const customer = await customerEntity.create({
occupation: req.body.customer.occupation,
firstname: req.body.customer.firstname,
lastname: req.body.customer.lastname,
email: req.body.customer.email,
identification: req.body.customer.identification,
})

const policy = await policyEntity.create({
customerId: customer._id,
availablePolicyId: availablePolicy._id,
status: 'waiting_verification',
})

const inspection = await autoinspector.inspections.goods.create({
locale: 'es_AR',
// Initialize the inspection with the started status. With this value, we avoid to start the inspection.
initialStatus: 'started',
delivery: {
// We disable the delivery of all notifications. By this way, we have full control about how to communicate with our users
disabled: true,
},
metadata: {
// We save as metadata our policy id. Later this value will be consume by the webhook at the moment of update our policy status
policyId: policy._id,
},
producer: {
internalId: customer._id,
},
consumer: {
email: customer.email,
firstName: customer.firstname,
lastName: customer.lastname,
identification: customer.identification,
},
// We pass the template id that we created before. Here, we are mapping the environment variables exported from .env file.
templateId: process.env.AUTOINSPECTOR_CUSTOM_TEMPLATE_ID,
})

// Update the policy entity defining the inspection id. This is a must if we want to associate policy <-> inspection
await policyEntity.updateOne(
{
_id: policy._id,
},
{
$set: {
inspectionId: inspection.inspectionId,
},
}
)

res.status(201).json({
policyId: policy._id,
inspectionId: inspection.inspectionId,
})
})

Agregar bienes a la inspección

Una vez que la inspección ya fue creada, podemos pasar al siguiente paso: Agregar bienes a la inspección.

Para esto, vamos a crear otro endpoint a nuestra API para poder agregar dichos bienes a la inspección y posteriormente empezar con la verificación de los mismos.

app.post('/policy/:policyId/items', async (req, res) => {
//Create a list of strings that belongs to available goods ids
const availableGoodsIds = req.body.goods.map(good => good.availableGoodId)

//Make a bulk get query to the database
const availableGoods = await availableGoodEntity
.find({
_id: {
$in: availableGoodsIds,
},
})
.lean()

//Prepare the goods array to send to Autoinspector API
const goodsMerged = availableGoods.map((availableGood, index) => {
const goodDetails = req.body.goods[index]

return {
...goodDetails,
...availableGood,
}
})

const policy = await policyEntity.findOne({
_id: req.params.policyId,
})

// Add the goods to the tinspection
const goods = await autoinspector.inspections.goods.addGoods(
policy.inspectionId,
goodsMerged
)

// Prepare the goods array to set into the policy object
const goodsToPush = availableGoods.map((availableGood, index) => {
const productInspectionId = goods.productIds[index]
const goodDetails = req.body.goods[index]

return {
availableGoodId: availableGood._id,
productInspectionId: productInspectionId,
type: availableGood.type,
category: availableGood.category,
make: goodDetails.make,
model: goodDetails.model,
price: goodDetails.price,
}
})

// Push into the goods property of the policy entity the goods list built
const policyUpdate = await policyEntity.findOneAndUpdate(
{
_id: req.params.policyId,
},
{
$push: {
goods: goodsToPush,
},
},
{
new: true,
}
)

res.status(201).json(policyUpdate.goods)
})

Inspeccionar bienes

Una vez que ya tenemos en nuestra póliza y en la inspección los bienes a inspeccionar, estamos preparados para empezar a validarlos

Para ello, primero vamos agregar una nueva ruta a nuestra API que tendrá la responsabilidad de generar un image token y que sera retornado al cliente.

app.post(
'/policy/:policyId/goods/:goodId/inspection/image',
async (req, res) => {
// Get the policy that good belongs to
const policy = await policyEntity.findOne({
_id: req.params.policyId,
'goods._id': req.params.goodId,
})

const goodPolicy = policy.goods.find(
good => good._id.toString() === req.params.goodId
)

// Generates the image token and return it
const imageToken = await autoinspector.images.generateToken({
productId: goodPolicy.productInspectionId,
side: req.body.side,
coordinates: req.body.coordinates,
})

res.status(201).json({ imageToken })
}
)

Finalmente, en nuestra aplicación a nivel frontend deberíamos enviar el imageToken junto con el BLOB (imagen en binario) directamente a la API de Autoinspector.

PROCESAMIENTO DE IMÁGENES

Las imágenes se procesan de manera async. Por lo que si necesitas implementar una lógica de reintentos en base a si la imagen fue aprobada o desaprobada, deberías escuchar el evento image_processed.

const uploadImage = (image, token, productId) => {
const form = new FormData()
form.append('image', image)

fetch({
url: "https://api.autoinspector.ai/v1/inspection/image/" + productId,
method: "POST",
body: form,
headers: {
"x-image-token": token,
},
})
.then(res => {
console.log('Image uploaded and processing succesfully!')
})
.catch(err => {
console.error('Something went wrong 😨', err)
})
}

Finalizar inspección

Una vez que los bienes que se encuentran en la póliza ya fueron verificados, estamos apto para finalizar la inspección.

Para ello, vamos a crear una nueva ruta que tendrá la responsabilidad de finalizar la inspección asociada a una póliza.

CONDICIONES PARA FINALIZAR UNA INSPECCIÓN

Una inspección unicamente puede ser finalizada si cumple con las especificaciones definidas en el template. En caso de que la inspección le falte verificar bienes o alguna imagen tiene un intento restante, Autoinspector retornara un error indicando dicho progreso faltante.

app.post('/policy/:policyId/inspection/finish', async (req, res) => {
const policy = await policyEntity.findOne({
_id: req.params.policyId,
})

// Complete the inspection
await autoinspector.inspections.finish({
inspectionId: policy.inspectionId,
})

res.status(200).json({ finish: true })
})

Veredicto de inspección

El ultimo tramo de esta implementación es el de esperar el veredicto (approved o disapproved) de dicha inspección para tomar la decision de si aprobar la creación de póliza o rechazarla.

Para ello, vamos a crear una nueva ruta dentro de nuestra api para recibir los eventos de Autoinspector.

CONFIGURACIÓN DEL WEBHOOK

Asegúrate de tener un webhook creado dentro de la empresa a la cual perteneces, que el endpoint proporcionado sea posible de alcanzar y este funcionando correctamente.

Si no sabes como crear uno, puedes verlo en la seccion Crear Webhook

//We ensure receive request body without any parser. Just raw.
app.post('/webhook', async (req, res) => {
// This is Autoinspector SHA256 signature to verify if the request body is corrupted and to ensure that who are making the request is Autoinspector API
const signature = req.headers['autoinspector-signature']

let webhook

try {
//Here we use the autoinspector sdk to handle al the hmac validation. Just pass the req.rawBody that we set at the beginning via middleware, the signature provided from request and the webhook secret generated by Autoinspector for us
webhook = autoinspector.webhooks.constructEvent(
req.rawBody,
signature,
process.env.AUTOINSPECTOR_WEBHOOK_SECRET
)
} catch (err) {
return res.status(400).json({ error: `Webhook error: ${err.message} ` })
}

// At this point is safely to map the webhook properties. We know that message is not corrupted and comes from Autoinspector
switch (webhook.event) {
case 'inspection_completed':
const isInspectionApproved = webhook.payload.veredict === 'approved'

if (isInspectionApproved) {
await policyEntity.updateOne(
{
_id: webhook.payload.metadata.policyId,
},
{
$set: {
status: 'issued',
startDate: new Date(),
},
}
)
}

if (!isInspectionApproved) {
await policyEntity.updateOne(
{
_id: webhook.payload.metadata.policyId,
},
{
$set: {
status: 'declined',
},
}
)
}

break

default:
console.log(`Unhandled autoinspector event: ${webhook.event}`)
}

res.status(200).json({ received: true })
})

Código fuente

Si deseas probar esta implementación, puedes ver el código fuente de los diferentes lenguajes y probarlo en local.