You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动