I am implementing Apple In-App Buy server-to-server notifications in Laravel. I’ve arrange the mandatory credentials (issuer_id
, key_id
, and p8 personal key) from App Retailer Join and configured them in my utility. Nevertheless, I am unable to decode the signedPayload acquired from Apple’s notifications.
Right here’s my implementation:
class ServerNotificationAppleController extends Controller
{
personal $storeKitKeysUrl="https://appleid.apple.com/auth/keys";
public perform handleNotification(Request $request)
{
Log::information('Apple Notification Request:', $request->all());
$signedPayload = $request->enter('signedPayload');
if (!$signedPayload) {
return response()->json(['error' => 'signedPayload not provided'], 400);
}
$jwtToken = $this->generateAppleJWT();
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $jwtToken,
])->get($this->storeKitKeysUrl);
Log::information('Apple Keys Standing:', ['status' => $response->status()]);
Log::information('Apple Keys Physique:', ['body' => $response->body()]);
if ($response->standing() !== 200) {
return response()->json(['error' => "Apple public keys couldn't be retrieved"], 401);
}
$keysData = $response->json();
$validatedPayload = $this->validateSignedPayload($signedPayload, $keysData);
if (!$validatedPayload) {
return response()->json(['error' => 'Invalid signedPayload'], 400);
}
Log::information("Apple Buy Knowledge:", (array)$validatedPayload);
return response()->json(['message' => 'Notification processed successfully'], 200);
}
personal perform generateAppleJWT()
{
$keyId = config('providers.apple.key_id');
$issuerId = config('providers.apple.issuer_id');
$privateKey = file_get_contents(storage_path(config('providers.apple.private_key')));
$nowUtc = Carbon::now();
$expirationUtc = $nowUtc->copy()->addMinutes(20);
$payload = [
'iss' => $issuerId,
'iat' => $nowUtc->timestamp,
'exp' => $expirationUtc->timestamp,
'aud' => 'appstoreconnect-v1',
];
$header = [
'kid' => $keyId,
'alg' => 'ES256',
'typ' => 'JWT'
];
return JWT::encode($payload, $privateKey, 'ES256', $keyId, $header);
}
personal perform validateSignedPayload($signedPayload, $keysData)
{
attempt {
$jwkKeys = JWK::parseKeySet($keysData);
$allowedAlgs = new stdClass();
$allowedAlgs->algos = ['ES256']; // Utilizing ES256
return JWT::decode($signedPayload, $jwkKeys, $allowedAlgs);
} catch (Exception $e) {
Log::error("Apple Buy Validation Error: " . $e->getMessage() . " Hint: " . $e->getTraceAsString());
return null;
}
}
}
The issue:
signedPayload
is acquired accurately.
Fetching Apple’s public keys works nice (standing 200).
validateSignedPayload()
fails to decode the payload and logs an error.
Questions:
- Am I accurately fetching and utilizing Apple’s public keys for validation?
- Do I must extract a particular a part of signedPayload earlier than decoding?
- Might the difficulty be associated to how I am parsing the JWK keys?
Any insights or corrections could be appreciated!