Si vous avez déjà travaillé avec le système de remboursement d'Apple, vous avez probablement déjà vu la fenêtre de réponse de 12 heures mentionnée dans la documentation. Ce qu'elle ne précise pas toujours, c'est comment répondre concrètement.
Le CONSUMPTION_REQUEST est une notification ; la réponse, elle, passe par un appel à une autre API, avec sa propre clé et un corps de requête qui a changé de forme à deux reprises depuis 2021. Nous avons déjà abordé ce que contient la notification elle-même il y a quelque temps. Voici la suite : le moment où vous devez réellement y répondre.
Points clés
Il s'agit d'une seule requête PUT vers l'App Store Server API. C'est tout le mécanisme.
Chaque requête est signée avec un JWT généré à partir de votre clé in-app purchase ; les tokens de plus de 60 minutes sont rejetés.
Le corps v2 comporte cinq champs, dont trois obligatoires.
Apple répond 202, et rien de plus. La décision de remboursement arrive plus tard, sous forme de notification distincte.
Douze heures à partir du moment où la notification arrive, et vous pouvez renvoyer une requête dans cette fenêtre.
Qu'est-ce qu'une demande de consommation Apple ?
Une demande de consommation Apple, derrière le jargon, est en fait une question. Un client a demandé à Apple d'être remboursé, et avant de lui répondre, Apple se tourne vers votre serveur pour demander : cette personne a-t-elle reçu ce qu'elle a payé, et l'a-t-elle utilisé ?
Apple combine ce que vous lui indiquez avec les éléments qu'elle connaît déjà, comme la fréquence à laquelle ce client demande des remboursements, et décide seule.
Les gens se trompent constamment sur ce dernier point. Vous n'approuvez ni ne refusez rien ; vous fournissez des faits.
Le CONSUMPTION_REQUEST envoyé par Apple arrive à l'URL de serveur que vous avez configurée dans App Store Connect, sous la forme d'une notification serveur version 2.
En 2021, elle ne se déclenchait que pour les consumables. Les subscriptions ont été ajoutées en 2024, et en décembre 2025, Apple a publié la version 2 de l'endpoint, dont la documentation indique qu'elle couvre tous les types de produits, non-consumables et non-renewing subscriptions inclus. La page de référence des notifications ne liste encore que les deux catégories d'origine ; je ferais plutôt confiance à la page la plus récente et je développerais pour les quatre.
Et puisqu'on parle de nommage : beaucoup cherchent une Apple refund API, mais elle n'existe pas en tant qu'élément distinct. Cet appel fait partie de l'App Store Server API.
Ce dont vous avez besoin avant de pouvoir envoyer votre requête
Trois de ces quatre éléments ne peuvent pas être précipités, lisez donc ceci avant votre première requête plutôt qu'après.
Le premier est une clé, plus précisément une clé in-app purchase, créée dans App Store Connect sous Users and Access. Apple ne vous remet le fichier .p8 qu'une seule fois ; vous aurez aussi besoin de son key ID et de votre issuer ID.
Deuxièmement, un moyen de passer d'un transaction ID (ce qu'Apple vous donne) à une personne dans votre base de données (ce dont vous avez besoin). La méthode la plus propre est appAccountToken, défini au moment de l'achat, qui revient ensuite dans chaque notification concernant cette transaction.
Si ce terme vous est inconnu, lisez d'abord pourquoi appAccountToken est essentiel pour se défendre face aux remboursements, car sans cette correspondance, le reste de cet article reste théorique.
Troisièmement, et c'est celui qu'on ne peut pas corriger à 2 h du matin : le consentement.
La documentation d'Apple indique clairement que si le client n'a pas accepté que vous partagiez ces données, vous ne devez pas répondre du tout. Elle précise aussi, dans un langage plus juridique, que ce qui constitue un consentement valide dépend de votre zone d'activité et qu'Apple ne peut pas vous conseiller à ce sujet.
Une clause d'opt-in dans vos conditions d'utilisation est la voie habituelle, et elle doit exister avant même que la requête n'arrive.
Le quatrième est le transaction ID lui-même, que vous n'obtenez qu'une fois la notification arrivée et après avoir vérifié la signature d'Apple.
Comment envoyer les informations de consommation étape par étape
La plupart des personnes qui recherchent send consumption information apple finissent sur la page de référence d'Apple, qui liste les champs puis s'arrête plutôt brusquement. Voici la séquence telle qu'elle se déroule en pratique.
Commencez par vérifier que la notification est authentique. Le signedPayload est un JWS ; l'App Store Server Library d'Apple le vérifie et renvoie la transaction décodée, transactionId et appAccountToken inclus.
Allez retrouver l'achat de votre côté. Ce que vous cherchez : avons-nous livré le produit, quelle proportion a été utilisée environ, le client a-t-il vu un échantillon ou un essai au préalable, et que préférerions-nous qu'Apple décide.
Passons au token. Header : alg vaut ES256, kid est votre key ID, typ est JWT. Payload : iss est votre issuer ID, iat correspond à maintenant, exp au plus 60 minutes plus tard, aud est la chaîne littérale appstoreconnect-v1, et bid est votre bundle ID. Le guide d'Apple sur la génération des JSON Web Tokens en donne la structure exacte, même si la library le construit pour vous.
Envoyez-la en PUT. L'URL de production est https://api.storekit.apple.com/inApps/v2/transactions/consumption/{transactionId}, avec le JWT en Bearer token dans l'en-tête Authorization, et un corps de requête qui ressemble à ceci :
{
"customerConsented": true,
"deliveryStatus": "DELIVERED",
"consumptionPercentage": 94000,
"sampleContentProvided": false,
"refundPreference": "DECLINE"
}
Apple répond 202 Accepted, et c'est tout ce que vous obtenez. Enregistrez l'heure, le corps de la requête et le transaction ID, car dans un mois, quelqu'un demandera ce que nous avons communiqué à Apple à ce sujet.
Puis vous attendez. La décision arrive sous la forme d'une notification REFUND ou REFUND_DECLINED. S'il s'agit d'un REFUND, retirez le contenu ou l'entitlement le jour même.
Donc si vous vous demandiez comment envoyer des informations de consommation qu'Apple utilisera réellement, voilà la réponse : une requête, cinq champs, dans la fenêtre de temps impartie.
Apple vous permet aussi d'envoyer un payload mis à jour dans ces 12 heures, donc si le premier appel est parti avec seulement une partie du tableau, envoyez-en un second.
Quelques points que les noms de champs ne précisent pas. consumptionPercentage s'exprime en milliunits, ce qui piège souvent les gens : 94000 signifie 94 %, 100000 signifie la totalité, et la valeur doit être 0 si deliveryStatus est différent de DELIVERED.
Les options pour un produit non livré sont UNDELIVERED_QUALITY_ISSUE, UNDELIVERED_WRONG_ITEM, UNDELIVERED_SERVER_OUTAGE et UNDELIVERED_OTHER.
refundPreference est facultatif et accepte GRANT_FULL, GRANT_PRORATED ou DECLINE ; pour un GRANT_PRORATED sur un auto-renewable subscription, omettez le pourcentage et Apple le calculera à partir du temps restant.
La page de référence Send Consumption Information contient la liste complète des erreurs.
Ce que signifient les codes de réponse
Il n'existe qu'une seule bonne réponse, 202. Tout le reste, c'est Apple qui vous indique ce qui n'a pas fonctionné, généralement en le nommant précisément.
Les codes 400 sont les plus utiles, car le corps de la réponse nomme l'erreur.
ConsumptionPercentageOutOfRangeError signifie presque toujours que quelqu'un a envoyé 94 au lieu de 94000.
UndeliveredConsumptionPercentageNonZeroError signifie que vous avez indiqué que l'article n'était jamais arrivé tout en affirmant qu'il avait été partiellement utilisé.
InvalidCustomerConsentedError se passe de commentaire.
Un 401 concerne le token : mauvaise clé, expiration de plus d'une heure, bundle ID qui ne correspond pas, ou horloge serveur suffisamment décalée pour que iat semble se situer dans le futur.
Un 404 signifie que le transaction ID n'a pas été trouvé, et ma première question est toujours de savoir si une transaction sandbox a été envoyée à l'URL de production (ou l'inverse).
429 correspond à la rate limit (50 requêtes par seconde par application en production, un dixième de ce chiffre en sandbox) ; consultez l'en-tête Retry-After avant de réessayer.
500 provient du côté d'Apple ; réessayez plus tard.
Comment tester cela dans le sandbox
Tester cela est plus simple qu'il n'y paraît, avec une seule réserve.
Configurez une URL de serveur sandbox dans App Store Connect, connectez-vous avec un sandbox Apple Account, achetez quelque chose, puis ouvrez la feuille de remboursement de votre application (beginRefundRequest) et envoyez une demande.
Le guide de test des demandes de remboursement d'Apple explique comment se comporte l'environnement de test : choisissez n'importe quel motif et le remboursement est approuvé automatiquement ; sélectionnez Other et tapez DECLINE pour qu'il soit refusé ; tapez GRANT_PRORATED pour un remboursement proportionnel.
La réserve se trouve dans ces règles : le sandbox décide en fonction de ce que vous avez saisi dans la feuille, et non de ce que votre serveur a envoyé, il ne peut donc pas vous dire si vos données convainquent Apple de quoi que ce soit.
Ce qu'il permet de tester, c'est la tuyauterie : la notification arrive, votre PUT vers api.storekit-sandbox.apple.com renvoie 202, et REFUND ou REFUND_DECLINED est bien traité lorsqu'il se présente.
Par ailleurs, le sandbox n'envoie pas d'e-mails de remboursement, ne vous attendez donc pas à en recevoir.
Apple Refund Automation : quand arrêter de le faire manuellement
Fait manuellement, ce travail consiste pour quelqu'un à surveiller une alerte à toute heure, exécuter une requête et envoyer cinq champs avant que le compte à rebours de 12 heures n'arrive à son terme.
Cela fonctionne bien pour une poignée d'Apple refund requests par mois. C'est bien moins le cas dès le premier long week-end où personne ne surveille.
Pour automatiser les Apple refund requests, vous reprenez tout ce qui précède et retirez la personne du processus : la notification arrive, vos règles transforment les données de livraison et d'utilisation en payload, l'appel part en quelques minutes, la décision est enregistrée pour le dossier concerné, et un REFUND déclenche une révocation.
Construire cela n'est pas un projet énorme ; c'est le suivi des évolutions d'Apple par la suite (le passage de V1 à v2, par exemple) qui continue discrètement de coûter du temps.
Si vous préférez ne pas vous en charger, nous avons passé en revue les services conçus pour cela dans notre comparatif des outils de gestion des remboursements Apple, en précisant où Refund Sensor s'inscrit et où il ne s'applique pas.
Une chose sur laquelle je reviens toujours : l'Apple refund automation change la fiabilité de votre réponse, pas qui décide. Rien dans cette API ne donne le dernier mot à un développeur.
Conclusion
L'appel lui-même est minimal : un token signé, un PUT, cinq champs, un 202 à enregistrer.
Ce qui fait vraiment la différence se joue ailleurs : dans le consentement déjà présent dans vos conditions d'utilisation, un appAccountToken sur chaque achat, des données d'utilisation que vous pouvez retrouver en quelques secondes, et quelque chose qui se réveille à 3 h du matin quand la notification arrive.
Réglez ces points, et cela cesse d'être une course contre la montre pour devenir quelque chose que votre serveur fait, tout simplement.
Questions fréquentes
Il s'agit d'un endpoint de l'App Store Server API utilisé pour répondre à un CONSUMPTION_REQUEST. Vous envoyez une requête PUT contenant les informations de consommation, et Apple renvoie 202 Accepted.
Vous disposez de 12 heures à partir de la réception du CONSUMPTION_REQUEST. Apple autorise l'envoi d'un payload mis à jour dans cette même fenêtre de 12 heures.
Oui. Apple vous permet d'envoyer un payload mis à jour dans la fenêtre de 12 heures. C'est utile lorsque le premier envoi était incomplet.
Non. Un 202 Accepted signifie seulement qu'Apple a bien reçu vos informations. Le résultat arrive plus tard sous la forme d'une notification REFUND ou REFUND_DECLINED.
Oui. Le champ customerConsented est obligatoire lors de l'envoi des informations de consommation. Si le client n'a pas donné son consentement, la documentation d'Apple indique de ne pas répondre.



