Todos los artículos
Deep dive8 min de lectura

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.

Un smartphone iluminado sobre un escritorio de noche, que ilustra el momento en que el acceso a una compra reembolsada todavía no ha sido revocado

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 marcarNoNo
Reembolso en Play Console, "Remove entitlement" marcadoSí, de inmediato
API orders.refund, parámetro revoke omitido (por defecto false)NoNo
API orders.refund, revoke=trueSí, de inmediato

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.

Un rack de servidores con una sola luz de estado roja entre filas de luces verdes, que representa una decisión de revocación silenciosa

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ónQué significaQué debe hacer tu servidor
REFUNDApple reembolsó una compra consumible, no consumible, de renovación automática o sin renovaciónRevoca de inmediato el acceso de no consumibles y suscripciones; descuenta tú mismo el saldo del consumible
REFUND_DECLINEDApple rechazó la solicitud de reembolso de un clienteNada. El acceso nunca estuvo en riesgo
REFUND_REVERSEDApple revirtió un reembolso que ya había concedidoRestaura cualquier acceso o saldo que la notificación REFUND te hizo eliminar
CONSUMPTION_REQUESTApple quiere tus pruebas antes de decidir sobre un reembolsoResponde 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

RefundHalt

El piloto automático de reembolsos para App Store y Google Play

Seguir leyendo

La próxima solicitud de reembolso ya está en camino.

Configura RefundHalt en el tiempo que tardas en leer otro correo de soporte sobre un reembolso que no llegaste a impugnar.