You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apple IAP收据验证Node.js报错求助:receipt-data格式异常

Hey there, let's break down what's going wrong and fix this receipt validation issue step by step—since you're new to JavaScript, I'll keep this clear and actionable.

The Core Issues

  1. Your Swift request is sending data in the wrong format: You're using application/x-www-form-urlencoded but passing a raw base64 string instead of a key-value pair. This confuses the Node.js body parser, so it can't read your receipt data correctly.
  2. Your Node.js code is overcomplicating receipt parsing: You're trying random conversions that don't match how the data is sent, and you're re-initializing the IAP library on every request (which is inefficient).

Step 1: Fix the Swift Request

We need to send the receipt as a proper key-value pair (required for x-www-form-urlencoded), and URL-encode the base64 string to avoid parsing errors. Here's the corrected code:

do {
    let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
    let receiptString = receiptData.base64EncodedString(options: [])
    
    // Create a proper key-value pair for form data, and URL-encode the receipt
    guard let encodedReceipt = receiptString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
        print("Failed to encode receipt string")
        return
    }
    let requestBodyString = "receipt=\(encodedReceipt)"
    
    guard let requestBodyData = requestBodyString.data(using: .utf8) else {
        print("Failed to create request body data")
        return
    }
    
    guard let url = URL(string: "https://us-central1-calendizer-6a849.cloudfunctions.net/receiptValidation") else {
        print("Invalid validation URL")
        return
    }
    
    var request = URLRequest(url: url)
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.httpMethod = "POST"
    request.httpBody = requestBodyData
    
    print("Sending receipt (truncated): \(encodedReceipt.prefix(50))...")
    Logs.log("✉️ Receipt Validation -> sending request ...")
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            print("Request failed: \(error.localizedDescription)")
            return
        }
        guard let data = data else {
            print("No response data received")
            return
        }
        if let responseText = String(data: data, encoding: .utf8) {
            print("Validation response: \(responseText)")
        }
    }
    task.resume()
} catch {
    print("Failed to read receipt data: \(error.localizedDescription)")
}

Step 2: Fix the Node.js Cloud Function

We'll simplify the receipt parsing, initialize the IAP library only once (not per request), and add CORS handling (required for iOS app cross-origin requests). Here's the corrected code:

const functions = require('firebase-functions');
const iap = require('in-app-purchase');

// Initialize the IAP library ONCE (not every request)
iap.config({ 
    applePassword: 'my shared key', 
    test: true // Keep this true for TestFlight; set to false for production
});

// Run setup once when the function cold-starts
iap.setup((error) => {
    if (error) {
        console.error("IAP setup failed:", error);
    } else {
        console.log("IAP library initialized successfully");
    }
});

exports.receiptValidation = functions.https.onRequest((request, response) => {
    // Handle CORS (required for iOS app requests)
    response.set('Access-Control-Allow-Origin', '*');
    if (request.method === 'OPTIONS') {
        // Respond to preflight OPTIONS requests
        response.set('Access-Control-Allow-Methods', 'POST');
        response.set('Access-Control-Allow-Headers', 'Content-Type');
        response.status(204).send('');
        return;
    }

    // Get the receipt data from the form body
    const receipt = request.body.receipt;
    if (!receipt) {
        console.error("No receipt data received");
        response.status(400).send("Missing receipt data");
        return;
    }

    console.log("Received receipt (truncated):", receipt.substring(0, 50) + "...");

    // Validate the receipt with the IAP library
    iap.validate(iap.APPLE, receipt, (error, appleResponse) => {
        if (error) {
            console.error("Validation error:", error);
            response.status(500).send("Validation failed: " + error.message);
            return;
        }

        if (iap.isValidated(appleResponse)) {
            console.log("Validation successful! Response:", JSON.stringify(appleResponse, null, 2));
            response.status(200).json(appleResponse);
        } else {
            console.log("Receipt is invalid");
            response.status(400).send("Receipt is not valid");
        }
    });
});

Key Fixes Explained

  • Swift: We now send the receipt as receipt=your_base64_string (the standard for form data) and URL-encode the string to avoid characters breaking the request.
  • Node.js: We initialize the IAP library once (not per request) for better performance, directly read the receipt from request.body.receipt (since the form parser now correctly parses the data), and add CORS handling to let your iOS app talk to the cloud function.

内容的提问来源于stack exchange,提问作者Michael Ros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:38:20