From ca8a8c6c155560872df865c1b07d186d0b53611f Mon Sep 17 00:00:00 2001 From: rimasazkya14 Date: Wed, 6 May 2026 15:37:18 +0700 Subject: [PATCH] update coba resend Co-authored-by: Copilot --- .env.example | 19 +++-- app/Http/Controllers/OtpController.php | 4 +- app/Mail/OtpMail.php | 1 - app/Mail/Transport/ResendCurlTransport.php | 94 ++++++++++++++++++++++ app/Providers/AppServiceProvider.php | 11 ++- app/Services/ResendEmailService.php | 67 +++++++++++++++ composer.json | 3 +- composer.lock | 61 +++++++++++++- config/mail.php | 6 +- 9 files changed, 249 insertions(+), 17 deletions(-) create mode 100644 app/Mail/Transport/ResendCurlTransport.php create mode 100644 app/Services/ResendEmailService.php diff --git a/.env.example b/.env.example index b918c3f..a442afd 100644 --- a/.env.example +++ b/.env.example @@ -47,15 +47,18 @@ REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 -MAIL_MAILER=smtp -MAIL_HOST=smtp.gmail.com -MAIL_PORT=587 -MAIL_ENCRYPTION=tls -MAIL_USERNAME=rimasazkya@gmail.com -MAIL_PASSWORD="uamw iegn gjtg dkuw" -MAIL_FROM_ADDRESS=SiJentik@noreply.com +# MAIL_MAILER=smtp +# MAIL_HOST=smtp.gmail.com +# MAIL_PORT=587 +# MAIL_ENCRYPTION=tls +# MAIL_USERNAME=rimasazkya@gmail.com +# MAIL_PASSWORD="uamw iegn gjtg dkuw" +# MAIL_FROM_ADDRESS=SiJentik@noreply.com # MAIL_FROM_NAME="${APP_NAME}" -MAIL_FROM_NAME="SiJentik" +# MAIL_FROM_NAME="SiJentik" +RESEND_API_KEY=re_TSzu3Bt8_F8akQaw1DeNWvnbvUpJs2XLk +MAIL_MAILER=resend_curl + AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= diff --git a/app/Http/Controllers/OtpController.php b/app/Http/Controllers/OtpController.php index fd9be3a..9c5f688 100644 --- a/app/Http/Controllers/OtpController.php +++ b/app/Http/Controllers/OtpController.php @@ -6,7 +6,6 @@ use Illuminate\Http\Request; use App\Mail\OtpMail; use Illuminate\Support\Facades\Mail; -use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Log; class OtpController extends Controller @@ -33,7 +32,6 @@ public function sendOtp(Request $request) Log::info("OTP DB: $otp"); try { - set_time_limit(120); Mail::to($user->email)->send(new OtpMail($otp)); } catch (\Exception $e) { Log::error($e->getMessage()); @@ -79,4 +77,4 @@ public function verifyOtp(Request $request) return response()->json(['message' => 'OTP valid'], 200); } -} +} \ No newline at end of file diff --git a/app/Mail/OtpMail.php b/app/Mail/OtpMail.php index de9a619..5f68b17 100644 --- a/app/Mail/OtpMail.php +++ b/app/Mail/OtpMail.php @@ -30,7 +30,6 @@ public function __construct($otp) public function build() { return $this - ->from(env('MAIL_FROM_ADDRESS', 'rifaulardiyanto@gmail.com')) ->subject('Kode OTP Anda') ->view('emails.otp') ->with(['otp' => $this->otp]); diff --git a/app/Mail/Transport/ResendCurlTransport.php b/app/Mail/Transport/ResendCurlTransport.php new file mode 100644 index 0000000..32fd2b2 --- /dev/null +++ b/app/Mail/Transport/ResendCurlTransport.php @@ -0,0 +1,94 @@ +getOriginalMessage(); + + if (! $originalMessage instanceof Email) { + throw new TransportException('Resend transport hanya mendukung Symfony Email.'); + } + + $fromAddresses = $originalMessage->getFrom(); + $from = $fromAddresses !== [] + ? $this->stringifyAddresses([$fromAddresses[0]])[0] + : $this->formatFrom($this->defaultFromAddress, $this->defaultFromName); + + $to = $this->stringifyAddresses($originalMessage->getTo()); + + if ($to === []) { + throw new TransportException('Penerima email kosong.'); + } + + $payload = [ + 'from' => $from, + 'to' => count($to) === 1 ? $to[0] : $to, + 'subject' => $originalMessage->getSubject() ?? '', + ]; + + $htmlBody = $this->normalizeBody($originalMessage->getHtmlBody()); + $textBody = $this->normalizeBody($originalMessage->getTextBody()); + + if ($htmlBody !== null) { + $payload['html'] = $htmlBody; + } + + if ($textBody !== null) { + $payload['text'] = $textBody; + } + + $response = $this->service->send($payload); + + if (isset($response['id']) && is_string($response['id'])) { + $message->setMessageId($response['id']); + } + + $message->appendDebug(json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: ''); + } + + private function formatFrom(string $address, ?string $name): string + { + return $name ? sprintf('%s <%s>', $name, $address) : $address; + } + + private function normalizeBody(mixed $body): ?string + { + if ($body === null) { + return null; + } + + if (is_resource($body)) { + $body = stream_get_contents($body); + } + + if (! is_string($body)) { + return null; + } + + $body = trim($body); + + return $body === '' ? null : $body; + } +} \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..e93a6cb 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,9 @@ namespace App\Providers; +use App\Mail\Transport\ResendCurlTransport; +use App\Services\ResendEmailService; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -19,6 +22,12 @@ public function register(): void */ public function boot(): void { - // + Mail::extend('resend_curl', function (array $config = []) { + return new ResendCurlTransport( + app(ResendEmailService::class), + $config['from']['address'] ?? config('mail.from.address'), + $config['from']['name'] ?? config('mail.from.name') + ); + }); } } diff --git a/app/Services/ResendEmailService.php b/app/Services/ResendEmailService.php new file mode 100644 index 0000000..914275e --- /dev/null +++ b/app/Services/ResendEmailService.php @@ -0,0 +1,67 @@ +endpoint); + + if ($ch === false) { + throw new RuntimeException('Gagal inisialisasi cURL.'); + } + + curl_setopt_array($ch, [ + CURLOPT_POST => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => [ + 'Authorization: Bearer '.$apiKey, + 'Content-Type: application/json', + ], + CURLOPT_POSTFIELDS => $body, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_TIMEOUT => 30, + ]); + + $response = curl_exec($ch); + $statusCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if ($response === false) { + $error = curl_error($ch); + curl_close($ch); + + throw new RuntimeException('Resend request gagal: '.$error); + } + + curl_close($ch); + + $decoded = json_decode($response, true); + + if ($statusCode < 200 || $statusCode >= 300) { + $message = is_array($decoded) && isset($decoded['message']) + ? $decoded['message'] + : $response; + + throw new RuntimeException('Resend API error: '.$message); + } + + return is_array($decoded) ? $decoded : ['raw' => $response]; + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 6875678..8b0c3d6 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "php": "^8.2", "laravel/framework": "^12.0", "laravel/sanctum": "^4.3", - "laravel/tinker": "^2.10.1" + "laravel/tinker": "^2.10.1", + "resend/resend-php": "^1.3" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index 2b984f3..eec9f1f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ad91ab9bde70e3576f2b64dd11542bc4", + "content-hash": "94cb2ce06c9e2860c6251ecd8f0072c3", "packages": [ { "name": "brick/math", @@ -3357,6 +3357,63 @@ }, "time": "2025-12-14T04:43:48+00:00" }, + { + "name": "resend/resend-php", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/resend/resend-php.git", + "reference": "87d29d98271a0ab1c09cdbee102daa2f9b3419db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resend/resend-php/zipball/87d29d98271a0ab1c09cdbee102daa2f9b3419db", + "reference": "87d29d98271a0ab1c09cdbee102daa2f9b3419db", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.5", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.13", + "mockery/mockery": "^1.6", + "pestphp/pest": "^1.0|^2.0|^3.0|^4.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resend.php" + ], + "psr-4": { + "Resend\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Resend and contributors", + "homepage": "https://github.com/resend/resend-php/contributors" + } + ], + "description": "Resend PHP library.", + "homepage": "https://resend.com/", + "keywords": [ + "api", + "client", + "php", + "resend", + "sdk" + ], + "support": { + "issues": "https://github.com/resend/resend-php/issues", + "source": "https://github.com/resend/resend-php/tree/v1.3.0" + }, + "time": "2026-04-11T10:48:32+00:00" + }, { "name": "symfony/clock", "version": "v7.4.0", @@ -8456,5 +8513,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/mail.php b/config/mail.php index 522b284..e944f98 100644 --- a/config/mail.php +++ b/config/mail.php @@ -14,7 +14,7 @@ | */ - 'default' => env('MAIL_MAILER', 'log'), + 'default' => env('MAIL_MAILER', 'resend_curl'), /* |-------------------------------------------------------------------------- @@ -65,6 +65,10 @@ 'transport' => 'resend', ], + 'resend_curl' => [ + 'transport' => 'resend_curl', + ], + 'sendmail' => [ 'transport' => 'sendmail', 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),