purchase down apple app php iphone objective-c in-app-purchase storekit

php - down - in purchase app



Verificar recibo en la compra de la aplicaciĆ³n (8)

Debe enviar el recibo como un archivo a su servidor PHP. En su lado de PHP, puede usar esta secuencia de comandos para validar:

<?php $path = ''receipt''; // $_FILE[''receipt-data'']["tmp_name"]; $receipt = file_get_contents($path); $json[''receipt-data''] = base64_encode($receipt); $post = json_encode($json); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,"https://buy.itunes.apple.com/verifyReceipt"); curl_setopt($ch, CURLOPT_POST,1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); $result=curl_exec ($ch); curl_close ($ch); ?>

https://gist.github.com/eduardo22i/9adc2191f71ea612a7d071342e1e4a6f

He estado jugando con las compras en la aplicación durante unos días, todo funciona bien hasta el momento en que trato de validar el recibo con la tienda de aplicaciones, ya que constantemente estoy recibiendo un estado no válido.

Paso los datos del recibo a mi servidor PHP y luego los reenvío a la tienda de aplicaciones y, una vez que obtengo una respuesta válida, intento agregar los datos del recibo a mi base de datos.

La guía de programación del kit de tienda y las referencias de clase son menos que inútiles para esta área en particular, ya que realmente no dan ningún tipo de ejemplo, encontré un article útil que me ayudó un poco, pero algo sigue siendo incorrecto.

Básicamente me pregunto si alguien que tenga la validación de recibos trabajando estaría dispuesto a compartir su código ya que no llegaré a ninguna parte.

Gracias


Después de luchar con esto por un tiempo, finalmente encontré una lista de códigos de estado en la documentación de Apple, incluido el temido 21002 (que es "Los datos en la propiedad de datos de recibo estaban mal formados"). Aunque he visto informes de otros códigos de estado no incluidos en esta lista, hasta ahora no he visto más allá de lo que Apple ha documentado. Tenga en cuenta que estos códigos solo son válidos para las suscripciones de renovación automática, no para otros tipos de compras en la aplicación (o eso dice el documento).

El documento en cuestión se puede encontrar here .




Para cualquier persona que se esté preguntando cómo manejar los errores de conexión o verificación que pueden ocurrir cuando está usando el modelo de servidor In-App-Purchase. La validación de recibos asegura que la transacción sea completa y exitosa. No desea hacer eso desde el iPhone porque realmente no puede confiar en el teléfono del usuario.

  1. El usuario inicia una compra en la aplicación
  2. Cuando se completa, la aplicación solicita a su servidor la validación
  3. Valida el recibo con Apple: si es válido, puedes realizar cualquier acción vinculada a la compra (desbloquear / entregar contenido, registrar suscripción ...)
  4. La aplicación elimina la transacción de la cola (finishTransaction)

Si el servidor no funciona, no debe finalizar la transacción, sino mostrar un "mensaje de indisponibilidad" al usuario.

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions

se llamará de nuevo más tarde.

Pero si descubre que un recibo no es válido, debe finalizar la transacción asociada. De lo contrario, puede tener transacciones extra que viven para siempre en la cola de transacciones. Eso significa que cada vez que se ejecute su aplicación, paymentQueue: updatedTransaction: se llamará una vez por transacción ...

En mis aplicaciones, la validación de recibos se realiza a través de un servicio web, devolviendo un código de error en caso de recibo no válido. Es por eso que se necesita un servidor externo. Si un usuario logra de algún modo omitir la validación de recibos (falsificando la respuesta de "éxito" del servicio web), no podrá desbloquear la funcionalidad de contenido / acceso porque el servidor no tiene rastro de la compra.


Primero, hay algunos errores tipográficos en el código publicado. Prueba esto. (Descargo de responsabilidad: Refactoring et al. Se deja como un ejercicio para los lectores!)

- (BOOL)verifyReceipt:(SKPaymentTransaction *)transaction { NSString *jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length]; NSString *completeString = [NSString stringWithFormat:@"http://url-for-your-php?receipt=%@", jsonObjectString]; NSURL *urlForValidation = [NSURL URLWithString:completeString]; NSMutableURLRequest *validationRequest = [[NSMutableURLRequest alloc] initWithURL:urlForValidation]; [validationRequest setHTTPMethod:@"GET"]; NSData *responseData = [NSURLConnection sendSynchronousRequest:validationRequest returningResponse:nil error:nil]; [validationRequest release]; NSString *responseString = [[NSString alloc] initWithData:responseData encoding: NSUTF8StringEncoding]; NSInteger response = [responseString integerValue]; [responseString release]; return (response == 0); } - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length { static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; uint8_t *output = (uint8_t *)data.mutableBytes; for (NSInteger i = 0; i < length; i += 3) { NSInteger value = 0; for (NSInteger j = i; j < (i + 3); j++) { value <<= 8; if (j < length) { value |= (0xFF & input[j]); } } NSInteger index = (i / 3) * 4; output[index + 0] = table[(value >> 18) & 0x3F]; output[index + 1] = table[(value >> 12) & 0x3F]; output[index + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : ''=''; output[index + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : ''=''; } return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; }

Puede hacer estos métodos internos en la clase que maneja sus mensajes SKPaymentTransactionObserver :

@interface YourStoreClass (Internal) - (BOOL)verifyReceipt:(SKPaymentTransaction *)transaction; - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length; @end

Nota: Puede usar algo como libcrypto para manejar la codificación base64, pero luego está viendo restricciones de exportación y pasos adicionales en el momento de la aprobación de la aplicación. Pero yo divago ...

Luego, donde sea que pretenda iniciar la grabación de la transacción en su servidor remoto, llame verifyReceipt: con su transacción y asegúrese de que sea positiva.

Mientras tanto, en su servidor, aquí hay un PHP muy simplificado para manejar cosas:

$receipt = json_encode(array("receipt-data" => $_GET["receipt"])); // NOTE: use "buy" vs "sandbox" in production. $url = "https://sandbox.itunes.apple.com/verifyReceipt"; $response_json = call-your-http-post-here($url, $receipt); $response = json_decode($response_json); // Save the data here! echo $response->status;

Donde call-your-http-post-here es su mecanismo de publicación HTTP favorito. ( cURL es una opción posible. YMMV. PHP.net tiene la primicia!)

Una cosa que me preocupa un poco es la longitud de la carga en la URL que va de la aplicación al servidor (a través de GET). Me olvido si hay un problema de longitud allí por los RFC. Tal vez está bien, o tal vez es específico del servidor. (Lectores: ¡Asesoramiento bienvenido en esta parte!)

También puede haber algunos obstáculos para hacer de esto una solicitud sincrónica. Es posible que desee publicarlo de forma asincrónica y mostrar el viejo UIActivityIndicatorView o algún otro HUD. Caso en punto: esa llamada initWithData: encoding: lleva mucho tiempo para mí. Unos segundos, que es una pequeña eternidad en iPhone land (o en cualquier otro lugar en línea, para el caso). Mostrar algún tipo de indicador de progreso indeterminado puede ser aconsejable.


Si obtiene respuestas nulas o códigos de error, por ejemplo 21002, intente agregar estas líneas. Si marcó los códigos de error de curl, es un error de certificado SSL ...

curl_setopt ($curl_handle, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt ($curl_handle, CURLOPT_SSL_VERIFYPEER, 0);


Solo para abrir esto de nuevo y agregar mis 2 centavos a cambio de flagelar estos formularios para obtener información.

Acabo de configurar un servicio IAP en mi aplicación y encontré el mismo error 21002. Descubrí que el 21002 ocurre cuando la publicación en su servidor PHP está vacía (por lo tanto, la solicitud HTTP a la tienda de aplicaciones está vacía) o mal formateada. Para que la nuestra funcione, en el lado del iPhone configuramos los datos de la publicación en un NSString como codificado en base64 y luego lo enviamos a nuestro servidor como una solicitud HTTP.

Luego, en nuestro servidor, lo insertamos en un array y lo jon-ed. Me gusta esto:

$receipt = json_encode(array("receipt-data"=>$_POST[''receipt-data'']));

Notarás que es el mismo que el anterior, excepto que estamos usando un POST en lugar de un GET. Preferencia personal en realidad.

Luego usamos CURL para publicarlo en el sandbox y usamos json_decode en la respuesta.