Pular para o conteúdo principal

Confirme o destino antes da expedição

Delivery Intelligence permite a um comerciante propor um destino, pedir ao destinatário que o confirme ou corrija e receber um resultado fiável antes de preparar a encomenda.

Processo completo​

  1. O seu backend cria um pedido de confirmação.
  2. Em Production, a Alkax envia ao destinatário uma ligação segura com prazo limitado. O Sandbox regista uma notificação suprimida no destino de teste; abra manualmente a ligação devolvida.
  3. O destinatário revê o destino proposto sem precisar de uma conta Alkax.
  4. Mantém-no, seleciona outro local Alkax ativo ou cria um local em falta.
  5. Pode acrescentar uma fotografia privada da entrada, que permanece indisponível até ser aprovada pela moderação.
  6. A Alkax regista o instantâneo confirmado ou corrigido.
  7. O seu backend consulta o resultado ou recebe um webhook assinado.

Antes de começar​

Crie um projeto na Consola de programadores e emita uma credencial específica do ambiente com os âmbitos necessários:

  • delivery:write para criar, cancelar, reenviar e repetir eventos;
  • delivery:read para consultar resultados.

As credenciais Sandbox têm o formato ak_test_<key-id>.<secret> e as credenciais de Produção utilizam ak_live_<key-id>.<secret>. Guarde o valor completo num gestor de segredos. É apresentado uma única vez e nunca deve ser colocado em código de navegador ou móvel.

export ALKAX_BASE_URL="https://api.alkax.io"
export ALKAX_API_KEY="ak_test_<key-id>.<secret>"

1. Preparar o webhook​

Utilize um endpoint HTTPS público que só devolva 2xx depois de o evento ser aceite de forma duradoura:

POST https://merchant.example/webhooks/alkax

A Alkax rejeita destinos sem HTTPS, de loopback e de redes privadas e não segue redirecionamentos.

2. Criar uma confirmação​

Substitua expiresAt por uma data/hora UTC futura e recipientContact por uma caixa de correio de teste que controla antes de executar este pedido. O modelo abaixo só é executável depois de fornecer esses valores.

Gere uma chave de idempotência por cada tentativa lógica de endereço de encomenda. Reutilize-a apenas ao repetir um pedido idêntico.

O exemplo Sandbox não envia correio. Production pode enviar correio; obtenha primeiro o consentimento do destinatário.

curl --request POST "$ALKAX_BASE_URL/api/v1/address-confirmations" \
--header "X-Alkax-Key: $ALKAX_API_KEY" \
--header "Idempotency-Key: order-1042-address-v1" \
--header "Content-Type: application/json" \
--header "Accept-Language: fr" \
--data '{
"merchantReference": "ORDER-1042",
"recipientContact": "[email protected]",
"locale": "fr",
"expiresAt": "<UTC timestamp in the future>",
"proposedAddress": {
"placeId": null,
"title": "Bonaberi depot",
"plusCode": "6GCR+8J",
"settlement": "Douala",
"locality": "Bonaberi",
"country": "CM",
"buildingName": null,
"floor": null,
"unit": null,
"latitude": 4.08,
"longitude": 9.66
},
"callbackUrl": "https://merchant.example/webhooks/alkax"
}'

A resposta inicial 201 inclui:

  • id, o identificador do pedido Alkax;
  • recipientUrl, devolvido apenas na criação inicial;
  • webhookSigningSecret, devolvido apenas na criação inicial quando a Alkax gera o segredo de callback.

Guarde imediatamente o segredo de assinatura. A Alkax não o pode devolver novamente.

3. Ler o ciclo de vida​

curl "$ALKAX_BASE_URL/api/v1/address-confirmations/<request-id>" \
--header "X-Alkax-Key: $ALKAX_API_KEY" \
--header "Accept-Language: en"
EstadoSignificado
PendingCriado; o destinatário ainda não abriu a ligação ativa
OpenedO destinatário abriu a experiência de confirmação
ConfirmedO destinatário aceitou o destino
CorrectedO destinatário selecionou ou criou um destino diferente
ExpiredO pedido com prazo limitado terminou sem conclusão
CancelledO comerciante terminou o pedido

Utilize o instantâneo confirmado imutável para preparar a encomenda. Não o reconstrua a partir do texto de apresentação nem da proposta original. Quando avaliada, a resposta também pode incluir confidenceScore, confidenceBand e confidencePolicyVersion.

4. Verificar as assinaturas dos webhooks​

Cada webhook inclui:

X-Alkax-Event-Id: <UUID>
X-Alkax-Timestamp: <Unix seconds>
X-Alkax-Signature: v1=<hex HMAC-SHA256>

Os bytes assinados são:

<timestamp>.<raw HTTP request body>

Exemplo em Node.js:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyAlkaxWebhook({ rawBody, timestamp, signature, secret }, nowSeconds = Math.floor(Date.now() / 1000)) {
if (!Buffer.isBuffer(rawBody) || typeof timestamp !== "string" || !/^\d{1,12}$/.test(timestamp) ||
Math.abs(nowSeconds - Number(timestamp)) > 300 || typeof signature !== "string" ||
!/^v1=[a-f0-9]{64}$/.test(signature) || typeof secret !== "string" || !secret) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.`, "utf8")
.update(rawBody)
.digest();
const supplied = Buffer.from(signature.slice(3), "hex");
return supplied.length === expected.length && timingSafeEqual(supplied, expected);
}

Processe o evento por esta ordem:

  1. Rejeite datas/horas fora de uma janela de atualidade de cinco minutos.
  2. Verifique a assinatura sobre o corpo em bruto sem alterações e compare eventId analisado com o cabeçalho.
  3. Guarde a identidade de ambiente/evento e o hash do corpo bruto com uma restrição de unicidade.
  4. Se a identidade já existir com o mesmo hash, devolva 2xx sem repetir efeitos. Um hash diferente é uma falha de integridade; não aplique alterações de estado.
  5. Confirme a gravação do evento e da alteração de negócio antes de devolver 2xx.

A Alkax repete pedidos com tempo esgotado e respostas 408, 425, 429 e 5xx. O ID do evento e o conteúdo permanecem estáveis entre novas tentativas e reenvios manuais.

Ações operacionais​

AçãoEndpointÂmbito necessário
CancelarPOST /api/v1/address-confirmations/{id}/cancelescrita
Reenviar a ligação do destinatárioPOST /api/v1/address-confirmations/{id}/resendescrita
Reenviar um webhook sem entregaPOST /api/v1/address-confirmations/webhooks/{eventId}/replayescrita

O reenvio substitui o token do destinatário e está sujeito a limites de frequência. Um pedido cancelado, concluído ou expirado não pode enviar uma notificação que permita uma ação.

Erros e novas tentativas​

  • 400: corrija o pedido; não o repita sem alterações.
  • 401: a credencial está em falta, malformada, inválida, expirada ou revogada.
  • 403: a credencial não tem o âmbito necessário ou acesso a produção.
  • 404: o pedido não existe neste tenant do comerciante.
  • 409: inspecione o ciclo de vida ou o conflito de idempotência.
  • 429: respeite Retry-After e acrescente espera com variação aleatória.
  • 5xx: repita com espera exponencial e a mesma chave de idempotência.

Os erros legíveis por pessoas suportam inglês, francês, árabe, português e suaíli. Guarde os códigos legíveis por máquina em vez de comparar texto traduzido.

Lista de verificação para entrada em produção​

  • Mantenha as credenciais de projeto e os segredos de assinatura num gestor de segredos.
  • Oculte nos registos os contactos e URLs dos destinatários, as instruções privadas e os conteúdos da entrada.
  • Preserve o corpo em bruto do webhook.
  • Elimine duplicados pelo ID do evento.
  • Crie alertas para respostas 429/5xx sustentadas e acumulação de webhooks.
  • Teste confirmação, correção, expiração, cancelamento, reenvio e entrega duplicada de webhooks.

Contrato de callback recuperável​

Para recuperar uma criação, gere e cifre de forma duradoura um segredo por tentativa antes de chamar a Alkax. Defina o campo opcional callbackSigningSecret com exatamente 64 caracteres hexadecimais minúsculos obtidos de 32 bytes aleatórios; requer callbackUrl. O segredo é usado como texto UTF-8 para HMAC e nunca é devolvido. Se for omitido, a Alkax gera um segredo e devolve-o uma única vez em webhookSigningSecret.

Defina callbackSchemaVersion como v2 para receber o callback mínimo explícito abaixo. Omita-o ou escolha v1 para manter o callback anterior com propriedades internas PascalCase e estado numérico. As respostas HTTP de confirmação usam camelCase e estados textuais; estes três formatos são distintos. A versão efetiva é guardada, e a repetição preserva o ID e os bytes exatos do evento em fila.

Repetições idênticas da criação mantêm o mesmo pedido e devolvem os campos de utilização única nulos, mesmo após o prazo original da ligação. Uma nova operação continua a exigir validade futura. Preserve o conteúdo original exato e a chave de idempotência; opções alteradas originam conflito. Cada nova tentativa precisa da sua própria referência opaca no projeto e ambiente. A resposta privada canónica inclui deliveryInstructions, createdAt e cancelledAt; não é uma projeção pública de tarefa.

No callback abaixo, compare eventId com o cabeçalho, valide a assinatura em bruto e a atualidade, elimine duplicados de forma duradoura e consulte o resultado canónico autenticado. O callback mínimo não contém coordenadas, instruções nem capacidades. Os eventos das subscrições de projeto continuam a ser um canal separado. As suas identidades de criação e cancelamento são estáveis por confirmação e transição; isto não elimina duplicados semânticos no canal anterior por confirmação.

{
"schemaVersion": "v2",
"eventId": "11111111-1111-4111-8111-111111111111",
"type": "address_confirmation.confirmed",
"occurredAt": "2026-10-25T12:00:00+00:00",
"request": {
"id": "22222222-2222-4222-8222-222222222222",
"merchantReference": "opaque-attempt-reference",
"status": "Confirmed",
"expiresAt": "2026-10-26T12:00:00+00:00"
}
}

O Sandbox regista as notificações num destino de teste e nunca envia correio ao destinatário. Teste manualmente a ligação devolvida; a ativação de Production exige validação alojada.

Implemente a versão compatível do fornecedor antes de usar as opções adicionais. O estado canónico guardado pode manter Pending ou Opened após o prazo até à deteção de expiração; a reconciliação em segundo plano continua a ser uma condição de publicação. O prazo da ligação não invalida um instantâneo já concluído. Alterações do ponto privado podem manter Confirmed quando os identificadores do local não mudam: compare o instantâneo canónico exato e mantenha uma versão do destino em vez de confiar apenas na classificação. O contrato de recuperação por si só não implementa o consentimento ou a divulgação do VolXpr. A apresentação privada alojada é ativada separadamente pela opção de contexto abaixo.

Visitas privadas da comunidade​

Para uma visita privada ao domicílio, acrescente estes campos ao pedido normal de criação:

{ "presentationContext": "private_visit", "locale": "fr" }

presentationContext é opcional; a omissão mantém a experiência normal de entrega. Apenas private_visit é aceite. O valor é guardado com a confirmação e participa na idempotência: alterá-lo exige nova tentativa e referência. As respostas autenticadas de criação/GET devolvem o valor ligado ao servidor para detetar um fornecedor antigo que ignore a opção.

A página explica que a casa continua privada, oculta a criação de locais públicos e as fotografias, e permite um marcador privado exato ou um ponto de encontro público existente opcional. A conclusão não cria nem altera um local público. O servidor rejeita entranceImageReference em visitas privadas, mesmo em pedidos fora da interface.

A resposta pública inclui effectiveLocale, normalizado para en, fr, ar, pt ou sw (inglês por omissão). O e-mail e a página inicial usam a mesma língua pedida. A troca de idioma conserva os dados e o árabe suporta RTL. Falhas temporárias oferecem nova tentativa sem declarar o link inválido.

Até à expiração do link original, repetir a conclusão com um token consumido devolve apenas um recibo: proposedAddress, confirmedAddress e os dados multimédia são nulos. GET continua indisponível após a conclusão; o recibo não reabre o local nem altera o resultado. As restantes limitações de ciclo de vida e transições terminais concorrentes continuam conforme documentado acima.