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
- Your Swift request is sending data in the wrong format: You're using
application/x-www-form-urlencodedbut 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. - 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
相关产品推荐
相关产品推荐

