Trois notifications de remboursement de l'App Store arrivent après la décision d'Apple, et REFUND_REVERSED vous rend la vente
Apple envoie quatre messages de remboursement via App Store Server Notifications V2, et la plupart des applis n'en gèrent que deux. REFUND vous dit de révoquer, REFUND_DECLINED signifie garder la vente, et REFUND_REVERSED vous rend la vente et vous demande de restaurer ce que vous avez retiré. Voici ce que chacun exige.

Points clés
- Apple envoie quatre messages liés au remboursement via App Store Server Notifications V2. CONSUMPTION_REQUEST demande vos preuves, et REFUND, REFUND_DECLINED et REFUND_REVERSED rapportent le résultat après qu'Apple a déjà décidé.
- Une notification REFUND signifie que l'App Store a remboursé la transaction. Elle porte revocationDate et revocationReason, et c'est votre signal pour révoquer le droit lié à cette seule transaction, pas à tous les achats de ce produit.
- revocationReason a deux valeurs. 1 signifie que le remboursement a été accordé en raison d'un problème avec votre produit, et 0 signifie qu'il a été accordé pour une autre raison. La valeur de problème est un signal de qualité qu'il vaut la peine d'enregistrer et de suivre en tendance.
- REFUND_DECLINED signifie qu'Apple a refusé le remboursement du client. Vous gardez la vente et ne changez rien, ce qui n'est sûr que si vous n'avez pas révoqué l'accès avant que la décision soit définitive.
- REFUND_REVERSED signifie qu'Apple a annulé un remboursement qu'elle avait accordé, en général après que le client le conteste. Les champs de révocation disparaissent de la transaction, et la consigne d'Apple elle-même est que si vous avez révoqué du contenu, vous devez le rétablir.
- Répondez aux quatre notifications par un HTTP 200. Si votre serveur était hors service et en a manqué une, le point de terminaison Get Refund History vous permet de rechercher les transactions remboursées par id de transaction et de réconcilier.
- Un remboursement d'une période d'abonnement passée ne signifie pas toujours que l'accès doit prendre fin. Si une période payée plus récente est encore active, révoquer sur l'ancienne transaction coupe un client qui est à jour.
Apple décide de votre remboursement, puis continue de parler. Une fois le résultat fixé, l'App Store envoie à votre serveur l'une des trois notifications de remboursement de l'App Store, et chacune demande une action différente. Un REFUND dit que l'argent est parti et que vous devriez retirer l'accès. Un REFUND_DECLINED dit que le client a perdu sa demande et que vous gardez la vente. Un REFUND_REVERSED dit qu'Apple a défait un remboursement déjà accordé, donc la vente est de nouveau à vous et vous devez rendre ce que vous avez retiré. La plupart des applis branchent la première et ignorent en silence les deux autres. C'est ainsi qu'un client payant se retrouve privé de quelque chose qu'il a payé.
Ces trois-là sont distinctes de CONSUMPTION_REQUEST, le seul message de remboursement qui vous demande de répondre. Les notifications d'après-décision ne veulent pas de débat. Elles veulent un HTTP 200 et le bon changement d'accès du client. Voici ce que signifie chacune, les champs exacts qui portent les faits, et où l'argent fuit quand vous les gérez mal.
Les quatre notifications de remboursement, et laquelle attend une réponse
App Store Server Notifications V2 est un flux unique. Vous le pointez vers une seule URL et Apple lui envoie tous les types de notification, donc vous recevez déjà les quatre messages de remboursement, que vous les gériez ou non. Quatre des types touchent aux remboursements, et un seul est une question.
| Notification | Ce qu'Apple vous dit | Votre action | Réponse attendue |
|---|---|---|---|
| CONSUMPTION_REQUEST | Un client a demandé un remboursement et Apple veut vos données | Send Consumption Information sous 12 heures | Oui, de vraies données |
| REFUND | L'App Store a remboursé la transaction | Révoquez le droit de cette transaction | Non, HTTP 200 |
| REFUND_DECLINED | L'App Store a refusé le remboursement | Gardez l'accès, ne changez rien | Non, HTTP 200 |
| REFUND_REVERSED | Apple a annulé un remboursement qu'elle avait accordé | Rétablissez le contenu que vous avez révoqué | Non, HTTP 200 |
Ce qu'une notification REFUND vous dit vraiment
REFUND se déclenche quand l'App Store a remboursé avec succès une transaction à un client. Cela s'applique à tous les types d'achat : un consommable, un non consommable, un abonnement à renouvellement automatique et un abonnement sans renouvellement. La transaction signée dans la notification porte désormais deux champs qu'elle n'avait pas avant le remboursement, et ces deux champs disent tout.
revocationDate et revocationReason portent les faits
revocationDate est l'heure UNIX, en millisecondes, à laquelle l'App Store a remboursé la transaction ou l'a révoquée. revocationReason vous indique la catégorie du remboursement, et prend exactement deux valeurs.
| revocationReason | Signification d'Apple | Comment l'interpréter |
|---|---|---|
| 1 | Le remboursement a été accordé en raison d'un problème avec le produit | Un signal de qualité ou de livraison. Enregistrez-le, suivez sa tendance et cherchez un motif dans un produit ou un build |
| 0 | Le remboursement a été accordé pour une autre raison | Un remboursement ordinaire. Révoquez le droit et passez à autre chose |
La présence d'un revocationDate sur une transaction est en soi le signal. Si vous récupérez une transaction plus tard et qu'elle a un revocationDate, cet achat a été remboursé, notification ou pas. Lisez la raison à côté pour qu'une vague de remboursements de valeur 1 sur une seule version ne vous échappe pas comme du bruit.
Révoquez par transaction, pas par produit
Le piège ici est de trop révoquer. Un REFUND nomme une transaction. Il ne vous dit pas de désactiver tous les achats que le client a jamais faits de cet id de produit. La consigne d'Apple elle-même est de vérifier quel accès le client détient encore avant de couper quoi que ce soit, car les droits se chevauchent. Le cas classique est un abonnement : un remboursement tombe sur le renouvellement du mois dernier alors que le renouvellement de ce mois est actif et entièrement payé. Révoquez sur le produit et vous venez de couper un client actuel et payant pour un remboursement d'une période déjà terminée.
REFUND_DECLINED signifie que vous avez déjà gagné, alors ne le défaites pas
REFUND_DECLINED arrive quand l'App Store a refusé la demande de remboursement du client. Le client a demandé, Apple a dit non, et vous gardez la vente. À première vue il n'y a rien à faire, et c'est bien là le point. L'erreur que cette notification révèle en est une autre : révoquer l'accès trop tôt.
Si votre code réagit au CONSUMPTION_REQUEST en retirant l'accès du client avant qu'Apple ait tranché, un REFUND_DECLINED est le moment où cette décision explose. Apple a gardé votre argent, et vous avez privé un client dont le remboursement a été refusé. Ce client paie maintenant pour un produit qu'il ne peut pas utiliser, ouvre un ticket d'assistance, et s'en souvient. Le correctif est une règle, pas une fonctionnalité : révoquez sur REFUND, jamais sur la demande. REFUND_DECLINED est simplement Apple confirmant qu'une révocation anticipée aurait été le mauvais choix.
REFUND_REVERSED est la notification qui vous rembourse
REFUND_REVERSED est celle que presque personne ne gère, et c'est celle qui vous rend de l'argent. Apple l'envoie quand elle annule un remboursement accordé auparavant, généralement après que le client conteste ce remboursement. Les champs de révocation qu'un REFUND avait ajoutés à la transaction sont de nouveau retirés, donc l'achat figure de nouveau comme payé. Apple résume le travail du développeur en une ligne : si votre appli a révoqué du contenu ou des services à la suite du remboursement associé, elle doit les rétablir. Cela s'applique à tout type d'achat, d'un consommable à un abonnement à renouvellement automatique.
Le problème des semaines plus tard
La vraie question que soulèvent les développeurs, sur les forums d'Apple eux-mêmes, est le moment. Un REFUND_REVERSED peut arriver des semaines après le REFUND d'origine, bien après qu'une période d'abonnement a expiré. Restaurez-vous l'accès alors ? Rétablissez ce que la transaction accorde réellement, limité à ce que cette transaction couvre. Pour un consommable ou un non consommable, réactivez le déblocage. Pour une période d'abonnement déjà écoulée, vous ne distribuez pas de temps nouveau, vous corrigez l'enregistrement pour que l'historique du client soit exact et que tout droit encore valide redevienne actif. Restaurez la transaction précise, et votre logique de chevauchement décide de ce qui est actif à ce moment.

Où est l'argent quand on fait bien les choses
Chacune de ces notifications correspond à un chiffre réel, et le coût d'une mauvaise gestion n'est pas seulement le prix de la vente.
REFUND : cessez de payer pour servir un client remboursé
Le prix de la vente est parti dès l'arrivée de REFUND. Ce que vous pouvez encore contrôler, c'est le coût de continuer à livrer. Chaque heure où un droit remboursé reste actif, vous continuez à dépenser pour ce que le client ne paie plus : calcul, appels à l'API du modèle, stockage, et tout versement à un créateur ou un partenaire lié à son usage. Révoquer sans tarder sur REFUND arrête ce compteur. Ignorer la notification signifie que vous financez un produit pour quelqu'un que la boutique a déjà remboursé.
REFUND_DECLINED : ne transformez pas une victoire en remboursement de bonne volonté
Quand vous révoquez tôt et que le remboursement est ensuite refusé, vous avez gardé la vente sur le papier et l'avez perdue en pratique. Le client qui a payé ne peut pas utiliser le produit, donc vous héritez d'une conversation d'assistance et, souvent, d'un remboursement à titre commercial pour arranger les choses. C'est payer deux fois pour une vente qui n'a jamais été en danger. Gérer REFUND_DECLINED correctement ne coûte rien, et c'est exactement pourquoi laisser l'accès intact jusqu'à REFUND est la règle la moins chère que vous puissiez adopter.
REFUND_REVERSED : le pire des cas, c'est que le client perde son argent et son accès à la fois
Ignorez REFUND_REVERSED et vous atteignez le pire résultat possible. Vous avez été payé, et le client n'a rien. Il a déjà contacté sa banque une fois pour annuler le remboursement, et une personne privée d'un produit qui lui est désormais facturé est une personne susceptible de contacter la banque une deuxième fois. Ce prochain litige peut devenir une rétrofacturation de carte, définitive du côté de la banque et plus coûteuse que la vente ne l'a jamais été. Rétablir l'accès dès l'arrivée de REFUND_REVERSED est l'assurance la moins chère de tout le flux de remboursement.
Ce qu'il faut mettre en place
La gestion est légère une fois le modèle correct. Indexez les droits sur l'id de transaction pour que chaque notification pointe vers un achat. Sur CONSUMPTION_REQUEST, envoyez vos données sous 12 heures. Sur REFUND, révoquez cette transaction. Sur REFUND_DECLINED, ne faites rien. Sur REFUND_REVERSED, rétablissez. Renvoyez un HTTP 200 rapidement sur toutes et effectuez le changement d'accès à votre rythme.
Pour le vide que laissent les notifications, utilisez le point de terminaison Get Refund History. Si votre serveur était hors service pendant une panne et a manqué un REFUND, appelez la recherche de remboursement de l'App Store Server API pour un id de transaction, à /inApps/v2/refund/lookup/{transactionId}, et relisez les transactions signées avec leurs revocationDate et revocationReason. Elle réconcilie une transaction à la fois et parcourt les achats remboursés d'un client par pages, de sorte qu'un webhook manqué ne devienne pas un droit mal réglé de façon permanente.
C'est la partie que RefundHalt exécute pour vous. Il écoute les quatre types, révoque sur REFUND, maintient l'accès intact sur REFUND_DECLINED, et rétablit automatiquement sur REFUND_REVERSED, chacun indexé sur la transaction exacte. Un remboursement annulé ne reste pas dans une file d'attente pendant qu'un client payant reste bloqué, et un refusé ne déclenche jamais une révocation qu'il faudrait défaire.
Questions fréquentes
- Quelle est la différence entre REFUND et REFUND_REVERSED ?
- REFUND signifie que l'App Store a remboursé une transaction et que vous devez révoquer ce droit, tandis que REFUND_REVERSED signifie qu'Apple a défait un remboursement accordé et que vous devez rétablir le contenu que vous avez révoqué. Les deux forment une paire : un achat peut passer à REFUND puis, si le litige du client est renversé, à REFUND_REVERSED. Indexez vos changements d'accès sur l'id de transaction pour que chaque notification agisse sur le bon achat.
- Dois-je renvoyer quelque chose pour une notification REFUND ?
- Non. Vous répondez à REFUND, REFUND_DECLINED et REFUND_REVERSED par un HTTP 200 sans corps. Seul CONSUMPTION_REQUEST vous demande d'envoyer des données, et il le fait via le point de terminaison Send Consumption Information sous 12 heures. Les trois autres sont Apple qui rapporte une décision, pas qui pose une question.
- Que dois-je faire quand je reçois une notification REFUND_DECLINED ?
- Rien ne change, car le remboursement du client a été refusé et vous gardez la vente. La seule façon dont REFUND_DECLINED crée du travail est si vous avez révoqué l'accès trop tôt, avant qu'Apple tranche. Révoquez sur REFUND plutôt que sur le CONSUMPTION_REQUEST, et un REFUND_DECLINED devient une confirmation que l'accès a été correctement laissé tel quel.
- Dois-je restaurer l'accès quand REFUND_REVERSED arrive des semaines après le remboursement ?
- Oui, rétablissez le droit qu'accorde cette transaction précise. Apple indique que si votre appli a révoqué du contenu à cause du remboursement associé, elle doit le rétablir. Pour un consommable ou un non consommable, réactivez le déblocage. Pour une période d'abonnement déjà expirée, vous corrigez l'enregistrement, vous n'accordez pas de temps nouveau, donc votre logique de chevauchement décide encore de ce qui est actif à ce moment.
- Comment rattraper une notification de remboursement que mon serveur a manquée ?
- Utilisez le point de terminaison Get Refund History de l'App Store Server API, qui recherche les transactions remboursées d'un client par id de transaction à /inApps/v2/refund/lookup/{transactionId}. Il renvoie des transactions signées avec revocationDate et revocationReason, donc après une panne vous pouvez réconcilier l'accès sans attendre une notification déjà déclenchée. Il traite un id de transaction par appel et parcourt les achats remboursés du client par pages.
Sources et lectures complémentaires
RefundHalt
Le pilote automatique des remboursements pour l'App Store et Google Play
Poursuivre la lecture
Chaque demande de remboursement Apple s'accompagne désormais d'un motif, et consumptionRequestReason est la façon de le lire
Depuis la WWDC24, chaque CONSUMPTION_REQUEST d'Apple contient un consumptionRequestReason, le motif déclaré par le client lui-même pour vouloir un remboursement. Il existe cinq valeurs, de UNINTENDED_PURCHASE à LEGAL, et chacune devrait changer ce que vous renvoyez dans votre fenêtre de 12 hours. Voici comment lire chacune d'elles.
L'examen de rétrofacturation de Google Play vous laisse 24 heures pour riposter, voici quoi envoyer
Quand une banque annule un débit Google Play, Google envoie à votre serveur une PendingRefundReviewNotification et lance un compte à rebours de 24 heures. Répondez-y via l'API ReviewRefund avec une préférence de remboursement et de vraies preuves de consommation, sinon le litige est tranché sans vous. Voici tout le flux, champ par champ.