Tous les articles
Analyse approfondie8 min de lecture

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.

Un cachet de cire en laiton et une enveloppe scellée à côté d'un ordinateur portable sur un bureau, sous la lumière chaude d'une lampe, une image de la raison pour laquelle on vérifie les notifications serveur de l'App Store avant de s'y fier

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

QuestionApp Store Server Notifications V2Google Play RTDN
Le message lui-même est-il signé ?Oui, signedPayload est un JWSNon, le champ data est du JSON encodé en base64
Ce qui prouve l'expéditeurChaîne de certificats x5c jusqu'à une racine AppleJeton OIDC Pub/Sub en push, ou votre propre pull authentifié
Objets signés imbriquéssignedTransactionInfo et signedRenewalInfo, chacun avec son propre JWSAucun
Champs à rapprocher de votre appbundleId, appAppleId, environmentpackageName
Réseau d'origine17.0.0.0/8, sandbox et productionGoogle Cloud Pub/Sub
Confirmer avant d'agirFacultatif, les données signées font foiObligatoire, Google demande d'appeler la Developer API
TransportHTTPS, TLS 1.2 ou ultérieurPush 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 loupe de bijoutier tenue au-dessus d'un certificat papier portant un sceau en relief sur un bureau en bois, le genre d'examen minutieux qu'on accorde à un message de remboursement avant de s'y fier

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.

ÉchecCe qui se passeCe que cela coûte
REFUND falsifié acceptéUn client payant perd l'accèsDu temps de support et une vraie demande de remboursement que vous avez provoquée
Achat falsifié acceptéAccès gratuit à des fonctionnalités payantesCalcul, appels d'API, stockage et versements sans revenus
CONSUMPTION_REQUEST authentique rejetéApple décide sans vos donnéesLa 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 preuvesPrix 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

RefundHalt

Le pilote automatique des remboursements pour l'App Store et Google Play

Poursuivre la lecture

La prochaine demande de remboursement est déjà en route.

Configurez RefundHalt dans le temps qu'il vous faudrait pour lire un nouvel e-mail d'assistance au sujet d'un remboursement que vous n'avez pas pu contester.