diff --git a/services/nginx/app/modules/stripe/endpoints/stripe_endpoint_payment_intents.php b/services/nginx/app/modules/stripe/endpoints/stripe_endpoint_payment_intents.php index a2dcd2d4..453014da 100644 --- a/services/nginx/app/modules/stripe/endpoints/stripe_endpoint_payment_intents.php +++ b/services/nginx/app/modules/stripe/endpoints/stripe_endpoint_payment_intents.php @@ -26,13 +26,15 @@ class stripe_endpoint_payment_intents /** * Get a payment intent by ID * @param string $id + * @param array|null $params + * @param array|null $opts * @return PaymentIntent * @throws ApiErrorException * @throws Exception */ - public function get(string $id): PaymentIntent + public function get(string $id, array $params = null, array $opts = null): PaymentIntent { - return self::getClient()->paymentIntents->retrieve($id); + return self::getClient()->paymentIntents->retrieve($id, $params, $opts); } /** diff --git a/services/nginx/app/objects/collected_order_invoices_o.php b/services/nginx/app/objects/collected_order_invoices_o.php index 7e6d3f6c..117766bd 100644 --- a/services/nginx/app/objects/collected_order_invoices_o.php +++ b/services/nginx/app/objects/collected_order_invoices_o.php @@ -8,6 +8,10 @@ use classes\object_property; use Exception; use traits\db_object_t; +const ECONOMIC_PROCESSOR = 1; +const STRIPE_PROCESSOR = 2; +const OTHER_PROCESSOR = 3; + class collected_order_invoices_o extends db { use db_object_t; @@ -30,9 +34,9 @@ class collected_order_invoices_o extends db * @var array|string[] */ public array $processors = [ - 1 => 'E-conomic', - 2 => 'Stripe', - 3 => 'Other, without tracking', + ECONOMIC_PROCESSOR => 'E-conomic', + STRIPE_PROCESSOR => 'Stripe', + OTHER_PROCESSOR => 'Other, without tracking', ]; public function structure(): void @@ -965,4 +969,34 @@ class collected_order_invoices_o extends db { return $price * 1.20; } + + /** + * Set the invoice collection as paid with Stripe + * @throws Exception If the request was not successful + * @throws Exception If the invoice collection is not set + * @throws Exception If the invoice collection is already closed + * @throws Exception If the invoice collection is already booked + */ + public function paidWithStripe(string $stripe_payment_intent_id): void + { + // Require the invoice collection to be selected + self::requireSelected(); + // Require the invoice collection to be open + self::requireOpen(); + // Require the processor to be Stripe (or null) + if (!empty($this->processor->value()) && $this->processor->value() != 2) { + throw new Exception('Invoice collection is not paid with Stripe'); + } + // Set the stripe payment intent id + $this->external_id->set($stripe_payment_intent_id); + // Set the processor to Stripe + $this->processor->set(STRIPE_PROCESSOR); + // Set the closed at date (if not already set) + if (empty($this->closed_at->value())) { + $this->closed_at->set(date('Y-m-d H:i:s')); + } + + // Object changed + self::objectChanged(); + } } \ No newline at end of file diff --git a/services/nginx/app/objects/orders_o.php b/services/nginx/app/objects/orders_o.php index 6fc61c43..42bf1c79 100644 --- a/services/nginx/app/objects/orders_o.php +++ b/services/nginx/app/objects/orders_o.php @@ -515,4 +515,20 @@ class orders_o extends db } return $count; } + + /** + * Get the order collection for the order + * @return collected_order_invoices_o The order collection + * @throws Exception If the order is not selected + */ + public function getOrderCollection(): collected_order_invoices_o + { + self::requireSelected(); + $order_collection = new collected_order_invoices_o(); + $order_collection->select($this->invoice_collection_id->value()); + if (!$order_collection->exists()) { + throw new Exception('Order collection not found'); + } + return $order_collection; + } } \ No newline at end of file diff --git a/services/nginx/app/routes/ordersRoute.php b/services/nginx/app/routes/ordersRoute.php index fbbdeb9a..a07d411a 100644 --- a/services/nginx/app/routes/ordersRoute.php +++ b/services/nginx/app/routes/ordersRoute.php @@ -403,7 +403,10 @@ class ordersRoute $stripe = new stripe(); try { $payment_intent = $stripe->payment_intents->get( - $stripe_payment_intents->payment_intent_id->value() + $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); @@ -465,6 +468,66 @@ class ordersRoute } }); + $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;