Compras dentro de la aplicación de Google Android "Entrega de contenido" ¿Cómo entregar contenido correctamente?

Actualmente estoy tratando de codificar las compras dentro de la aplicación. Estaba buscando documentación, información, tutoriales sobre las mejores prácticas para algunas de las cosas que Google no maneja.

Lo que he hecho hasta ahora

Tengo un servicio de facturación en ejecución que se encarga de hablar con Google Play. Este servicio puede completar las transacciones de "muestra" y mi aplicación recibe el mensaje.

Ahora quiero enviar el contenido al dispositivo. Lo que creo que debe suceder a continuación:

  1. Mi aplicación necesita ponerse en contacto con mi servidor y mostrar alguna prueba de la transacción exitosa. Haz un apretón de manos de Cert o alguna tontería por el estilo.

  2. Luego, descargaré el contenido y lo colocaré en una base de datos. Probablemente debería cifrar la base de datos con algún tipo de cifrado único de dispositivo.

Estoy buscando aprender a hacer las 2 cosas anteriores y cualquier otra cosa que deba hacerse. Me gustaría una cantidad razonable de seguridad/cifrado. Cualquier documentación / tutoriales / proyectos de muestra sería genial, he intentado buscar estas cosas y no he encontrado lo que estaba buscando.

preguntado el 03 de mayo de 12 a las 19:05

1 Respuestas

Deberá realizar algunos cambios en el código de cliente del servicio de facturación de la muestra.

Primero, debe llamar a su servidor para obtener el nonce que se usará para RestoreTransactions o para realizar la compra, para que todo sea lo más seguro posible.

Sigamos lo que sucede. Aquí está el BillingReceiver al que llama Google Play:

/**
 * This is called when Android Market sends information about a purchase state
 * change. The signedData parameter is a plaintext JSON string that is
 * signed by the server with the developer's private key. The signature
 * for the signed data is passed in the signature parameter.
 * @param context the context
 * @param signedData the (unencrypted) JSON string
 * @param signature the signature for the signedData
 */
private void purchaseStateChanged(Context context, String signedData, String signature) {
    Intent intent = new Intent(Consts.ACTION_PURCHASE_STATE_CHANGED);
    intent.setClass(context, BillingService.class);
    intent.putExtra(Consts.INAPP_SIGNED_DATA, signedData);
    intent.putExtra(Consts.INAPP_SIGNATURE, signature);
    context.startService(intent);
}

Si observa handleCommand dentro de BillingService.java, maneja esta intención:

/**
 * The {@link BillingReceiver} sends messages to this service using intents.
 * Each intent has an action and some extra arguments specific to that action.
 * @param intent the intent containing one of the supported actions
 * @param startId an identifier for the invocation instance of this service
 */
public void handleCommand(Intent intent, int startId) {
    String action = intent.getAction();
    if (Consts.DEBUG) {
        Log.i(TAG, "handleCommand() action: " + action);
    }
    if (Consts.ACTION_CONFIRM_NOTIFICATION.equals(action)) {
        String[] notifyIds = intent.getStringArrayExtra(Consts.NOTIFICATION_ID);
        confirmNotifications(startId, notifyIds);
    } else if (Consts.ACTION_GET_PURCHASE_INFORMATION.equals(action)) {
        String notifyId = intent.getStringExtra(Consts.NOTIFICATION_ID);
        getPurchaseInformation(startId, new String[] { notifyId });
    } else if (Consts.ACTION_PURCHASE_STATE_CHANGED.equals(action)) {
        String signedData = intent.getStringExtra(Consts.INAPP_SIGNED_DATA);
        String signature = intent.getStringExtra(Consts.INAPP_SIGNATURE);
        purchaseStateChanged(startId, signedData, signature);
    } else if (Consts.ACTION_RESPONSE_CODE.equals(action)) {
        long requestId = intent.getLongExtra(Consts.INAPP_REQUEST_ID, -1);
        int responseCodeIndex = intent.getIntExtra(Consts.INAPP_RESPONSE_CODE,
                ResponseCode.RESULT_ERROR.ordinal());
        ResponseCode responseCode = ResponseCode.valueOf(responseCodeIndex);
        checkResponseCode(requestId, responseCode);
    }
}

Esto luego llama a la función PurchaseStateChanged. Esta función debe reemplazarse por una llamada a su servidor para crear una sesión para la entrega de su contenido. El código de Security.java debe transferirse al lado del servidor para validar la transacción dentro de la nube.

/**
 * Verifies that the data was signed with the given signature, and calls
 * {@link ResponseHandler#purchaseResponse(Context, PurchaseState, String, String, long)}
 * for each verified purchase.
 * @param startId an identifier for the invocation instance of this service
 * @param signedData the signed JSON string (signed, not encrypted)
 * @param signature the signature for the data, signed with the private key
 */
private void purchaseStateChanged(int startId, String signedData, String signature) {
    ArrayList<Security.VerifiedPurchase> purchases;
    purchases = Security.verifyPurchase(signedData, signature);
    if (purchases == null) {
        return;
    }

    ArrayList<String> notifyList = new ArrayList<String>();
    for (VerifiedPurchase vp : purchases) {
        if (vp.notificationId != null) {
            notifyList.add(vp.notificationId);
        }
        ResponseHandler.purchaseResponse(this, vp.purchaseState, vp.productId,
                vp.orderId, vp.purchaseTime, vp.developerPayload);
    }
    if (!notifyList.isEmpty()) {
        String[] notifyIds = notifyList.toArray(new String[notifyList.size()]);
        confirmNotifications(startId, notifyIds);
    }
}

Asegúrese de poner su clave pública en el lado del servidor en el archivo Security.java portado.

contestado el 03 de mayo de 12 a las 23:05

¡Gracias! Algunas otras preguntas: ¿Simplemente descargo BillingSecurity.java en un servidor en alguna parte? Y luego, ¿cómo me conecto para obtener el aviso y comparar las claves públicas, etc.? - naradlov

He instalado una instancia de AppEngine antes, así que esa es mi experiencia. Debería tener una llamada a su servidor para guardar el nonce en una base de datos (en el lado del servidor) y asociarlo con la transacción en cuestión. Usualmente uso una combinación de una marca de tiempo y algunos datos que obtengo del teléfono, como ANDROID_ID. Luego, ese nonce se envía al cliente de facturación. El módulo Security.java contiene el código para validar la firma en función de su clave pública del panel del desarrollador. - dagalpin

Gracias, todavía tengo trabajo por hacer. Esto ayudará mucho. - naradlov

¿Qué sucede si primero compro un artículo y luego de un tiempo quiero descargarlo (la aplicación no se reinstaló)? Sí, puedo activar en mi aplicación que se compra el artículo y la aplicación permitirá descargar este contenido solo si está marcado como comprado. Pero no parece seguro porque el estado "comprado" se almacena en el lado del cliente. - Solvek

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.