У меня это работает в песочнице, почти вживую ...
Нужно использовать сервер для проверки квитанций.
На сервере вы можете записать udid устройства с помощьюданные квитанции, поскольку квитанции всегда генерируются заново, и они будут работать на нескольких устройствах, поскольку квитанции всегда генерируются заново.
На устройстве не нужно хранить какие-либо конфиденциальные данные и не следует :)
При каждом запуске приложения необходимо проверять последнюю квитанцию в магазине.Приложение вызывает сервер, а сервер проверяет в магазине.До тех пор, пока магазин возвращает действительное приложение квитанции, обслуживающее эту функцию.
Я разработал приложение Rails3.x для обработки на стороне сервера, фактический код для проверки выглядит следующим образом:
APPLE_SHARED_PASS = "enter_yours"
APPLE_RECEIPT_VERIFY_URL = "https://sandbox.itunes.apple.com/verifyReceipt" #test
# APPLE_RECEIPT_VERIFY_URL = "https://buy.itunes.apple.com/verifyReceipt" #real
def self.verify_receipt(b64_receipt)
json_resp = nil
url = URI.parse(APPLE_RECEIPT_VERIFY_URL)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
json_request = {'receipt-data' => b64_receipt, 'password' => APPLE_SHARED_PASS}.to_json
resp, resp_body = http.post(url.path, json_request.to_s, {'Content-Type' => 'application/x-www-form-urlencoded'})
if resp.code == '200'
json_resp = JSON.parse(resp_body)
logger.info "verify_receipt response: #{json_resp}"
end
json_resp
end
#App Store error responses
#21000 The App Store could not read the JSON object you provided.
#21002 The data in the receipt-data property was malformed.
#21003 The receipt could not be authenticated.
#21004 The shared secret you provided does not match the shared secret on file for your account.
#21005 The receipt server is not currently available.
#21006 This receipt is valid but the subscription has expired.
ОБНОВЛЕНИЕ
Мое приложение было отклонено, поскольку в метаданных 1017 * явно не указана информация об автообновляемых подписках.
В ваших метаданныхв iTunes Connect (в описании вашего приложения): вам необходимо четко и наглядно раскрыть пользователям следующую информацию о вашей автоматически возобновляемой подписке:
- Название публикации или услуги
- Длинаподписки (период времени и / или количество доставок в течение каждого периода подписки)
- Цена подписки и цена за выпуск при необходимости
- Оплата будет списана с учетной записи iTunes при подтверждении покупки
- Подписка автоматически продлевается, если автоматическое продление не отключено по крайней мере за 24 часа до окончания срока действияnt period
- С аккаунта будет взиматься плата за продление в течение 24 часов до конца текущего периода, и указывается стоимость продления
- Подписка может управляться пользователем и автоматическипродление можно отключить, зайдя в настройки учетной записи пользователя после покупки
- Отмена текущей подписки не допускается в течение активного периода подписки
- Ссылки на вашу политику конфиденциальности и условия использования
- Любая неиспользованная часть бесплатного пробного периода, если таковая предлагается, будет аннулирована, когда пользователь приобретет подписку на эту публикацию. "
ОБНОВЛЕНИЕ II
Приложениеполучил снова.Квитанция о подписке не проверяется производственным URL AppStore.Я не могу воспроизвести эту проблему в песочнице, мое приложение работает безупречно.Единственный способ отладки - это снова отправить приложение на проверку и посмотреть журнал сервера.
ОБНОВЛЕНИЕ III
Еще одно отклонение.Между тем Apple задокументировала еще два статуса:
#21007 This receipt is a sandbox receipt, but it was sent to the production service for verification.
#21008 This receipt is a production receipt, but it was sent to the sandbox service for verification.
Прежде чем отправлять приложение на проверку, не следует переключать сервер на URL подтверждения получения чека.если это так, то при проверке возвращается статус 21007.
На этот раз отклонение выглядит следующим образом:
Приложение запускает процесс покупки в приложении нестандартным способом.Мы включили следующую информацию, чтобы помочь объяснить проблему, и надеемся, что вы рассмотрите возможность пересмотра и повторной отправки своего приложения.
Имя пользователя и пароль iTunes запрашиваются сразу после запуска приложения.Пожалуйста, обратитесь к приложенному скриншоту для получения дополнительной информации.
Я понятия не имею, почему это происходит.Всплывающее диалоговое окно пароля, потому что предыдущая транзакция восстанавливается?или он появляется в момент запроса информации о продуктах из магазина приложений?
ОБНОВЛЕНИЕ IV
Я понял это сразу после 5 отклонений.Мой код делал самую очевидную ошибку.Нужно всегда быть уверенным, что транзакции всегда завершаются, когда они доставляются в приложение.
Если транзакции не завершены, они возвращаются обратно в приложение, и все происходит странным образом.
Сначала нужно инициировать платеж, например:
//make the payment
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
Затем приложение в скором времени выйдет из активного состояния, и этот метод для делегата приложения будет вызван:
- (void)applicationWillResignActive:(UIApplication *)application
Пока приложение неактивно, в App Store появляются его диалоговые окна.как приложение снова становится активным:
- (void)applicationDidBecomeActive:(UIApplication *)application
ОС доставляет транзакцию через:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased: {
[self completeTransaction:transaction];
break;
}
case SKPaymentTransactionStateFailed: {
[self failedTransaction:transaction];
break;
}
case SKPaymentTransactionStateRestored: {
[self restoreTransaction:transaction];
break;
}
default:
break;
}
}
}
А затем завершает транзакцию:
//a fresh purchase
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[self recordTransaction: transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
См.,как вызывать метод finishTransaction
сразу после передачи полученной транзакции на recordTransaction
, который затем вызывает сервер приложений и выполняет проверку получения подписки в App Store.Например:
- (void)recordTransaction: (SKPaymentTransaction *)transaction
{
[self subscribeWithTransaction:transaction];
}
- (void)subscribeWithTransaction:(SKPaymentTransaction*)transaction {
NSData *receiptData = [transaction transactionReceipt];
NSString *receiptEncoded = [Kriya base64encode:(uint8_t*)receiptData.bytes length:receiptData.length];//encode to base64 before sending
NSString *urlString = [NSString stringWithFormat:@"%@/api/%@/%@/subscribe", [Kriya server_url], APP_ID, [Kriya deviceId]];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:[[transaction payment] productIdentifier] forKey:@"product"];
[request setPostValue:receiptEncoded forKey:@"receipt"];
[request setPostValue:[Kriya deviceModelString] forKey:@"model"];
[request setPostValue:[Kriya deviceiOSString] forKey:@"ios"];
[request setPostValue:[appDelegate version] forKey:@"v"];
[request setDidFinishSelector:@selector(subscribeWithTransactionFinished:)];
[request setDidFailSelector:@selector(subscribeWithTransactionFailed:)];
[request setDelegate:self];
[request startAsynchronous];
}
Ранее мой код пытался вызвать finishTransaction
только после того, как мой сервер подтвердил получение, но к тому времени транзакция уже была как-то потеряна.так что просто убедитесь, что finishTransaction
как можно скорее.
Еще одна проблема, с которой можно столкнуться, это то, что когда приложение находится в песочнице, оно вызывает URL-адрес верификации в песочнице App Store, но когда онов обзоре, это как-то между мирами.Поэтому мне пришлось изменить свой серверный код следующим образом:
APPLE_SHARED_PASS = "83f1ec5e7d864e89beef4d2402091cd0" #you can get this in iTunes Connect
APPLE_RECEIPT_VERIFY_URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt"
APPLE_RECEIPT_VERIFY_URL_PRODUCTION = "https://buy.itunes.apple.com/verifyReceipt"
def self.verify_receipt_for(b64_receipt, receipt_verify_url)
json_resp = nil
url = URI.parse(receipt_verify_url)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
json_request = {'receipt-data' => b64_receipt, 'password' => APPLE_SHARED_PASS}.to_json
resp, resp_body = http.post(url.path, json_request.to_s, {'Content-Type' => 'application/x-www-form-urlencoded'})
if resp.code == '200'
json_resp = JSON.parse(resp_body)
end
json_resp
end
def self.verify_receipt(b64_receipt)
json_resp = Subscription.verify_receipt_for(b64_receipt, APPLE_RECEIPT_VERIFY_URL_PRODUCTION)
if json_resp!=nil
if json_resp.kind_of? Hash
if json_resp['status']==21007
#try the sandbox then
json_resp = Subscription.verify_receipt_for(b64_receipt, APPLE_RECEIPT_VERIFY_URL_SANDBOX)
end
end
end
json_resp
end
Таким образом, в основном каждый всегда сверяется с рабочим URL, но если он возвращает код 21007, то это означает, что квитанция песочницы была отправлена на рабочий URL изатем просто попробуйте снова с URL песочницы.Таким образом, ваше приложение работает одинаково в песочнице и в производственном режиме.
И, наконец, Apple захотела, чтобы я добавил кнопку RESTORE рядом с кнопками подписки для обработки нескольких устройств, принадлежащих одному пользователю.Затем эта кнопка вызывает [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
, и приложение будет доставлено с восстановленными транзакциями (если есть).
Кроме того, иногда тестовые учетные записи пользователей как-то загрязняются, и все перестает работать, и вы можете получить «Не удается подключиться»в iTunes store "сообщение при подписке.Это помогает создать нового тестового пользователя.
Вот остаток соответствующего кода:
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[self recordTransaction: transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code == SKErrorPaymentCancelled)
{
//present error to user here
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
Я желаю вам приятного опыта программирования InAppPurchase.: -)