From 1fd883b07b80d6a77ee5677bff4f9efbbb17c73d Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Mon, 26 Aug 2024 08:01:00 +0800 Subject: [PATCH] Consolidate subscription setup payment intent checking and initial activation when applicable #799 --- config/routes/apiv2.yaml | 16 +++- config/services.yaml | 1 + .../CustomerAppAPI/SubscriptionController.php | 84 ++++++++++++++++++- .../CustomerAppAPI/VehicleController.php | 7 +- src/Entity/Subscription.php | 9 +- src/Service/PayMongoConnector.php | 5 ++ 6 files changed, 111 insertions(+), 11 deletions(-) diff --git a/config/routes/apiv2.yaml b/config/routes/apiv2.yaml index 5faa26dc..7cc70e46 100644 --- a/config/routes/apiv2.yaml +++ b/config/routes/apiv2.yaml @@ -336,7 +336,17 @@ apiv2_subscription_create: controller: App\Controller\CustomerAppAPI\SubscriptionController::createSubscription methods: [POST] -apiv2_subscription_payment_intent: - path: /apiv2/subscription/payment_intent/{pid} - controller: App\Controller\CustomerAppAPI\SubscriptionController::getPaymentIntent +apiv2_subscription_finalize: + path: /apiv2/subscription/{id}/finalize + controller: App\Controller\CustomerAppAPI\SubscriptionController::finalizeSubscription methods: [GET] + +#apiv2_subscription_payment_intent: +# path: /apiv2/subscription/payment_intent/{pi_id} +# controller: App\Controller\CustomerAppAPI\SubscriptionController::getPaymentIntent +# methods: [GET] + +#apiv2_subscription_activate: +# path: /apiv2/subscription/{id}/activate +# controller: App\Controller\CustomerAppAPI\SubscriptionController::activateSubscription +# methods: [POST] diff --git a/config/services.yaml b/config/services.yaml index f2db62c5..c8b61c7a 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -23,6 +23,7 @@ parameters: subscription_paymongo_public_key: "%env(SUBSCRIPTION_PAYMONGO_PUBLIC_KEY)%" subscription_paymongo_secret_key: "%env(SUBSCRIPTION_PAYMONGO_SECRET_KEY)%" subscription_paymongo_webhook_id: "%env(SUBSCRIPTION_PAYMONGO_WEBHOOK_ID)%" + subscription_months: "%env(SUBSCRIPTION_MONTHS)%" services: # default configuration for services in *this* file diff --git a/src/Controller/CustomerAppAPI/SubscriptionController.php b/src/Controller/CustomerAppAPI/SubscriptionController.php index f178c840..e0ac7aea 100644 --- a/src/Controller/CustomerAppAPI/SubscriptionController.php +++ b/src/Controller/CustomerAppAPI/SubscriptionController.php @@ -148,6 +148,74 @@ class SubscriptionController extends ApiController ]); } + public function finalizeSubscription(Request $req, $id, PayMongoConnector $pm) + { + // check requirements + $validity = $this->validateRequest($req); + + if (!$validity['is_valid']) { + return new ApiResponse(false, $validity['error']); + } + + // initialize paymongo connector + $this->initializeSubscriptionPayMongoConnector($pm); + + // get subscription + $sub_obj = $this->em->getRepository(Subscription::class)->findOneBy([ + 'id' => $id, + 'status' => SubscriptionStatus::PENDING, + 'customer' => $this->session->getCustomer(), + ]); + + if (empty($sub_obj)) { + return new ApiResponse(false, 'Invalid subscription provided.'); + } + + // get paymongo subscription so we can verify if the latest invoice is paid or not + $pm_sub = $pm->getSubscription($sub_obj->getExtApiId()); + if (empty($pm_sub['response']['data']['id'])) { + return new ApiResponse(false, 'Error retrieving subscription. Please try again later.'); + } + + // make sure the latest invoice has been paid + // NOTE: ignore this, this is unreliable due to race condition + /* + if ($pm_sub['response']['data']['attributes']['latest_invoice']['status'] !== 'paid') { + return new ApiResponse(false, 'Latest invoice for subscription is not yet paid.'); + } + */ + + // get payment intent + $pi = $pm->getPaymentIntent($pm_sub['response']['data']['attributes']['latest_invoice']['payment_intent']['id']); + if (empty($pi['response']['data']['id'])) { + return new ApiResponse(false, 'Error retrieving payment intent. Please try again later.'); + } + + // if the paymongo sub is active, and the payment was successful, update the internal subscription status + if ( + $sub_obj->getStatus() === SubscriptionStatus::PENDING && + $pi['response']['data']['attributes']['status'] === 'succeeded' + ) { + $sub_start_date = new DateTime(); + $sub_end_date = clone $sub_start_date; + $sub_end_date->modify('+' . $this->getParameter('subscription_months') . ' month'); + + $sub_obj->setStatus(SubscriptionStatus::ACTIVE) + ->setDateStart($sub_start_date) + ->setDateEnd($sub_end_date); + + $this->em->flush(); + } + + error_log("PI STATUS: " . $pi['response']['data']['attributes']['status']); + + // response + return new ApiResponse(true, '', [ + 'payment_intent' => $pi['response']['data'], + ]); + } + + /* public function getPaymentIntent(Request $req, $pid, PayMongoConnector $pm) { // check requirements @@ -172,7 +240,7 @@ class SubscriptionController extends ApiController ]); } - public function setSubscriptionAsPaid(Request $req, $sub_id, PayMongoConnector $pm) + public function activateSubscription(Request $req, $id, PayMongoConnector $pm) { // check requirements $validity = $this->validateRequest($req); @@ -186,7 +254,7 @@ class SubscriptionController extends ApiController // get subscription $obj = $this->em->getRepository(Subscription::class)->findOneBy([ - 'id' => $sub_id, + 'id' => $id, 'status' => SubscriptionStatus::PENDING, 'customer' => $this->session->getCustomer(), ]); @@ -195,6 +263,17 @@ class SubscriptionController extends ApiController return new ApiResponse(false, 'Invalid subscription provided.'); } + // get paymongo subscription so we can verify if the latest invoice is paid or not + $pm_sub = $pm->getSubscription($obj->getExtApiId()); + if (empty($pm_sub['response']['data']['id'])) { + return new ApiResponse(false, 'Error retrieving subscription. Please try again later.'); + } + + // make sure the latest invoice has been paid + if ($pm_sub['response']['data']['attributes']['latest_invoice']['status'] !== 'succeeded') { + return new ApiResponse(false, 'Latest invoice for subscription is not yet paid.'); + } + // mark subscription as paid $obj->setStatus(SubscriptionStatus::ACTIVE) ->setDateStart(new DateTime()); @@ -206,4 +285,5 @@ class SubscriptionController extends ApiController 'success' => true, ]); } + */ } diff --git a/src/Controller/CustomerAppAPI/VehicleController.php b/src/Controller/CustomerAppAPI/VehicleController.php index 0ca16872..3c62ebec 100644 --- a/src/Controller/CustomerAppAPI/VehicleController.php +++ b/src/Controller/CustomerAppAPI/VehicleController.php @@ -16,6 +16,7 @@ use App\Ramcar\JOStatus; use App\Ramcar\ServiceType; use App\Ramcar\TradeInType; use App\Ramcar\InsuranceApplicationStatus; +use App\Ramcar\SubscriptionStatus; use App\Service\PayMongoConnector; use App\Service\PriceTierManager; use DateTime; @@ -518,9 +519,9 @@ class VehicleController extends ApiController // get active subscription row if ($include_active_sub) { $active_sub = null; - $sobj = $cv->getLatestActiveSubscription(); - if (!empty($sobj)) { - + $sobj = $cv->getLatestSubscription(); + if (!empty($sobj) && $sobj->getStatus() == SubscriptionStatus::ACTIVE) { + $active_sub = $sobj; } $row['active_subscription'] = $active_sub; diff --git a/src/Entity/Subscription.php b/src/Entity/Subscription.php index 1db78332..344279e5 100644 --- a/src/Entity/Subscription.php +++ b/src/Entity/Subscription.php @@ -159,7 +159,8 @@ class Subscription public function setStatus($status) { - return $this->status = $status; + $this->status = $status; + return $this; } public function getStatus() @@ -169,7 +170,8 @@ class Subscription public function setExtApiId($ext_api_id) { - return $this->ext_api_id = $ext_api_id; + $this->ext_api_id = $ext_api_id; + return $this; } public function getExtApiId() @@ -179,7 +181,8 @@ class Subscription public function setMetadata($metadata) { - return $this->metadata = $metadata; + $this->metadata = $metadata; + return $this; } public function getMetadata() diff --git a/src/Service/PayMongoConnector.php b/src/Service/PayMongoConnector.php index 5b1ff86b..dcc2d509 100644 --- a/src/Service/PayMongoConnector.php +++ b/src/Service/PayMongoConnector.php @@ -281,6 +281,11 @@ class PayMongoConnector return $this->doRequest('/v1/subscriptions', 'POST', $body); } + public function getSubscription($sub_id) + { + return $this->doRequest('/v1/subscriptions/'. $sub_id, 'GET'); + } + public function getPaymentIntent($pi_id) { return $this->doRequest('/v1/payment_intents/' . $pi_id, 'GET');