isSandbox = PAYPAL_SANDBOX; $this->clientId = PAYPAL_CLIENT_ID; $this->secret = PAYPAL_CLIENT_SECRET; $this->apiBase = PAYPAL_BASE_URL; $this->webhookId = PAYPAL_WEBHOOK_ID; } /** * Vérifie la signature de l’événement envoyé par PayPal. */ public function verifySignature(array $headers, string $body): bool { $token = $this->getAccessToken(); if (!$token) return false; $payload = [ 'auth_algo' => $headers['paypal-auth-algo'] ?? '', 'cert_url' => $headers['paypal-cert-url'] ?? '', 'transmission_id' => $headers['paypal-transmission-id'] ?? '', 'transmission_sig' => $headers['paypal-transmission-sig'] ?? '', 'transmission_time' => $headers['paypal-transmission-time'] ?? '', 'webhook_id' => $this->webhookId, 'webhook_event' => json_decode($body, true), ]; $ch = curl_init($this->apiBase.'/v1/notifications/verify-webhook-signature'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => [ 'Authorization: Bearer '.$token, 'Content-Type: application/json' ], CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $res = curl_exec($ch); $http= curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http !== 200) return false; $json = json_decode($res, true); return ($json['verification_status'] ?? '') === 'SUCCESS'; } private function getAccessToken(): ?string { $ch = curl_init($this->apiBase.'/v1/oauth2/token'); curl_setopt_array($ch, [ CURLOPT_USERPWD => $this->clientId.':'.$this->secret, CURLOPT_POST => true, CURLOPT_POSTFIELDS => 'grant_type=client_credentials', CURLOPT_RETURNTRANSFER => true, ]); $res = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http !== 200) return null; $json = json_decode($res, true); return $json['access_token'] ?? null; } /** * Point d’entrée du webhook */ public function handleEvent(string $body, array $headers) { // 1️⃣ Vérification signature if (!$this->verifySignature($headers, $body)) { http_response_code(400); echo json_encode(['error' => 'Signature invalide']); return; } // 2️⃣ Décodage événement $event = json_decode($body, true); $type = $event['event_type'] ?? ''; $resource = $event['resource'] ?? []; if (in_array($type, ['PAYMENT.CAPTURE.REFUNDED','PAYMENT.REFUND.COMPLETED'])) { $captureId = null; foreach ((array)($resource['links'] ?? []) as $lnk) { if (($lnk['rel'] ?? '') === 'up' && strpos($lnk['href'], '/captures/') !== false) { $captureId = basename($lnk['href']); } } if ($captureId) { // 3️⃣ Charger les détails avec ta classe PayPalCheckout $checkout = new PayPalCheckout(); $capture = $checkout->getCaptureDetails($captureId); $refunds = $checkout->getRefundsForCapture($capture); // 4️⃣ Enregistrer ou mettre à jour la DB file_put_contents(__DIR__.'/paypal_refund_log.txt', print_r($refunds,true), FILE_APPEND); } } http_response_code(200); echo json_encode(['status'=>'ok']); } }