initial project

This commit is contained in:
2025-11-06 13:41:06 +08:00
commit 1b08727cb2
209 changed files with 31602 additions and 0 deletions
View File
+238
View File
@@ -0,0 +1,238 @@
<?php
namespace App\Libraries;
use App\Models\LogPaymentTransactions;
use App\Models\Orders;
use App\Models\TopupModel;
class Fiuu
{
protected $endpoint;
protected $merchantId;
protected $verifyKey;
public function __construct()
{
$this->endpoint = FIUU_SANDBOX_ENDPOINT;
$this->merchantId = FIUU_MERCHANT_ID;
$this->verifyKey = FIUU_VERIFY_KEY;
helper('general');
helper('order');
}
// public function createPayment($orderId, $amount)
// {
// $vcode = md5($amount . $this->merchantId . $orderId . $this->verifyKey);
// $query = http_build_query([
// 'merchantid' => $this->merchantId,
// 'orderid' => $orderId,
// 'amount' => $amount,
// 'vcode' => $vcode,
// ]);
// $redirectUrl = 'https://sandbox-payment.fiuu.com/RMS/pay/' . $this->merchantId . '/?' . $query;
// return [
// 'status' => 'success',
// 'vcode' => $vcode,
// 'redirect_url' => $redirectUrl,
// ];
// }
public function createPayment($order_so, $amount, $customer = [])
{
$vcode = md5($amount . $this->merchantId . $order_so . $this->verifyKey);
$fields = [
'orderid' => $order_so,
'currency' => 'MYR',
'amount' => $amount,
'vcode' => $vcode,
'bill_name' => 'John Doe',
'bill_email' => 'johndoe@example.com',
'bill_mobile' => '60123456789',
'bill_desc' => 'Test Payment',
'returnurl' => 'https://uspizza.ipsgroup.com.my/screens/payment/loading_payment?type=order',
'callbackurl' => 'https://icom.ipsgroup.com.my/api/payment/fiuu/notification'
];
$query = http_build_query($fields);
$redirectUrl = $this->endpoint . '?' . $query;
return [
'status' => 'success',
'vcode' => $vcode,
'redirect_url' => $redirectUrl,
];
}
public function createTopup($order_so, $amount, $customer = [])
{
$vcode = md5($amount . $this->merchantId . $order_so . $this->verifyKey);
$fields = [
'orderid' => $order_so,
'currency' => 'MYR',
'amount' => $amount,
'vcode' => $vcode,
'bill_name' => 'John Doe',
'bill_email' => 'johndoe@example.com',
'bill_mobile' => '60123456789',
'bill_desc' => 'Test Payment',
'returnurl' => 'https://uspizza.ipsgroup.com.my/screens/payment/loading_payment?type=topup',
'callbackurl' => 'https://icom.ipsgroup.com.my/api/topup/fiuu/notification'
];
$query = http_build_query($fields);
$redirectUrl = $this->endpoint . '?' . $query;
return [
'status' => 'success',
'vcode' => $vcode,
'redirect_url' => $redirectUrl,
];
}
public function validateToken($data){
$tranID = $data['tranID'];
$order_so = $data['orderid'];
$status = $data['status'];
$domain = $data['domain'];
$amount = $data['amount'];
$currency = $data['currency'];
$appcode = $data['appcode'];
$paydate = $data['paydate'];
$skey = $data['skey'];
$pre_skey = md5($tranID.$order_so.$status.$domain.$amount.$currency);
$myskey = md5($paydate.$domain.$pre_skey.$appcode.$this->verifyKey);
return $skey == $myskey;
}
public function paymentNotification($data){
$tranID = $data['tranID'];
$order_so = $data['orderid'];
$status = $data['status'];
$domain = $data['domain'];
$amount = $data['amount'];
$currency = $data['currency'];
$appcode = $data['appcode'];
$paydate = $data['paydate'];
$skey = $data['skey'];
$log_payment_transactions = new LogPaymentTransactions();
$log_payment_transactions->insert([
'order_id' => 0,
'order_type' => 'order',
'url' => 'notification',
'request' => json_encode($data),
'respond' => '',
'result' => $skey.'-check-token',
'status' => $status,
]);
$is_valid = $this->validateToken($data);
if (!$is_valid) {
$result = 'invalid-token'; // Invalid SKEY, ignore or log as fraud attempt
}
$orders = new Orders();
$order = $orders->where('order_so', $order_so)->first();
$order_id = $order['id'];
if($status == '00' || $status == '0'){
if(completeOrder($order_id)){
$result = 'success-payment';
}else{
$result = 'failed-payment';
}
}else{
$explode_order_so = explode('-', $order_so);
$order_so_number = $explode_order_so[1] ?? 0;
$order_so_number++;
$order_so = $explode_order_so[0].'-'.$order_so_number;
$orders->update($order_id, ['order_so' => $order_so]);
$result = 'failed-payment';
}
$log_payment_transactions = new LogPaymentTransactions();
$log_payment_transactions->insert([
'order_id' => $order_id,
'order_type' => 'order',
'url' => 'notification',
'request' => json_encode($data),
'respond' => json_encode($result),
'result' => $skey.'-'.$is_valid,
'status' => $status,
]);
return $result;
// if($status == '00'){
// //send notification to fiuu server
// send_api_request('POST', );
// }
}
public function topupNotification($data)
{
$tranID = $data['tranID'];
$topup_number = $data['orderid'];
$status = $data['status'];
$domain = $data['domain'];
$amount = $data['amount'];
$currency = $data['currency'];
$appcode = $data['appcode'];
$paydate = $data['paydate'];
$skey = $data['skey'];
$log_payment_transactions = new LogPaymentTransactions();
$log_payment_transactions->insert([
'order_id' => 0,
'order_type' => 'topup',
'url' => 'topup-notification',
'request' => json_encode($data),
'respond' => '',
'result' => $skey.'-check-token',
'status' => $status,
]);
$is_valid = $this->validateToken($data);
if (!$is_valid) {
$result = 'invalid-token';
}
$topups = new TopupModel();
$topup = $topups->where('topup_number', $topup_number)->first();
if (!$topup) {
$result = 'topup-not-found';
} else {
if ($status == '00' || $status == '0') {
$topups->update($topup['id'], ['status' => 'Success']);
$result = 'success-topup';
} else {
$topups->update($topup['id'], ['status' => 'Failed']);
$result = 'failed-topup';
}
}
$log_payment_transactions->insert([
'order_id' => $topup['id'] ?? 0,
'order_type' => 'topup',
'url' => 'topup-notification',
'request' => json_encode($data),
'respond' => json_encode($result),
'result' => $skey.'-'.$is_valid,
'status' => $status,
]);
return $result;
}
}
+522
View File
@@ -0,0 +1,522 @@
<?php
namespace App\Libraries;
use App\Models\Outlet;
use App\Models\Orders;
use App\Models\LogGrab;
use App\Models\OrderDeliveries;
class Grab {
protected $api_url;
protected $client_id;
protected $client_secret;
protected $access_token;
protected $outlet;
public function __construct() {
$this->api_url = defined('GRAB_API_URL') ? GRAB_API_URL : '';
$this->client_id = defined('GRAB_CLIENT_ID') ? GRAB_CLIENT_ID : '';
$this->client_secret = defined('GRAB_CLIENT_SECRET') ? GRAB_CLIENT_SECRET : '';
helper("general");
$this->outlet = new Outlet();
// Get access token
$this->getAccessToken();
}
/**
* Get OAuth 2.0 access token for GrabExpress API
*/
private function getAccessToken() {
$token_url = $this->api_url . '/grabid/v1/oauth2/token';
$request_data = [
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'grant_type' => 'client_credentials',
'scope' => 'grab_express.partner_deliveries'
];
$headers = [
'Cache-Control: no-cache',
'Content-Type: application/json'
];
$response = send_api_request('POST', $token_url, $headers, json_encode($request_data));
if (isset($response['access_token'])) {
$this->access_token = $response['access_token'];
} else {
log_message('error', 'Failed to get Grab access token: ' . json_encode($response));
}
}
/**
* Request delivery quotation from GrabExpress
*/
public function requestQuotation($selected_date = null, $selected_time = null, $total_amount = null, $outlet_id = null, $latitude = null, $longitude = null, $address = null) {
// Determine service type based on amount
if ($total_amount > 150) {
$service_type = 'CAR';
} else {
$service_type = 'MOTORCYCLE';
}
// Validate service type
if (!in_array($service_type, ['MOTORCYCLE', 'CAR'])) {
$service_type = 'MOTORCYCLE'; // Default to motorcycle
}
// Get outlet details
$outlet_data = $this->outlet->where('id', $outlet_id)->first();
$outlet_address = $outlet_data['address'];
$outlet_latitude = $outlet_data['latitude'];
$outlet_longitude = $outlet_data['longitude'];
// Validate coordinates
if (empty($outlet_latitude) || empty($outlet_longitude) || empty($latitude) || empty($longitude)) {
return [
'error' => 'Invalid coordinates provided',
'outlet_coords' => ['lat' => $outlet_latitude, 'lng' => $outlet_longitude],
'customer_coords' => ['lat' => $latitude, 'lng' => $longitude]
];
}
// Prepare request data following GrabExpress API structure
$request_data = [
'serviceType' => 'INSTANT',
'vehicleType' => $service_type === 'CAR' ? 'CAR' : 'BIKE',
'codType' => 'REGULAR',
'packages' => [
[
'name' => 'Food Package',
'description' => 'Food delivery package',
'quantity' => 1,
'price' => $total_amount,
'dimensions' => [
'height' => 0,
'width' => 0,
'depth' => 0,
'weight' => 0
]
]
],
'origin' => [
'address' => $outlet_address,
'keywords' => 'US Pizza Outlet',
'cityCode' => 'KUL', // Kuala Lumpur, Malaysia
'coordinates' => [
'latitude' => (float) $outlet_latitude,
'longitude' => (float) $outlet_longitude
]
],
'destination' => [
'address' => $address,
'keywords' => 'Customer Address',
'cityCode' => 'KUL', // Kuala Lumpur, Malaysia
'coordinates' => [
'latitude' => (float) $latitude,
'longitude' => (float) $longitude
]
]
];
// Handle preorder/scheduled delivery
if ($selected_date && $selected_time) {
$local_datetime = $selected_date . ' ' . $selected_time;
$dt = new \DateTime($local_datetime, new \DateTimeZone('Asia/Kuala_Lumpur'));
$request_data['schedule'] = [
'pickupTimeFrom' => $dt->format('Y-m-d\TH:i:s+08:00'),
'pickupTimeTo' => $dt->add(new \DateInterval('PT1H'))->format('Y-m-d\TH:i:s+08:00')
];
}
$headers = [
'Authorization: Bearer ' . $this->access_token,
'Content-Type: application/json',
'cache-control: no-cache'
];
$response = send_api_request('POST', $this->api_url . '/grab-express-sandbox/v1/deliveries/quotes', $headers, json_encode($request_data));
// Debug: Log the response
log_message('info', 'Grab Quotation Response: ' . json_encode($response));
// Log the request
$log_grab = new LogGrab();
$log_grab->insert([
'url' => $this->api_url . '/grab-express-sandbox/v1/deliveries/quotes',
'request' => json_encode($request_data),
'respond' => json_encode($response),
'quotation_id' => $response['quotes'][0]['quoteId'] ?? null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
return $response;
}
/**
* Submit delivery order to GrabExpress
*/
public function submitOrder($order_id) {
$orders = new Orders();
$order = $orders->select('orders.*, customer_addresses.address, customer_addresses.latitude, customer_addresses.longitude, customer_addresses.name as recipient_name, customer_addresses.phone as recipient_phone, customer_addresses.note as recipient_note, outlets.pic_name, outlets.pic_phone')
->join('outlets', 'outlets.id = orders.outlet_id')
->join('customer_addresses', 'customer_addresses.id = orders.customer_address_id AND customer_addresses.deleted_at IS NULL', 'left')
->where('orders.id', $order_id)
->where('orders.deleted_at', null)
->first();
$quotation_id = $order['grab_quot_id'];
// Get outlet data for origin coordinates
$outlet_data = $this->outlet->where('id', $order['outlet_id'])->first();
if (!$outlet_data) {
return ['error' => 'Outlet not found'];
}
$request_data = [
'merchantOrderID' => 'USPIZZA_' . $order_id,
'serviceType' => 'INSTANT',
'vehicleType' => 'BIKE',
'codType' => 'REGULAR',
'paymentMethod' => 'CASHLESS',
'packages' => [
[
'name' => 'Food Package',
'description' => 'Food delivery package',
'quantity' => 1,
'price' => (int) (($order['grand_total'] ?? 0)),
'dimensions' => [
'height' => 1,
'width' => 1,
'depth' => 1,
'weight' => 1
]
]
],
'origin' => [
'address' => $outlet_data['address'],
'cityCode' => 'KUL',
'coordinates' => [
'latitude' => round((float) $outlet_data['latitude'], 6),
'longitude' => round((float) $outlet_data['longitude'], 6)
]
],
'destination' => [
'address' => $order['address'],
'cityCode' => 'KUL',
'coordinates' => [
'latitude' => round((float) $order['latitude'], 6),
'longitude' => round((float) $order['longitude'], 6)
]
],
'recipient' => [
'firstName' => $order['recipient_name'] ?? 'Customer',
'lastName' => '',
'email' => 'customer@example.com',
'phone' => $order['recipient_phone'] ? $order['recipient_phone'] : '0123456789',
'smsEnabled' => true
],
'sender' => [
'firstName' => $outlet_data['pic_name'] ?? 'US Pizza',
'companyName' => 'US Pizza',
'email' => 'delivery@uspizza.com',
'phone' => $outlet_data['pic_phone'] ? $outlet_data['pic_phone'] : '0123456789',
'smsEnabled' => true
]
];
// Add schedule if it's a preorder
if ($order['selected_date'] && $order['selected_time']) {
$local_datetime = $order['selected_date'] . ' ' . $order['selected_time'];
$dt = new \DateTime($local_datetime, new \DateTimeZone('Asia/Kuala_Lumpur'));
$request_data['schedule'] = [
'pickupTimeFrom' => $dt->format('Y-m-d\TH:i:s+08:00'),
'pickupTimeTo' => $dt->add(new \DateInterval('PT1H'))->format('Y-m-d\TH:i:s+08:00')
];
}
// Remove schedule temporarily to test if that's causing the issue
// unset($request_data['schedule']);
// Validate required fields
if (empty($request_data['origin']['address']) || empty($request_data['destination']['address'])) {
return ['error' => 'Missing address information'];
}
if (empty($request_data['origin']['coordinates']['latitude']) || empty($request_data['origin']['coordinates']['longitude']) ||
empty($request_data['destination']['coordinates']['latitude']) || empty($request_data['destination']['coordinates']['longitude'])) {
return ['error' => 'Missing coordinate information'];
}
$headers = [
'Authorization: Bearer ' . $this->access_token,
'Content-Type: application/json; charset=utf-8',
'cache-control: no-cache'
];
log_message('info', 'Grab Submit Order Headers: ' . json_encode($headers));
log_message('info', 'Grab Submit Order Request: ' . json_encode($request_data));
// Debug: Check JSON encoding
$json_request = json_encode($request_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if (json_last_error() !== JSON_ERROR_NONE) {
log_message('error', 'JSON encoding error: ' . json_last_error_msg());
return ['error' => 'JSON encoding failed: ' . json_last_error_msg()];
}
log_message('info', 'Grab Submit Order JSON: ' . $json_request);
$response = send_api_request('POST', $this->api_url . '/grab-express-sandbox/v1/deliveries', $headers, $json_request);
// Debug: Log the response
log_message('info', 'Grab Submit Order Response: ' . json_encode($response));
// Log the request
$log_grab = new LogGrab();
$log_grab->insert([
'url' => $this->api_url . '/grab-express-sandbox/v1/deliveries',
'request' => json_encode($request_data),
'respond' => json_encode($response),
'quotation_id' => $quotation_id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
return $response;
}
/**
* Handle webhook notifications from GrabExpress
*/
public function handleWebhook($data) {
// Log the webhook
$log_grab = new LogGrab();
$log_grab->insert([
'url' => $this->api_url . '/webhook',
'request' => json_encode($data),
'respond' => 'Status: ' . ($data['status'] ?? 'Unknown'),
'quotation_id' => null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
// Handle delivery status updates
if (isset($data['deliveryID']) && isset($data['status'])) {
$delivery_id = $data['deliveryID'];
$status = $data['status'];
$merchant_order_id = $data['merchantOrderID'] ?? null;
$tracking_url = $data['trackURL'] ?? null;
$pod_url = $data['dropoffProofURL'] ?? null;
// Update order status
$this->updateOrderStatus($delivery_id, $status, $merchant_order_id, $tracking_url, $pod_url);
// Log additional webhook data
log_message('info', 'Grab Webhook - Delivery ID: ' . $delivery_id . ', Status: ' . $status . ', Order: ' . $merchant_order_id);
// Log driver information if available
if (isset($data['driver'])) {
log_message('info', 'Grab Webhook - Driver: ' . $data['driver']['name'] . ', Phone: ' . $data['driver']['phone'] . ', License: ' . $data['driver']['licensePlate']);
}
// Log pickup pin if available
if (isset($data['pickupPin'])) {
log_message('info', 'Grab Webhook - Pickup Pin: ' . $data['pickupPin']);
}
}
return true;
}
/**
* Update order status based on delivery status
*/
private function updateOrderStatus($delivery_id, $status, $merchant_order_id = null, $tracking_url = null, $pod_url = null) {
$orders = new Orders();
// Try to find order by delivery ID first, then by merchant order ID
$order = null;
if ($delivery_id) {
$order = $orders->select('orders.*, customer_addresses.phone as recipient_phone')
->join('order_deliveries', 'order_deliveries.order_id = orders.id')
->join('customer_addresses', 'customer_addresses.id = orders.customer_address_id AND customer_addresses.deleted_at IS NULL', 'left')
->where('order_deliveries.provider_name', 'Grab')
->where('order_deliveries.provider_order_id', $delivery_id)
->first();
}
// If not found by delivery ID, try by merchant order ID
if (!$order && $merchant_order_id) {
$order_id = str_replace('USPIZZA_', '', $merchant_order_id);
$order = $orders->where('id', $order_id)->first();
}
// echo(123);exit;
if ($order) {
$order_id = $order['id'];
// echo($order_id);
// exit;
$order_status = 'pending';
// Map GrabExpress status to internal status
switch ($status) {
case 'PICKING_UP':
$order_status = 'picked_up';
break;
case 'IN_DELIVERY':
$order_status = 'on_the_way';
break;
case 'COMPLETED':
$order_status = 'completed';
break;
case 'CANCELLED':
$order_status = 'cancelled';
break;
default:
log_message('info', 'Grab Webhook - Unknown status: ' . $status);
$order_status = 'pending';
break;
}
// Update order status
$orders->update($order_id, ['status' => $order_status]);
//send notification to customer
$message = '';
switch($order_status){
case 'on_the_way':
$message = "🚚 Your order [**".$order['order_so']."**] is on the way!\nThe driver is delivering your food now.\nPlease get ready to receive it.\nHere is the tracking link: \n\n" . $tracking_url;
break;
case 'completed':
$message = "🎉 Your order [**".$order['order_so']."**] has been completed.\nWe hope you enjoy your meal!\nThank you for ordering with us.";
break;
}
if($message){
$wato = new Wato();
$wato->pushNotification($order['recipient_phone'], $message);
}
log_message('info', 'Grab Webhook - Updated order ' . $order_id . ' status to: ' . $order_status);
// Update delivery record with additional information
$order_deliveries = new OrderDeliveries();
$delivery_update_data = [
'status' => $order_status,
'updated_at' => date('Y-m-d H:i:s'),
'tracking_link' => $tracking_url,
'POD_url' => $pod_url
];
// Add delivery completion timestamp if completed
if ($order_status === 'completed') {
$delivery_update_data['delivered_at'] = date('Y-m-d H:i:s');
}
// Update delivery record
$order_deliveries->where('order_id', $order_id)
->where('provider_name', 'Grab')
->where('provider_order_id', $delivery_id)
->set($delivery_update_data)
->update();
} else {
log_message('warning', 'Grab Webhook - Order not found for delivery ID: ' . $delivery_id . ' or merchant order ID: ' . $merchant_order_id);
}
}
/**
* Get delivery details from GrabExpress
*/
public function getDeliveryDetails($delivery_id) {
$headers = [
'Authorization: Bearer ' . $this->access_token,
'Content-Type: application/json',
'cache-control: no-cache'
];
$response = send_api_request('GET', $this->api_url . '/grab-express-sandbox/v1/deliveries/' . $delivery_id, $headers);
return $response;
}
/**
* Cancel delivery request
*/
public function cancelDelivery($delivery_id) {
$headers = [
'Authorization: Bearer ' . $this->access_token,
'Content-Type: application/json',
'cache-control: no-cache'
];
$response = send_api_request('DELETE', $this->api_url . '/grab-express-sandbox/v1/deliveries/' . $delivery_id, $headers);
// Log the cancellation
$log_grab = new LogGrab();
$log_grab->insert([
'url' => $this->api_url . '/grab-express-sandbox/v1/deliveries/' . $delivery_id,
'request' => 'DELETE request',
'respond' => json_encode($response),
'quotation_id' => null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
return $response;
}
/**
* Store additional webhook information like driver details, pickup pin, etc.
*/
private function storeWebhookInfo($delivery_id, $webhook_data) {
$order_deliveries = new OrderDeliveries();
// Find the delivery record
$delivery_record = $order_deliveries->where('provider_name', 'Grab')
->where('provider_order_id', $delivery_id)
->first();
if ($delivery_record) {
$update_data = [];
// Store driver information if available
if (isset($webhook_data['driver'])) {
$update_data['driver_name'] = $webhook_data['driver']['name'] ?? '';
$update_data['driver_phone'] = $webhook_data['driver']['phone'] ?? '';
$update_data['driver_license'] = $webhook_data['driver']['licensePlate'] ?? '';
}
// Store pickup pin if available
if (isset($webhook_data['pickupPin'])) {
$update_data['pickup_pin'] = $webhook_data['pickupPin'];
}
// Store tracking URL if available
if (isset($webhook_data['trackURL'])) {
$update_data['tracking_link'] = $webhook_data['trackURL'];
}
// Store distance if available
if (isset($webhook_data['distance'])) {
$update_data['distance_meters'] = $webhook_data['distance'];
}
// Store country if available
if (isset($webhook_data['country'])) {
$update_data['country'] = $webhook_data['country'];
}
// Update the delivery record with additional information
if (!empty($update_data)) {
$update_data['updated_at'] = date('Y-m-d H:i:s');
$order_deliveries->where('id', $delivery_record['id'])
->set($update_data)
->update();
}
}
}
}
+292
View File
@@ -0,0 +1,292 @@
<?php
namespace App\Libraries;
use App\Models\Outlet;
use App\Models\Orders;
use App\Models\LogLalamove;
use App\Models\OrderDeliveries;
class Lalamove {
protected $api_url;
protected $api_secret;
protected $api_key;
protected $outlet;
public function __construct() {
$this->api_url = defined('LALAMOVELINK') ? LALAMOVELINK : '';
$this->api_secret = defined('LALAMOVESECRET') ? LALAMOVESECRET : '';
$this->api_key = defined('LALAMOVEAPIKEY') ? LALAMOVEAPIKEY : '';
helper("general");
$this->outlet = new Outlet();
}
public function requestQuotation($selected_date = null, $selected_time = null, $total_amount = null, $outlet_id = null, $latitude = null, $longitude = null, $address = null) {
//check service type
if($total_amount > 150){
$service_type = 'CAR';
}else{
$service_type = 'MOTORCYCLE';
}
//get outlet details
$outlet_data = $this->outlet->where('id', $outlet_id)->first();
$outlet_address = $outlet_data['address'];
$outlet_latitude = $outlet_data['latitude'];
$outlet_longitude = $outlet_data['longitude'];
// echo($outlet_latitude);exit;
$stops = [
[
"coordinates" => [
"lat" => $outlet_latitude,
"lng" => $outlet_longitude
],
"address" => $outlet_address
],
[
"coordinates" => [
"lat" => $latitude,
"lng" => $longitude
],
"address" => $address
]
];
$request_data = [
"data" => [
'serviceType' => $service_type,
'stops' => $stops,
'language' => 'en_MY'
]
];
//handling preorder
if($selected_date && $selected_time){
// Convert local Y-m-d H:i:s to UTC ISO 8601 format
$local_datetime = $selected_date . ' ' . $selected_time;
$dt = new \DateTime($local_datetime, new \DateTimeZone('Asia/Kuala_Lumpur'));
$request_data['data']['scheduleAt'] = $dt->format('Y-m-d\TH:i:s\Z');
}
$request_body = json_encode($request_data);
$timestamp = time() * 1000;
$raw_signature = $timestamp . "\r\nPOST\r\n/v3/quotations\r\n\r\n" . $request_body;
$signature = hash_hmac('sha256', $raw_signature, $this->api_secret);
$token = $this->api_key.':'.$timestamp.':'.$signature;
$headers = [
'Authorization: hmac ' . $token,
'Content-Type: application/json',
'Market: MY',
'Request-ID: 211b9d85-ips-476f-8675-b61ec923cc27'
];
$response = send_api_request('POST', $this->api_url.'/quotations', $headers, $request_body);
$log_lalamove = new LogLalamove();
$log_lalamove->insert([
'url' => $this->api_url.'/quotations',
'request' => $request_body,
'respond' => json_encode($response),
'quotation_id' => $response['data']['quotationId'] ?? null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
return $response;
}
public function submitOrder($order_id){
$orders = new Orders();
$order = $orders->select('orders.*, customer_addresses.name as recipient_name, customer_addresses.phone as recipient_phone, customer_addresses.note as recipient_note, log_lalamove.quotation_id, log_lalamove.respond, outlets.pic_name, outlets.pic_phone')
->join('log_lalamove', 'log_lalamove.quotation_id = orders.lalamove_quot_id')
->join('outlets', 'outlets.id = orders.outlet_id')
->join('customer_addresses', 'customer_addresses.id = orders.customer_address_id AND customer_addresses.deleted_at IS NULL', 'left')
->where('orders.id', $order_id)
->where('orders.deleted_at', null)
->first();
$quotation_id = $order['lalamove_quot_id'];
$quotation_data = json_decode($order['respond'], true);
$request_data = [
"data" => [
'quotationId' => $quotation_id,
"sender" => [
"stopId" => $quotation_data['data']['stops'][0]['stopId'],
"name" => $order['pic_name'] ?? 'US Pizza',
"phone" => $order['pic_phone'] ?? '+60123456789'
],
"recipients" => [
[
"stopId" => $quotation_data['data']['stops'][1]['stopId'],
"name" => $order['recipient_name'],
"phone" => $order['recipient_phone'] ? '+6'.$order['recipient_phone'] : '+60123456789',
"remarks" => $order['recipient_note']
]
],
"isPODEnabled" => true,
"partner" => "USPizza Delivery",
"metadata" => [
"order_id" => $order['id'],
]
]
];
$request_body = json_encode($request_data);
$timestamp = time() * 1000;
$raw_signature = $timestamp . "\r\nPOST\r\n/v3/orders\r\n\r\n" . $request_body;
$signature = hash_hmac('sha256', $raw_signature, $this->api_secret);
$token = $this->api_key.':'.$timestamp.':'.$signature;
$headers = [
'Authorization: hmac ' . $token,
'Content-Type: application/json',
'Market: MY',
'Request-ID: 211b9d85-ips-476f-8675-b61ec923cc27'
];
$response = send_api_request('POST', $this->api_url.'/orders', $headers, $request_body);
$log_lalamove = new LogLalamove();
$log_lalamove->insert([
'url' => $this->api_url.'/orders',
'request' => $request_body,
'respond' => json_encode($response),
'quotation_id' => $quotation_id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
return $response;
}
public function handleWebhook($data){
//compare signature
$signature = $data['signature'];
$timestamp = $data['timestamp'];
$http_verb = 'POST';
$path = '/api/lalamove/webhook';
$json_body = json_encode($data['data'], JSON_UNESCAPED_SLASHES);
$raw_signature = $timestamp . "\r\n" . $http_verb . "\r\n" . $path . "\r\n\r\n" . $json_body;
$mysignature = hash_hmac('sha256', $raw_signature, $this->api_secret);
if($signature == $mysignature){
$log_lalamove = new LogLalamove();
$log_lalamove->insert([
'url' => $this->api_url.'/webhook',
'request' => json_encode($data),
'respond' => $data['eventType'] == 'ORDER_STATUS_CHANGED' ? 'ORDER_STATUS_CHANGED ('.$data['data']['order']['orderId'].'): '.$data['data']['order']['status'] : $data['eventType'],
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
if($data['eventType'] == 'ORDER_STATUS_CHANGED'){
$orders = new Orders();
$order = $orders->select('orders.*, customer_addresses.phone as recipient_phone, order_deliveries.tracking_link')
->join('customer_addresses', 'customer_addresses.id = orders.customer_address_id AND customer_addresses.deleted_at IS NULL', 'left')
->join('order_deliveries', 'order_deliveries.order_id = orders.id')
->where('order_deliveries.provider_name', 'Lalamove')
->where('order_deliveries.provider_order_id', $data['data']['order']['orderId'])
->first();
if($order){
$order_id = $order['id'];
$lalamove_order_id = $data['data']['order']['orderId'];
$order_status = 'pending';
switch($data['data']['order']['status']){
case 'ON_GOING':
$order_status = 'on_the_way';
break;
case 'PICKED_UP':
$order_status = 'picked_up';
break;
case 'COMPLETED':
$order_status = 'completed';
break;
case 'CANCELLED':
$order_status = 'cancelled';
break;
}
$orders->update($order_id, ['status' => $order_status]);
//send notification to customer
$message = '';
switch($order_status){
case 'on_the_way':
$message = "🚚 Your order [**".$order['order_so']."**] is on the way!\nThe driver is delivering your food now.\nPlease get ready to receive it.\nHere is the tracking link: \n\n" . $order['tracking_link'];
break;
case 'completed':
$message = "🎉 Your order [**".$order['order_so']."**] has been completed.\nWe hope you enjoy your meal!\nThank you for ordering with us.";
break;
}
if($message){
$wato = new Wato();
$wato->pushNotification($order['recipient_phone'], $message);
}
if($order_status == 'completed'){
//get order details
$order_details = $this->getOrderDetails($lalamove_order_id);
$pod_details = $order_details['data']['stops'][1]['POD'];
if($pod_details['status'] == 'DELIVERED' || $pod_details['status'] == 'SIGNED'){
$order_deliveries = new OrderDeliveries();
$delivery_records = $order_deliveries->where('order_id', $order_id)
->where('provider_name', 'Lalamove')
->where('provider_order_id', $lalamove_order_id)
->get()->getResultArray();
if (!empty($delivery_records)) {
// Update delivery status to completed
$order_deliveries->where('order_id', $order_id)
->where('provider_name', 'Lalamove')
->where('provider_order_id', $lalamove_order_id)
->set(['POD_url' => $pod_details['image'], 'delivered_at' => $pod_details['deliveredAt']])
->update();
}
}
} elseif($order_status == 'cancelled'){
// Update delivery status to cancelled
// $order_deliveries = new OrderDeliveries();
// $order_deliveries->where('order_id', $order_id)
// ->where('provider_name', 'Lalamove')
// ->where('provider_order_id', $lalamove_order_id)
// ->set(['status' => 'cancelled', 'cancelled_at' => date('Y-m-d H:i:s')])
// ->update();
}
}
}
}
return true;
}
public function getOrderDetails($lalamove_order_id){
$timestamp = time() * 1000;
$raw_signature = $timestamp . "\r\nGET\r\n/v3/orders/" . $lalamove_order_id . "\r\n\r\n";
$signature = hash_hmac('sha256', $raw_signature, $this->api_secret);
$token = $this->api_key.':'.$timestamp.':'.$signature;
$headers = [
'Authorization: hmac ' . $token,
'Content-Type: application/json',
'Market: MY',
'Request-ID: 211b9d85-ips-476f-8675-b61ec923cc27'
];
$response = send_api_request('GET', $this->api_url.'/orders/'.$lalamove_order_id, $headers);
return $response;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Libraries;
class Wato {
protected $api_url;
protected $api_key;
protected $company;
public function __construct() {
$this->api_url = defined('WATOAPILINK') ? WATOAPILINK : '';
$this->api_key = defined('WATOAPIKEY') ? WATOAPIKEY : '';
$this->company = defined('COMPANY') ? COMPANY : 'Company';
helper("general");
}
public function pushNotification($mobile, $message) {
$mobile = str_replace('+', '', $mobile);
$postFields = [
'token' => $this->api_key,
'to' => $mobile,
'message' => $message
];
return send_api_request('POST', $this->api_url, [], $postFields);
}
}