Подтвердить что ты не робот

Любой (ранний) опыт работы с автоматически возобновляемыми подписками для iOS

Вчера Apple наконец представила так называемые автоматически возобновляемые подписки. Поскольку у меня только мало (только для песочницы) опыта с покупкой в ​​приложении, я не уверен, что у меня все получилось. Кажется, вам нужна проверка на стороне сервера квитанций. Кажется, единственный способ узнать, действительно ли подписка по-прежнему действительна, - хранить исходные данные транзакции на стороне сервера. Руководство по программированию яблок по этой теме является для меня загадочным.

Мое ожидание состояло в том, что я могу работать только с клиентом iOS, просто попросив iTunes через магазин api, он уже купил этот продукт (подписка) и получил ответ "да/нет" вместе с датой истечения срока действия.

Есть ли у кого-нибудь опыт использования автоматических возобновляемых подписчиков или (потому что они кажутся как-то похожими) не потребляемыми продуктами? Есть ли хорошие уроки об этом?

Спасибо.

4b9b3361

Ответ 1

У меня он работает в песочнице, почти вживую...

Для проверки чеков следует использовать сервер.

На сервере вы можете записать устройство 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.

ОБНОВЛЕНИЕ

Мое приложение было отклонено, потому что метаданные явно не указали некоторую информацию об абонементах с автоматическим возобновлением.

В ваших метаданных в iTunes Connect (в описании вашего приложения): You необходимо четко и четко раскрыть пользователям следующие информацию о подписке на автообновление:

  • Название публикации или услуги
  • Длина подписки (период времени и/или количество поставок в течение каждого периода подписки)
  • Цена подписки и цена за выпуск в случае необходимости
  • Платеж будет снят с учетной записи iTunes при подтверждении покупки.
  • Подписка автоматически возобновляется, если автоматическое обновление не отключается по крайней мере за 24 часа до конца текущего периода.
  • Аккаунт будет взиматься за продление в течение 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-адрес проверки содержимого приложения Sandbox, но когда он находится в процессе обзора, это как-то между мирами. Поэтому мне пришлось изменить код сервера следующим образом:

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.: -)

Ответ 2

Чтобы определить, имеет ли пользователь действительную подписку, вы либо должны: a) проверить существующую квитанцию, как описано в документе, к которому вы привязались, или b) попросить пользователя выкупить подписку и получить ответ от Apple.

Последнее не требует какого-либо взаимодействия на стороне сервера на вашем конце, но неправильно и может быть отклонено, поскольку вам нужно будет побудить пользователя эффективно "выкупить" ваш продукт каждый раз, когда вы хотите проверить их под.

Таким образом, единственный вариант - как рекомендует Apple - сохранить и затем проверить получение магазина.

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

Это означает, что ответ на ваш вопрос "могу ли я работать с клиентом iOS" "технически да", но для этого было бы очень плохо информировано из-за ряда проблем безопасности. К счастью, архитектура на стороне сервера, которую вам нужно собрать, очень проста - просто связать квитанции iTunes с UDID-устройствами и простой API для связи с ними. Если вы не можете это исправить, я уверен, что очень скоро сторонние помощники по покупке приложений, такие как Urban Airship, добавят автоматически обновляемые подписчики к своим продуктам.

Связывание UDID и квитанция работает нормально, потому что, когда пользователь совершает покупку на другом устройстве, Apple автоматически восстанавливает свои предыдущие покупки. Таким образом, вы можете снова сохранить квитанцию, на этот раз привязанный к новому UDID.

Ответ 3

Возможно, серверы с автоматической перезагрузкой песочницы недоступны? Затраты на расходные материалы/нерасходуемые/подписки на песочницу работают, но автоматически возобновляемая покупка возвращает эту ошибку:

Ошибка домена = код SKErrorDomain = 0 "Не удается подключиться к iTunes Store" UserInfo = 0x15b600 {NSLocalizedDescription = Не удается подключиться к iTunes Магазин}

Ответ 4

нет необходимости хранить его на сервере. вы можете проверить его локально на клиенте. мы в настоящее время кодируем автоматически возобновляемый script

но в настоящее время кажется, что серверы опускаются или что-то в этом роде. проверка с яблочными серверами не работает