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
- O seu backend cria um pedido de confirmação.
- 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.
- O destinatário revê o destino proposto sem precisar de uma conta Alkax.
- Mantém-no, seleciona outro local Alkax ativo ou cria um local em falta.
- Pode acrescentar uma fotografia privada da entrada, que permanece indisponível até ser aprovada pela moderação.
- A Alkax regista o instantâneo confirmado ou corrigido.
- 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:writepara criar, cancelar, reenviar e repetir eventos;delivery:readpara 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"
| Estado | Significado |
|---|---|
Pending | Criado; o destinatário ainda não abriu a ligação ativa |
Opened | O destinatário abriu a experiência de confirmação |
Confirmed | O destinatário aceitou o destino |
Corrected | O destinatário selecionou ou criou um destino diferente |
Expired | O pedido com prazo limitado terminou sem conclusão |
Cancelled | O 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:
- Rejeite datas/horas fora de uma janela de atualidade de cinco minutos.
- Verifique a assinatura sobre o corpo em bruto sem alterações e compare
eventIdanalisado com o cabeçalho. - Guarde a identidade de ambiente/evento e o hash do corpo bruto com uma restrição de unicidade.
- Se a identidade já existir com o mesmo hash, devolva
2xxsem repetir efeitos. Um hash diferente é uma falha de integridade; não aplique alterações de estado. - 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ção | Endpoint | Âmbito necessário |
|---|---|---|
| Cancelar | POST /api/v1/address-confirmations/{id}/cancel | escrita |
| Reenviar a ligação do destinatário | POST /api/v1/address-confirmations/{id}/resend | escrita |
| Reenviar um webhook sem entrega | POST /api/v1/address-confirmations/webhooks/{eventId}/replay | escrita |
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: respeiteRetry-Aftere 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/5xxsustentadas 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.