validate(['signedPayload' => 'required|string|max:60000']); $event = $verifier->envelope($v['signedPayload']); abort_unless(isset($event['notificationUUID'],$event['notificationType']) && ($event['data']['bundleId'] ?? '') === config('tarnax.apple.bundle_id') && ($event['data']['environment'] ?? '') === config('tarnax.apple.environment'), 422); DB::transaction(function () use ($event, $verifier) { if (! DB::table('webhook_events')->insertOrIgnore(['id' => 'apple:'.$event['notificationUUID'], 'type' => $event['notificationType'], 'processed_at' => now()])) { return; } if (empty($event['data']['signedTransactionInfo'])) { return; } $claims = $verifier->transaction($event['data']['signedTransactionInfo']); $status = isset($claims['revocationDate']) ? 'revoked' : ((($claims['expiresDate'] ?? PHP_INT_MAX) < time() * 1000) ? 'expired' : 'verified'); DB::table('purchases')->where('provider', 'apple')->where('transaction_id', 'apple:'.$claims['transactionId'])->where(function ($q) use ($claims) { $q->whereNull('provider_updated_at')->orWhere('provider_updated_at', '<', Carbon::createFromTimestampMs($claims['signedDate'])); })->update(['status' => $status, 'provider_updated_at' => Carbon::createFromTimestampMs($claims['signedDate']), 'updated_at' => now()]); }); return response()->json(['received' => true]); } }