feat(auth): migrate WhatsApp OTP to otp.wibudev.com with API Key authentication

- Create new wa-service.ts helper library
  - sendWhatsAppOtp(): Send OTP via otp.wibudev.com with Bearer token auth
  - sendWhatsAppOtpLegacy(): Deprecated legacy function for backward compat
  - Proper error handling and response validation

- Update all auth routes to use new WA service:
  - login/route.ts: Use sendWhatsAppOtp for login OTP
  - register/route.ts: Use sendWhatsAppOtp for registration OTP
  - resend/route.ts: Use sendWhatsAppOtp for resend OTP
  - send-otp-register/route.ts: Use sendWhatsAppOtp for registration

- Add environment variables to .env.local:
  - WIBU_WA_API_KEY: JWT token for authentication
  - WIBU_WA_API_URL: https://otp.wibudev.com

Benefits:
✓ Secure authentication with JWT API Key
✓ Centralized WA service for all OTP sending
✓ Better error handling and logging
✓ Consistent API response format
✓ Easy to maintain and extend

API Key Info:
- Name: website-desa-darmasaba
- Description: untuk website desa darmasaba
- Expiration: Feb 12, 2116
- Issued: Mar 05, 2026

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-03-05 12:07:58 +08:00
parent 144ac37e12
commit 781d125d4c
5 changed files with 170 additions and 40 deletions

View File

@@ -3,6 +3,7 @@ import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
import { randomOTP } from "../_lib/randomOTP";
import { cookies } from "next/headers";
import { sendWhatsAppOtp } from "@/lib/wa-service";
export async function POST(req: Request) {
if (req.method !== "POST") {
@@ -34,31 +35,22 @@ export async function POST(req: Request) {
const otpNumber = Number(codeOtp);
const waMessage = `Website Desa Darmasaba - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun Admin lainnya.\n\n>> Kode OTP anda: ${codeOtp}.`;
const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(waMessage)}`;
console.log("🔍 Debug WA URL:", waUrl);
try {
const res = await fetch(waUrl);
const sendWa = await res.json();
console.log("📱 WA Response:", sendWa);
if (sendWa.status !== "success") {
console.error("❌ WA Service Error:", sendWa);
return NextResponse.json(
{
success: false,
message: "Gagal mengirim OTP via WhatsApp",
debug: sendWa
},
{ status: 400 }
);
}
} catch (waError) {
console.error("❌ Fetch WA Error:", waError);
// Send OTP via WhatsApp using authenticated API
const waResult = await sendWhatsAppOtp({
nomor,
message: waMessage,
});
if (!waResult.success) {
console.error("❌ WA Service Error:", waResult);
return NextResponse.json(
{ success: false, message: "Terjadi kesalahan saat mengirim WA" },
{ status: 500 }
{
success: false,
message: waResult.message || "Gagal mengirim OTP via WhatsApp",
debug: waResult.data
},
{ status: 400 }
);
}

View File

@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import prisma from '@/lib/prisma';
import { randomOTP } from '../_lib/randomOTP';
import { sendWhatsAppOtp } from '@/lib/wa-service';
export async function POST(req: Request) {
try {
@@ -24,12 +25,18 @@ export async function POST(req: Request) {
const otpNumber = Number(codeOtp);
const waMessage = `Website Desa Darmasaba - Kode verifikasi Anda: ${codeOtp}`;
const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(waMessage)}`;
const waRes = await fetch(waUrl);
const waData = await waRes.json();
if (waData.status !== "success") {
return NextResponse.json({ success: false, message: 'Gagal mengirim OTP via WhatsApp' }, { status: 400 });
// Send OTP via WhatsApp using authenticated API
const waResult = await sendWhatsAppOtp({
nomor,
message: waMessage,
});
if (!waResult.success) {
return NextResponse.json(
{ success: false, message: waResult.message || 'Gagal mengirim OTP via WhatsApp', debug: waResult.data },
{ status: 400 }
);
}
// ✅ Simpan OTP ke database

View File

@@ -3,6 +3,7 @@ import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
import { randomOTP } from "../_lib/randomOTP";
import { sendWhatsAppOtp } from "@/lib/wa-service";
export async function POST(req: Request) {
try {
@@ -18,15 +19,17 @@ export async function POST(req: Request) {
const codeOtp = randomOTP();
const otpNumber = Number(codeOtp);
// Kirim OTP via WhatsApp
// Kirim OTP via WhatsApp menggunakan API terautentikasi
const waMessage = `Website Desa Darmasaba - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun Admin lainnya.\n\n>> Kode OTP anda: ${codeOtp}.`;
const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(waMessage)}`;
const waRes = await fetch(waUrl);
const waData = await waRes.json();
if (waData.status !== "success") {
const waResult = await sendWhatsAppOtp({
nomor,
message: waMessage,
});
if (!waResult.success) {
return NextResponse.json(
{ success: false, message: "Gagal mengirim OTP via WhatsApp" },
{ success: false, message: waResult.message || "Gagal mengirim OTP via WhatsApp", debug: waResult.data },
{ status: 400 }
);
}

View File

@@ -1,6 +1,7 @@
import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
import { randomOTP } from "../_lib/randomOTP";
import { sendWhatsAppOtp } from "@/lib/wa-service";
export async function POST(req: Request) {
try {
@@ -22,13 +23,18 @@ export async function POST(req: Request) {
const codeOtp = randomOTP();
const otpNumber = Number(codeOtp);
// Kirim WA
const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=Website Desa Darmasaba - Kode verifikasi Anda: ${codeOtp}`;
const res = await fetch(waUrl);
const sendWa = await res.json();
// Kirim WA menggunakan API terautentikasi
const waMessage = `Website Desa Darmasaba - Kode verifikasi Anda: ${codeOtp}`;
const waResult = await sendWhatsAppOtp({
nomor,
message: waMessage,
});
if (sendWa.status !== "success") {
return NextResponse.json({ success: false, message: 'Gagal mengirim OTP' }, { status: 400 });
if (!waResult.success) {
return NextResponse.json(
{ success: false, message: waResult.message || 'Gagal mengirim OTP', debug: waResult.data },
{ status: 400 }
);
}
// Simpan OTP

122
src/lib/wa-service.ts Normal file
View File

@@ -0,0 +1,122 @@
/**
* WhatsApp Service - Send OTP via WhatsApp using otp.wibudev.com API
* Uses API Key authentication for secure access
*/
interface SendWaOtpParams {
nomor: string;
message: string;
}
interface SendWaOtpResponse {
success: boolean;
message?: string;
data?: any;
}
/**
* Send WhatsApp message using otp.wibudev.com API with authentication
* @param params - { nomor: string, message: string }
* @returns Promise<SendWaOtpResponse>
*/
export async function sendWhatsAppOtp({
nomor,
message,
}: SendWaOtpParams): Promise<SendWaOtpResponse> {
const apiKey = process.env.WIBU_WA_API_KEY;
const waApiUrl = process.env.WIBU_WA_API_URL || 'https://otp.wibudev.com';
if (!apiKey) {
console.error('❌ WIBU_WA_API_KEY is not configured');
return {
success: false,
message: 'WhatsApp API key not configured',
};
}
try {
// Using the new API endpoint with authentication
// Format: POST https://otp.wibudev.com/code?nom=...&text=...
const url = `${waApiUrl}/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(message)}`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'X-API-Key': apiKey,
},
});
const result = await response.json();
if (!response.ok) {
console.error('❌ WA API Error:', result);
return {
success: false,
message: result.message || 'Failed to send WhatsApp message',
data: result,
};
}
// Check if response has success status
if (result.status !== 'success') {
console.error('❌ WA API Status Error:', result);
return {
success: false,
message: result.message || 'Failed to send WhatsApp message',
data: result,
};
}
console.log('✅ WA Response:', result);
return {
success: true,
message: 'WhatsApp sent successfully',
data: result,
};
} catch (error) {
console.error('❌ Fetch WA API Error:', error);
return {
success: false,
message: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
/**
* Legacy function for backward compatibility (deprecated)
* @deprecated Use sendWhatsAppOtp instead
*/
export async function sendWhatsAppOtpLegacy({
nomor,
message,
}: SendWaOtpParams): Promise<SendWaOtpResponse> {
const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(message)}`;
try {
const res = await fetch(waUrl);
const result = await res.json();
if (result.status !== 'success') {
console.error('❌ WA Legacy Error:', result);
return {
success: false,
message: result.message || 'Failed to send WhatsApp message',
data: result,
};
}
return {
success: true,
message: 'WhatsApp sent successfully',
data: result,
};
} catch (error) {
console.error('❌ Fetch WA Legacy Error:', error);
return {
success: false,
message: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}