使用lambda通过Node.js进行iOS Receipt验证

时间:2016-09-28 21:19:28

标签: ios node.js swift lambda

我正在开发Swift中的iOS应用,并尝试为应用内购买实施收据验证。我无法弄清楚如何在Swift中实现这一点,所以我在this看到Giulio Roggero的例子之后尝试让我的应用程序通过Node.js中的Lambda函数发送请求。题。我的Swift代码如下所示:

let receiptPath = Bundle.main.appStoreReceiptURL?.path
    if FileManager.default.fileExists(atPath: receiptPath!){
        var receiptData:NSData?
        do{
            receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
        }
        catch{
            print("ERROR: " + error.localizedDescription)
        }
        let receiptString = receiptData?.base64EncodedString(options: .endLineWithLineFeed)
        let invocationRequest = AWSLambdaInvokerInvocationRequest()
        invocationRequest?.functionName = "sendReceiptRequest"
        invocationRequest?.invocationType = AWSLambdaInvocationType.requestResponse
        invocationRequest?.payload = ["receipt-data" : receiptString!, "password" : SUBSCRIPTION_SECRET]

        let lambdaInvoker = AWSLambdaInvoker.default()
        lock()
        lambdaInvoker.invoke(invocationRequest!).continue(with: AWSExecutor.mainThread(), with: { (task:AWSTask!) -> AnyObject! in
            if task.error != nil {
                self.sendErrorPopup("Error: \(task.error?.localizedDescription)")
            } else {
                print("TOKEN: ", task.result)
            }
            self.unlock()
            return nil
        })}

My Lambda node.js函数如下所示:

function (receiptData_base64, password, production, cb)
{
var url = production ? 'buy.itunes.apple.com' : 'sandbox.itunes.apple.com'
var receiptEnvelope = {
    "receipt-data": receiptData_base64,
    "password":password
};
var receiptEnvelopeStr = JSON.stringify(receiptEnvelope);
var options = {
    host: url,
    port: 443,
    path: '/verifyReceipt',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(receiptEnvelopeStr)
    }
};

var req = https.request(options, function(res) {
    res.setEncoding('utf8');
    res.on('data', function (chunk) {
        console.log("body: " + chunk);
        cb(true, chunk);
    });
    res.on('error', function (error) {
        console.log("error: " + error);
        cb(false, error);
    });
});
req.write(receiptEnvelopeStr);
req.end();
}

但是,在运行此代码时,无论是通过lambda测试还是通过我的应用程序,我都会收到一条简单的Response body: {"errorMessage":"true"}错误消息。我注意到,如果我调整代码,我可以创建更多预期错误 - 例如,如果我对收据数据有其他值,我会得到21002错误代码作为响应,如果我更改&#34 ;生产"如果是这样,我收到21007错误。问题的一部分是我不确切地知道回调应该如何工作 - https.request内的块是否正确我想在Swift中做什么?我得到的结论是收据数据格式正确,因为更改它会产生不同的结果,那么为什么最终结果仍然是错误?

编辑:

我之前没有注意到的是,当我运行Lambda函数时,行"正文:(收据数据)"出现,其中(收据数据)是我发送给函数的基本64位编码数据。这让我怀疑我根本没有到达错误回调块,并且该错误与我将回调结果发送回我的应用程序的方式有关。这块是什么:

var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
    console.log("body: " + chunk);
    cb(true, chunk);
});
res.on('error', function (error) {
    console.log("error: " + error);
    cb(false, error);
});
});
应该怎么做?是否有可能我需要启用一些权限来接收回调?

4 个答案:

答案 0 :(得分:1)

对于后来发现这一点的人来说,潜在的问题:

  1. 这里的一个大问题是Content-Type不正确。你需要发布JSON,而不是url编码;当代码执行此操作时,它使用错误的类型来告诉服务器它使用的格式。它应该是application/json而不是application/x-www-form-urlencoded
  2. 发送到Apple服务器的Base64 必须(截至上周,至少)包含行结尾。我不知道这是一个问题,但这是我过去遇到的问题,而.endLineWithLineFeed让我怀疑。
  3. 用于在服务器之间切换的算法。该方法应该尝试验证对生产服务器的接收,而不是允许在生产和沙箱之间切换。如果结果对象的status21007,则代码应该针对沙盒服务器尝试相同的收据。一旦收据被解码和验证,客户端就可以检查收据,看看它被验证的服务器和(如有必要)忽略沙箱收据。

答案 1 :(得分:0)

最终我通过跟随this示例并使用Python而不是Node.js解决了这个问题。我之前仍然不知道我的代码到底出了什么问题,但是在Swift中使用base-64编码然后将收据数据和密码发送到Python Lambda函数给了我正确的结果。

答案 2 :(得分:0)

在你的Swift代码中,你检查"如果task.error!= nil"然后你有一个错误

cb(true, chunk);

但是在您的节点代码中,您可以通过以下方式调用回调:

cb(null, chunk);

第一个参数是你的错误,它不会是零。你应该用以下代码替换这一行:

{{1}}

你将以这种方式获得正确的Json

答案 3 :(得分:0)

这对我有用:

'use strict'
var AWS = require('aws-sdk')
var https = require('https')

exports.handler = (event, context, callback) => {

    var payload = JSON.stringify({
        'receipt-data': event.arguments.input.base64_receipt
    })

    function generateOptions(url) {
        return {
            host: url,
            port: 443,
            path: '/verifyReceipt',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': Buffer.byteLength(payload)
            }
        }
    }

    function validateReceipt(url, payload, errorCallback) {
        var req = https.request(generateOptions(url), function(res) {
            res.setEncoding('utf8')
            res.on('data', function(chunk) {
                var data = JSON.parse(chunk)
                // success
                callback(null, event)
            })
            res.on('error', function(error) {
                //error
                errorCallback ? errorCallback() : callback('There was an error validating the transaction', event)
            })
        })
        req.write(payload)
        req.end()
    }

    // attempt to validate on production and sandbox
    validateReceipt('buy.itunes.apple.com', payload, function() {
        validateReceipt('sandbox.itunes.apple.com', payload)
    })
}