Douze heures, c'est tout ce qu'Apple vous accorde pour justifier un remboursement avec des preuves. Ce délai ne vaut rien si la transaction que vous avez sous les yeux appartient à un client que vous ne pouvez pas identifier. appAccountToken est l'UUID qui fournit cette identité, et il ne peut être attaché que pendant que l'achat a lieu.
Imaginez la notification qui arrive. Apple veut savoir quelle part du produit cet acheteur a réellement consommée, depuis combien de temps il est client, combien il a dépensé au total. Des questions raisonnables, et votre base de données contient toutes les réponses. Le problème, c'est que la charge utile devant vous propose un originalTransactionId et un identifiant de produit, dont aucun ne correspond à quoi que ce soit dans votre table d'utilisateurs. Vous tenez une facture sans nom, alors que les réponses dont vous avez besoin sont classées par nom.
Ce décalage a une solution, et cette solution a une échéance. appAccountToken est un UUID que votre système génère, transmet à StoreKit lors du paiement, et qu'Apple inscrit ensuite sur la transaction qui en résulte. À partir de là, tout ce qu'Apple vous communique au sujet de cet achat arrive déjà identifié. Mais si vous manquez ce moment, la vente en question reste anonyme pour toujours. Ci-dessous : ce qu'est ce champ, comment l'attacher, le point de terminaison qui sauve rétroactivement les achats que votre application n'a jamais vus, et le calcul de ce qu'un remboursement anonyme retire réellement à votre activité.
Points clés à retenir
Attachez un UUID à un achat sur l'App Store, et la transaction qu'il crée pointe directement vers un utilisateur précis dans vos registres. Apple conserve cette valeur sur la transaction et la restitue partout où cette transaction réapparaît.
Le format est la seule chose qu'Apple contrôle, et ce format, c'est UUID. Fournissez-lui un identifiant de base de données, une adresse e-mail, une chaîne assemblée de votre côté, et StoreKit l'écarte sans la moindre objection, vous laissant nil là où appAccountToken devrait se trouver.
StoreKit 2 l'expose comme une simple option d'achat, Product.PurchaseOption.appAccountToken(_:), alimentée par un UUID généré au préalable par votre système et enregistré au nom de ce compte.
Attachez-le à l'achat initial, et Apple propage cette même valeur à travers chaque renouvellement, chaque nouvelle tentative de facturation et chaque mise à niveau le long de la chaîne d'abonnement.
À partir de 2025, le point de terminaison Set App Account Token donne à votre serveur un moyen d'étiqueter les achats que le parcours intégré à l'application n'a jamais vus, notamment les activations de codes promotionnels et les achats mis en avant.
La CONSUMPTION_REQUEST d'Apple ne devient répondable que grâce à ce token. Sans lui, vous ne pouvez pas relier le remboursement au client précis dont vous êtes censé détailler la consommation dans la fenêtre de 12 heures.
Un remboursement non identifiable est un remboursement indéfendable. L'argent est reversé sur des ventes pour lesquelles vous déteniez des preuves, et le calcul, les appels API et les versements déjà consommés pour les livrer disparaissent avec lui.
Ce que ce champ fait réellement
Apple traite cette valeur comme dénuée de sens, et c'est précisément le but. Quel que soit l'UUID que vous remettez à StoreKit lors du paiement, il est inscrit sur la transaction et conservé indéfiniment, sans aucune tentative de l'interpréter, de valider ce à quoi il fait référence, ou de vérifier qu'il correspond à quoi que ce soit. La propre définition d'Apple le décrit comme « l'UUID qui associe la transaction au compte de l'utilisateur sur votre propre service ». L'interprétation relève entièrement de votre domaine. Apple se contente de transporter la valeur et de la restituer sur demande.
C'est l'endroit où cette valeur est restituée qui rend ce champ utile. Les notifications serveur la transportent à l'intérieur de la transaction signée. Interrogez l'App Store Server API avec Get Transaction Info, et elle y figure. Chaque renouvellement ultérieur dans une chaîne d'abonnement la reproduit, à condition que l'achat d'origine la portait. Attachez la valeur à un seul moment, en d'autres termes, et elle continue de ressurgir aussi longtemps que ce client continue de vous payer.
Transmettez autre chose qu'un UUID, et il disparaît sans avertissement
Les développeurs découvrent généralement cette règle en l'enfreignant. On transmet quelque chose qui semblait raisonnable, un identifiant de compte interne, une adresse e-mail, quelques champs concaténés, et StoreKit ne soulève absolument aucune objection. Aucune exception, aucun avertissement, aucun échec d'achat. La transaction revient simplement avec appAccountToken fixé à nil, et le lien que vous pensiez avoir établi n'a jamais existé. Les forums de développeurs d'Apple regorgent régulièrement de cette même confusion, formulée typiquement ainsi : « appAccountToken is missing in the transaction payload », et l'explication est presque toujours une valeur qui n'a pas satisfait à l'exigence de format.
Cette exigence est stricte : StoreKit 2 n'accepte qu'un UUID RFC 4122, rien d'autre. Le schéma sûr consiste donc à générer l'UUID sur votre serveur, à le conserver au niveau du compte, et à vous assurer qu'aucune autre sorte de valeur ne puisse jamais atteindre l'appel d'achat.
C'est le silence qui rend cela dangereux. Une valeur mal formée ne vous coûte ni erreur ni vente échouée, seulement le lien lui-même, écarté discrètement tandis que tout le reste se déroule normalement. Vérifier que la valeur respecte le format UUID avant qu'elle n'atteigne StoreKit coûte une ligne de code et élimine entièrement ce mode d'échec.
Attacher le token pendant le paiement
Une seule option d'achat suffit à toute l'implémentation. Générez l'UUID côté serveur lors de l'inscription, ou dès qu'un client arrive pour la première fois au paiement, inscrivez-le sur sa fiche de compte, puis fournissez cette même valeur à chaque fois qu'il achète.
La signature de l'option s'écrit Product.PurchaseOption.appAccountToken(_ token: UUID), et l'appel lui-même ressemble à try await product.purchase(options: [.appAccountToken(token)]). Quelle que soit la façon dont la transaction finalisée vous parvient, que vous la vérifiiez vous-même auprès de l'App Store Server API ou que vous la receviez sous forme de notification serveur, cet UUID voyage à l'intérieur, ce qui réduit l'identification du client à une simple recherche indexée.
Un token par client, réutilisé indéfiniment
Une erreur tentante consiste à générer un nouveau token par transaction. Cela démantèle discrètement l'avantage recherché. Apple reproduit le token présent sur l'achat d'origine à travers les renouvellements, les nouvelles tentatives de facturation et les mises à niveau au sein de cette chaîne, ce qui signifie qu'un UUID durable par compte garde tout l'historique de paiement d'un client connecté. Faites tourner la valeur, et chaque achat devient une île isolée. La stabilité est la propriété qui compte ici, alors choisissez un UUID à la création du compte et ne le changez jamais.
Le point de terminaison qui sauve les achats que votre application n'a jamais touchés
Deux parcours d'achat déjouaient autrefois totalement ce mécanisme. L'activation d'un code promotionnel se produit dans l'App Store, pas dans votre application. Il en va de même pour l'achat intégré mis en avant depuis une fiche de la boutique. Aucun de ces parcours n'exécute votre code d'achat, ce qui signifiait qu'il n'existait aucun moment où un token pouvait être fourni, et ces transactions restaient anonymes de façon permanente.
Cette limitation a pris fin lors de la WWDC 2025 avec le point de terminaison Set App Account Token. Votre serveur émet un PUT vers /inApps/v1/transactions/{originalTransactionId}/appAccountToken auprès de l'App Store Server API, en transportant l'UUID dans le corps de la requête, et Apple l'inscrit sur la transaction. La couverture s'étend à tous les types de produits, les activations de codes promotionnels et les achats mis en avant très certainement inclus, et une valeur envoyée de cette façon remplace le token que la transaction portait déjà. L'identification est donc devenue quelque chose que vous pouvez effectuer après coup, entièrement côté serveur, sur des achats auxquels votre application n'a jamais participé.
L'endroit où vous attachez le token dépend de la façon dont l'achat s'est produit :
Pour un achat intégré ordinaire, définissez-le dans l'option d'achat de StoreKit au moment de l'achat, via Product.PurchaseOption.appAccountToken(UUID).
Pour une activation de code promotionnel, définissez-le avec le point de terminaison Set App Account Token depuis votre serveur, car il n'y a rien dans l'application auquel s'accrocher et vous devez l'attacher après coup.
Pour un achat intégré mis en avant depuis l'App Store, définissez-le de la même façon avec le point de terminaison Set App Account Token, puisque la transaction se déroule en dehors de votre application.
Pour un renouvellement d'abonnement, vous ne faites rien. Le token est hérité automatiquement de l'achat d'origine.
Ce qu'un lien rompu fait à vos revenus
Il ne s'agit pas ici d'une question d'ordre ou de tenue de registres. L'argument, c'est qu'Apple pose aux développeurs exactement une question lors d'un remboursement, délivrée sous forme de CONSUMPTION_REQUEST, et y répondre exige de savoir lequel de vos clients est concerné.
Les demandes de remboursement sur des consommables et des abonnements non renouvelables déclenchent cette notification, et Send Consumption Information est le moyen d'y répondre, dans un délai de douze heures. Apple attend des précisions sur un individu : la proportion du produit consommée, l'ancienneté du compte, les dépenses cumulées, le statut de livraison. Le token apparaît comme un champ au sein de la requête elle-même, mais son rôle le plus important est structurel, car c'est la seule chose capable de transformer cette notification en une ligne dans votre propre base de données. Retirez le token et la requête devient impossible à répondre, quelle que soit la rigueur de votre suivi d'utilisation.
Le prix d'une réponse vide
Deux options restent une fois qu'un remboursement arrive non identifié, et aucune des deux n'est bonne. Soumettez une réponse de consommation vide, et Apple l'interprète comme une utilisation minimale, ce qui fait pencher la décision vers l'approbation, gros utilisateurs très certainement inclus. Inventez plutôt une estimation, et vous rapportez de la fiction à un détenteur de plateforme. Quelle que soit la voie choisie, des ventes que vous auriez pu justifier sont remboursées malgré tout, après que vous avez déjà payé pour les livrer.
Le coût de la livraison est la partie qui fait mal, car il n'a aucun rapport avec le prix de vente. Des appels API de modèle consommés pendant une génération, des images produites, une vidéo rendue et exportée, un versement à un créateur libéré en aval : tout cet argent a quitté vos comptes au moment de la livraison, et rembourser le client ne le fait revenir en rien. Maintenant, mettez le problème à l'échelle. Un même client anonyme réclame à répétition. Les habitués du remboursement visent spécifiquement les entreprises incapables de les identifier. Le token omis cesse alors d'être une petite négligence et commence à fonctionner comme un robinet ouvert.
La question d'Apple concerne la consommation réelle d'un client, et y répondre exige de savoir qui est ce client. Sans le token, vous perdez des ventes que vous auriez pu justifier, ainsi que le calcul et les dépenses API déjà engagés pour les livrer.
Android applique la même logique sous un autre nom
setObfuscatedAccountId remplit ce rôle sur Google Play, en liant un identifiant de compte à un achat afin que l'examen des litiges via orders.reviewrefund dispose d'une personne réelle à examiner. Les implémentations diffèrent considérablement, mais la discipline sous-jacente se transpose sans modification : l'identité est liée pendant la transaction, ou le litige est perdu par défaut plus tard. La version de cette discipline chez Apple, c'est appAccountToken, et elle exige un UUID.
Trois pratiques qui gardent chaque achat identifié
Générez un UUID par compte et conservez-le. Un seul token durable par client, créé à l'inscription ou au premier paiement, inscrit sur sa fiche, réutilisé pour tout ce qu'il achète.
Vérifiez la valeur avant de la transmettre. Confirmez qu'il s'agit bien d'un UUID dans votre code d'achat, afin qu'un identifiant mal formé ne puisse jamais discrètement se transformer en token nil sur la transaction.
Complétez rétroactivement tout ce qui a été acheté hors de l'application. Quand une notification serveur arrive pour une activation de code promotionnel ou un achat mis en avant ne portant aucun token, recourez au point de terminaison Set App Account Token et attachez le bon.
Adoptez ces trois pratiques, et l'identification cesse d'être quelque chose que vous tentez après coup. Elle devient une propriété que possède déjà chaque transaction au moment où Apple vous la remet, demandes de remboursement très certainement incluses.
Refund Sensor repose directement sur ce postulat. Nous extrayons l'appAccountToken de chaque transaction et notification serveur, nous le rapprochons des données de consommation déjà enregistrées pour ce compte, et nous soumettons une réponse à la requête d'Apple largement dans le délai de douze heures, avec des chiffres qui décrivent réellement cet acheteur. Un remboursement que vous pouvez nommer est un remboursement que vous pouvez contester, et c'est la seule sorte qui vaille la peine d'être contestée.
Où ces règles sont documentées
Apple Developer : appAccountToken sur la Transaction StoreKit
Apple Developer : l'option d'achat Product.PurchaseOption.appAccountToken
Apple Developer : Set App Account Token dans l'App Store Server API
Apple Developer : appAccountToken dans l'App Store Server API
WWDC25 : Dive into App Store server APIs for In-App Purchase
Questions fréquentes
Un UUID que votre système possède et que StoreKit se contente d'enregistrer. Vous le fournissez lors du paiement, Apple l'inscrit sur la transaction qui en résulte, et il réapparaît ensuite partout où cet achat se manifeste : dans ses informations de transaction, dans toute notification serveur qui le concerne, et dans chaque renouvellement plus loin dans la même chaîne. L'effet concret, c'est que tout ce qu'Apple vous indique par la suite, y compris une demande de remboursement, peut être rattaché à la bonne ligne dans votre propre base de données.
Le format, presque certainement. RFC 4122 est la norme que StoreKit 2 accepte, et une valeur qui échoue à ce test est écartée sans qu'aucune erreur ne soit levée, laissant nil en place tandis que l'achat se termine comme si de rien n'était. Les identifiants internes de base de données, les adresses e-mail et les chaînes assemblées à la main échouent toutes de cette façon. L'autre possibilité, c'est que votre application n'a jamais traité l'achat elle-même, une activation de code promotionnel étant la cause habituelle, si bien qu'aucun de vos codes n'a jamais été exécuté pour fournir un token.
Depuis 2025, oui. Apple a introduit Set App Account Token dans l'App Store Server API précisément pour ce cas de figure. Un PUT vers /inApps/v1/transactions/{originalTransactionId}/appAccountToken, avec l'UUID fourni dans le corps de la requête, inscrit un token sur une transaction déjà existante, ou remplace celui qui y figurait déjà. Tous les types de produits sont concernés, et la fonctionnalité a été conçue pour les achats que votre application n'a jamais traités, en particulier les codes promotionnels et les achats mis en avant.
Oui, sans exception. Ce que l'UUID désigne de votre côté reste entièrement votre affaire et Apple ne l'examine jamais, mais la valeur elle-même doit respecter le format UUID. Si ce n'est pas le cas, StoreKit refuse de la conserver, laissant nil sur la transaction.
Les remboursements déclenchent une CONSUMPTION_REQUEST, et Apple attend une réponse dans les douze heures décrivant comment cet acheteur précis a utilisé le produit. Produire une telle réponse implique de convertir la transaction de la notification en une fiche client que vous pouvez réellement interroger, ce qui est exactement la fonction du token. Il circule aussi comme champ au sein de la requête. Sans lui, votre réponse est soit vide, soit inventée, et une réponse vide se solde généralement par l'octroi du remboursement.



