DONE: update .env for production
This commit is contained in:
+10
-171
@@ -87,182 +87,21 @@ DB_DATABASE_11=arrahn_pputeh
|
|||||||
DB_USERNAME_11=spider
|
DB_USERNAME_11=spider
|
||||||
DB_PASSWORD_11=G0dz!ll42020
|
DB_PASSWORD_11=G0dz!ll42020
|
||||||
|
|
||||||
DB_CONNECTION_12=mysql
|
|
||||||
DB_HOST_12=127.0.0.1
|
|
||||||
DB_PORT_12=3306
|
|
||||||
DB_DATABASE_12=arrahn_rpanjang
|
|
||||||
DB_USERNAME_12=spider
|
|
||||||
DB_PASSWORD_12=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_13=mysql
|
|
||||||
DB_HOST_13=127.0.0.1
|
|
||||||
DB_PORT_13=3306
|
|
||||||
DB_DATABASE_13=arrahn_tmerah
|
|
||||||
DB_USERNAME_13=spider
|
|
||||||
DB_PASSWORD_13=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_14=mysql
|
|
||||||
DB_HOST_14=127.0.0.1
|
|
||||||
DB_PORT_14=3306
|
|
||||||
DB_DATABASE_14=arrahn_wb
|
|
||||||
DB_USERNAME_14=spider
|
|
||||||
DB_PASSWORD_14=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_15=mysql
|
|
||||||
DB_HOST_15=127.0.0.1
|
|
||||||
DB_PORT_15=3306
|
|
||||||
DB_DATABASE_15=arrahn_wcy
|
|
||||||
DB_USERNAME_15=spider
|
|
||||||
DB_PASSWORD_15=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_16=mysql
|
|
||||||
DB_HOST_16=127.0.0.1
|
|
||||||
DB_PORT_16=3306
|
|
||||||
DB_DATABASE_16=arrahn_bentong
|
|
||||||
DB_USERNAME_16=spider
|
|
||||||
DB_PASSWORD_16=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_17=mysql
|
|
||||||
DB_HOST_17=192.168.0.102
|
|
||||||
DB_PORT_17=3306
|
|
||||||
DB_DATABASE_17=arrahn_kajang
|
|
||||||
DB_USERNAME_17=spider
|
|
||||||
DB_PASSWORD_17=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_18=mysql
|
|
||||||
DB_HOST_18=192.168.0.102
|
|
||||||
DB_PORT_18=3306
|
|
||||||
DB_DATABASE_18=arrahn_kangar
|
|
||||||
DB_USERNAME_18=spider
|
|
||||||
DB_PASSWORD_18=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_19=mysql
|
|
||||||
DB_HOST_19=192.168.0.102
|
|
||||||
DB_PORT_19=3306
|
|
||||||
DB_DATABASE_19=arrahn_kuantan
|
|
||||||
DB_USERNAME_19=spider
|
|
||||||
DB_PASSWORD_19=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_20=mysql
|
|
||||||
DB_HOST_20=192.168.0.102
|
|
||||||
DB_PORT_20=3306
|
|
||||||
DB_DATABASE_20=arrahn_pgudang
|
|
||||||
DB_USERNAME_20=spider
|
|
||||||
DB_PASSWORD_20=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_21=mysql
|
|
||||||
DB_HOST_21=192.168.0.102
|
|
||||||
DB_PORT_21=3306
|
|
||||||
DB_DATABASE_21=arrahn_raub
|
|
||||||
DB_USERNAME_21=spider
|
|
||||||
DB_PASSWORD_21=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_22=mysql
|
|
||||||
DB_HOST_22=192.168.0.102
|
|
||||||
DB_PORT_22=3306
|
|
||||||
DB_DATABASE_22=arrahn_rlaut
|
|
||||||
DB_USERNAME_22=spider
|
|
||||||
DB_PASSWORD_22=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_23=mysql
|
|
||||||
DB_HOST_23=192.168.0.102
|
|
||||||
DB_PORT_23=3306
|
|
||||||
DB_DATABASE_23=arrahn_skudai
|
|
||||||
DB_USERNAME_23=spider
|
|
||||||
DB_PASSWORD_23=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_24=mysql
|
|
||||||
DB_HOST_24=192.168.0.102
|
|
||||||
DB_PORT_24=3306
|
|
||||||
DB_DATABASE_24=arrahn_spetani
|
|
||||||
DB_USERNAME_24=spider
|
|
||||||
DB_PASSWORD_24=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_25=mysql
|
|
||||||
DB_HOST_25=192.168.0.102
|
|
||||||
DB_PORT_25=3306
|
|
||||||
DB_DATABASE_25=arrahn_temerloh
|
|
||||||
DB_USERNAME_25=spider
|
|
||||||
DB_PASSWORD_25=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_26=mysql
|
|
||||||
DB_HOST_26=192.168.0.103
|
|
||||||
DB_PORT_26=3306
|
|
||||||
DB_DATABASE_26=arrahn_api2
|
|
||||||
DB_USERNAME_26=spider
|
|
||||||
DB_PASSWORD_26=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_27=mysql
|
|
||||||
DB_HOST_27=192.168.0.103
|
|
||||||
DB_PORT_27=3306
|
|
||||||
DB_DATABASE_27=arrahn_beaufort
|
|
||||||
DB_USERNAME_27=spider
|
|
||||||
DB_PASSWORD_27=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_28=mysql
|
|
||||||
DB_HOST_28=192.168.0.103
|
|
||||||
DB_PORT_28=3306
|
|
||||||
DB_DATABASE_28=arrahn_kbelud
|
|
||||||
DB_USERNAME_28=spider
|
|
||||||
DB_PASSWORD_28=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_29=mysql
|
|
||||||
DB_HOST_29=192.168.0.103
|
|
||||||
DB_PORT_29=3306
|
|
||||||
DB_DATABASE_29=arrahn_keningau
|
|
||||||
DB_USERNAME_29=spider
|
|
||||||
DB_PASSWORD_29=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_30=mysql
|
|
||||||
DB_HOST_30=192.168.0.103
|
|
||||||
DB_PORT_30=3306
|
|
||||||
DB_DATABASE_30=arrahn_putatan
|
|
||||||
DB_USERNAME_30=spider
|
|
||||||
DB_PASSWORD_30=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_31=mysql
|
|
||||||
DB_HOST_31=192.168.0.103
|
|
||||||
DB_PORT_31=3306
|
|
||||||
DB_DATABASE_31=arrahn_sandakan
|
|
||||||
DB_USERNAME_31=spider
|
|
||||||
DB_PASSWORD_31=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_32=mysql
|
|
||||||
DB_HOST_32=192.168.0.103
|
|
||||||
DB_PORT_32=3306
|
|
||||||
DB_DATABASE_32=arrahn_tawau
|
|
||||||
DB_USERNAME_32=spider
|
|
||||||
DB_PASSWORD_32=G0dz!ll42020
|
|
||||||
|
|
||||||
DB_CONNECTION_33=mysql
|
|
||||||
DB_HOST_33=192.168.0.103
|
|
||||||
DB_PORT_33=3306
|
|
||||||
DB_DATABASE_33=arrahn_tuaran
|
|
||||||
DB_USERNAME_33=spider
|
|
||||||
DB_PASSWORD_33=G0dz!ll42020
|
|
||||||
|
|
||||||
CACHE_DRIVER=file
|
CACHE_DRIVER=file
|
||||||
QUEUE_CONNECTION=sync
|
QUEUE_CONNECTION=sync
|
||||||
|
MAX_MUTUEMAS=true
|
||||||
|
|
||||||
WABOT_API=https://md.wabot.my/api/send.php
|
TENNANT_MASTER_DB=master_erahn
|
||||||
WABOT_INSTANCES_ID=63F40BD120DB4
|
TENNANT_KAUNTER_DB=kaunter_erahn
|
||||||
# WABOT_INSTANCES_ID=64892FD7440A6
|
|
||||||
WABOT_TOKEN_KEYS=cbee7e95cf3ffbfa0531d5fddfd44f87
|
|
||||||
|
|
||||||
# Meta WhatsApp Cloud (test number). Paste token locally; do not commit it.
|
KAUNTER_URL=http://host.docker.internal:8081
|
||||||
WHATSAPP_TOKEN=
|
INTERNAL_NOTIFICATION_SECRET=
|
||||||
|
|
||||||
|
WHATSAPP_TOKEN= # Paste token from Graph API explorer.
|
||||||
WHATSAPP_PHONE_NUMBER_ID=1323449077519027
|
WHATSAPP_PHONE_NUMBER_ID=1323449077519027
|
||||||
WHATSAPP_API_VERSION=v25.0
|
WHATSAPP_API_VERSION=v25.0
|
||||||
WHATSAPP_TEMPLATE=hello_world
|
WHATSAPP_TEMPLATE=hello_world
|
||||||
WHATSAPP_TEMPLATE_LANG=en_US
|
WHATSAPP_TEMPLATE_LANG=en_US
|
||||||
|
WHATSAPP_VERIFY_TOKEN= # webhook verify token
|
||||||
ONEWAYSMS_API=http://gateway.onewaysms.com.my:10001/api.aspx
|
WHATSAPP_APP_SECRET= # X-Hub-Signature-256 checks; need to be enable first
|
||||||
ONEWAYSMS_AUTH_USER=APIUK330HDAFO
|
ADMIN_PHONE_NUMBER=60193450165
|
||||||
ONEWAYSMS_AUTH_PASS=APIUK330HDAFOUK330
|
|
||||||
|
|
||||||
ADMIN_PHONE_NUMBER=60193450165
|
|
||||||
ACTIVE_JOB_SERVER=false
|
|
||||||
|
|
||||||
# Kaunter inbox (PC modal request → operasi bell). Same secret as kaunter INTERNAL_NOTIFICATION_SECRET.
|
|
||||||
KAUNTER_URL=http://host.docker.internal:8081
|
|
||||||
INTERNAL_NOTIFICATION_SECRET=
|
|
||||||
+22
-2
@@ -59,9 +59,29 @@ DB_DATABASE_7=master_erahn
|
|||||||
DB_USERNAME_7=spider
|
DB_USERNAME_7=spider
|
||||||
DB_PASSWORD_7=arrahn@123
|
DB_PASSWORD_7=arrahn@123
|
||||||
|
|
||||||
MAX_MUTUEMAS=true
|
DB_CONNECTION_9=mysql
|
||||||
|
DB_HOST_9=192.168.1.216
|
||||||
|
DB_PORT_9=3306
|
||||||
|
DB_DATABASE_9=annur_tampoi
|
||||||
|
DB_USERNAME_9=spider
|
||||||
|
DB_PASSWORD_9=arrahn@123
|
||||||
|
|
||||||
CACHE_DRIVER=file
|
CACHE_DRIVER=file
|
||||||
QUEUE_CONNECTION=sync
|
QUEUE_CONNECTION=sync
|
||||||
|
|
||||||
TENNANT_MASTER_DB=master_erahn
|
MAX_MUTUEMAS=true
|
||||||
|
|
||||||
|
TENNANT_MASTER_DB=master_erahn
|
||||||
|
TENNANT_KAUNTER_DB=kaunter_erahn
|
||||||
|
|
||||||
|
KAUNTER_URL=https://ujrah.erahn.com.my
|
||||||
|
INTERNAL_NOTIFICATION_SECRET="nIDTe(u@6[)VLJyd"
|
||||||
|
|
||||||
|
WHATSAPP_TOKEN=EAAZAonT56AO0BSlxZAKa8QX6HIWJpp5Czsz2KQ8BYE4iyZAe5fg8ZAmaSwZCXZCPf44xazDZAhaXLHJKHKUrUZCxei86EWl6Ar3RingfybZAlXp79Yd6WwOG5IZAnu9o5mevm895ygrs39H11G01fpYU2lYRpIWdn6URkTA0kMJdqZAlTaRILTjUM5KhvV05Q4lLAZDZD
|
||||||
|
WHATSAPP_PHONE_NUMBER_ID=1323449077519027
|
||||||
|
WHATSAPP_API_VERSION=v25.0
|
||||||
|
WHATSAPP_TEMPLATE=pembiayaan_online_paid
|
||||||
|
WHATSAPP_TEMPLATE_LANG=ms
|
||||||
|
WHATSAPP_VERIFY_TOKEN=418ed86d49422071681c76bed25ae1cb7e90456c794a92f1c9ea417945a35a0a
|
||||||
|
WHATSAPP_APP_SECRET=
|
||||||
|
ADMIN_PHONE_NUMBER=60193450165
|
||||||
@@ -8,6 +8,7 @@ use App\Cawangan;
|
|||||||
use App\Mohonid;
|
use App\Mohonid;
|
||||||
use App\MohonAnggota;
|
use App\MohonAnggota;
|
||||||
use App\Customer;
|
use App\Customer;
|
||||||
|
use App\Services\StaffNotificationClient;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
||||||
@@ -444,6 +445,8 @@ public function create(REQUEST $request,$kodcaw,$norujukan,$roles)
|
|||||||
$audit->updated_at = $harini;
|
$audit->updated_at = $harini;
|
||||||
$audit->save();
|
$audit->save();
|
||||||
|
|
||||||
|
$this->notifyOperasiAssignAnggota($kodcaw, $cawangan, $kelulusan, $request);
|
||||||
|
|
||||||
// ✅ Return JSON response ke frontend
|
// ✅ Return JSON response ke frontend
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
@@ -466,6 +469,58 @@ public function create(REQUEST $request,$kodcaw,$norujukan,$roles)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inbox for all operasi staff. Failures must not block the teller request.
|
||||||
|
*
|
||||||
|
* @param string $kodcaw
|
||||||
|
* @param \App\Cawangan $cawangan
|
||||||
|
* @param \App\MohonAnggota $kelulusan
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function notifyOperasiAssignAnggota($kodcaw, $cawangan, $kelulusan, Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$nama = trim((string) ($request->nama ?? ''));
|
||||||
|
if ($nama === '' && !empty($kelulusan->nokp)) {
|
||||||
|
$customer = Customer::on('mysql7')
|
||||||
|
->where('kpbaru', $kelulusan->nokp)
|
||||||
|
->orWhere('kplama', $kelulusan->nokp)
|
||||||
|
->first();
|
||||||
|
if ($customer && !empty($customer->nama)) {
|
||||||
|
$nama = trim((string) $customer->nama);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$namaCaw = $kodcaw;
|
||||||
|
if ($cawangan) {
|
||||||
|
$resolved = trim((string) $cawangan->nama_cawangan);
|
||||||
|
if ($resolved !== '') {
|
||||||
|
$namaCaw = $resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app(StaffNotificationClient::class)->notify([
|
||||||
|
'type' => 'anggota.pending_operasi',
|
||||||
|
'cawangan' => $kodcaw,
|
||||||
|
'audience' => ['role' => 'operasi'],
|
||||||
|
'data' => [
|
||||||
|
'id' => $kelulusan->id,
|
||||||
|
'nokp' => $kelulusan->nokp,
|
||||||
|
'nama' => $nama,
|
||||||
|
'teller' => $kelulusan->teller,
|
||||||
|
'komen' => $kelulusan->komen,
|
||||||
|
'cawangan_nama' => $namaCaw,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('Assign anggota operasi notification failed: '.$e->getMessage(), [
|
||||||
|
'kodcaw' => $kodcaw,
|
||||||
|
'nokp' => isset($kelulusan->nokp) ? $kelulusan->nokp : null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//KEPOHONAN EDIT DAN HAPUS
|
//KEPOHONAN EDIT DAN HAPUS
|
||||||
|
|
||||||
public function kepohonancreate(REQUEST $request,$kodcaw,$norujukan)
|
public function kepohonancreate(REQUEST $request,$kodcaw,$norujukan)
|
||||||
@@ -600,26 +655,86 @@ public function create(REQUEST $request,$kodcaw,$norujukan,$roles)
|
|||||||
return response()->json($arraykelulusan,200);
|
return response()->json($arraykelulusan,200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listoperasikelulusan()
|
public function listoperasikelulusan(Request $request)
|
||||||
{
|
{
|
||||||
$harini = Carbon::now();
|
$dateFrom = $request->query('date_from');
|
||||||
$harini = new Carbon;
|
$dateTo = $request->query('date_to');
|
||||||
|
$search = trim((string) $request->query('search', ''));
|
||||||
|
|
||||||
|
// Default to last 30 days to avoid loading entire history
|
||||||
|
if (!$dateFrom && !$dateTo) {
|
||||||
|
$dateTo = Carbon::now()->toDateString();
|
||||||
|
$dateFrom = Carbon::now()->subDays(30)->toDateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cawangan_all = Cawangan::on('mysql7')
|
||||||
|
->select('mysql', 'details_caw', 'kodcaw')
|
||||||
|
->get();
|
||||||
|
|
||||||
$cawangan_all = Cawangan::on('mysql7')->get();
|
|
||||||
$arraykelulusan = [];
|
$arraykelulusan = [];
|
||||||
|
|
||||||
foreach($cawangan_all as $index=>$cawangan){
|
foreach ($cawangan_all as $cawangan) {
|
||||||
$kelulusan=Mohon::on($cawangan['mysql'])
|
$query = Mohon::on($cawangan['mysql'])
|
||||||
->where('peloperasi','=','operasi')
|
->select([
|
||||||
->where('operasi','!=','')
|
'tarikh',
|
||||||
->get();
|
'norujukan',
|
||||||
|
'komen',
|
||||||
|
'jumpinj',
|
||||||
|
'nilaimarhun',
|
||||||
|
'berat',
|
||||||
|
'margin',
|
||||||
|
'status',
|
||||||
|
'operasi',
|
||||||
|
'masalulus',
|
||||||
|
])
|
||||||
|
->where('peloperasi', '=', 'operasi')
|
||||||
|
->where('operasi', '!=', '');
|
||||||
|
|
||||||
if(sizeof($kelulusan) != 0){
|
if ($dateFrom) {
|
||||||
array_push($arraykelulusan,["kelulusanall" => $kelulusan,"cawangan" => $cawangan['details_caw'],"cawangankod" => $cawangan['kodcaw']]);
|
$query->where('tarikh', '>=', $dateFrom);
|
||||||
|
}
|
||||||
|
if ($dateTo) {
|
||||||
|
$query->where('tarikh', '<=', $dateTo);
|
||||||
|
}
|
||||||
|
if ($search !== '') {
|
||||||
|
$query->where(function ($q) use ($search) {
|
||||||
|
$q->where('norujukan', 'like', "%{$search}%")
|
||||||
|
->orWhere('operasi', 'like', "%{$search}%")
|
||||||
|
->orWhere('status', 'like', "%{$search}%")
|
||||||
|
->orWhere('komen', 'like', "%{$search}%");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($arraykelulusan,200);
|
$kelulusan = $query->orderBy('tarikh', 'desc')->get();
|
||||||
|
|
||||||
|
foreach ($kelulusan as $kel) {
|
||||||
|
$komenParts = explode('|', (string) ($kel->komen ?? ''));
|
||||||
|
$komenParts = array_values(array_filter($komenParts, function ($part) {
|
||||||
|
return $part !== '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
$arraykelulusan[] = [
|
||||||
|
'tarikh' => $kel->tarikh ?? '-',
|
||||||
|
'norujukan' => $kel->norujukan ?? '-',
|
||||||
|
'status' => $kel->status ?? '-',
|
||||||
|
'komen' => $komenParts[0] ?? ($kel->komen ?: '-'),
|
||||||
|
'pinjaman' => $kel->jumpinj ?? '-',
|
||||||
|
'nilaimarhun' => $komenParts[1] ?? ($kel->nilaimarhun ?? '-'),
|
||||||
|
'berat' => $komenParts[2] ?? ($kel->berat ?? '-'),
|
||||||
|
'margin' => $komenParts[3] ?? ($kel->margin ?? '-'),
|
||||||
|
'pelulus' => $kel->operasi ?? '-',
|
||||||
|
'masalulus' => $kel->masalulus ?? '-',
|
||||||
|
'cawangan' => $cawangan['details_caw'],
|
||||||
|
'cawangankod' => $cawangan['kodcaw'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($arraykelulusan, function ($a, $b) {
|
||||||
|
return strcmp((string) ($b['tarikh'] ?? ''), (string) ($a['tarikh'] ?? ''));
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json($arraykelulusan, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function UpdateHapusAnsuranOperasi(Request $request){
|
public function UpdateHapusAnsuranOperasi(Request $request){
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta WhatsApp Cloud webhook: verify challenge + acknowledge events.
|
||||||
|
* Sufficient for production onboarding; delivery statuses are logged only.
|
||||||
|
*/
|
||||||
|
class WhatsappWebhookController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* GET /api/whatsapp/webhook
|
||||||
|
* Meta sends hub.mode, hub.verify_token, hub.challenge.
|
||||||
|
*/
|
||||||
|
public function verify(Request $request)
|
||||||
|
{
|
||||||
|
$mode = $request->query('hub_mode', $request->query('hub.mode'));
|
||||||
|
$token = $request->query('hub_verify_token', $request->query('hub.verify_token'));
|
||||||
|
$challenge = $request->query('hub_challenge', $request->query('hub.challenge'));
|
||||||
|
|
||||||
|
$expected = (string) config('whatsapp.verify_token');
|
||||||
|
|
||||||
|
if ($mode === 'subscribe' && $expected !== '' && hash_equals($expected, (string) $token)) {
|
||||||
|
Log::info('WhatsApp webhook verified');
|
||||||
|
return response((string) $challenge, 200)
|
||||||
|
->header('Content-Type', 'text/plain');
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::warning('WhatsApp webhook verify failed', [
|
||||||
|
'mode' => $mode,
|
||||||
|
'token_match' => $expected !== '' && hash_equals($expected, (string) $token),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response('Forbidden', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/whatsapp/webhook
|
||||||
|
* Acknowledge quickly; optionally check X-Hub-Signature-256 when app secret is set.
|
||||||
|
*/
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$raw = $request->getContent();
|
||||||
|
|
||||||
|
if (!$this->signatureOk($request, $raw)) {
|
||||||
|
Log::warning('WhatsApp webhook invalid signature');
|
||||||
|
return response('Invalid signature', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = json_decode($raw, true);
|
||||||
|
if (!is_array($payload)) {
|
||||||
|
return response('OK', 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->logEvents($payload);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('WhatsApp webhook log failed: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response('OK', 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function signatureOk(Request $request, $raw)
|
||||||
|
{
|
||||||
|
$secret = trim((string) config('whatsapp.app_secret'));
|
||||||
|
if ($secret === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = (string) $request->header('X-Hub-Signature-256', '');
|
||||||
|
if ($header === '' || strpos($header, 'sha256=') !== 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
|
||||||
|
|
||||||
|
return hash_equals($expected, $header);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function logEvents(array $payload)
|
||||||
|
{
|
||||||
|
$entries = $payload['entry'] ?? [];
|
||||||
|
if (!is_array($entries)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$changes = $entry['changes'] ?? [];
|
||||||
|
if (!is_array($changes)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($changes as $change) {
|
||||||
|
$value = $change['value'] ?? [];
|
||||||
|
if (!is_array($value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($value['statuses'] ?? [] as $status) {
|
||||||
|
Log::info('WhatsApp status', [
|
||||||
|
'id' => $status['id'] ?? null,
|
||||||
|
'status' => $status['status'] ?? null,
|
||||||
|
'recipient' => $status['recipient_id'] ?? null,
|
||||||
|
'errors' => $status['errors'] ?? null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($value['messages'] ?? [] as $message) {
|
||||||
|
Log::info('WhatsApp inbound', [
|
||||||
|
'from' => $message['from'] ?? null,
|
||||||
|
'type' => $message['type'] ?? null,
|
||||||
|
'id' => $message['id'] ?? null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,4 +6,6 @@ return [
|
|||||||
'api_version' => env('WHATSAPP_API_VERSION', 'v25.0'),
|
'api_version' => env('WHATSAPP_API_VERSION', 'v25.0'),
|
||||||
'template' => env('WHATSAPP_TEMPLATE', 'hello_world'),
|
'template' => env('WHATSAPP_TEMPLATE', 'hello_world'),
|
||||||
'template_lang' => env('WHATSAPP_TEMPLATE_LANG', 'en_US'),
|
'template_lang' => env('WHATSAPP_TEMPLATE_LANG', 'en_US'),
|
||||||
|
'verify_token' => env('WHATSAPP_VERIFY_TOKEN'),
|
||||||
|
'app_secret' => env('WHATSAPP_APP_SECRET'),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -699,6 +699,9 @@ $router->group(['prefix'=>'api'], function () use ($router){
|
|||||||
$router->get('initiate/modal-online', ['uses' => 'ModalOnlineController@initiate']);
|
$router->get('initiate/modal-online', ['uses' => 'ModalOnlineController@initiate']);
|
||||||
|
|
||||||
// Operasi kelulusan pembiayaan online (must be before {kodcaw} routes)
|
// Operasi kelulusan pembiayaan online (must be before {kodcaw} routes)
|
||||||
|
$router->get('whatsapp/webhook', ['uses' => 'WhatsappWebhookController@verify']);
|
||||||
|
$router->post('whatsapp/webhook', ['uses' => 'WhatsappWebhookController@handle']);
|
||||||
|
|
||||||
$router->get('pembiayaan-online/operasi/batches', ['uses' => 'PembiayaanOnlineController@operasiListBatches']);
|
$router->get('pembiayaan-online/operasi/batches', ['uses' => 'PembiayaanOnlineController@operasiListBatches']);
|
||||||
$router->get('pembiayaan-online/operasi/{kodcaw}/batches/{batchId}', ['uses' => 'PembiayaanOnlineController@operasiShowBatch']);
|
$router->get('pembiayaan-online/operasi/{kodcaw}/batches/{batchId}', ['uses' => 'PembiayaanOnlineController@operasiShowBatch']);
|
||||||
$router->post('pembiayaan-online/operasi/{kodcaw}/lines/{id}/approve', ['uses' => 'PembiayaanOnlineController@operasiApproveLine']);
|
$router->post('pembiayaan-online/operasi/{kodcaw}/lines/{id}/approve', ['uses' => 'PembiayaanOnlineController@operasiApproveLine']);
|
||||||
|
|||||||
Reference in New Issue
Block a user