Revocar el acceso tras un reembolso: el paso que Apple y Google no dan por ti
Tanto Apple como Google pueden procesar un reembolso mientras el cliente conserva su compra. Esto es exactamente cuándo se elimina el acceso automáticamente, cuándo tiene que hacerlo tu servidor, y la única notificación que la mayoría de las integraciones nunca gestiona.

Puntos clave
- En Google Play, reembolsar un pedido no elimina por sí solo el acceso del usuario. El acceso solo se elimina cuando se marca "Remove entitlement" en Play Console, o cuando la llamada a la API incluye revoke=true, lo cual no es el valor predeterminado.
- Un reembolso emitido sin la opción de revocar nunca aparece en la Voided Purchases API. Un proceso de revocación que solo lea ese feed pasará por alto, en silencio, cada uno de esos pedidos.
- Apple puede revocar automáticamente una licencia para productos no consumibles y suscripciones, pero un producto consumible se considera entregado en el momento en que se gasta, así que StoreKit no tiene ningún mecanismo para revocarlo. Tu servidor tiene que descontar el saldo por su cuenta.
- Apple también puede deshacer su propio reembolso. Una notificación REFUND_REVERSED significa que un reembolso concedido previamente fue revertido, revocationDate vuelve a null, y cualquier acceso o saldo que hayas eliminado debe restaurarse.
- El campo voidedReason de Google tiene nueve valores posibles, desde remorse y accidental_purchase hasta fraud, friendly_fraud y unacknowledged_purchase, así que el mismo feed que te indica revocar el acceso también te dice cuáles revocaciones son abuso y cuáles son un fallo de tu propia integración.
- Cada día que una compra reembolsada conserva su acceso es un día en el que pagas por el cómputo, el almacenamiento y las llamadas a la API que hay detrás, con dinero que ya devolviste.
Aprobar un reembolso y eliminar el acceso son dos pasos distintos, y solo uno de ellos ocurre automáticamente. Los desarrolladores que suponen que un reembolso procesado equivale a una compra revocada se equivocan en ambas tiendas principales, de formas distintas y por razones distintas. Saber exactamente dónde se detiene la automatización es lo que hace falta para revocar el acceso tras un reembolso, en lugar de descubrir meses después que nunca ocurrió.
Por qué un reembolso no revoca el acceso por defecto
Tanto Apple como Google tratan el dinero y el acceso como dos operaciones distintas. El lado del dinero es política de la tienda: una vez aprobado el reembolso, es definitivo y el libro contable se actualiza. El lado del acceso es opcional, o responsabilidad del desarrollador, o ambas cosas, según la plataforma y el tipo de compra. Ninguna de las dos tiendas asume que quieres que el acceso desaparezca en el instante en que se completa el reembolso, porque muchos casos legítimos (un reembolso parcial, un crédito de cortesía, un gesto de soporte) no están pensados para quitar el producto en absoluto.
Google Play: la casilla que nadie nota
En Play Console, reembolsar un pedido es un formulario de dos partes: eliges el importe del reembolso y luego decides, por separado, si marcar "Remove entitlement". Dejarla sin marcar es el camino por defecto que toman la mayoría de los agentes de soporte, porque la mayoría de las solicitudes de reembolso son gestos de buena voluntad en los que el cliente conserva lo que compró. El problema es que nada en la pantalla de confirmación del reembolso te dice que así es también como un cliente conserva una suscripción o un artículo dentro de la app después de un reembolso que pretendías que fuera definitivo.
La vía de la API tiene la misma trampa
La misma elección existe en el código. El endpoint orders.refund acepta un parámetro de consulta opcional revoke; cuando es true, el acceso al artículo se termina de inmediato, y en una suscripción recurrente se cancelan con él todos los pagos futuros. Si se omite el parámetro, su valor por defecto es false: el reembolso se registra, el registro de la compra se actualiza, y el usuario sigue usando el producto exactamente igual que antes.
| Acción | ¿Se elimina el acceso? | ¿Aparece en la Voided Purchases API? |
|---|---|---|
| Reembolso en Play Console, "Remove entitlement" sin marcar | No | No |
| Reembolso en Play Console, "Remove entitlement" marcado | Sí, de inmediato | Sí |
| API orders.refund, parámetro revoke omitido (por defecto false) | No | No |
| API orders.refund, revoke=true | Sí, de inmediato | Sí |
Esa última fila es la que atrapa a las integraciones. La Voided Purchases API existe para que puedas construir un flujo de revocación sin comprobar Play Console a mano, pero solo enumera los pedidos que realmente se anularon con revocación. Un reembolso procesado sin la casilla, o sin revoke=true, es invisible para ese feed por diseño, no por error.

Leyendo el campo voidedReason Cuando una compra sí aparece como anulada, el campo voidedReason te dice por qué, en una escala de nueve valores: other, remorse, not_received, defective, accidental_purchase, fraud, friendly_fraud, chargeback y unacknowledged_purchase. Eso no es solo contabilidad. Remorse y accidental_purchase son reembolsos normales. Fraud, friendly_fraud y chargeback son los que merece la pena enviar a una cola de revisión de fraude en lugar de un simple camino de revocar y olvidar, porque estadísticamente esa misma cuenta tiene más probabilidades de repetir.
Un detalle operativo importa si construyes directamente sobre este feed: purchases.voidedpurchases.list tiene un límite de 6,000 consultas al día y 30 consultas en cualquier ventana de 30 segundos. Un diseño que consulta en un bucle ajustado alcanza ese límite rápido; el patrón previsto es una extracción programada con tokens de paginación, no un sondeo continuo.
Apple: la App Store puede revocar una licencia, pero no un consumible
Las App Store Server Notifications V2 de Apple envían una notificación REFUND en el momento en que se completa un reembolso, tanto para consumibles como para no consumibles, suscripciones de renovación automática y suscripciones sin renovación. Lo que ocurre después de esa notificación depende por completo del tipo de compra, porque el propio mecanismo de revocación de StoreKit solo cubre dos de los cuatro.
Por qué los consumibles son la excepción
Un no consumible o una suscripción tiene un derecho permanente que los sistemas de Apple pueden desactivar. Un consumible no: una vez usado, se considera entregado, y no queda nada en StoreKit que revocar. Si un cliente compró 500 monedas, las gastó y recibió un reembolso, las monedas ya han desaparecido desde el punto de vista de Apple. El único libro contable que todavía las tiene es el tuyo. La notificación REFUND es tu única señal para encontrar el ID de transacción original correspondiente y descontar ese saldo de tus propios registros; nada del lado de Apple lo hará por ti.
| Notificación | Qué significa | Qué debe hacer tu servidor |
|---|---|---|
| REFUND | Apple reembolsó una compra consumible, no consumible, de renovación automática o sin renovación | Revoca de inmediato el acceso de no consumibles y suscripciones; descuenta tú mismo el saldo del consumible |
| REFUND_DECLINED | Apple rechazó la solicitud de reembolso de un cliente | Nada. El acceso nunca estuvo en riesgo |
| REFUND_REVERSED | Apple revirtió un reembolso que ya había concedido | Restaura cualquier acceso o saldo que la notificación REFUND te hizo eliminar |
| CONSUMPTION_REQUEST | Apple quiere tus pruebas antes de decidir sobre un reembolso | Responde en un plazo de 12 horas; esto se dispara antes de un REFUND, no después |
La notificación que casi nadie gestiona
REFUND_REVERSED es la que la mayoría de las integraciones se saltan por completo, porque describe a Apple deshaciendo su propia decisión, un caso para el que pocos desarrolladores diseñan en el primer intento. Cuando llega, revocationDate en la transacción vuelve a null: en lo que respecta a Apple, la compra vuelve a ser válida. Si tu servidor eliminó un derecho, canceló un registro de suscripción, o puso a cero un saldo de consumible cuando llegó el REFUND original, REFUND_REVERSED es la señal para devolverlo todo.
Cómo cerrar la brecha, paso a paso
- En Google Play, establece revoke=true por defecto en cada vía de reembolso, ya sea a través de Play Console o de la API, a menos que un caso de soporte concreto exija que el cliente conserve el acceso.
- Consulta purchases.voidedpurchases.list de forma programada con tokens de paginación, no en un bucle ajustado, y mantente por debajo de 6,000 solicitudes al día y 30 cada 30 segundos.
- Dirige los valores fraud, friendly_fraud y chargeback de voidedReason a una cola de revisión, en lugar de un revoke-and-close automático.
- En Apple, suscríbete a las App Store Server Notifications V2 y revoca el acceso de no consumibles y suscripciones en el momento en que una notificación REFUND los mencione.
- Guarda el ID de transacción original junto a cada compra de un consumible, para que una notificación REFUND pueda encontrar el saldo exacto que descontar sin una búsqueda manual.
- Gestiona REFUND_REVERSED de forma explícita y registra cada revocación junto a su ID de transacción o de pedido, para que una reversión pueda restaurar exactamente lo que se eliminó.
Lo que la brecha realmente cuesta
Nada de esto tiene que ver con ser justo con el cliente; se trata de lo que sigue funcionando después de que el dinero ya se fue. Una suscripción reembolsada que sigue llamando a tu API, almacenando datos y consumiendo cómputo no es un problema moral, es una partida de gasto: pagaste la factura de infraestructura de una compra sobre la que ya devolviste el ingreso. Las tiendas no rastrean ese costo por ti, porque desde su lado el reembolso está completo en el momento en que el dinero se mueve. El lado del acceso es enteramente tu factura por controlar.
Esta es la misma razón por la que RefundHalt trata la revocación como parte del flujo de reembolso, y no como una idea de último momento: un evento REFUND y un cambio de acceso se registran como una sola acción, y un evento REFUND_REVERSED restaura el estado automáticamente, en lugar de esperar a que alguien note que el cliente sigue sin acceso a algo que Apple ya devolvió.
Preguntas frecuentes
- ¿Reembolsar una compra en Google Play revoca el acceso automáticamente?
- No. El acceso solo se elimina si se marca "Remove entitlement" en Play Console o si la llamada a la API orders.refund incluye revoke=true. Ninguno de los dos es el valor por defecto, así que un reembolso puede registrarse mientras el cliente sigue usando el producto.
- ¿Qué ocurre cuando se reembolsa una compra consumible dentro de la app en el App Store?
- Apple envía una notificación REFUND, pero StoreKit no tiene forma de revocar un consumible porque se considera entregado en cuanto se gasta. El servidor del desarrollador debe encontrar la transacción original y descontar el saldo manualmente.
- ¿Qué es la notificación REFUND_REVERSED y necesita un manejo especial?
- REFUND_REVERSED significa que Apple revirtió un reembolso que ya había concedido; revocationDate vuelve a null y la compra vuelve a ser válida. Cualquier acceso o saldo que el REFUND original haya eliminado debe restaurarse, y la mayoría de las integraciones nunca escuchan esta notificación en absoluto.
- ¿Por qué un pedido reembolsado de Google Play no aparecería en la Voided Purchases API?
- La API solo enumera los pedidos que fueron anulados con revocación. Si un reembolso se procesó sin la opción de revocar, el pedido no aparece en ese endpoint en absoluto, así que un flujo que dependa únicamente de ella se perderá esos reembolsos por completo.
- ¿Con qué frecuencia puedo consultar la Voided Purchases API de Google?
- El endpoint tiene un límite de 6,000 consultas al día y 30 consultas en cualquier ventana de 30 segundos, así que está pensado para una extracción programada con tokens de paginación, no para un sondeo continuo.
Fuentes y lecturas adicionales
- Apple: Handling refund notifications
- Apple: notificationType (App Store Server Notifications)
- Apple: revocationReason (App Store Server Notifications)
- Google: Voided Purchases API overview
- Google: purchases.voidedpurchases REST resource
- Google: orders.refund API reference (revoke parameter)
- Google Play Console Help: manage your app's orders and issue refunds
RefundHalt
El piloto automático de reembolsos para App Store y Google Play
Seguir leyendo
The refunds you cannot contest: what Apple and Google decide without asking you
Only two refund flows ever ask for your evidence: Apple's consumption request and Google Play's chargeback review. Everything else, the 48-hour Play refund, support refunds, voided purchases, unacknowledged trials, is decided without you. Here is the full map, what you can prevent, and what you can only record.
Apple's CONSUMPTION_REQUEST, explained: the 12 hours that decide your refund
When a customer asks Apple for a refund, Apple asks you first. Here's exactly what the CONSUMPTION_REQUEST notification contains, what Apple expects back, how to answer one end to end, and why most developers silently forfeit.