Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

App Attest receipts response 400

I tried to send it on the nodejs server I built. No success received 200

My work steps are as follows:

  1. The app executes “DCAppAttestService.shared.attestKey” to get receiptData from the acquired attestation.
  2. The app sends "receiptData.base64EncodedString()" to the server (code-1)
  3. Nodejs code (code-2)
  4. Because the app has been uploaded to TestFlight, I set the server IP to "data.appattest.apple.com"

Is there something wrong with my steps?

code-1

public func attestData(receipt:Data) {
        if DCDevice.current.isSupported {
            let sesh = URLSession(configuration: .default)
            var req = URLRequest(url: URL(string: "http://10.254.239.27:3000/attestationData")!)
            
            print(req)
            
            req.addValue("application/json", forHTTPHeaderField: "Content-Type")
            req.httpMethod = "POST"

            let data = try! JSONSerialization.data(withJSONObject: ["receipt": receipt.base64EncodedString()], options: [])

            req.httpBody = data
            let task = sesh.dataTask(with: req, completionHandler: { (data, response, error) in
                if let data = data, let jsonString = String(data: data, encoding: .utf8) {
                    print(jsonString)
                }
            })
            task.resume()
        } else {
            print("Platform is not supported. Make sure you aren't running in an emulator.")
            //self.stopActivity()
        }
    }

code-2

versionRouter.post('/attestationData', function(req, response) {
  console.log("\n\n\n\n\n");
  console.log("receiptApi");
  
  var receiptBase64 = req.body.receipt;

  if (!receiptBase64) {
      return response.status(400).send({ error: 'Missing receipt data' });
  }

  let binaryReceipt;

  if (typeof receiptBase64 === 'string') {
      const cleaned = receiptBase64.trim();
      binaryReceipt = Buffer.from(cleaned, 'base64');  
  } 

  if (Buffer.isBuffer(binaryReceipt)) {
      //binaryReceipt = receiptBase64;  
      console.log("receipt is base64 或 Buffer: "+ Buffer.isBuffer(binaryReceipt));
  } else {
      console.error('⚠️ receipt is not base64 or Buffer');
      response.status(400).send("Receipt format error");
      return;
  }
  
  var jwToken = jwt.sign({}, cert, { algorithm: 'ES256',header: { typ: undefined }, issuer: teamId, keyid: keyId});

  var post_options = {
      host: 'data.appattest.apple.com', 
      port: '443',
      path: '/v1/attestationData',
      method: 'POST',
      headers: {
          'Authorization': jwToken,
          'Content-Type': 'application/octet-stream',
          'Content-Length': binaryReceipt.length
      }
  };
  
  var post_req = https.request(post_options, function(res) {
      res.setEncoding('utf8');

      console.log("📨 Apple Response Header:", res.headers);
      console.log("📨 Apple StatusCode:", res.statusCode);

      var data = "";
      res.on('data', function (chunk) {
        data += chunk;
      });
      res.on('end', function() {
        console.log(data);
        response.send({"status": res.statusCode, data: data});
      });

  });
  
  post_req.on('error', function(e) {
     console.error('error:', e);
     response.status(500).send({ error: e.message });
  });

  post_req.write(binaryReceipt);
  post_req.end();
});

Have you solved the problem? I also encountered accessing the apple server interface, and the http pStatusCode has always been 400.

App Attest receipts response 400
 
 
Q