Consolidate subscription setup payment intent checking and initial activation when applicable #799

This commit is contained in:
Ramon Gutierrez 2024-08-26 08:01:00 +08:00
parent 40c629eee3
commit 1fd883b07b
6 changed files with 111 additions and 11 deletions

View file

@ -336,7 +336,17 @@ apiv2_subscription_create:
controller: App\Controller\CustomerAppAPI\SubscriptionController::createSubscription controller: App\Controller\CustomerAppAPI\SubscriptionController::createSubscription
methods: [POST] methods: [POST]
apiv2_subscription_payment_intent: apiv2_subscription_finalize:
path: /apiv2/subscription/payment_intent/{pid} path: /apiv2/subscription/{id}/finalize
controller: App\Controller\CustomerAppAPI\SubscriptionController::getPaymentIntent controller: App\Controller\CustomerAppAPI\SubscriptionController::finalizeSubscription
methods: [GET] 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]

View file

@ -23,6 +23,7 @@ parameters:
subscription_paymongo_public_key: "%env(SUBSCRIPTION_PAYMONGO_PUBLIC_KEY)%" subscription_paymongo_public_key: "%env(SUBSCRIPTION_PAYMONGO_PUBLIC_KEY)%"
subscription_paymongo_secret_key: "%env(SUBSCRIPTION_PAYMONGO_SECRET_KEY)%" subscription_paymongo_secret_key: "%env(SUBSCRIPTION_PAYMONGO_SECRET_KEY)%"
subscription_paymongo_webhook_id: "%env(SUBSCRIPTION_PAYMONGO_WEBHOOK_ID)%" subscription_paymongo_webhook_id: "%env(SUBSCRIPTION_PAYMONGO_WEBHOOK_ID)%"
subscription_months: "%env(SUBSCRIPTION_MONTHS)%"
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file

View file

@ -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) public function getPaymentIntent(Request $req, $pid, PayMongoConnector $pm)
{ {
// check requirements // 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 // check requirements
$validity = $this->validateRequest($req); $validity = $this->validateRequest($req);
@ -186,7 +254,7 @@ class SubscriptionController extends ApiController
// get subscription // get subscription
$obj = $this->em->getRepository(Subscription::class)->findOneBy([ $obj = $this->em->getRepository(Subscription::class)->findOneBy([
'id' => $sub_id, 'id' => $id,
'status' => SubscriptionStatus::PENDING, 'status' => SubscriptionStatus::PENDING,
'customer' => $this->session->getCustomer(), 'customer' => $this->session->getCustomer(),
]); ]);
@ -195,6 +263,17 @@ class SubscriptionController extends ApiController
return new ApiResponse(false, 'Invalid subscription provided.'); 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 // mark subscription as paid
$obj->setStatus(SubscriptionStatus::ACTIVE) $obj->setStatus(SubscriptionStatus::ACTIVE)
->setDateStart(new DateTime()); ->setDateStart(new DateTime());
@ -206,4 +285,5 @@ class SubscriptionController extends ApiController
'success' => true, 'success' => true,
]); ]);
} }
*/
} }

View file

@ -16,6 +16,7 @@ use App\Ramcar\JOStatus;
use App\Ramcar\ServiceType; use App\Ramcar\ServiceType;
use App\Ramcar\TradeInType; use App\Ramcar\TradeInType;
use App\Ramcar\InsuranceApplicationStatus; use App\Ramcar\InsuranceApplicationStatus;
use App\Ramcar\SubscriptionStatus;
use App\Service\PayMongoConnector; use App\Service\PayMongoConnector;
use App\Service\PriceTierManager; use App\Service\PriceTierManager;
use DateTime; use DateTime;
@ -518,9 +519,9 @@ class VehicleController extends ApiController
// get active subscription row // get active subscription row
if ($include_active_sub) { if ($include_active_sub) {
$active_sub = null; $active_sub = null;
$sobj = $cv->getLatestActiveSubscription(); $sobj = $cv->getLatestSubscription();
if (!empty($sobj)) { if (!empty($sobj) && $sobj->getStatus() == SubscriptionStatus::ACTIVE) {
$active_sub = $sobj;
} }
$row['active_subscription'] = $active_sub; $row['active_subscription'] = $active_sub;

View file

@ -159,7 +159,8 @@ class Subscription
public function setStatus($status) public function setStatus($status)
{ {
return $this->status = $status; $this->status = $status;
return $this;
} }
public function getStatus() public function getStatus()
@ -169,7 +170,8 @@ class Subscription
public function setExtApiId($ext_api_id) 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() public function getExtApiId()
@ -179,7 +181,8 @@ class Subscription
public function setMetadata($metadata) public function setMetadata($metadata)
{ {
return $this->metadata = $metadata; $this->metadata = $metadata;
return $this;
} }
public function getMetadata() public function getMetadata()

View file

@ -281,6 +281,11 @@ class PayMongoConnector
return $this->doRequest('/v1/subscriptions', 'POST', $body); 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) public function getPaymentIntent($pi_id)
{ {
return $this->doRequest('/v1/payment_intents/' . $pi_id, 'GET'); return $this->doRequest('/v1/payment_intents/' . $pi_id, 'GET');