@@ -25,8 +25,9 @@ import org.json.JSONObject
2525 * Sealed result returned by every Rethink server billing endpoint.
2626 *
2727 * Use [RpnPurchaseAckServerResponse.from] to deserialise raw HTTP response bodies.
28- * A 2xx response with `"success" != false` is treated as [Ok]; everything
29- * else (non-2xx, `"error"` key present, or `"success" == false`) is [Err].
28+ * A 2xx response with a JSON body and `"success" != false` is treated as [Ok];
29+ * everything else (non-2xx, missing or non-JSON body, `"error"` key present, or
30+ * `"success" == false`) is [Err].
3031 */
3132sealed class RpnPurchaseAckServerResponse {
3233 data class Ok (val payload : ResponseOk ) : RpnPurchaseAckServerResponse()
@@ -48,7 +49,14 @@ sealed class RpnPurchaseAckServerResponse {
4849 return Err (payload)
4950 }
5051
51- val json = body?.trimmedJson() ? : JSONObject ()
52+ // The server always replies with a JSON object (it carries at least "ray"),
53+ // so a missing or non-JSON 2xx body (e.g. from a proxy) is not a success.
54+ val json = body?.trimmedJson()
55+ if (json == null ) {
56+ val payload = ResponseErr (error = " malformed response" , httpCode = httpCode)
57+ Logger .d(LOG_IAB , " PlayErr: $payload " )
58+ return Err (payload)
59+ }
5260
5361 // If "error" key is present, or "success" is explicitly false → error
5462 return if (json.has(" error" ) || ! json.optBoolean(" success" , true )) {
@@ -63,10 +71,10 @@ sealed class RpnPurchaseAckServerResponse {
6371 }
6472 }
6573
66- private fun String.trimmedJson (): JSONObject = try {
74+ private fun String.trimmedJson (): JSONObject ? = try {
6775 JSONObject (this .trim())
6876 } catch (_: Exception ) {
69- JSONObject ()
77+ null
7078 }
7179 }
7280}
0 commit comments