I’m utilizing the next code to implement apple server notifications, nevertheless it’s giving me an error “Invalid JWT Header”. I’m utilizing storekit2 to buy subscriptions within the app and I am utilizing a node js backend to maintain observe of subscription updates. Can somebody assist?
Once I debug additional I get JWT verification failed: Error: Invalid JWT header: Lacking child
I believe the difficulty is that apple has not enabled V2 and continues to be sending V1 payload as a result of I do not see signedPayload within the response from apple. How can I get it to ship V2?
const APPLE_JWKS_URL = "https://appleid.apple.com/auth/keys";
const getApplePublicKey = async (child) => {
strive {
const response = await axios.get(APPLE_JWKS_URL);
const keys = response.information.keys;
const key = keys.discover((ok) => ok.child === child);
if (!key) {
throw new Error("Matching key not discovered");
}
return jwksClient.certToPEM({
kty: key.kty,
n: key.n,
e: key.e,
});
} catch (error) {
console.error("Didn't fetch Apple's public key:", error);
throw new Error("Couldn't get Apple public key");
}
};
// Perform to confirm Apple's JWT signature
const verifyAppleJWT = async (token) => {
strive {
const decodedHeader = jwt.decode(token, { full: true });
if (!decodedHeader || !decodedHeader.header.child) {
throw new Error("Invalid JWT header");
}
const publicKey = await getApplePublicKey(decodedHeader.header.child);
return jwt.confirm(token, publicKey, { algorithms: ["RS256"] });
} catch (error) {
console.error("JWT verification failed:", error);
return null;
}
};
// Apple Server Notification Endpoint
router.publish("/apple-server-notifications", async (req, res) => {
strive {
const { signedPayload } = req.physique;
if (!signedPayload) {
return res.standing(400).json({ error: "Lacking signedPayload" });
}
// Confirm the JWT signature with Apple's public key
const payload = await verifyAppleJWT(signedPayload);
if (!payload) {
return res.standing(400).json({ error: "Invalid notification" });
}
console.log("Verified Notification:", JSON.stringify(payload, null, 2));
// Extract related information
const eventType = payload.notificationType;
const originalTransactionId = payload.information.originalTransactionId;
// Deal with subscription occasions
change (eventType) {
case "SUBSCRIBED":
case "DID_RENEW":
await updateSubscription(originalTransactionId, "lively");
break;
case "EXPIRED":
await updateSubscription(originalTransactionId, "expired");
break;
case "CANCEL":
await updateSubscription(originalTransactionId, "canceled");
break;
case "DID_REVOKE":
await updateSubscription(originalTransactionId, "revoked");
break;
default:
console.log(`Unhandled occasion kind: ${eventType}`);
}
res.standing(200).json({ message: "Notification processed" });
} catch (error) {
console.error("Error dealing with notification:", error);
res.standing(500).json({ error: "Inside Server Error" });
}
});