android - studio - pay app billing
Android en la compra de la aplicación: la verificación de firma falló (12)
Basado en la respuesta de GMTDev, esto es lo que hago para solucionar los problemas de prueba al consumir productos de la manera más simple posible . En Security.java, reemplace el método verifyPurchase () con esto:
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return BuildConfig.DEBUG; // Line modified by Cristian. Original line was: return false;
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
}
Solo modifiqué una línea (ver comentario), y de esta manera puede mantener el código así para la depuración y publicar sus versiones de lanzamiento de forma segura.
He intentado durante varios días resolver este problema utilizando el código de demostración de Dungeons que viene con el SDK. Intenté buscar una respuesta de Google pero no puedo encontrarla.
- En la demo de Dungeons, pasé mi clave pública desde la consola de desarrollo.
- Firmado el apk y cargado en la consola sin publicar.
- Pruebas tanto para
android.test.purchased
como para la lista de productos creada en la consola y publicada para la suscripción (la función principal que deseo para mi aplicación).
Pero aún recibo un error de Signature verification failed
de Signature verification failed
y luego la firma no coincide con los datos. ¿Como puedo resolver esto?
public static ArrayList<VerifiedPurchase> verifyPurchase(String signedData, String signature)
{
if (signedData == null) {
Log.e(TAG, "data is null");
return null;
}
if (Consts.DEBUG) {
Log.i(TAG, "signedData: " + signedData);
}
boolean verified = false;
if (!TextUtils.isEmpty(signature)) {
String base64EncodedPublicKey = "MIIBIjA....AQAB";
PublicKey key = Security.generatePublicKey(base64EncodedPublicKey);
verified = Security.verify(key, signedData, signature);
if (!verified) {
Log.w(TAG, "signature does not match data.");
return null;
}
}
}
public static boolean verify(PublicKey publicKey, String signedData, String signature)
{
if (Consts.DEBUG) {
Log.i(TAG, "signature: " + signature);
}
Signature sig;
try {
sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
}
return false;
}
El error se debe a la clave de licencia incorrecta. Tal vez la clave de licencia sea probablemente de otra aplicación.
La solución es usar la clave de licencia adecuada de:
Play console> Aplicación> Herramientas de desarrollo> Licencias y facturación en la aplicación
Este problema todavía está sucediendo en la versión actual de facturación de Google. Básicamente, android.test.purchased está roto; Después de comprar android.test.purchased, la función verifyPurchase en Security.java siempre fallará y QueryInventoryFinishedListener se detendrá en la línea if (result.isFailure ()) ; esto se debe a que el elemento android.test.purchased siempre falla la comprobación TextUtils.isEmpty (firma) en Security.java, ya que no es un elemento real y no tiene una firma devuelta por el servidor.
Mi consejo (por falta de cualquier otra solución) es NUNCA usar "android.test.purchased". Hay varios ajustes de código en la red, pero ninguno de ellos funciona al 100%.
Si ha utilizado android.test.purchased, una forma de deshacerse del error es hacer lo siguiente:
- Edite Security.java y cambie la línea "return false" en verifyComprar para "devolver verdadero"; esto es temporal, lo volveremos a poner en un minuto.
En su QueryInventoryFinishedListener, después de las líneas "if (result.isFailure ()) {...}" agregue lo siguiente para consumir y deshacerse de su elemento inagotable android.test.purchased:
if (inventory.hasPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD)) { mHelper.consumeAsync(inventory.getPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD),null); }
Ejecute su aplicación para que consunmeAsync suceda, esto elimina el elemento "android.test.purchased" en el servidor.
- Elimine el código consumeAsync (o coméntelo).
- De vuelta en Security.java, cambie "return true" a "return false".
Su QueryInventoryFinishedListener ya no tendrá errores en la verificación, todo volverá a ser "normal" (si puede llamarlo así). Recuerde: no se moleste en usar android.test.purchased nuevamente, ya que solo causará este error nuevamente ... ¡está roto! La única manera real de probar su compra es cargar un APK, esperar que aparezca y luego probarlo (el mismo APK) en su dispositivo con el registro habilitado.
Esto es lo que funcionó para mí:
- Llame a BillingClient.querySkuDetailsAsync para consultar si el artículo está disponible
- Espere a SkuDetailsResponseListener.onSkuDetailsResponse
- Espera otros 500ms
- Comience la compra usando BillingClient.launchBillingFlow ...
El paso 3 no debería ser necesario porque cuando recibo enSkuDetailsResponse debería estar bien pero no es así, tuve que esperar un poco. Después de que la compra funcione, no más "Error de artículo no disponible". Así es como lo probé:
- borrar mis datos de la aplicación
- borrar datos de Google Play
- ejecutar la aplicación
- compra android.test.purchased
- tratar de comprar mis artículos (falla con el artículo no disponible)
- usar mi solución arriba, funciona
La verificación de firma falla solo para el producto de prueba predeterminado. Una solución rápida:
- Ir a la clase IabHelper.
- Invierta las condiciones if de
Security.verifyPurchase
.
¡Eso es!
Recuerde revertir los cambios cuando el producto de prueba sea reemplazado por un producto real
Lo que funcionó para mí, al usar In-app Billing v3 y las clases de utilidades incluidas, consumía la compra de prueba dentro de la llamada devuelta en ActionResult.
No se necesitan cambios en IabHelper, Seguridad ni en ninguna de las clases de utilidades de Factura en la aplicación para evitar esto en futuras compras de prueba.
Si ya ha intentado comprar el producto de prueba y ahora está atascado en el error de verificación de la firma de compra, que probablemente sea así porque está buscando respuestas para este error, entonces debe:
- hacer los cambios recomendados por GMTDev
- ejecuta la aplicación para asegurarte de que consume el producto
- eliminar / deshacer los cambios de GMTDev
- implementar el siguiente código en onActivityResult.
Esto no solo permite que el proceso de prueba de compra sea fluido, sino que también debe evitar cualquier conflicto con iab al devolver el error " Artículo que ya posee " al intentar comprar nuevamente el producto de prueba.
Si se llama desde dentro de un fragmento y no se está llamando a su resultado onActivityResult, asegúrese de llamar a YourFragmentName.onActivityResult (requestCode, resultCode, data) desde su ActivityFragment padre si es necesario. Esto se explica con más detalle en Llamar a startIntentSenderForResult desde Fragment (Android Billing v3) .
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_PURCHASE) {
//this ensures that the mHelper.flagEndAsync() gets called
//prior to starting a new async request.
mHelper.handleActivityResult(requestCode, resultCode, data);
//get needed data from Intent extra to recreate product object
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
// Strip out getActivity() if not being used within a fragment
if (resultCode == getActivity().RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
//only auto consume the android.test.purchased product
if (sku.equals("android.test.purchased")) {
//build the purchase object from the response data
Purchase purchase = new Purchase("inapp", purchaseData, dataSignature);
//consume android.test.purchased
mHelper.consumeAsync(purchase,null);
}
} catch (JSONException je) {
//failed to parse the purchase data
je.printStackTrace();
} catch (IllegalStateException ise) {
//most likely either disposed, not setup, or
//another billing async process is already running
ise.printStackTrace();
} catch (Exception e) {
//unexpected error
e.printStackTrace();
}
}
}
}
Solo eliminará la compra si se trata de sku "android.test.purchased", por lo que debe ser seguro.
Puede omitir el proceso de verificación de esos ID de productos "android.test. *". Si está utilizando el código de ejemplo del ejemplo TrivialDrive, abra IabHelper.java, busque el siguiente código de línea, cámbielo de
if (Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }
dentro
boolean verifySignature = !sku.startsWith("android.test."); // or inplace the condition in the following line
if (verifySignature && !Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }
Es inofensivo, incluso si olvidó revertir el código. Por lo tanto, puede continuar probando el siguiente paso del flujo de trabajo.
Sí, el problema aún ocurre. Después de comprar android.test.purchased, empiezo a obtener el error al consultar el inventario. Es posible reparar su teléfono simplemente borrando datos de la aplicación Google Play Store y ejecutando Google Play una vez. Cuando borras datos de Google Play olvida que compraste android.test.purchased
Tengo el mismo problema y sigo @Deadolus dijo basado en https://www.gaffga.de/implementing-in-app-billing-for-android/
El punto clave es que debemos hacer que el SKU sea consumible incluso si el resultado de la consulta de inventario falla. A continuación se muestra la muestra de cómo lo hice.
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
try {
Purchase purchase = new Purchase("inapp", "{/"packageName/":/"PACKAGE_NAME/","+
"/"orderId/":/"transactionId.android.test.purchased/","+
"/"productId/":/"android.test.purchased/",/"developerPayload/":/"/",/"purchaseTime/":0,"+
"/"purchaseState/":0,/"purchaseToken/":/"inapp:PACKAGE_NAME :android.test.purchased/"}",
"");
} catch (JSONException e) {
e.printStackTrace();
}
mHelper.consumeAsync(purchase, null);
complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
/*
* Check for items we own. Notice that for each purchase, we check
* the developer payload to see if it''s correct! See
* verifyDeveloperPayload().
*/
}
};
Reemplace PACKAGE_NAME en el código anterior con el nombre del paquete de su aplicación.
Verifique esta answer :
¿La cuenta principal en su dispositivo de prueba es la misma que su cuenta de desarrollador de Google Play?
De lo contrario, no obtendrás firmas en las respuestas estáticas de android.test. * A menos que la aplicación se haya publicado previamente en Play.
Consulte la tabla en http://developer.android.com/guide/market/billing/billing_testing.html#static-responses-table para conocer el conjunto completo de condiciones.
Y es un comentario:
No creo que los ID estáticos devuelvan la firma nunca más. Consulte https://groups.google.com/d/topic/android-developers/PCbCJdOl480/discussion
Además, anteriormente, el código de muestra (utilizado por muchas aplicaciones grandes) de la Biblioteca de Facturación de Google Play permitía una firma vacía. Es por eso que las compras estáticas funcionaron allí.
Pero era un agujero de seguridad, por lo que cuando se published , Google envió una update .
Verifique que base64EncodedPublicKey
y el de Play Developer Console sean iguales. Una vez que vuelva a cargar la APK en Developer Console , la clave pública puede cambiar, de ser así, actualice su base64EncodedPublicKey
.
This solución funcionó para mí. Cambié el nuevo método de verificación de compra en la clase de compra con el anterior.