iOS内购促销优惠购买失败:提示「无法购买,请联系开发者获取更多信息」的问题排查与解决求助
iOS内购促销优惠购买失败:提示「无法购买,请联系开发者获取更多信息」的问题排查与解决求助
我目前正在尝试实现iOS内购的促销优惠功能,具体流程是从我们的服务器获取编码签名、nonce、时间戳和密钥标识,接着创建SKPaymentDiscount对象,并将其赋值给SKMutablePayment对象的paymentDiscount属性。
但在测试过程中遇到了以下问题:
- 第一次弹出的支付窗口正常显示了优惠后的价格,输入Apple ID密码验证成功并点击「完成」后
- 立刻弹出第二个错误窗口,提示:Unable to Purchase Contact the developer for more information.

另外还有一个关键的异常现象: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'); } } });
iOS端相关代码
func ValidatePromotionalOffer(productIdentifier: String, offerIdentifier: String, product: SKProduct){ print("ValidatePromotionalOffer called") let parameters: Parameters = [ "appBundleID": "bundleid", "productIdentifier": productIdentifier, "offerIdentifier": offerIdentifier, "applicationUsername": username ] print("parameters",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("successful") let jsonResult = JSON(response.value! as Any) print("response",jsonResult) let signature = jsonResult["signature"].stringValue let keyID = jsonResult["keyID"].stringValue let timestamp = jsonResult["timestamp"].numberValue let nonce = UUID(uuidString: jsonResult["nonce"].stringValue) // Create offer let discountOffer = SKPaymentDiscount(identifier: offerIdentifier, keyIdentifier: keyID, nonce: nonce!, signature: signature, timestamp: timestamp) // Pass offer in completion block //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("error response",jsonResult) } case .failure(let error): print("failure yes", error) self.dismiss(animated: false, completion: nil) UIApplication.shared.endIgnoringInteractionEvents() } } } public func buyProduct1(product: SKProduct, forUser usernameHash: String, withOffer discountOffer: SKPaymentDiscount) { // The original product being purchased. let payment = SKMutablePayment(product: product) // You must set applicationUsername to be the same as the one used to generate the signature. payment.applicationUsername = usernameHash // Add the offer to the payment. payment.paymentDiscount = discountOffer // Add the payment to the queue for purchase. SKPaymentQueue.default().add(payment) }
备注:内容来源于stack exchange,提问作者Lakshmi




