Вопрос или проблема
Я пытаюсь заставить рекламное предложение In-App Purchase работать. Я получаю закодированную подпись, nonce, метку времени и идентификатор ключа с нашего сервера. Я создаю объект SKPaymentDiscount и устанавливаю его в paymentDiscount объекта SKMutablePayment.
При первом всплывающем окне цена отображается пересчитанной, как и ожидалось -> введите пароль и продолжите -> после успешной проверки: готово -> Второе всплывающее окно: отображает следующую ошибку: Не удается купить. Свяжитесь с разработчиком для получения дополнительной информации.
Примечание: метод updatedTransactions не вызывается.
Несмотря на то, сколько раз я пытаюсь, ошибка остается прежней: Не удается купить. Свяжитесь с разработчиком для получения дополнительной информации. Что можно сделать для решения этой проблемы? Буду признателен за любую помощь.
Заранее спасибо!
Код Node.JS, который генерирует закодированную подпись.
exports.ValidatePromotionalOffer = functions.https.onRequest((req,res) =>{
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","Origin,X-Requested-With,Content-Type,Accept");
var appBundleID = req.body.appBundleID;
var productIdentifier = req.body.productIdentifier;
var offerIdentifier = req.body.offerIdentifier;
var Username = req.body.applicationUsername;
console.log("Body",req.body)
var applicationUsername = crypto.createHash('sha256').update(Username).digest('hex');
var currentDate = new Date()
var timestamp = currentDate.getTime();
var nonce = uuidv4();
console.log("nonce",nonce)
var keyID = getKeyID();
console.log("keyID",keyID)
var payload = appBundleID + '\u2063' +
keyID + '\u2063' +
productIdentifier + '\u2063' +
offerIdentifier + '\u2063' +
applicationUsername + '\u2063' +
nonce + '\u2063' +
timestamp;
var keyString = getKeyStringForID();
console.log("keyString",keyString)
const sign = crypto.createSign('RSA-SHA256')
.update(payload)
.sign(keyString, 'base64');
console.log("sign",sign)
res.status(200).send({"keyID": keyID, "nonce": nonce, "timestamp": timestamp, "signature": sign});
function getKeyID(){
return process.env.SUBSCRIPTION_OFFERS_KEY_ID;
}
function getKeyStringForID(){
if(keyID == process.env.SUBSCRIPTION_OFFERS_KEY_ID){
return process.env.SUBSCRIPTION_OFFERS_PRIVATE_KEY;
}
else{
throw "Key Id not recognized";
res.status(400).send('Invalid signature');
}
}
});
Код на стороне приложения
func ValidatePromotionalOffer(productIdentifier: String, offerIdentifier: String, product: SKProduct){
print("ValidatePromotionalOffer вызвано")
let parameters: Parameters = [
"appBundleID": "bundleid",
"productIdentifier": productIdentifier,
"offerIdentifier": offerIdentifier,
"applicationUsername": username
]
print("параметры",parameters)
AF.request("https://us-central1-projectname.cloudfunctions.net/ValidatePromotionalOffer", method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON{ response in
let statuscode = response.response?.statusCode
switch response.result{
case .success(let resultdata):
if(statuscode == 200) {
print("успешно")
let jsonResult = JSON(response.value! as Any)
print("ответ",jsonResult)
let signature = jsonResult["signature"].stringValue
let keyID = jsonResult["keyID"].stringValue
let timestamp = jsonResult["timestamp"].numberValue
let nonce = UUID(uuidString: jsonResult["nonce"].stringValue)
// Создать предложение
let discountOffer = SKPaymentDiscount(identifier: offerIdentifier, keyIdentifier: keyID, nonce: nonce!, signature: signature, timestamp: timestamp)
// Передать предложение в блок завершения
//completion(discountOffer)
//SKPaymentQueue.default().add(self)
self.buyProduct1(product: product, forUser: jsonResult["nonce"].stringValue, withOffer: discountOffer);
self.dismiss(animated: false, completion: nil)
UIApplication.shared.endIgnoringInteractionEvents()
}
else
{
print("SUCCESS EARLY FOR FIRST TIME 2")
self.dismiss(animated: false, completion: nil)
UIApplication.shared.endIgnoringInteractionEvents()
let jsonResult = JSON(response.value! as Any)
print("ошибка ответ",jsonResult)
}
case .failure(let error):
print("ошибка да", error)
self.dismiss(animated: false, completion: nil)
UIApplication.shared.endIgnoringInteractionEvents()
}
}
}
public func buyProduct1(product: SKProduct, forUser usernameHash: String, withOffer discountOffer: SKPaymentDiscount) {
// Оригинальный продукт, который покупается.
let payment = SKMutablePayment(product: product)
// Вы должны установить applicationUsername, чтобы он был таким же, как тот, который использовался для генерации подписи.
payment.applicationUsername = usernameHash
// Добавьте предложение к платежу.
payment.paymentDiscount = discountOffer
// Добавьте платеж в очередь на покупку.
SKPaymentQueue.default().add(payment)
}
Ответ или решение
Невозможно совершить покупку с использованием промо-офера: Причины и решения
Ваша проблема с невозможностью совершения покупки через In-App Purchase с использованием промо-офера может иметь несколько причин. В данном ответе мы разберем потенциальные проблемы и предложим решения на основе предоставленной информации о вашем коде и процессе.
1. Ошибка при валидации промо-офера
Ошибка "Невозможно совершить покупку. Обратитесь к разработчику для получения дополнительной информации" может возникать из-за неправильной генерации или передачи данных для промо-офера. Убедитесь, что следующие элементы правильно настроены:
-
Параметры для валидации: Проверьте, что все параметры, отправляемые на сервер (bundleID, productIdentifier, offerIdentifier, applicationUsername), соответствуют тем, что ожидает ваш сервер. Полная корректность этих значений — критически важный аспект для успешного выполнения запроса.
-
Создание цифровой подписи: Убедитесь, что функция
ValidatePromotionalOffer
на сервере генерирует правильную цифровую подпись. Проверьте, чтоkeyString
(частный ключ) правильно загружается и используется для создания подписи. Особенно важно, чтобыkeyID
соответствовал ранее зарегистрированному в App Store Connect.
2. Проверка наличия ошибок на стороне клиента
Обратите внимание, что метод updatedTransactions
не вызывается. Это может означать, что запрос на покупку не доходит до очереди транзакций. Убедитесь, что:
-
Регистрация в очереди платежей: Ваша функция
buyProduct1
должна вызываться корректно, иSKPaymentQueue.default().add(payment)
действительно добавляет платеж в очередь. Сделайте отладочный вывод, чтобы убедиться, что эта часть кода выполняется. -
Настройки Sandbox или Production: Проверьте, что вы используете правильные учетные данные для тестирования или продуктивной среды. В тестовой среде могут быть свои ограничения и условия.
3. Анализ возвратов серверных ответов
При успешной валидации промо-офера ваш сервер возвращает объект JSON, содержащий данные, такие как signature
, keyID
, timestamp
и nonce
. После этого создается SKPaymentDiscount
. Убедитесь, что этот объект создается корректно и передается в метод buyProduct1
.
Проверьте, что значения:
timestamp
корректно преобразуются в правильный формат.nonce
соответствует ожидаемому UUID, и его значение передается корректно.
4. Логи и отладка
Используйте отладочные логи для отслеживания каждую стадию выполнения вашего кода:
- Логируйте все параметры, передаваемые серверу.
- Логируйте ответ сервера и проверяйте его на наличие ошибок.
- Логируйте процесс создания
SKPaymentDiscount
и вызываемых методов.
Заключение
Для успешной интеграции промо-офера необходимо учитывать множество факторов, включая корректность передаваемых данных и работу с очередью платежей. Если вы продолжаете сталкиваться с ошибками, рекомендуется перезапускать ваше приложение или даже устройство, чтобы исключить временные проблемы. Также подумайте о том, чтобы обратиться за помощью в службу поддержки Apple Developer, предоставив им все логи и описание проблемы.
Если следовать вышеуказанным рекомендациям, это должно помочь в устранении проблемы с невозможностью совершения покупок через промо-оферы.