Vérifiez les notifications serveur de l'App Store avant d'agir sur un remboursement, car n'importe qui peut envoyer des requêtes à votre URL de webhook
Une notification de remboursement n'est qu'une requête HTTPS tant que vous n'avez pas vérifié sa signature. Voici comment vérifier les notifications serveur de l'App Store et les messages RTDN de Google Play, ce que signe chaque store et ce que vous coûte un remboursement falsifié ou rejeté à tort.

Points clés
- Apple envoie chaque notification serveur de l'App Store en version 2 sous la forme d'un signedPayload, une JSON Web Signature composée de trois parties en Base64URL : un en-tête, une charge utile et une signature.
- L'en-tête JWS d'une notification serveur de l'App Store contient une chaîne de certificats x5c de trois certificats : un certificat feuille de signature, un intermédiaire Apple portant l'extension Apple Worldwide Developer Relations 1.2.840.113635.100.6.2.1, et une racine Apple.
- L'App Store Server Library d'Apple vérifie les notifications avec un SignedDataVerifier qui contrôle la chaîne de certificats, le bundle ID, l'environnement et, en production, l'appAppleId, et qui peut vérifier en ligne la révocation des certificats.
- Les notifications en temps réel pour les développeurs de Google Play ne sont pas signées par Google Play. Sur un abonnement push, Pub/Sub peut joindre un jeton OpenID Connect signé dans l'en-tête Authorization, et votre serveur en contrôle la signature, l'audience et les claims d'email.
- Google indique qu'une RTDN vous apprend seulement que l'état d'un achat a changé. La réponse prudente à toute notification de remboursement consiste donc à la confirmer avec la Google Play Developer API avant de révoquer l'accès.
- Apple envoie les notifications serveur de l'App Store depuis le sous-réseau 17.0.0.0/8, mais une liste d'adresses IP autorisées ne fait que restreindre qui peut atteindre votre endpoint. C'est la signature qui prouve que le message vient d'Apple.
- Un vérificateur qui rejette à tort des messages authentiques coûte aussi cher qu'un vérificateur qui accepte des falsifications, car un CONSUMPTION_REQUEST que vous écartez a un délai de réponse de 12 heures et un examen de rétrofacturation Google Play un délai de 24 heures.
Quiconque connaît votre URL de webhook peut lui envoyer un remboursement. C'est la réalité inconfortable derrière chaque notification serveur de l'App Store et chaque notification en temps réel pour les développeurs de Google Play : tant que vous ne l'avez pas vérifié, le message n'est qu'un POST HTTPS contenant un peu de JSON. Si votre serveur révoque un abonnement, reprend des crédits ou bannit un compte parce qu'une requête indiquait REFUND, vous avez confié ce pouvoir à quiconque sait écrire une commande curl.
Les deux stores vous donnent un moyen de vérifier. Apple signe la notification elle-même, ce qui vous permet de vérifier les notifications serveur de l'App Store avec le seul certificat racine d'Apple. Google Play emprunte une autre voie. Le message n'est pas signé, mais la livraison Pub/Sub peut l'être, et Google vous demande de toute façon de confirmer chaque changement auprès de son API. Voici ce que prouve chacun, et ce que coûte une erreur dans un sens comme dans l'autre.
Ce que chaque store signe réellement
| Question | App Store Server Notifications V2 | Google Play RTDN |
|---|---|---|
| Le message lui-même est-il signé ? | Oui, signedPayload est un JWS | Non, le champ data est du JSON encodé en base64 |
| Ce qui prouve l'expéditeur | Chaîne de certificats x5c jusqu'à une racine Apple | Jeton OIDC Pub/Sub en push, ou votre propre pull authentifié |
| Objets signés imbriqués | signedTransactionInfo et signedRenewalInfo, chacun avec son propre JWS | Aucun |
| Champs à rapprocher de votre app | bundleId, appAppleId, environment | packageName |
| Réseau d'origine | 17.0.0.0/8, sandbox et production | Google Cloud Pub/Sub |
| Confirmer avant d'agir | Facultatif, les données signées font foi | Obligatoire, Google demande d'appeler la Developer API |
| Transport | HTTPS, TLS 1.2 ou ultérieur | Push HTTPS, ou pull via une bibliothèque cliente |
Comment vérifier les notifications serveur de l'App Store
La documentation d'Apple décrit le signedPayload comme une chaîne de trois composants encodés en Base64URL et séparés par des points : un en-tête, une charge utile et une signature, signée selon la spécification JSON Web Signature. Décoder la charge utile est simple. Lui faire confiance demande quatre contrôles.
- Lisez le tableau x5c de l'en-tête décodé. Apple en documente l'ordre : d'abord le certificat portant la clé de signature, puis un certificat intermédiaire Apple, puis un certificat racine Apple.
- Validez cette chaîne par rapport à une racine Apple que vous avez téléchargée vous-même sur le site PKI d'Apple, en pratique Apple Root CA - G3. Ne faites jamais confiance à la racine arrivée dans le message, car un faussaire peut y placer n'importe quel certificat.
- Vérifiez que l'intermédiaire porte l'extension Apple Worldwide Developer Relations, 1.2.840.113635.100.6.2.1, puis vérifiez la signature JWS avec la clé publique du certificat feuille.
- Comparez les données décodées à votre app : bundleId, appAppleId en production, et environment. Une signature Apple valide sur l'app d'un autre développeur, ou sur un achat sandbox qui atteint votre endpoint de production, ne correspond toujours pas à votre remboursement.
Recommencez ensuite pour ce qui se trouve à l'intérieur. L'objet data contient signedTransactionInfo et signedRenewalInfo, et chacun est un JWS distinct avec sa propre chaîne x5c. Un gestionnaire de REFUND qui vérifie l'enveloppe mais lit la transaction sans la vérifier laisse une faille au milieu.
Utilisez la bibliothèque d'Apple plutôt que d'écrire la vôtre
Apple publie l'App Store Server Library pour Swift, Java, Python et Node. Son SignedDataVerifier prend vos certificats racine, un indicateur de contrôles en ligne, l'environnement, votre bundle ID et, en production, votre appAppleId. Le code source Node montre ce qu'il impose : exactement trois certificats dans la chaîne, l'extension feuille Apple 1.2.840.113635.100.6.11.1 et l'extension de l'intermédiaire, les dates de validité des certificats, ainsi qu'un bundle ID, un identifiant d'app et un environnement concordants. Avec les contrôles en ligne activés, il interroge aussi le répondeur OCSP pour savoir si les certificats feuille et intermédiaire ont été révoqués.

Une liste d'adresses IP autorisées suffit-elle ?
Non. Apple indique que si votre serveur a besoin d'une liste d'autorisation, vous devez y ajouter le sous-réseau 17.0.0.0/8, et que cela vaut pour la sandbox comme pour la production. Cela réduit le bruit. Cela ne prouve pas qu'un message est authentique, et cela cesse de fonctionner dès que le réseau d'Apple change sans que vous en soyez informé. Gardez le contrôle de signature comme véritable barrière.
Comment vérifier les notifications de remboursement de Google Play
Google Play publie les messages RTDN dans un sujet Pub/Sub qui vous appartient, et seul le compte de service google-play-developer-notifications@system.gserviceaccount.com doit y détenir le rôle d'éditeur Pub/Sub. Votre abonnement livre ensuite les messages de l'une de deux façons, chacune avec sa propre preuve.
- Abonnement pull : votre serveur ouvre une connexion authentifiée vers Pub/Sub et récupère les messages. Il n'y a pas d'endpoint public, donc rien vers quoi un inconnu pourrait envoyer des requêtes.
- Abonnement push avec authentification activée : Pub/Sub signe un JSON Web Token et l'envoie dans l'en-tête Authorization comme jeton Bearer. Le guide de Google demande de valider la signature du jeton et de vérifier que ses claims d'email et d'audience correspondent aux valeurs que vous avez configurées, et son code d'exemple exige aussi que email_verified soit true. Le jeton peut dater de jusqu'à une heure.
- Abonnement push sans authentification : tout ce qui peut atteindre l'URL peut envoyer un message ayant la forme d'une RTDN.
Pourquoi Google veut que vous confirmiez avec l'API
Même une RTDN parfaitement authentifiée ne raconte pas toute l'histoire. Le guide de configuration de Google indique clairement que ces notifications vous apprennent seulement que l'état d'un achat a changé et que vous devez appeler la Google Play Developer API pour obtenir le statut complet. Pour une voidedPurchaseNotification, cela signifie retrouver l'achat dans la Voided Purchases API, ou récupérer son état actuel, avant de révoquer quoi que ce soit. L'appel à l'API est authentifié avec votre propre compte de service, de sorte que sa réponse ne peut pas être contrefaite.
Ce que coûte en argent un remboursement falsifié ou rejeté
Une notification de remboursement falsifiée à laquelle vous vous fiez vous coûte un client payant. Vous bloquez quelqu'un dont vous détenez toujours l'argent, il ouvre un ticket au support, et certains iront demander au store le remboursement que votre système a déjà fait semblant d'accorder. Un achat ou un renouvellement falsifié coûte dans l'autre sens : vous fournissez du calcul GPU, des appels payants à des API tierces, du stockage et des versements aux créateurs en contrepartie de revenus qui n'ont jamais existé.
L'échec inverse est plus discret et souvent plus lourd. Un vérificateur qui rejette des messages authentiques, à cause d'un appAppleId erroné, d'un réglage sandbox en production ou d'un certificat racine obsolète, écarte les deux flux de remboursement où vos preuves comptent. Apple demande des données de consommation dans les 12 heures suivant un CONSUMPTION_REQUEST. Google vous laisse 24 heures pour répondre à un examen de rétrofacturation via orders.reviewrefund, et pour les commandes passées après le 3 août 2026, une rétrofacturation Google Play perdue vous coûte le prix d'achat moins les frais de service de Play, plus les frais facturés par la banque de l'acheteur.
| Échec | Ce qui se passe | Ce que cela coûte |
|---|---|---|
| REFUND falsifié accepté | Un client payant perd l'accès | Du temps de support et une vraie demande de remboursement que vous avez provoquée |
| Achat falsifié accepté | Accès gratuit à des fonctionnalités payantes | Calcul, appels d'API, stockage et versements sans revenus |
| CONSUMPTION_REQUEST authentique rejeté | Apple décide sans vos données | La vente, plus tout ce qui a déjà été dépensé pour la fournir |
| Examen de rétrofacturation authentique rejeté | Google décide sans vos preuves | Prix d'achat et frais bancaires sur les commandes après le 3 août 2026 |
Ne laissez pas un bug de vérification vous faire manquer un délai
- Stockez la requête brute avant de la vérifier, afin qu'un message rejeté ne soit jamais vraiment perdu.
- Déclenchez une alerte à chaque échec de vérification. Un message falsifié, c'est du bruit. Tous les messages qui échouent en même temps, c'est votre propre bug.
- Côté Apple, vous pouvez rejouer ce que vous avez rejeté. Get Notification History renvoie le signedPayload d'origine sur les 180 derniers jours : corrigez le vérificateur et faites-y repasser les charges utiles stockées ou récupérées.
- Côté Google, un examen de rétrofacturation en attente n'existe que dans sa notification, donc une audience push mal configurée équivaut à un examen manqué. Testez-la avec Send Test Message dans Play Console après chaque modification.
RefundHalt vérifie les notifications des deux stores, confirme les remboursements auprès des API des stores avant tout changement, et répond aux CONSUMPTION_REQUEST et aux examens de rétrofacturation dans leurs délais, afin qu'un faux message ne vous coûte pas un client et qu'un vrai ne passe pas entre les mailles du filet.
Questions fréquentes
- Comment vérifier une notification serveur de l'App Store ?
- Vérifiez le signedPayload en tant que JSON Web Signature. Lisez la chaîne de certificats x5c dans son en-tête, validez-la par rapport à un certificat racine Apple téléchargé sur le site PKI d'Apple, vérifiez la signature avec la clé du certificat feuille, puis contrôlez que bundleId, appAppleId et environment correspondent à votre app. L'App Store Server Library d'Apple fait tout cela dans son SignedDataVerifier.
- Quelle clé utiliser pour valider les notifications serveur de l'App Store ?
- Vous n'avez besoin d'aucune clé issue d'App Store Connect. La clé publique se trouve dans le premier certificat de la chaîne x5c, dans l'en-tête JWS, et vous ne lui faites confiance qu'après avoir validé la chaîne jusqu'à un certificat racine Apple obtenu par vos propres moyens. Votre clé d'achats intégrés sert à appeler les API d'Apple, pas à vérifier les notifications.
- Les notifications en temps réel pour les développeurs de Google Play sont-elles signées ?
- Non, Google Play ne signe pas le message RTDN lui-même. Sur un abonnement push authentifié, Pub/Sub envoie dans l'en-tête Authorization un jeton OpenID Connect signé que vous validez, et Google demande d'appeler la Google Play Developer API après chaque notification pour obtenir le statut complet de l'achat.
- Mon serveur doit-il renvoyer une erreur lorsqu'une notification échoue à la vérification ?
- Stockez d'abord la requête brute, puis décidez. Apple renvoie une notification en version 2 après une réponse 40x ou 50x, à 1, 12, 24, 48 et 72 heures après la tentative précédente. Si l'échec vient de votre propre configuration, Get Notification History vous permet de récupérer le signedPayload d'origine pendant 180 jours et de le vérifier à nouveau une fois le problème corrigé.
- Dois-je encore vérifier les notifications si je n'autorise que la plage d'adresses IP d'Apple ?
- Oui. Apple demande aux serveurs dotés d'une liste d'autorisation d'accepter le sous-réseau 17.0.0.0/8, ce qui limite qui peut se connecter mais ne prouve pas qu'un message est authentique. La signature JWS est le contrôle qui rattache une notification à Apple et à votre app.
Sources et lectures complémentaires
- Apple Developer: signedPayload
- Apple Developer: JWSDecodedHeader
- Apple Developer: Enabling App Store Server Notifications
- Apple Developer: Responding to App Store Server Notifications
- Apple on GitHub: App Store Server Library (Node)
- Android Developers: Get ready (Real-time developer notifications setup)
- Google Cloud: Authentication for push subscriptions
- Play Console Help: Updates to refund protection and chargeback cost responsibility
RefundHalt
Le pilote automatique des remboursements pour l'App Store et Google Play
Poursuivre la lecture
Des demandes de remboursement excessives peuvent vous coûter plus cher que les remboursements eux-mêmes, car les deux stores y voient un signal de qualité
La règle 5.6.4 d'Apple cite les demandes de remboursement excessives comme le signe qu'une app ne respecte pas les normes de qualité, et le contrat de distribution de Google l'autorise à retirer les apps des nouveaux développeurs en fonction de leur taux de remboursement. Voici ce que dit chaque store, ce que coûte un retrait et comment l'éviter.
Manquer des notifications de remboursement peut vous coûter les seuls remboursements que vous pouvez contester, et les deux stores vous laissent les récupérer si vous regardez à temps
Quand votre serveur manque une notification de remboursement, les deux procédures qui demandent vos preuves se ferment sans vous. Voici comment Apple et Google Play relancent l'envoi, jusqu'à quand vous pouvez récupérer les notifications de remboursement manquées, et où se trouvent les angles morts.