update coba resend

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
rimasazkya14 2026-05-06 15:37:18 +07:00
parent 16ca1eead7
commit ca8a8c6c15
9 changed files with 249 additions and 17 deletions

View File

@ -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=

View File

@ -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);
}
}
}

View File

@ -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]);

View File

@ -0,0 +1,94 @@
<?php
namespace App\Mail\Transport;
use App\Services\ResendEmailService;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mime\Email;
class ResendCurlTransport extends AbstractTransport
{
public function __construct(
private readonly ResendEmailService $service,
private readonly string $defaultFromAddress,
private readonly ?string $defaultFromName = null,
) {
parent::__construct();
}
public function __toString(): string
{
return 'resend_curl';
}
protected function doSend(SentMessage $message): void
{
$originalMessage = $message->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;
}
}

View File

@ -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')
);
});
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Services;
use RuntimeException;
class ResendEmailService
{
private string $endpoint = 'https://api.resend.com/emails';
public function send(array $payload): array
{
$apiKey = config('services.resend.key');
if (! $apiKey) {
throw new RuntimeException('RESEND_API_KEY belum diatur.');
}
$body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($body === false) {
throw new RuntimeException('Gagal menyiapkan payload email untuk Resend.');
}
$ch = curl_init($this->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];
}
}

View File

@ -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",

61
composer.lock generated
View File

@ -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"
}

View File

@ -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'),