secretKey)) { return InitiateResult::failure('paymongo_not_configured'); } try { $response = Http::withBasicAuth($this->secretKey, '') ->timeout(15) ->post($this->endpoint.'/checkout_sessions', [ 'data' => [ 'attributes' => [ 'line_items' => [[ 'name' => $payment->purpose, 'amount' => $payment->amount_centavos, 'currency' => $payment->currency, 'quantity' => 1, ]], 'payment_method_types' => ['gcash', 'paymaya', 'card'], 'success_url' => $this->successUrl.'?p='.$payment->uuid, 'cancel_url' => $this->cancelUrl.'?p='.$payment->uuid, 'metadata' => ['payment_uuid' => $payment->uuid], ], ], ]); } catch (ConnectionException $e) { Log::warning('PayMongo connection failed', ['error' => $e->getMessage()]); return InitiateResult::failure('connection_failed'); } if (! $response->successful()) { Log::warning('PayMongo error', ['status' => $response->status(), 'body' => $response->body()]); return InitiateResult::failure('provider_error_'.$response->status()); } $data = $response->json('data') ?? []; $sessionId = $data['id'] ?? null; $checkoutUrl = $data['attributes']['checkout_url'] ?? null; $payment->forceFill([ 'provider' => Payment::PROVIDER_PAYMONGO, 'provider_payment_id' => $sessionId, 'provider_data' => $data, 'status' => Payment::STATUS_PROCESSING, ])->save(); return InitiateResult::success($checkoutUrl, $sessionId); } public function verifyWebhook(string $rawBody, string $signature): bool { if (empty($this->webhookSecret)) { return false; } // PayMongo webhook header format: t=timestamp,te=signature,li=... $parts = []; foreach (explode(',', $signature) as $kv) { [$k, $v] = array_pad(explode('=', $kv, 2), 2, ''); $parts[$k] = $v; } $timestamp = $parts['t'] ?? ''; $signed = $parts['te'] ?? ''; $expected = hash_hmac('sha256', "{$timestamp}.{$rawBody}", $this->webhookSecret); return hash_equals($expected, $signed); } public function applyWebhook(array $payload): ?Payment { $eventType = $payload['data']['attributes']['type'] ?? null; $paymentUuid = $payload['data']['attributes']['data']['attributes']['metadata']['payment_uuid'] ?? null; if (! $paymentUuid) return null; $payment = Payment::where('uuid', $paymentUuid)->first(); if (! $payment) return null; if (in_array($eventType, ['checkout_session.payment.paid', 'payment.paid'], true)) { $payment->forceFill([ 'status' => Payment::STATUS_PAID, 'paid_at' => now(), 'provider_data' => $payload, ])->save(); } elseif (in_array($eventType, ['payment.failed'], true)) { $payment->forceFill([ 'status' => Payment::STATUS_FAILED, 'provider_data' => $payload, ])->save(); } return $payment->fresh(); } }