Introducción
KD Push es un servicio de notificaciones push totalmente gestionado para plataformas y sus comerciantes. Los comerciantes nunca tocan OneSignal, Firebase ni certificados de Apple: añaden su app en el portal, toman una App Key y una Server Key, y su plataforma envía a través de esta API.
Todas las peticiones y respuestas son JSON sobre HTTPS, las fechas son ISO 8601 y los ids públicos son ULID.
Inicio rápido
- 1Crea tu app y toma sus claves
- 2Registra el dispositivo desde la appcURL
curl -X POST https://push.kd-app.com/api/v1/sdk/devices \ -H "X-KD-App-Key: kdp_app_…" \ -H "Content-Type: application/json" \ -d '{ "platform": "android", "token": "fcm-registration-token", "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90", "android": { "fcm_project_id": "neon-push" }, "external_user_id": "42", "user_signature": "9f2c4e…", "locale": "ar", "app_version": "2.1.0" }' - 3Envía desde tu servidorcURL
curl -X POST https://push.kd-app.com/api/v1/messages \ -H "Authorization: Bearer kdp_live_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: app-created-1234" \ -d '{ "title": "تطبيق جديد", "body": "تمت إضافة واتساب الذهبي للمتجر", "url": "neonstore://app/1234", "image_url": "https://cdn.example.com/icons/1234.png", "data": { "app_id": "1234" }, "audience": { "type": "all" } }'
Autenticación
Cada app tiene dos tipos de claves, y cada una tiene un único lugar:
kdp_app_…X-KD-App-Key: kdp_app_…- Dónde:
- Dentro de la app móvil (pública)
- Puede:
- Registrar dispositivos, leer la configuración, registrar aperturas
- Nunca puede enviar
kdp_live_…Authorization: Bearer kdp_live_…- Dónde:
- Solo en el servidor de tu plataforma (secreta)
- Puede:
- Enviar, seguir, cancelar, estadísticas
- Se muestra una vez: solo guardamos un hash
404.Errores
Todos los errores tienen la misma forma y nunca revelan detalles internos:
{
"error": {
"code": "validation_failed",
"message": "The body field is required.",
"fields": {
"body": [
"The body field is required."
]
}
}
}invalid_app_keyinvalid_server_keysubscription_inactivequota_exceededinvalid_user_signatureweb_disabled · origin_not_allowedinvalid_subscription · provider_mismatchapp_disabled · account_bannednot_foundnot_cancelablevalidation_failedfields.invalid_tokenrate_limitedRetry-After.server_errorLímites de uso
Si superas el límite recibes 429 rate_limited con la cabecera Retry-After. Enviar a muchos dispositivos es una sola petición: el motor la reparte internamente.
Idempotencia
Envía una cabecera Idempotency-Key con cada envío (por ejemplo, el id de tu evento). Repetir la petición con la misma clave en 24 h devuelve la misma notificación con 200 en lugar de crear una nueva, así que reintentar tras un fallo de red siempre es seguro.
Idempotency-Key: app-created-1234-v2.1.0Configuración
/sdk/configApp Key — app móvilPídela al iniciar la app. Android toma de aquí sus opciones de Firebase, así que cambiar de proyecto de Firebase o de proveedor nunca obliga a recompilar la app.
Cache-Control: max-age=300.curl https://push.kd-app.com/api/v1/sdk/config \
-H "X-KD-App-Key: kdp_app_…"{
"app": {
"id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
"name": "My Store"
},
"identity_verification": "required",
"android": {
"provider": "fcm",
"firebase": {
"project_id": "neon-push",
"app_id": "1:123456789:android:abcdef",
"api_key": "AIza…",
"sender_id": "123456789",
"storage_bucket": null
}
},
"ios": {
"provider": "apns"
},
"web": {
"provider": "webpush",
"vapid_public_key": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
"icon_url": null,
"prompt": {
"enabled": true,
"delay_seconds": 6,
"title": null,
"text": null
}
}
}Registrar un dispositivo
/sdk/devicesApp Key — app móvilLlámalo en cada inicio de la app, renovación del token, inicio y cierre de sesión. 201 la primera vez y 200 después.
platformios | android | webobligatoriotokenstringobligatorioinstallation_iduuidopcionalproviderapns | fcm | webpushopcionalios.bundle_idstringopcionalBundle.main.bundleIdentifier.ios.profile_app_idstringopcionalapplication-identifier de embedded.mobileprovision, sin el prefijo del equipo.ios.team_idstring(10)opcionalTeamIdentifier del mismo archivo.ios.environmentproduction | sandboxopcionalaps-environment.android.fcm_project_idstringopcionalweb.p256dh · web.authbase64urlsegún el casoexternal_user_idstring | nullopcionalnull al cerrar sesión.user_signaturehexsegún el casoexternal_user_id mientras la verificación de identidad esté activa. Cómo calcularlatagsobjectopcionallocale · timezone · app_version · os_version · device_model · sdk_versionstringopcionalcurl -X POST https://push.kd-app.com/api/v1/sdk/devices \
-H "X-KD-App-Key: kdp_app_…" \
-H "Content-Type: application/json" \
-d '{
"platform": "android",
"token": "fcm-registration-token",
"installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
"android": { "fcm_project_id": "neon-push" },
"external_user_id": "42",
"user_signature": "9f2c4e…",
"locale": "ar",
"app_version": "2.1.0"
}'{
"device": {
"installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
"platform": "android",
"status": "active",
"external_user_id": "42"
}
}{
"error": {
"code": "invalid_user_signature",
"message": "user_signature does not match external_user_id."
}
}Cancelar suscripción
/sdk/devices/unsubscribeApp Key — app móvilCuando el usuario desactiva las notificaciones dentro de la app. El dispositivo deja de recibirlas hasta que se registre de nuevo.
installation_iduuidobligatoriocurl -X POST https://push.kd-app.com/api/v1/sdk/devices/unsubscribe \
-H "X-KD-App-Key: kdp_app_…" \
-H "Content-Type: application/json" \
-d '{ "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90" }'{
"unsubscribed": true
}Registrar la apertura de una notificación
/sdk/eventsApp Key — app móvilCuando el usuario toca una notificación. Se cuenta una vez por dispositivo y mensaje, y aparece en el contador «abiertas».
type"opened"obligatoriomessage_idULIDobligatoriokd_message_id del payload.installation_iduuidobligatoriocurl -X POST https://push.kd-app.com/api/v1/sdk/events \
-H "X-KD-App-Key: kdp_app_…" \
-H "Content-Type: application/json" \
-d '{
"type": "opened",
"message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90"
}'{
"accepted": true
}Notificaciones del sitio (navegadores)
Las mismas notificaciones llegan a los visitantes de tu sitio en su navegador, sin app, sin Firebase y sin certificados: cada app tiene su propia clave VAPID, creada por KD Push. Funciona en Chrome, Edge, Firefox, Samsung Internet y Safari en Mac, y en iPhone y iPad una vez añadido el sitio a la pantalla de inicio (iOS 16.4+).
- En el portal del comerciante: tu app →
Web, activa las notificaciones del sitio y añade tu dominio (o desde tu servidor con PUT /app/web). - Crea
/kdpush-sw.jsen la raíz de tu sitio con esta única línea:
importScripts('https://push.kd-app.com/sdk/web/v1/kdpush-sw.js');- Añade el script del SDK a cada página, antes de
</head>:
<script src="https://push.kd-app.com/sdk/web/v1/kdpush.js"
data-app-key="kdp_app_…" async></script>Y listo: una pequeña tarjeta invita a los visitantes a activar las notificaciones (su texto y su retraso se configuran en el portal), el navegador se registra automáticamente y se reporta una vez al día. Al hacer clic en una notificación se abre su enlace y cuenta como abierta.
Opciones y API
<!-- data-* options -->
data-app-key required
data-service-worker /kdpush-sw.js (default)
data-prompt "off" hides the built-in card
data-lang ar | en | es | fr | de (default: the page language)
data-user-id your user id + data-user-signature (identity verification)
data-tags JSON, e.g. '{"plan":"vip"}'
// JavaScript
KDPush.subscribe() // ask (from a click) and subscribe → "subscribed" | "denied" | "default" | "unsupported" | "disabled"
KDPush.unsubscribe()
KDPush.isSubscribed() // Promise<boolean>
KDPush.login('42', signature) // link the browser to your user
KDPush.logout()
KDPush.setTags({ plan: 'vip' })os (ios · android · desktop), así que puedes, por ejemplo, anunciar una app de iPhone solo a los visitantes con iPhone: {"platforms":["web"],"audience":{"type":"tags","tags":{"os":"ios"}}}. Si tu sitio ya tiene su propio service worker, añádele la línea importScripts y apunta data-service-worker a él.Ajustes web
/app/webServer Key — tu servidorPara una plataforma que vincula los sitios de sus comerciantes: activa la web y define los dominios cuando una tienda se conecta o cambia de dominio. Activarla requiere al menos un dominio y crea la clave VAPID de la app. Los campos omitidos conservan su valor.
enabledbooleanobligatoriooriginsstring[] ≤ 20según el casoicon_urlhttps URLopcionaldefault_urlURLopcionalprompt{enabled, delay_seconds, title, text}opcionalcurl -X PUT https://push.kd-app.com/api/v1/app/web \
-H "Authorization: Bearer kdp_live_…" \
-H "Content-Type: application/json" \
-d '{"enabled":true,"origins":["shop.example.com"],"icon_url":"https://cdn.example.com/logo.png"}'{
"web": {
"enabled": true,
"origins": [
"https://shop.example.com",
"https://www.shop.example.com"
],
"vapid_public_key": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
"subscribers": 0,
"icon_url": "https://cdn.example.com/logo.png",
"default_url": null,
"prompt": {
"enabled": true,
"delay_seconds": 6,
"title": null,
"text": null
}
}
}Enviar una notificación
/messagesServer Key — tu servidorEnvía (o programa) una notificación solo a los dispositivos de tu app. La respuesta es inmediata con el estado queued; la entrega se hace en segundo plano: sigue el estado y los contadores con Consultar una notificación.
bodystring ≤ 2000obligatoriotitlestring ≤ 255opcionalurlstringopcionalhttps:// o un enlace interno como neonstore://app/1234.image_urlhttps URLopcionaldataobject ≤ 30opcionalkd_ están reservadas.audienceobjectopcional{"type":"all"} (por defecto) · users + external_user_ids · devices + installation_ids · tags + tags — hasta 1000 ids.platforms["ios","android","web"]opcionalscheduled_atISO 8601opcionalttl_secondsintopcionalpriorityhigh | normalopcionalcollapse_idstring ≤ 64opcionalCabeceras
Idempotency-Keystring ≤ 128opcional402 subscription_inactive. Sin ella no funciona nada: ni la configuración (/sdk/config), ni el registro de dispositivos, ni los cambios de ajustes de la app desde tu servidor.curl -X POST https://push.kd-app.com/api/v1/messages \
-H "Authorization: Bearer kdp_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: app-created-1234" \
-d '{
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر",
"url": "neonstore://app/1234",
"image_url": "https://cdn.example.com/icons/1234.png",
"data": { "app_id": "1234" },
"audience": { "type": "all" }
}'{
"message": {
"id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"status": "queued",
"source": "api",
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر",
"image_url": "https://cdn.example.com/icons/1234.png",
"url": "neonstore://app/1234",
"data": {
"app_id": "1234"
},
"audience": {
"type": "all"
},
"platforms": null,
"scheduled_at": null,
"counts": {
"targeted": 0,
"sent": 0,
"failed": 0,
"invalid": 0,
"opened": 0
},
"error": null,
"failures": [],
"created_at": "2026-10-03T12:00:00+00:00",
"started_at": null,
"finished_at": null
}
}{
"error": {
"code": "subscription_inactive",
"message": "No active KD Push subscription for this account."
}
}Consultar una notificación
/messages/{id}Server Key — tu servidorEstado y contadores. Consulta el objeto mensaje para cada campo.
curl https://push.kd-app.com/api/v1/messages/01JB2QA4N7C2X9V5B8M3K6P1RD \
-H "Authorization: Bearer kdp_live_…"{
"message": {
"id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"status": "sent",
"source": "api",
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر",
"image_url": "https://cdn.example.com/icons/1234.png",
"url": "neonstore://app/1234",
"data": {
"app_id": "1234"
},
"audience": {
"type": "all"
},
"platforms": null,
"scheduled_at": null,
"counts": {
"targeted": 1240,
"sent": 1227,
"failed": 3,
"invalid": 10,
"opened": 214
},
"error": null,
"failures": [],
"created_at": "2026-10-03T12:00:00+00:00",
"started_at": "2026-10-03T12:00:01+00:00",
"finished_at": "2026-10-03T12:00:04+00:00"
}
}Listar notificaciones
/messagesServer Key — tu servidorEl historial de tu app, del más reciente al más antiguo.
statusstringopcionalscheduled · queued · sending · sent · failed · canceledper_page1–100opcionalcurl "https://push.kd-app.com/api/v1/messages?status=sent&per_page=20" \
-H "Authorization: Bearer kdp_live_…"{
"data": [
{
"id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"status": "sent",
"source": "api",
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر",
"image_url": "https://cdn.example.com/icons/1234.png",
"url": "neonstore://app/1234",
"data": {
"app_id": "1234"
},
"audience": {
"type": "all"
},
"platforms": null,
"scheduled_at": null,
"counts": {
"targeted": 0,
"sent": 0,
"failed": 0,
"invalid": 0,
"opened": 0
},
"error": null,
"failures": [],
"created_at": "2026-10-03T12:00:00+00:00",
"started_at": null,
"finished_at": null
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 1,
"last_page": 1
}
}Cancelar una notificación
/messages/{id}/cancelServer Key — tu servidorCancela una notificación programada, en cola o en envío. Los lotes aún no enviados se detienen de inmediato.
curl -X POST https://push.kd-app.com/api/v1/messages/01JB2QA4N7C2X9V5B8M3K6P1RD/cancel \
-H "Authorization: Bearer kdp_live_…"{
"message": {
"id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"status": "canceled",
"source": "api",
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر",
"image_url": "https://cdn.example.com/icons/1234.png",
"url": "neonstore://app/1234",
"data": {
"app_id": "1234"
},
"audience": {
"type": "all"
},
"platforms": null,
"scheduled_at": null,
"counts": {
"targeted": 0,
"sent": 0,
"failed": 0,
"invalid": 0,
"opened": 0
},
"error": null,
"failures": [],
"created_at": "2026-10-03T12:00:00+00:00",
"started_at": null,
"finished_at": null
}
}{
"error": {
"code": "not_cancelable",
"message": "Only scheduled, queued or sending messages can be canceled."
}
}App y suscripción
/appServer Key — tu servidorComprueba la clave y si es posible enviar antes de activar las notificaciones automáticas: sending.can_send tiene en cuenta la suscripción y la cuota de este mes, y sending.reason indica la causa con los mismos códigos que POST /messages (subscription_inactive · quota_exceeded). plan y usage muestran la situación del comerciante; portal enlaza a donde puede solucionarla.
curl https://push.kd-app.com/api/v1/app \
-H "Authorization: Bearer kdp_live_…"{
"app": {
"id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
"name": "My Store",
"platform": "neon",
"status": "active",
"ios_bundle_id": "com.store.app",
"android_package": "com.store.app"
},
"subscription": {
"status": "active",
"expires_at": "2026-11-02T12:00:00+00:00",
"grace_until": null,
"days_left": 19,
"can_send": true
},
"plan": {
"name": "Pro",
"period": "monthly",
"monthly_messages": 100000,
"max_devices": null,
"max_apps": 3
},
"usage": {
"deliveries_this_month": 18240,
"remaining_this_month": 81760,
"active_devices": 1240
},
"sending": {
"can_send": true,
"reason": null
},
"portal": {
"plans": "https://push.kd-app.com/portal/plans",
"subscription": "https://push.kd-app.com/portal/subscription"
},
"devices": {
"active": 1240,
"ios": 800,
"android": 440,
"web": 0
},
"web": {
"enabled": false,
"origins": [],
"vapid_public_key": null,
"subscribers": 0,
"icon_url": null,
"default_url": null,
"prompt": {
"enabled": true,
"delay_seconds": 6,
"title": null,
"text": null
}
}
}Paquete de Android
/app/androidServer Key — tu servidorPara una plataforma que permite a su comerciante elegir el paquete de Android: envía el paquete con el que se compila la app de la tienda cada vez que cambie, así el portal lo muestra y el archivo de Firebase que se suba después se comprueba con él.
packagestringobligatoriocom.store.app.409 package_mismatch y firebase_package indica el registrado, para que las notificaciones de Android nunca se detengan sin aviso.curl -X PUT https://push.kd-app.com/api/v1/app/android \
-H "Authorization: Bearer kdp_live_…" \
-H "Content-Type: application/json" \
-d '{"package":"com.store.app"}'{
"app": {
"id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
"android_package": "com.store.app"
}
}{
"error": {
"code": "package_mismatch",
"message": "The app's Firebase project is registered for com.store.app; upload one for com.other.app first.",
"firebase_package": "com.store.app"
}
}Estadísticas de dispositivos
/devices/statsServer Key — tu servidorDispositivos activos por plataforma.
curl https://push.kd-app.com/api/v1/devices/stats \
-H "Authorization: Bearer kdp_live_…"{
"devices": {
"active": 1240,
"ios": 800,
"android": 440,
"web": 0
}
}Conectar con KD Push
Vincula la tienda de un comerciante a una app de KD Push con un clic, sin que nadie copie una clave: el comerciante aprueba en una página de KD Push y tu plataforma recibe las claves de servidor a servidor (OAuth 2 authorization code con PKCE).
- Registramos tu plataforma y te damos un
client_idy unclient_secret, con las URI de redirección exactas permitidas. - Envía al comerciante a
/portal/connect/authorizecon los parámetros de abajo. - El comerciante inicia sesión o crea una cuenta (con los datos que enviaste), elige o crea la app y autoriza.
- Lo devolvemos a
redirect_uri?code=…&state=…, o aerror=access_deniedsi lo rechaza. - Tu servidor canjea el código por las claves con POST /connect/token.
Parámetros de autorización
response_typecodeobligatorioclient_idstringobligatorioredirect_urihttps URLobligatoriostatestring ≤ 255obligatoriocode_challengebase64urlobligatorioBASE64URL(SHA256(code_verifier)); guarda el code_verifier (43–128 caracteres) en tu lado.code_challenge_methodS256obligatorioapp_namestring ≤ 120opcionalios_bundle_idstringopcionalandroid_packagestringopcionalexternal_refstring ≤ 191opcionalexternal_labelstring ≤ 255opcionalhint_name · hint_email · hint_company · hint_phonestringopcionalstate. El código vale 10 minutos y una sola vez; presentarlo dos veces revoca lo que produjo. Ni el code_verifier ni el client_secret llegan nunca al navegador.// 1. Send the merchant to KD Push (keep $verifier and $state in their session)
$verifier = Str::random(64);
$state = Str::random(40);
session(['kdpush' => compact('verifier', 'state')]);
return redirect('https://push.kd-app.com/portal/connect/authorize?' . http_build_query([
'response_type' => 'code',
'client_id' => config('services.kdpush.connect_client_id'),
'redirect_uri' => 'https://your-platform.example/kdpush/callback',
'state' => $state,
'code_challenge' => rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '='),
'code_challenge_method' => 'S256',
'app_name' => $store->name, // optional: prefills a new app
'ios_bundle_id' => $store->bundleId, // optional
'android_package' => $store->androidPackage, // optional
'external_ref' => $store->id, // optional: your id for the store
'hint_email' => $owner->email, // optional: prefills sign-up
], '', '&', PHP_QUERY_RFC3986));Canjear el código
/connect/tokenclient_id + client_secretgrant_typeauthorization_codeobligatorioclient_id · client_secretstringobligatoriocodestringobligatorioredirect_uristringobligatoriocode_verifierstringobligatorioRecibes la app_key (para la app), una server_key nueva emitida para esta conexión (se muestra una vez), el identity_secret y la situación del comerciante en account, con la misma forma que GET /app.
curl -X POST https://push.kd-app.com/api/v1/connect/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"client_id": "kdp_cli_…",
"client_secret": "kdp_cs_…",
"code": "kdp_code_…",
"redirect_uri": "https://your-platform.example/kdpush/callback",
"code_verifier": "…"
}'{
"connection": {
"id": "01JB2QX5V8K4Y3N5M7R9T1W0XZ",
"connected_at": "2026-10-14T12:00:00+00:00"
},
"app": {
"id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
"name": "My Store",
"platform": "neon",
"ios_bundle_id": "com.store.app",
"android_package": null
},
"merchant": {
"name": "Store owner",
"email": "[email protected]"
},
"app_key": "kdp_app_…",
"server_key": "kdp_live_…",
"identity_secret": "…",
"api_base": "https://push.kd-app.com/api/v1",
"account": {
"subscription": {
"status": "active",
"expires_at": "2026-11-02T12:00:00+00:00",
"grace_until": null,
"days_left": 19,
"can_send": true
},
"plan": {
"name": "Pro",
"period": "monthly",
"monthly_messages": 100000,
"max_devices": null,
"max_apps": 3
},
"usage": {
"deliveries_this_month": 18240,
"remaining_this_month": 81760,
"active_devices": 1240
},
"sending": {
"can_send": true,
"reason": null
},
"portal": {
"plans": "https://push.kd-app.com/portal/plans",
"subscription": "https://push.kd-app.com/portal/subscription"
}
}
}{
"error": {
"code": "invalid_grant",
"message": "Authorization code already used."
}
}Desconectar
/connect/revokeServer Key — tu servidorLa Server Key que envías deja de funcionar al instante; la app, sus dispositivos y su historial se quedan con el comerciante. El comerciante también puede desconectar desde su panel: tu clave responde entonces 401 invalid_server_key; trátalo como desconectado.
curl -X POST https://push.kd-app.com/api/v1/connect/revoke \
-H "Authorization: Bearer kdp_live_…"{
"revoked": true
}El objeto mensaje
idULIDopcionalstatusstringopcionalsent en cuanto al menos un dispositivo la acepta, failed si no la acepta ninguno (motivo en error).sourceapi | portal | adminopcionalcounts.targetedintopcionalcounts.sentintopcionalcounts.invalidintopcionalcounts.failedintopcionalcounts.openedintopcionalfailures[{provider, code, http_status, count}]opcionalTopicDisallowed), el estado HTTP y cuántos dispositivos.created_at · started_at · finished_at · scheduled_atISO 8601opcionalVerificación de la identidad del usuario
Para vincular un dispositivo a un usuario (para notificaciones personales como «tu suscripción vence pronto»), la app necesita una firma calculada por tu servidor tras el inicio de sesión; si no, cualquiera podría registrar su teléfono como otro usuario y recibir sus notificaciones.
user_signature = hex( HMAC-SHA256( external_user_id , identity_secret ) )// After the user logs in — on your backend, never in the app
$signature = hash_hmac('sha256', (string) $user->id, config('services.kdpush.identity_secret'));
return ['user_id' => $user->id, 'push_signature' => $signature];Lo que recibe la app
{
"aps": {
"alert": {
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر"
},
"sound": "default",
"mutable-content": 1
},
"kd_message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"kd_url": "neonstore://app/1234",
"kd_image": "https://cdn.example.com/icons/1234.png",
"app_id": "1234"
}{
"notification": {
"title": "تطبيق جديد",
"body": "تمت إضافة واتساب الذهبي للمتجر",
"image": "https://cdn.example.com/icons/1234.png"
},
"data": {
"kd_message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
"kd_url": "neonstore://app/1234",
"app_id": "1234"
}
}Abre kd_url al tocar y envía kd_message_id a /sdk/events. Los valores de data llegan como texto.
FirebaseMessaging.onMessageOpenedApp.listen((message) {
final url = message.data['kd_url'];
final id = message.data['kd_message_id'];
if (url != null) openDeepLink(url); // e.g. neonstore://app/1234
if (id != null) reportOpened(id); // POST /sdk/events
});Lista de integración de la plataforma
- 1El comerciante crea la app en el portal y pega la App Key y la Server Key en el panel de tu plataforma.
- 2Tu plataforma compila la app con la App Key dentro: la app llama a /sdk/config y luego registra el dispositivo.
- 3Tras el inicio de sesión, tu servidor entrega a la app una user_signature para el id del usuario.
- 4Tu plataforma envía notificaciones automáticas (app nueva, actualización) con una Idempotency-Key por evento.
- 5Cuando se conecta un catálogo por primera vez, no notifiques por cada app: envía un solo resumen.
- 6Comprueba subscription.can_send con /app antes de activar las notificaciones automáticas.
Postman
- Descarga la colección e impórtala en Postman (Import → elige el archivo).
- En las variables de la colección pon
app_key,server_keyy un token de dispositivo de prueba endevice_token. - Ejecuta Register device y luego Send a notification: el id se guarda en
message_idpara las peticiones siguientes. - Opcional: pon
external_user_ideidentity_secrety la colección calculauser_signaturepor sí sola.