La API de Envíos contiene todos los endpoints y webhooks necesarios para crear un envío, obtener información sobre un envío y mantenerse actualizado sobre los eventos que afectan a un envío.
Necesitarás un endpoint público para que podamos comunicarte las novedades de los envíos en tiempo real.
Tu endpoint deberá terminar en /shipping/updates
Ahí te notificaremos si hay actualizaciones sobre un envío. Serás responsable de buscar el recurso para obtener el estado actualizado.
POST https://api.current-customer.com/whatever/shipping/updates
{
'shipment_id': 'shi-20VphpUWbbGm0Nwo8DMqsFBJgYv',
'meta': {
'resource_url': 'http://localhost:8080/v1/{shipment_id}'
}
}
curl --request POST \
--url https://api.current-customer.com/whatever/shipping/updates \
--header 'Content-Type: application/json' \
--data '{
'shipment_id' : 'shi-20VphpUWbbGm0Nwo8DMqsFBJgYv',
'meta' : {
'resource_url' : 'http://localhost:8080/v1/shi-20VphpUWbbGm0Nwo8DMqsFBJgYv'
}
}'
Como medida de seguridad, todas las notificaciones de novedades incluirán una firma hmac-sha256, pero no es necesario que la respuesta sea firmada por los clientes.
Para asegurarnos de que los únicos participantes en la comunicación sean nuestros backends (Pomelo y el Cliente), en el momento del onboarding le proporcionaremos una api-key y api-secret para firmar digitalmente el contenido de la comunicación.
A continuación explicamos con más profundidad los procesos de Intercambio de llaves y firma del pedido.
Durante el proceso de onboarding, crearemos una api-key
y api-secret
específicas para ti.
También es posible usar gpg o algún plugin de correo electrónico (como por ejemplo https://flowcrypt.com/).
Veamos un ejemplo usando openssl en la línea de comandos.
$ echo -e 'api-key=$(openssl rand -base64 32)\napi-secret=$(openssl rand -base64 32)' > api-credentials.txt
$ cat api-credentials.txt
api-key=tgeAkX0795jKTxrVR0cJbb//D8UlhHn0KZwTcDG3gyg=
api-secret=un/OHwD+fMN1TTSaEhs0vupQEDQS7DVaUdlNOu7Fpyw=
$ openssl genrsa -out private.pem 2048
$ openssl rsa -in private.pem -pubout -out public.pem
Nos envías tu clave pública public.pem
por email o Slack.
Encriptamos el archivo de credenciales con la clave pública que nos diste:
$ openssl rsautl -encrypt -in api-credentials.txt -out api-credentials.txt.enc -inkey public.pem -pubin
Enviamos el archivo api-credentials.txt.enc
por email o Slack
Desencriptas el archivo api-credentials.txt.enc
con tu clave privada private.pem
:
$ openssl rsautl -decrypt -in api-credentials.txt.enc -inkey private.pem
api-key=tgeAkX0795jKTxrVR0cJbb//D8UlhHn0KZwTcDG3gyg=
api-secret=un/OHwD+fMN1TTSaEhs0vupQEDQS7DVaUdlNOu7Fpyw=
api-secret
en un lugar seguro y que sea accesible únicamente por tu aplicación, asociada al api-key
.Junto con el pedido de autorización o ajuste te enviaremos headers HTTP con la firma, el timestamp de la firma y la api-key
para que verifiques que la firma sea correcta.
Los headers HTTP que enviamos son:
x-api-key
: este header te permitirá identificar qué api-secret
tenés que usar en el caso que se hayan configurado múltiples pares de api-key
y api-secret
.
x-signature
: este header contiene la firma digital (timestamp + endpoint + body) que deberás verificar para asegurar la integridad del request. Si la firma no coincide, deberás rechazar el pedido.
x-timestamp
: este header contiene el momento en el que se firmó el pedido en formato unix-epoch para que puedas corroborar que la firma no expiró.
x-endpoint
: el endpoint al que se realiza el pedido y usaste para generar la firma. Usa este header para regenerar la firma a validar, compararlo con el endpoint de tu servicio y verificar que coinciden.
Veamos un curl de ejemplo indicando cómo se envía esta información dentro del request:
curl --location --request POST 'localhost:9090/shipping/updates' \
--header 'x-api-key: h3Ws4Cv09JcCdw7732ig+1Eq3I2b+IWOI1anUu1A4dE=' \
--header 'x-signature: hmac-sha256 kLV3Jeyn7qbKfGHLDQKKuy5xzG/kbPrYEg8RvD8jb8A=' \
--header 'x-timestamp: 1637117179' \
--header 'x-endpoint: {clientPath}/shipping/updates'
--header 'Content-Type: application/json' \
--data-raw '{
'shipment_id' : 'shi-20VphpUWbbGm0Nwo8DMqsFBJgYv',
'meta' : {
'resource_url' : 'http://localhost:8080/v1/shi-20VphpUWbbGm0Nwo8DMqsFBJgYv'
}
}'
Para el armado de la firma se puede utilizar el siguiente snippet de código (en python 3) como por ejemplo:
import hmac, hashlib, base64, time
############# Pomelo's side ################
############################################
# pomelo's secret key
pomelo_secret_key = base64.b64decode('kLV3Jeyn7qbKfGHLDQKKuy5xzG/kbPrYEg8RvD8jb8A=')
# the update data pomelo is gonna use to make the signature
pomelo_update_body = '{
'shipment_id' : 'shi-20VphpUWbbGm0Nwo8DMqsFBJgYv',
'meta' : {
'resource_url' : 'http://localhost:8080/v1/shi-20VphpUWbbGm0Nwo8DMqsFBJgYv'
}
}'
client_req_endpoint = '/shipping/updates'
pomelo_timestamp = str(int(time.time()))
# the request data signed out by pomelo
pomelo_signature = hmac.new(pomelo_secret_key, bytes(pomelo_timestamp + client_req_endpoint + pomelo_update_body, 'utf-8'), hashlib.sha256).digest()
############# Client's side ################
############################################
# client's secret key
client_secret_key = base64.b64decode('kLV3Jeyn7qbKfGHLDQKKuy5xzG/kbPrYEg8RvD8jb8A=')
# client's api endpoint
client_api_endpoint = '/transactions/authorizations'
# signature expiration offset
signature_expiration_offset = 60 #A minute of expiration
# the authorizacion data the client needs to use to recreate the signature
client_request_data = bytes(pomelo_timestamp + client_req_endpoint + pomelo_update_body, 'utf-8')
# the signature the client has recreated
signature = hmac.new(client_secret_key, client_request_data, hashlib.sha256).digest()
# if signature is not expired and they match you can trust the request, otherwise reject it
now = int(time.time()) - signature_expiration_offset
signatureExpired = int(pomelo_timestamp) < now
signaturesMatch = hmac.compare_digest(signature, pomelo_signature)
endpointsMatch = client_api_endpoint in client_req_endpoint
requestIsValid = not signatureExpired and signaturesMatch
################ Results ###################
############################################
print('pomelo' signature = {0}'.format(base64.b64encode(pomelo_signature)))
print('client's signature = {0}'.format(base64.b64encode(signature)))
print('signature expired = {0}'.format(signatureExpired))
print('signatures match = {0}'.format(signaturesMatch))
print('endpoints match = {0}'.format(endpointsMatch))
print('request is valid = {0}'.format(requestIsValid))
A la hora de definir tu integración, podrás optar por NO utilizar el servicio de envíos que ofrece Pomelo junto a sus socios.
En ese caso, los grupos de afinidad reflejarán la configuración que hayas elegido. Además, al momento de crear una tarjeta o un lote de tarjetas, te devolveremos un identificador en el campo shipment_id
, que te recomendamos almacenar en tu integración ya que lo necesitarás para hacer el retiro. Las tarjetas estarán disponibles para su retiro en la planta embozadora definida en la integración.
El endpoint /shipping/v1/
se usa para crear un nuevo envío.
El campo region
se corresponde con:
El campo courier.tracking_url de la respuesta, estará disponible una vez que el envío sea confirmado por el partner logístico correspondiente. Hasta entonces, tendrá valor nulo
documentNumber
y documentType
no serán requeridos.region
con el código UF de dos caracteres. Ejemplo: 'SP' para São Paulo
Si operas en Colombia, el campo zip_code
es opcional, es decir que podrás no enviarlo.
El endpoint /shipping/v1/
te permite buscar un grupo de envios según los atributos que especifiques.
Tendrás que especificar tus filtros como parámetros siguiendo este patrón: filter[campo]=valor
. Por ejemplo: filter[shipment_type]=BOX
.
Para filtrar un atributo con varios posibles valores, deberás separar los valores por coma. Veamos un ejemplo: filter[shipment_type]=BOX,WAREHOUSE
Los resultados serán paginados y podrás especificar la cantidad de datos por página y también qué página ver utilizando: page[1]
y page[2]
Podrás especificar el orden de los resultados con determinados parámetros que deberás enviar como una lista de strings en el filtro de tipo sort. Por ejemplo: /shipping/v1/?sort=status,shipment_type
.
El ordenamiento por defecto será ascendente. Para especificar un orden descendente, deberás enviar el carácter '-' como prefijo del atributo. Por ejemplo: /shipping/v1/?sort=status,-shipment_type
.
Los atributos para ordenar son:
shipment_type
status
status_detail
created_at
Si un parámetro es incorrecto o está mal escrito, responderemos con un error.
El endpoint /shipping/v1/{shipment_id}
te permite obtener información sobre un envío en particular.
Deberás especificar el shipment_id
para hacer la consulta.
Para los envíos hacia un warehouse y también para los que se realizan en México, no devolveremos un ID externo de seguimiento, ¡pero descuida! Te mantendremos informado sobre el estado del envío desde el Dashboard y también vía webhooks.