Подтвердите получение для покупки в приложении
Я играл с покупками приложений в течение нескольких дней, все работает нормально до того момента, когда я пытаюсь проверить квитанцию в магазине приложений, поскольку я постоянно получаю неверный статус.
Я передаю данные квитанции на мой PHP-сервер, затем пересылаю оттуда в магазин приложений, и как только я получу действительный ответ, я намереваюсь добавить данные квитанции в свою базу данных.
Руководство по программированию магазина и ссылки на классы для этой конкретной области более чем бесполезны, поскольку они не дают вам какого-либо примера. Я нашел одну полезную статью, которая немного помогла мне, но что-то все еще не так.
По сути, мне интересно, если бы кто-то, у кого работает проверка квитанции, захотел бы поделиться своим кодом, поскольку я ничего не получу.
Спасибо
8 ответов
Во-первых, в опубликованном коде есть несколько опечаток. Попробуй это. (Отказ от ответственности: Refactoring et. Al оставлен как упражнение для читателей!)
- (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];
}
Вы можете сделать эти Внутренние методы в классе, который обрабатывает ваши сообщения 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;
Где call-your-http-post-here - ваш любимый механизм HTTP post. (cURL - один из возможных вариантов. YMMV. У PHP.net есть совок!)
Одна вещь, которая меня немного беспокоит, - это длина полезной нагрузки в URL, идущей от приложения к серверу (через GET). Я забыл, есть ли проблема длины в RFC. Может быть, все в порядке, или, может быть, это зависит от сервера. (Читатели: Советы приветствуются в этой части!)
Может также быть некоторая задержка при выполнении этого синхронного запроса. Возможно, вы захотите опубликовать его асинхронно и установить старый UIActivityIndicatorView или другой HUD. Показательный пример: этот initWithData: encoding: call занимает слишком много времени для меня. Несколько секунд, что является небольшой вечностью на земле iPhone (или где-то еще онлайн, если на то пошло). Желательно показывать неопределенный индикатор прогресса.
Полный исходный код, а также размещенный пример реализации PHP доступен по адресу: http://www.chrismaddern.com/validate-app-store-iap-receipt-codes-online-tool/
Надеюсь, это поможет вам!
Для тех, кто интересуется, как обрабатывать ошибки подключения или проверки, которые могут возникнуть при использовании модели сервера In-App-Purchase. Проверка получения гарантирует, что транзакция завершена и успешна. Вы не хотите делать это с iPhone, потому что вы не можете доверять телефону пользователя.
- Пользователь инициирует покупку в приложении
- По завершении приложение запрашивает у вашего сервера подтверждение
- Вы подтверждаете квитанцию в Apple: если она действительна, вы можете выполнить любое действие, связанное с покупкой (разблокировать / доставить контент, зарегистрировать подписку...)
- Приложение удаляет транзакцию из очереди (finishTransaction)
Если сервер не работает, вы не должны завершать транзакцию, а отображать "сообщение о недоступности" для пользователя.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
будет вызван снова позже.
Но если вы обнаружите, что квитанция недействительна, вы должны завершить соответствующую транзакцию. Если нет, то в очереди транзакций могут быть дополнительные транзакции. Это означает, что каждый раз, когда ваше приложение запускается, paymentQueue: updatedTransaction: будет вызываться один раз за транзакцию...
В моих приложениях проверка квитанции выполняется через веб-сервис, возвращая код ошибки в случае недействительной квитанции. Вот почему нужен внешний сервер. Если пользователю каким-то образом удастся пропустить проверку квитанции (путем фальсификации ответа "успех" веб-службы), он не сможет разблокировать функциональность доступа к контенту, поскольку сервер не отслеживает покупку.
Я удивлен, что не нашел здесь учебник Рэя Вендерлиха - просто спас мне жизнь. Пройдет проверку квитанций без сервера (не рекомендуемое решение, но в любом случае оно выполняется).
Поработав с этим некоторое время, я наконец нашел список кодов состояния в документации Apple, включая страшный 21002 ("Данные в свойстве receive-data были искажены".). Хотя я видел сообщения о других кодах состояния, не включенных в этот список, я до сих пор не видел ничего, кроме того, что Apple документировала. Обратите внимание, что эти коды действительны только для подписок с автоматическим продлением, а не для других видов покупок в приложении (или так говорится в документе).
Документ, о котором идет речь, можно найти здесь.
Вы должны отправить квитанцию в виде файла на ваш сервер PHP. На вашей стороне 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
Просто, чтобы открыть это снова и добавить мои 2 цента в обмен на обыски этих форм для информации.
Я просто настроил службу IAP в своем приложении и столкнулся с той же ошибкой 21002. Я обнаружил, что 21002 происходит, когда сообщение на вашем PHP-сервере пустое (таким образом, HTTP-запрос к магазину приложений пусто) или неправильно отформатировано. Чтобы заставить нас работать, на стороне iPhone мы устанавливаем данные поста в строке NSString как закодированные в base64, а затем отправляем их на наш сервер в виде HTTP-запроса.
Затем на нашем сервере мы вставили его в массив и json-ed. Как это:
$receipt = json_encode(array("receipt-data"=>$_POST['receipt-data']));
Вы заметите, что это то же самое, что и выше, за исключением того, что мы используем POST вместо GET. Личные предпочтения действительно.
Затем мы использовали CURL, чтобы отправить его в песочницу, и использовали json_decode в ответе.
Это отличная библиотека, которая делает все, что нужно каждому по этому вопросу:
https://github.com/aporat/store-receipt-validator
Это выходит за рамки и подтверждает:
- Itunes
- Магазин игр
- Amazon App Store
Я надеюсь, что это помогает кому-то еще, как помогает мне.
Если вы получаете нулевые ответы или коды ошибок, например, 21002, попробуйте добавить эти строки. Если вы проверили коды ошибок curl, это ошибка сертификата SSL...
curl_setopt ($curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt ($curl_handle, CURLOPT_SSL_VERIFYPEER, 0);