Tous les articles
Playbook7 min de lecture

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.

Un smartphone affichant un reçu de paiement à côté d'une enveloppe retournée et d'une seule pièce, illustrant les notifications de remboursement de l'App Store qu'Apple envoie après avoir décidé d'un remboursement

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.

NotificationCe qu'Apple vous ditVotre actionRéponse attendue
CONSUMPTION_REQUESTUn client a demandé un remboursement et Apple veut vos donnéesSend Consumption Information sous 12 heuresOui, de vraies données
REFUNDL'App Store a remboursé la transactionRévoquez le droit de cette transactionNon, HTTP 200
REFUND_DECLINEDL'App Store a refusé le remboursementGardez l'accès, ne changez rienNon, HTTP 200
REFUND_REVERSEDApple 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.

revocationReasonSignification d'AppleComment l'interpréter
1Le remboursement a été accordé en raison d'un problème avec le produitUn signal de qualité ou de livraison. Enregistrez-le, suivez sa tendance et cherchez un motif dans un produit ou un build
0Le remboursement a été accordé pour une autre raisonUn 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.

Une pièce que l'on repose à côté d'un smartphone, illustrant un remboursement annulé de l'App Store qui restaure la vente au développeur

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

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.