2009-08-19 2 views
40

Я занимаюсь с покупками приложений в течение нескольких дней, все работает нормально до тех пор, пока я не попытаюсь проверить квитанцию ​​с магазином приложений, так как я постоянно возвращаю недействительный положение дел.Подтверждение получения для покупки приложения

Передаю данные квитанции на мой PHP-сервер, а затем отправляю оттуда в хранилище приложений, и как только я получу правильный ответ, я намерен добавить данные квитанции в свою базу данных.

Руководство по программированию магазина и ссылки на классы менее бесполезны для этой конкретной области, так как они действительно не дают вам никакого примера. Я нашел один полезный article, который немного помог мне, но что-то еще неправильно.

В принципе, мне интересно, сможет ли кто-то, у кого есть проверка валидации квитанции, поделиться своим кодом, поскольку я никуда не уйду.

Благодаря

ответ

69

Во-первых, есть несколько опечаток в размещенном коде. Попробуй это. (Отказ от ответственности:. Рефакторинг и др оставляется в качестве упражнения для читателей!)

- (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[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/="; 

    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]; 
} 

Вы можете сделать эти внутренние методы класса, который обрабатывает ваши SKPaymentTransactionObserver сообщения:

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

Примечание: Вы могли бы использовать что-то вроде libcrypto для обработки кодировки base64, но затем вы смотрите на ограничения на экспорт и дополнительные шаги в момент утверждения приложения. Но я отвлекся ...

Затем, где бы вы не начали запись транзакции на своем удаленном сервере, позвоните verifyReceipt: с вашей транзакцией и убедитесь, что она возвращается положительно.

Между тем, на сервере, вот некоторый супер-урезанный PHP для обработки вещи:

$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; 

Где вызова вашего-клиент-пост-здесь Вашего любимого HTTP POST механизм. (Curl это один из возможных вариантов. YMMV. PHP.net есть совок!)

Одна вещь, которая меня немного беспокоит длина полезной нагрузки в URL, идущей от приложения на сервер (через GET) , Я забываю, что в RFC есть проблема с длиной. Возможно, все в порядке, или, возможно, это зависит от сервера. (Читатели: Пособие приветствуется в этой части!)

Возможно, также возникнут некоторые отклонения от синхронного запроса. Вы можете отправить его асинхронно и установить ol 'UIActivityIndicatorView или какой-либо другой HUD. Дело в том, что initWithData: encoding: звонок занимает время loooooong для меня. Несколько секунд, что является небольшой вечностью в iPhone на земле (или где-нибудь еще в Интернете, если на то пошло). Может показаться, что какой-то неопределенный индикатор прогресса может быть полезен.

+0

Спасибо за тыс наконец, получил это работает, думаю, что моя главная проблема была в моем PHP-код. Еще раз спасибо, Andy – Andy

+1

Привет, используя предоставленный здесь скрипт, я получаю следующий ответ с сервера: 21002 ... можете ли вы рассказать мне, что это значит? Я вижу другого парня в этом сообщении, в котором упоминается аналогичная проблема. Спасибо за ваше время. – SpaceDog

+0

Хм ... не могу сказать, что я когда-либо видел этот ответ. :(Единственное, что я хотел бы проверить дважды, это то, что вы делаете HTTP _post_, а не HTTP_get_. То есть, полезная нагрузка поступает в данные сообщения, а не в строку запроса. –

0

Просто, чтобы открыть это снова и добавить мои 2-центов в обмен на бичевание этих форм для информации.

Я только что установил службу IAP в своем приложении и столкнулся с той же ошибкой 21002. Я обнаружил, что 21002 происходит, когда либо почта на ваш сервер PHP пуста (поэтому HTTP-запрос в хранилище приложений пуст) или неправильно отформатирован. Чтобы заставить нас работать, на стороне iPhone мы установили данные post в NSString как закодированные base64, а затем отправили их на наш сервер в виде HTTP-запроса.

Затем, на нашем сервере, мы вставляем его и массируем, и json-ed. Например:

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

Вы заметите, что это то же самое, что и выше, за исключением того, что мы используем POST вместо GET. Личное предпочтение действительно.

Затем мы использовали CURL для отправки его в песочницу и использовали json_decode для ответа.

+0

У меня возникли проблемы с использованием CURL с моей просьбой. Запрос возвращается пустым. И я подтвердил, что моя $ квитанция равна 64-битной кодированной строке с использованием объектного кода c выше $ receipt = json_encode (массив ("receipt-data" => $ _GET ["получение"])); $ url = "https://sandbox.itunes.apple.com/verifyReceipt"; echo 'Получение:'.$ Расписка; $ ch = curl_init(); curl_setopt ($ ch, CURLOPT_POST, true); curl_setopt ($ ch, CURLOPT_POSTFIELDS, $ получение); curl_setopt ($ ch, CURLOPT_URL, 'https://sandbox.itunes.apple.com/verifyReceipt'); $ response_json = curl_exec ($ ch); Любые идеи? – Convolution

-1

Если вы получаете нулевые ответы или коды ошибок, например 21002, попробуйте добавить эти строки. Если вы проверили свертыванию коды ошибок, это ошибка сертификата SSL ...

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

Слепое отключение проверки сверстников не является разумным. Это позволяет кому-то еще олицетворять сервер проверки Apple, а безопасность - это упражнение здесь. –

+0

Hi @Kyle Poole - вы правы. Вроде. К сожалению, коды ошибок Apple перекрываются с кодами ошибок SSL, так что это может быть и! Отличное планирование? –

+0

Для этого нет веских оснований! – mcfedr

3

Для тех, кто интересно, как обрабатывать соединения или проверки ошибок, которые могут возникнуть, когда вы используете-App-Purchase в серверном модель. Проверка валидации гарантирует, что транзакция будет завершена и успешна. Вы не хотите делать это с iPhone, потому что не можете доверять телефону пользователя.

  1. Пользователь инициирует в приложении покупки
  2. После завершения приложение запрашивает сервер для проверки
  3. Вы подтвердите получение с Apple: если это действительно, вы можете выполнять любые действия, связанные с покупкой (разблокировка/доставки контента, зарегистрировать подписку ...)
  4. приложение удаляет транзакцию из очереди (finishTransaction)

Если сервер выключен, вы не должны завершить сделку, но смещ ay «сообщение о недоступности» для пользователя.

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

будет называться позже.

Но Если вы обнаружили, что квитанция недействительна, вы должны завершить связанную транзакцию. Если нет, у вас могут быть дополнительные транзакции, живущие вечно в очереди транзакций. Это означает, что каждый раз, когда ваше приложение запускается, paymentQueue: updatedTransaction: будет вызываться один раз за транзакцию ...

В моих приложениях проверка чека выполняется через веб-службу, возвращая код ошибки в случае недопустимой квитанции. Поэтому необходим внешний сервер. Если пользователю каким-то образом удается пропустить проверку валидации (путем подталкивания ответа «успех» веб-службы), он не сможет разблокировать функциональность контента/доступа, потому что у сервера нет следов покупки.

1

После долгого боя с этим я наконец нашел список статусов в документации Apple, , включая страшный 21002 (что «Данные в свойстве данных квитанции были искажены»). Хотя я видел сообщения о других кодах статуса, не включенных в этот список, я до сих пор не видел ничего, кроме того, что Apple задокументировала. Обратите внимание, что эти коды действительны только для автоматического возобновления подписки, а не для других видов покупок в приложении (или, соответственно, в документе).

Этот документ можно найти here.

1

Я удивлен тем, что здесь не нашел урок Луи Вендерлиха - только что спас мне жизнь. Проходит проверку валидации квитанций без сервера (не рекомендуемое решение, но в любом случае сильно оно сделано).

http://www.raywenderlich.com/23266/in-app-purchases-in-ios-6-tutorial-consumables-and-receipt-validation

1

Вы должны отправить квитанцию ​​ как файл на PHP сервер. В РНР стороне вы можете использовать этот скрипт для проверки:

<?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

Смежные вопросы