Comment fonctionne Pushproof

Pushproof mesure si vos notifications push arrivent réellement sur l'appareil, au-delà de l'acceptation par FCM/APNs. Cette page explique le fonctionnement global, le SDK natif et les wrappers. Pour les détails d'API (endpoints, payloads), voir la référence API dans le dashboard.

Vue d'ensemble

FCM et APNs vous disent qu'une push a été acceptée pour distribution — jamais qu'elle est arrivée. Pushproof capte la réception réelle côté appareil et la remonte à un backend qui calcule le taux de livraison et l'affiche dans un dashboard.

Deux frontières structurent tout le produit :

  • Capter (gratuit, sur l'appareil) — le SDK natif et les wrappers sont open-source (MIT). Vous restez sur votre stack d'envoi FCM/APNs directe.
  • Consulter (payant, chez nous) — l'ingestion managée, le calcul, la rétention et le dashboard. C'est l'objet de l'abonnement, facturé au volume d'accusés, pas au MAU.
Le protocole est ouvert : ingestUrl est configurable. Vous pouvez auto-héberger l'ingestion — la valeur vendue (dashboard, calcul, rétention managée) reste chez nous.

Comment ça marche

De l'envoi à la consultation, le parcours d'une notification :

  1. Votre backend d'envoi génère un notif_id (UUID) et l'injecte dans le payload. iOS : une vraie notification avec "mutable-content": 1 (réveille la NSE ; un push silencieux ne la déclenche pas). Android : un message data-only (garantit le service en arrière-plan).
  2. La push transite par FCM / APNs jusqu'à l'appareil.
  3. Sur l'appareil, le SDK capte la réception (NSE iOS / service Android) et fait un POST « cette notif est arrivée » vers l'endpoint d'ingestion.
  4. Pushproof agrège l'accusé et expose le taux de livraison dans le dashboard.

Le notif_id est la clé de jointure entre « envoyée » (connue de votre backend) et « livrée » (remontée par l'appareil). Le SDK ne le génère jamais : il le lit dans le payload reçu. Déclarez aussi vos envois via POST /v1/sent pour obtenir un vrai taux (livré / envoyé).

Option Pro : ajoutez un user_id opaque dans le payload pour répondre à « tel utilisateur a-t-il reçu telle notif ? ». Il est hashé à l'ingestion, jamais stocké en clair.

Architecture core-first

Le cœur du système est natif. On ne construit pas « un plugin Capacitor qui contient du Swift », mais un SDK natif réutilisable que des wrappers fins enrobent par écosystème :

        SDK natif Pushproof  (le vrai produit)
          - iOS : Swift Package (NSE + in-app)
          - Android : library Kotlin (FMS + in-app)
          - protocole d'ingestion (POST, notif_id)
                 │
        ┌────────┼─────────┐
   Capacitor  React Native  Flutter      ← wrappers fins
   (1er livré)  (plus tard) (plus tard)

Le même code natif sert tous les frameworks ; un bug corrigé dans la NSE l'est pour tous les wrappers à la fois. Le backend ne voit que le protocole : il sert tous les écosystèmes sans effort.

SDK natif

Ce qui vit dans le SDK natif (réutilisable à 100 %) :

iOS — Notification Service Extension (NSE)

Une NSE est une cible Xcode séparée, réveillée par iOS à la réception d'une notification mutable-content: 1, avant affichage, y compris app fermée (fenêtre ~30 s). Dans didReceive, elle poste l'accusé puis laisse passer la notification via contentHandler.

Android — interception directe

Pas de NSE : on surcharge onMessageReceived dans un FirebaseMessagingService, appelé à la réception. Plus simple et plus fiable qu'iOS. Recommandation : envoyer des messages data-only pour garantir le passage par le service même app en arrière-plan.

Wrapper Capacitor

Installation :

npm install @pushproof/capacitor@1.0.0
npx cap sync

API TypeScript exposée (in-app) :

import { Pushproof } from '@pushproof/capacitor';

await Pushproof.configure({
  ingestUrl: 'https://api.pushproof.dev/v1/receipts',
  ingestKey: 'pk_ingest_…',        // clé publique, write-only
  appGroup:  'group.com.example.app' // iOS : partagé app ↔ NSE
});

L'enregistrement du token push reste dans votre stack existante (@capacitor/push-notifications, Firebase, code natif) — Pushproof ne gère pas les tokens. Il ne remonte que les accusés de livraison une fois la notification arrivée.

La cible NSE iOS reste à créer dans le projet de l'app (contrainte Apple, indépendante du framework — voir ci-dessous). Le wrapper fournit le code Swift à importer et outille l'installation.

Installation NSE iOS

La NSE est un processus distinct de l'app : elle ne tourne pas dans le webview/JS et n'est accessible par aucun bridge. Elle doit donc exister comme cible Xcode dédiée dans le projet de chaque app. Procédure :

  1. Xcode → File → New → Target → Notification Service Extension, nommée PushproofNSE, bundle id <BUNDLE_ID>.nse.
  2. Remplacer le NotificationService.swift généré par celui fourni (ou l'importer via le SPM/pod du wrapper).
  3. Activer App Groups sur la cible App et la cible NSE, même groupe group.<BUNDLE_ID>.
  4. Vérifier que le backend d'envoi envoie bien mutable-content: 1.
On ne patche pas le .xcodeproj de force (format fragile). La procédure manuelle est le chemin officiel ; un script install-nse.js best-effort sera fourni en complément.

Limites

Le taux iOS est une borne inférieure. iOS peut sauter la NSE sous pression mémoire/batterie, ou la tuer avant la fin de l'appel réseau. Le taux réel est au moins celui affiché.

Référence API

Les détails techniques des endpoints (authentification, payloads, codes d'erreur, exemples curl) sont documentés dans le dashboard, au plus près de vos clés :

Ouvrir la référence API →