App Store & Play Store Development

Toda compra en App Store necesita un appAccountToken, o tu defensa ante reembolsos no tiene en qué sostenerse

Cuando un cliente solicita un reembolso, Apple envía un CONSUMPTION_REQUEST que nunca lo identifica por nombre. appAccountToken es el UUID que vincula una compra con tu usuario. Configúralo y podrás responder con datos reales; omítelo y estarás adivinando.

5 min read
Toda compra en App Store necesita un appAccountToken, o tu defensa ante reembolsos no tiene en qué sostenerse

Doce horas es todo lo que Apple te da para justificar un reembolso con evidencia. Ese reloj no sirve de nada si la transacción que tienes delante pertenece a un cliente al que no puedes identificar. appAccountToken es el UUID que proporciona esa identidad, y solo se puede adjuntar mientras ocurre la compra.

Imagina que llega la notificación. Apple quiere saber cuánto del producto consumió realmente ese comprador, cuánto tiempo lleva siendo cliente, cuánto ha gastado en total. Son preguntas razonables, y tu base de datos tiene todas las respuestas. El problema es que el payload que tienes delante ofrece un originalTransactionId y un identificador de producto, ninguno de los cuales corresponde a nada en tu tabla de usuarios. Tienes en tus manos una factura sin nombre, y las respuestas que necesitas están archivadas por nombre.

Ese desajuste tiene solución, y la solución tiene un plazo límite. appAccountToken es un UUID que tu sistema genera, entrega a StoreKit durante el checkout, y que Apple luego estampa en la transacción resultante. A partir de ese momento, todo lo que Apple te informe sobre esa compra llega ya identificado de antemano. Pero si dejas pasar ese momento, la venta en cuestión permanece anónima de forma permanente. A continuación: qué es este campo, cómo adjuntarlo, el endpoint que rescata retroactivamente las compras que tu app nunca vio, y la aritmética de lo que un reembolso anónimo realmente le quita a tu negocio.

Puntos clave

  • Adjunta un UUID a una compra en App Store y la transacción que esta genera apunta directamente a un usuario específico en tus registros. Apple conserva ese valor en la transacción y te lo devuelve en todos los lugares donde esa transacción aparezca.

  • El formato es lo único que Apple vigila, y el formato es UUID. Dale un id de base de datos, una dirección de correo electrónico, una cadena armada a mano, y StoreKit lo descarta sin quejarse, entregándote nil donde debería estar appAccountToken.

  • StoreKit 2 lo expone como una única opción de compra, Product.PurchaseOption.appAccountToken(_:), alimentada por un UUID que tu sistema generó y archivó previamente contra esa cuenta.

  • Adjúntalo a la compra inicial y Apple propaga ese mismo valor a través de cada renovación, cada intento de cobro reintentado y cada actualización a lo largo de la cadena de suscripción.

  • A partir de 2025, el endpoint Set App Account Token le da a tu servidor una forma de etiquetar compras que el flujo dentro de la app nunca vio, entre ellas los canjes de códigos de oferta y las compras promocionadas.

  • El CONSUMPTION_REQUEST de Apple solo se vuelve respondible gracias a este token. Sin él, no puedes conectar el reembolso con el mismo cliente cuyo consumo se espera que detalles dentro de la ventana de 12 horas.

  • Un reembolso no identificable es un reembolso indefendible. El dinero se devuelve en ventas de las que tenías pruebas, y con él se va también el cómputo, las llamadas a la API y los pagos ya consumidos para entregarlas.

Qué hace realmente este campo

Apple trata el valor como algo sin significado, y ese es precisamente el punto. Cualquier UUID que le entregues a StoreKit durante el checkout queda escrito en la transacción y se conserva indefinidamente, sin que se intente interpretarlo, validar a qué se refiere o comprobar si corresponde a algo. La propia definición de Apple lo llama "el UUID que asocia la transacción con la cuenta del usuario en tu propio servicio". La interpretación es responsabilidad exclusivamente tuya. Apple simplemente transporta el valor y te lo devuelve cuando lo solicitas.

Dónde te lo devuelve es lo que hace útil al campo. Las notificaciones del servidor lo llevan dentro de la transacción firmada. Si le pides a la App Store Server API el Get Transaction Info, aparece ahí. Cada renovación posterior dentro de una cadena de suscripción lo reproduce, siempre que la compra original lo haya llevado. En otras palabras, adjunta el valor en un solo momento y seguirá resurgiendo mientras ese cliente te siga pagando.

Pasa cualquier cosa que no sea un UUID y desaparece sin previo aviso

Los desarrolladores suelen descubrir esta regla al infringirla. Se pasa algo que parecía razonable, un id de cuenta interno, una dirección de correo electrónico, un par de campos concatenados, y StoreKit no pone ninguna objeción. Sin excepción, sin advertencia, sin compra fallida. La transacción simplemente regresa con appAccountToken en nil, y la conexión que suponías tener nunca se creó. Los foros de desarrolladores de Apple tienen un flujo constante de esta misma confusión, formulada típicamente como "appAccountToken is missing in the transaction payload", y la explicación es casi siempre un valor que no cumplió con el requisito de formato.

Ese requisito es estricto: StoreKit 2 acepta un UUID RFC 4122 y nada más. Así que el patrón seguro es generar el UUID en tu servidor, conservarlo asociado a la cuenta, y asegurarte de que ningún otro tipo de valor pueda llegar jamás a la llamada de compra.

El silencio es lo que vuelve esto peligroso. Un valor mal formado no te cuesta ningún error ni ninguna venta fallida, solo el vínculo en sí, descartado silenciosamente mientras todo lo demás sigue su curso con normalidad. Verificar el valor contra el formato UUID antes de que llegue a StoreKit cuesta una sola línea y elimina por completo este modo de falla.

Adjuntar el token durante el checkout

Una sola opción de compra es toda la implementación. Genera el UUID en el servidor al momento del registro, o cuando el cliente llega por primera vez al checkout, escríbelo en su registro de cuenta, y luego proporciona ese mismo valor cada vez que compre.

La firma de la opción es Product.PurchaseOption.appAccountToken(_ token: UUID), y la llamada en sí se ve como try await product.purchase(options: [.appAccountToken(token)]). Sin importar por qué vía te llegue la transacción finalizada, ya sea que la verifiques tú mismo contra la App Store Server API o que la recibas como notificación del servidor, ese UUID viaja dentro de ella, lo que reduce la identificación del cliente a una sola búsqueda indexada.

Un token por cliente, reutilizado para siempre

Un error tentador es generar un token nuevo por cada transacción. Hacerlo desmantela silenciosamente el beneficio. Apple reproduce el token que tenía la compra original a lo largo de las renovaciones, los intentos de cobro reintentados y las actualizaciones dentro de esa cadena, lo que significa que un UUID duradero por cuenta mantiene conectado todo el historial de pagos de un cliente. Si rotas el valor, cada compra se convierte en una isla aislada. La estabilidad es la propiedad que importa aquí, así que elige un UUID cuando se crea la cuenta y nunca lo cambies.

El endpoint que rescata compras que tu app nunca tocó

Dos rutas de compra solían anular esto por completo. Canjear un código de oferta ocurre en App Store, no en tu app. Lo mismo pasa al comprar una compra dentro de la app promocionada desde una ficha de la app. Ninguna de las dos rutas ejecuta tu código de compra, lo que significaba que no había ningún momento en el que se pudiera proporcionar un token, y esas transacciones permanecían anónimas de forma permanente.

Esa limitación terminó en la WWDC 2025 con el endpoint Set App Account Token. Tu servidor emite un PUT a /inApps/v1/transactions/{originalTransactionId}/appAccountToken contra la App Store Server API, llevando el UUID en el cuerpo, y Apple lo escribe en la transacción. La cobertura se extiende a todo tipo de producto, incluyendo muy especialmente los canjes de códigos de oferta y las compras promocionadas, y un valor enviado de esta forma reemplaza cualquier token que la transacción ya tuviera. La identificación se ha convertido, por lo tanto, en algo que puedes realizar después, enteramente del lado del servidor, en compras en las que tu app nunca estuvo involucrada.

Dónde adjuntas el token depende de cómo haya ocurrido la compra:

  • Para una compra dentro de la app ordinaria, configúralo en la opción de compra de StoreKit en el momento de comprar, mediante Product.PurchaseOption.appAccountToken(UUID).

  • Para un canje de código de oferta, configúralo con el endpoint Set App Account Token desde tu servidor, porque no hay nada dentro de la app a lo cual engancharse y tienes que adjuntarlo después.

  • Para una compra dentro de la app promocionada desde App Store, configúralo de la misma forma con el endpoint Set App Account Token, ya que la transacción ocurre fuera de tu app.

  • Para una renovación de suscripción, no tienes que hacer nada. El token se hereda automáticamente de la compra original.

Qué le hace a tus ingresos un vínculo roto

Aquí el argumento no es tener registros ordenados. El argumento es que Apple le hace a los desarrolladores exactamente una pregunta durante un reembolso, entregada como un CONSUMPTION_REQUEST, y responderla requiere saber de cuál de tus clientes se trata.

Las solicitudes de reembolso de consumibles y suscripciones no renovables activan esa notificación, y Send Consumption Information es cómo respondes, dentro de las doce horas. Apple espera datos específicos sobre un individuo: la proporción del producto consumida, la antigüedad de la cuenta, el gasto total histórico, el estado de la entrega. El token aparece como un campo dentro de la propia solicitud, pero su papel más importante es estructural, porque es lo único capaz de convertir esa notificación en una fila de tu propia base de datos. Quita el token y la solicitud se vuelve imposible de responder, sin importar qué tan completo sea tu seguimiento de uso.

El precio de responder sin nada

Quedan dos opciones una vez que llega un reembolso sin identificar, y ninguna es buena. Envía una respuesta de consumo vacía y Apple la interpreta como uso mínimo, lo que inclina la decisión hacia la aprobación, incluyendo muy especialmente a los usuarios intensivos. Inventa una estimación en su lugar y estarás reportando ficción a quien controla la plataforma. Tomes la ruta que tomes, las ventas que podrías haber sustentado terminan reembolsadas de todas formas, después de que ya pagaste por cumplirlas.

El costo de cumplimiento es la parte que duele, porque no guarda ninguna relación con el precio de venta. Las llamadas a la API del modelo consumidas durante una generación, imágenes producidas, un video renderizado y exportado, un pago a un creador liberado más adelante en la cadena: todo ese dinero salió de tus cuentas en el momento de la entrega, y reembolsar al cliente no hace nada para recuperarlo. Ahora multiplica el problema. Un solo cliente anónimo presenta solicitudes repetidamente. Quienes abusan habitualmente de los reembolsos apuntan específicamente a negocios que no pueden identificarlos. En ese punto, el token omitido deja de ser una pequeña omisión y empieza a funcionar como un grifo abierto.

La pregunta de Apple tiene que ver con el consumo real de un cliente, y responderla requiere saber quién es ese cliente. Sin el token, pierdes ventas que podrías haber sustentado, junto con el gasto en cómputo y API ya comprometido para entregarlas.

Android aplica la misma jugada bajo otro nombre

setObfuscatedAccountId cumple esta función en Google Play, vinculando un identificador de cuenta a una compra para que la revisión de contracargos mediante orders.reviewrefund tenga una persona real que examinar. Las implementaciones difieren bastante, pero la disciplina subyacente se traslada sin modificación: la identidad se vincula durante la transacción, o la disputa se pierde por defecto más adelante. La versión de Apple de esa disciplina es appAccountToken, y exige que sea un UUID.

Tres prácticas que mantienen identificada toda compra

  • Genera un UUID por cuenta y consérvalo. Un único token duradero por cliente, creado en el registro o en el primer checkout, escrito en su registro, y reutilizado en todo lo que compre.

  • Verifica el valor antes de entregarlo. Confirma que realmente sea un UUID dentro de tu código de compra, para que un identificador mal formado nunca pueda convertirse silenciosamente en un token nil en la transacción.

  • Completa retroactivamente todo lo comprado fuera de la app. Cuando llegue una notificación del servidor por un canje de código de oferta o una compra promocionada sin token, recurre al endpoint Set App Account Token y adjunta el correcto.

Adopta las tres y la identificación deja de ser algo que intentas después de los hechos. Se convierte en una propiedad que toda transacción ya tiene cuando Apple te la entrega, incluyendo muy especialmente las solicitudes de reembolso.

Refund Sensor está construido directamente sobre esa premisa. Extraemos el appAccountToken de cada transacción y notificación del servidor, lo cruzamos con los datos de consumo ya registrados para esa cuenta, y presentamos una respuesta a la solicitud de Apple bien dentro de la ventana de doce horas, usando cifras que realmente describen a ese comprador. Un reembolso al que puedes ponerle nombre es un reembolso que puedes disputar, y ese es el único tipo que vale la pena disputar.

Dónde está documentado todo esto

Preguntas frecuentes

Un UUID que le pertenece a tu sistema y que StoreKit solo registra. Tú lo proporcionas durante el checkout, Apple lo escribe en la transacción resultante y, a partir de ahí, reaparece dondequiera que aparezca esa compra: en su información de transacción, en cualquier notificación del servidor que la mencione y en cada renovación posterior dentro de la misma cadena. El efecto práctico es que todo lo que Apple te informe después, incluida una solicitud de reembolso, puede vincularse con la fila correcta en tu propia base de datos.

Casi con toda seguridad, el formato. RFC 4122 es el estándar que acepta StoreKit 2, y un valor que no cumple con esa prueba se descarta sin generar ningún error, dejando nil mientras la compra se completa como si nada hubiera salido mal. Los ids de registros internos, las direcciones de correo electrónico y las cadenas armadas a mano fallan todas de esta manera. La otra posibilidad es que tu app nunca procesó la compra en absoluto, siendo el canje de un código de oferta la causa más habitual, por lo que ningún código tuyo llegó a ejecutarse para proporcionar un token.

Desde 2025, sí. Apple introdujo Set App Account Token en la App Store Server API justamente para esta situación. Un PUT a /inApps/v1/transactions/{originalTransactionId}/appAccountToken, con el UUID incluido en el cuerpo, escribe un token en una transacción que ya existe, o reemplaza uno que ya estuviera ahí. Todo tipo de producto califica, y la función fue diseñada pensando en compras que tu app nunca manejó, en particular los códigos de oferta y las compras promocionadas.

Sí, sin excepción. A qué apunta ese UUID de tu lado es asunto exclusivamente tuyo y Apple nunca lo inspecciona, pero el valor en sí tiene que cumplir con el formato UUID. Si no lo cumple, StoreKit se niega a conservarlo, dejando nil en la transacción.

Los reembolsos activan un CONSUMPTION_REQUEST, y Apple espera una respuesta dentro de doce horas que describa cómo usó el producto ese comprador en específico. Producir esa respuesta significa convertir la transacción de la notificación en un registro de cliente que realmente puedas consultar, que es precisamente la función del token. Además, viaja como un campo dentro de la solicitud. Sin él, tu respuesta queda vacía o inventada, y una respuesta vacía generalmente termina en que se conceda el reembolso.

#appaccounttoken#storekit#refunds#in-app purchases#ios
Refund SensorRefund Sensor TeamRefund defense for App Store and Google Play developers