Files
api/services/nginx/app/routes/ordersRoute.php
T
Jepp9350 c19ed3389c Update permission check for Stripe payment intent
Replaced 'charge_order_stripe' with a more general 'charge_order' permission check in the Stripe payment intent endpoint. This ensures consistent permission handling across payment modules.
2025-04-28 09:01:15 +02:00

607 lines
28 KiB
PHP

<?php
namespace routes;
use classes\authentication;
use classes\response;
use classes\stripe;
use objects\collected_order_invoices_o;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_orders_o;
use objects\stripe_payment_intents_o;
use objects\users_o;
use traits\route_t;
class ordersRoute
{
use route_t;
public function run(): void
{
$this->get('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_orders');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('orders', 'global', 1, $user->id, 'LIST_ORDERS', 'Successfully listed orders');
// Create economic_module_orders object
$economic_module_orders = new economic_module_orders();
$orders = new orders_o();
// Return the list of departments
$orders->setView('orders_with_invoice_collections');
$response->success(
$orders->listObjectsWithPaginationIfSet(
function ($order) {
// Add the invoice status to the order
$order['economic_invoice_module'] = (new economic_module_orders())->getByOrderId($order['id'])->asArray();
// Add the total amount to the order
$order['total_net_amount'] = (new orders_o())->select($order['id'])->getNetAmount();
// Add the stripe status to the order
$stripe_module_orders = (new stripe_module_orders_o())->select($order['id']);
if ($stripe_module_orders->exists()) {
$order['stripe_invoice_module'] = $stripe_module_orders->asArray();
}
// If the invoice collection is set, add it to the order
if (!empty($order['invoice_collection_id'])) {
$order['invoice_collection'] = [
'id' => $order['invoice_collection_id'],
'closed_at' => (new collected_order_invoices_o())->select($order['invoice_collection_id'])->closed_at->value(),
'booked_invoice_id' => (new collected_order_invoices_o())->select($order['invoice_collection_id'])->booked_invoice_id->value() ?? null,
'processor' => (int)(new collected_order_invoices_o())->select($order['invoice_collection_id'])->processor->value() ?? null,
];
}
// Add the customer name to the order
$order['customer_name'] = (new users_o())->getCustomerName($order['customer_id']);
/** @var array $order */
return $order;
},
$orders->forceRestrictFilters(
[
// This makes sure that the user can only see orders from the departments they explicitly have access to
'department_id' => $user->getGroup()->getDepartments(),
]
)
)
);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'LIST_ORDERS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_orders' => 'List all orders'
]
);
$this->post('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
$data = $this->getData($data, $response);
// Validate the department
if (!(new departments_o())->getDepartmentById((int)$data['department_id'])) {
$response->error('Department not found', 400);
}
// Make sure the customer number set is valid
$targetUser = (new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id']);
if (!$targetUser->exists()) {
$response->error('Customer not found', 400);
}
// Check if the user requires a reference
if ($targetUser->requiresReference() && empty($data['reference'])) {
$response->error('Reference is required by the customer', 400);
}
// Get the registration number
$reg_1 = $data['reg_1'];
// Get the registration numbers (If they are set, they 2-3 are optional)
$reg_2 = $data['reg_2'] ?? '';
$reg_3 = $data['reg_3'] ?? '';
// Create the order
$order = (new orders_o())->add((int)$data['customer_id'], $user->id, $data['reference'], $data['notes'], (int)$data['department_id'], (string)$reg_1, (string)$reg_2, (string)$reg_3);
// Log the incident
(new logs_o())->add('orders', $data['department_id'], 1, $user->id, 'ADD_ORDER', 'Successfully added an order (ID: ' . $data['department_id'] . ')');
// Return a success message, containing the orders array
$response->success($order->asArray());
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'ADD_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'add_order' => 'Add an order'
]
);
$this->put('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('edit_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// If the customer ID is set, validate it
if (isset($data['customer_id'])) {
if (!(new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id'])->exists() || empty($data['customer_id'])) {
$response->error('Customer not found or invalid', 400);
}
$order->customer_id->set((int)$data['customer_id']);
}
// If the reference is set, validate it
if (isset($data['reference'])) {
$order->reference->set($data['reference']);
}
// If the notes are set, validate them
if (isset($data['notes'])) {
$order->notes->set($data['notes']);
}
// If the registration number is set, validate it
if (isset($data['reg_1'])) {
$order->reg_1->set($data['reg_1']);
}
// If the department ID is set, validate it
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'EDIT_ORDER', 'Successfully updated an order (ID: ' . $data['id'] . ')');
// Return a success message
$response->success(['message' => 'Order updated successfully']);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'EDIT_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'edit_order' => 'Edit an order'
]
);
$this->delete('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('delete_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the payload
$id = $this->fromRequest('id');
// Check if the ID is set
if (!$id) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Delete the order
$order->delete();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_ORDER', 'Successfully deleted an order (ID: ' . $id . ')');
// Return a success message
$response->success(['message' => 'Order deleted successfully']);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'DELETE_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'delete_order' => 'Delete an order'
]
);
$this->post('/orders/mark_as_completed', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('mark_order_as_completed');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Mark the order as completed
$order->markAsCompleted();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'MARK_ORDER_AS_COMPLETED', 'Successfully marked an order as completed (ID: ' . $data['id'] . ')');
// Return a success message
$response->success(['message' => 'Order marked as completed successfully']);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'MARK_ORDER_AS_COMPLETED', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'mark_order_as_completed' => 'Mark an order as completed'
]
);
$this->post('/orders/module/stripe/payment_intent', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('charge_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Get the department
$department = (new departments_o())->selectId((int)$order->department_id->value());
// Check if the department is configured for Stripe payments
if (!$department->isStripeConfigured()) {
$response->error('Department is not configured for Stripe payments', 400);
}
// Check if the reader is set
if (!isset($data['reader'])) {
$response->error('Reader ID is required', 400);
}
// Get the tax percentage (if any)
$tax_percentage = (isset($data['tax_percentage'])) ? (int)$data['tax_percentage'] : null;
// Check if the tax percentage is valid
if ($tax_percentage !== null && ($tax_percentage < 0 || $tax_percentage > 100)) {
$response->error('Invalid tax percentage', 400);
}
function addTaxNetAmount($net_amount, $tax_percentage): float
{
// Check if the tax percentage is above 0
if (empty($tax_percentage) || $tax_percentage <= 0) {
return $net_amount;
}
return $net_amount + ($net_amount * ($tax_percentage / 100));
}
// Get the Stripe payment intent
$stripe = new stripe();
$paymentIntent = $stripe->payment_intents->create(
addTaxNetAmount(
(float)$order->getNetAmount() * 100,
$tax_percentage ?? 0
),
[
'description' => 'Order ID: ' . $order->id,
'metadata' => [
'order_id' => $order->id,
'customer_id' => $order->customer_id->value(),
'department_id' => $order->department_id->value(),
'tax_percentage' => $tax_percentage ?? 0,
],
'payment_method_types' => ['card_present'],
'capture_method' => 'manual',
]
);
// Validate the payment intent
try {
$stripe->payment_intents->get($paymentIntent->id);
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
// Set the payment intent ID in the order
$stripe_payment_intents = new stripe_payment_intents_o();
$stripe_payment_intents->add(
(int)$order->id,
$paymentIntent->id,
$paymentIntent->client_secret,
$paymentIntent->toJSON()
);
// Send the payment intent to the reader
$stripe->readers->sendPaymentIntent(
$data['reader'],
$paymentIntent->id,
);
// Set the reader on the stripe payment intent
$stripe_payment_intents->reader_id->set($data['reader']);
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'CHARGE_ORDER', 'Successfully charged an order (ID: ' . $data['id'] . ')');
$response->success([
'payment_intent' => $paymentIntent->id,
'client_secret' => $paymentIntent->client_secret,
]);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'CHARGE_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'charge_order' => 'Charge an order'
]
);
$this->get('/orders/module/stripe/payment_intent', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('get_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
self::requireParameters([
'id'
]);
// Check if the required fields are set
$id = self::getParameter('id');
if (!isset($id)) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
// Get the Stripe payment intent
$stripe = new stripe();
try {
$payment_intent = $stripe->payment_intents->get(
$stripe_payment_intents->payment_intent_id->value(),
[
//'expand' => ['latest_charge'], // This is used to get the latest charge, that can be used to check if the payment has been refunded.
]
);
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
$response->success(
$payment_intent->toJSON()
);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'GET_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'get_payment_intent' => 'Get a payment intent'
]
);
$this->delete('/orders/module/stripe/payment_intent', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('delete_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the data
self::requireParameters([
'id'
]);
$id = self::fromRequest('id');
// Get the current order
$order = (new orders_o())->getOrderById((int)$id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
try {
$stripe_payment_intents->delete();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_PAYMENT_INTENT', 'Successfully deleted a payment intent (ID: ' . $id . ')');
// Return a success message
$response->success(['message' => 'Payment intent deleted successfully']);
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'DELETE_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/orders/module/stripe/payment_intent/capture', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('confirm_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
// Confirm the payment intent
$stripe = new stripe();
try {
$paymentIntent = $stripe->payment_intents->capture(
$stripe_payment_intents->payment_intent_id->value(),
[] // Since we are capturing the payment, we don't need to pass any data
);
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'CONFIRM_PAYMENT_INTENT', 'Successfully confirmed a payment intent (ID: ' . $data['id'] . ')');
// Check if the payment intent was successful
if ($paymentIntent->status !== 'succeeded') {
$response->error('Payment intent not successful', 400);
} else {
// Update the order collection to reflect the payment
$order_collection = $order->getOrderCollection();
$order_collection->paidWithStripe($paymentIntent->id);
}
// Return a success message
$response->success($paymentIntent->toJSON());
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'CONFIRM_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'confirm_payment_intent' => 'Confirm a payment intent'
]
);
$this->post('/orders/module/stripe/debug/simulate_payment', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('debug_simulate_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
// Simulate the payment
$stripe = new stripe();
//$paymentIntent = $stripe->readers->simulatePayment(
// $stripe_payment_intents->payment_intent_id->value(),
//);
$response->success('TEST_SUCCESS', 200);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'SIMULATE_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'simulate_payment_intent' => 'Simulate a payment intent, this is only for testing purposes and should under no circumstances be used in production'
]
);
}
/**
* @param mixed $data
* @param response $response
* @return mixed
*/
private function getData(mixed $data, response $response): mixed
{
if (!isset($data['customer_id'])) {
$response->error('Customer ID is required', 400);
}
if ((int)$data['customer_id'] < 1 || !is_numeric((int)$data['customer_id'])) {
$response->error('Customer ID is required', 400);
}
if (!isset($data['department_id'])) {
$response->error('Department ID is required', 400);
}
if (!isset($data['reference'])) {
$response->error('Reference is required', 400);
}
if (!isset($data['notes'])) {
$response->error('Notes is required', 400);
}
if (!isset($data['reg_1'])) {
$response->error('Registration number 1 is required', 400);
}
if (strlen($data['reg_1']) < 4) {
$response->error('Registration number 1 must be at least 4 characters', 400);
}
// Optional fields are not checked here, as they are optional and can be empty
return $data;
}
}