Merge branch 'master' into 800-automatically-reject-jo-if-no-riders-or-inventory

This commit is contained in:
Ramon Gutierrez 2024-06-04 17:49:39 +08:00
commit 90838b004a
6 changed files with 215 additions and 32 deletions

View file

@ -109,6 +109,12 @@ services:
arguments:
$callback_url: "%env(WARRANTY_SERIAL_CALLBACK_URL)%"
App\Command\ProcessLatePaymongoTransactionsCommand:
arguments:
$em: "@doctrine.orm.entity_manager"
$paymongo: "@App\\Service\\PayMongoConnector"
$webhook_id: "%env(PAYMONGO_WEBHOOK_ID)%"
# rider tracker service
App\Service\RiderTracker:
arguments:

View file

@ -0,0 +1,124 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Doctrine\ORM\EntityManagerInterface;
use App\Ramcar\TransactionStatus;
use App\Entity\GatewayTransaction;
use App\Service\PayMongoConnector;
use DateTime;
class ProcessLatePaymongoTransactionsCommand extends Command
{
protected $em;
protected $paymongo;
protected $webhook_id;
public function __construct(EntityManagerInterface $em, PayMongoConnector $paymongo, $webhook_id)
{
$this->em = $em;
$this->paymongo = $paymongo;
$this->webhook_id = $webhook_id;
parent::__construct();
}
protected function configure()
{
$this->setName('paymongo:checkpending')
->setDescription('Check for any late PayMongo transactions and process if needed.')
->setHelp('Check for any late PayMongo transactions and process if needed.')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Ignore webhook status and process anyway.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$force = $input->getOption('force');
// if we aren't forcing, check webhook status first
if (!$force) {
$output->writeln('Checking webhook status...');
// check if webhook is disabled
$webhook = $this->paymongo->getWebhook($this->webhook_id);
if ($webhook['success'] && $webhook['response']['data']['attributes']['status'] === 'enabled') {
$output->writeln('<info>Webhook is enabled, no need to do anything.</info>');
return 0;
} else {
$output->writeln('<comment>Webhook is disabled! Logging event and proceeding...</comment>');
$this->paymongo->log('WEBHOOK', "[]", json_encode($webhook['response'], JSON_PRETTY_PRINT), 'webhook');
}
}
$output->writeln('Fetching all late pending transactions...');
// set date threshold to 24 hours ago
$date_threshold = (new DateTime())->modify('-24 hours');
$transactions = $this->em->getRepository(GatewayTransaction::class)
->createQueryBuilder('t')
->select('t')
->where('t.status = :status')
->andWhere('t.date_create <= :date_threshold')
->setParameter('status', TransactionStatus::PENDING)
->setParameter('date_threshold', $date_threshold)
->getQuery()
->getResult();
$output->writeln('Found '. count($transactions) . ' rows matching criteria.');
$x = 0;
foreach ($transactions as $trans) {
// check paymongo status
$checkout = $this->paymongo->getCheckout($trans->getExtTransactionId());
if ($checkout['success']) {
// check if we have any payments made
$payments = $checkout['response']['data']['attributes']['payments'] ?? [];
if (!empty($payments)) {
$amount_paid = 0;
// for good measure, we get all successful payments and add them up
foreach ($payments as $payment) {
if ($payment['attributes']['status'] === TransactionStatus::PAID) {
$amount_paid = bcadd($amount_paid, $payment['attributes']['amount']);
}
}
// this transaction is fully paid, so we mark it as paid
if (bccomp($trans->getAmount(), $amount_paid) <= 0) {
$trans->setStatus(TransactionStatus::PAID);
$trans->setDatePay(new DateTime());
$this->em->flush();
$output->writeln('Marked transaction '. $trans->getID() . ' as paid.');
$x++;
} else {
$output->writeln('<comment>Insufficient payment amount (' . $amount_paid . '/' . $trans->getAmount() . ') for this transaction: ' . $trans->getID() . '</comment>');
}
} else {
$output->writeln('<comment>No payments found for transaction: ' . $trans->getID() . '</comment>');
}
} else {
$output->writeln('<comment>Checkout not found: ' . $checkout['error']['message'] . '</comment>');
}
}
$output->writeln('<info>Done! Processed ' . $x . ' rows.</info>');
return 0;
}
}

View file

@ -413,7 +413,9 @@ class RiderAppController extends ApiController
return new APIResponse(false, $msg);
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// TODO: refactor this into a jo handler class, so we don't have to repeat for control center
@ -466,7 +468,9 @@ class RiderAppController extends ApiController
return new APIResponse(true, $msg);
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// requeue it, instead of cancelling it
$jo->requeue();
@ -527,7 +531,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB);
@ -570,7 +576,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB_PRE_JO);
@ -613,7 +621,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB_PRE_JO);
@ -656,7 +666,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_START);
@ -700,7 +712,9 @@ class RiderAppController extends ApiController
$jo->setStatus(JOStatus::IN_PROGRESS);
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE);
@ -761,7 +775,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB);
@ -854,7 +870,9 @@ class RiderAppController extends ApiController
return new APIResponse(false, $msg);
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// need to check if service type is battery sales
// if so, serial is a required parameter
@ -1002,7 +1020,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB_POST_JO);
@ -1046,7 +1066,9 @@ class RiderAppController extends ApiController
$jo = $rider->getCurrentJobOrder();
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// set delivery status
$jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB_POST_JO);
@ -1265,7 +1287,9 @@ class RiderAppController extends ApiController
$jo = $em->getRepository(JobOrder::class)->find($jo_id);
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// check if we have trade in items
$ti_items = [];
@ -1376,7 +1400,9 @@ class RiderAppController extends ApiController
return new APIResponse(false, $msg);
// check if JO can be modified first
$this->checkJOProgressionAllowed($jo, $rider);
if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) {
return new APIResponse(false, 'Job order can no longer be modified.');
}
// check service type
$stype_id = $req->request->get('stype_id');
@ -1740,22 +1766,40 @@ class RiderAppController extends ApiController
return $msg;
}
protected function checkJOProgressionAllowed(JobOrder $jo, $rider)
protected function checkJOProgressionAllowed(EntityManagerInterface $em, JobOrder $jo, &$rider)
{
$allowed = true;
error_log("JO delivery status is " . $jo->getDeliveryStatus() . " (not allowed: " . DeliveryStatus::CANCELLED . ")");
error_log("JO status is " . $jo->getStatus() . " (not allowed: " . JOStatus::CANCELLED . ")");
// TODO: add more statuses to block if needed, hence. this is a failsafe in case MQTT is not working.
// check delivery status
switch ($jo->getDeliveryStatus())
{
case DeliveryStatus::CANCELLED:
$allowed = false;
break;
}
// check JO status as well
switch ($jo->getStatus())
{
case JOStatus::CANCELLED:
// if this is the rider's current JO, set to null
if ($rider->getCurrentJobOrder() === $jo) {
$rider->setCurrentJobOrder();
}
return new APIResponse(false, 'Job order can no longer be modified.');
$allowed = false;
break;
default:
return true;
}
// if this is the rider's current JO, set to null
if (!$allowed) {
if ($rider->getCurrentJobOrder() === $jo) {
$rider->setCurrentJobOrder();
$em->persist($rider);
$em->flush();
}
}
return $allowed;
}
protected function debugRequest(Request $req)

View file

@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use DateTime;
class PayMongoController extends Controller
{
protected $pm;
@ -45,10 +47,8 @@ class PayMongoController extends Controller
switch ($event_name) {
case "payment.paid":
return $this->handlePaymentPaid($event);
break;
case "payment.failed":
return $this->handlePaymentPaid($event);
break;
return $this->handlePaymentFailed($event);
case "payment.refunded": // TODO: handle refunds
case "payment.refund.updated":
case "checkout_session.payment.paid":
@ -69,6 +69,7 @@ class PayMongoController extends Controller
if (!empty($obj)) {
// mark as paid
$obj->setStatus(TransactionStatus::PAID);
$obj->setDatePay(new DateTime());
$this->em->flush();
}
@ -77,7 +78,7 @@ class PayMongoController extends Controller
]);
}
protected function handlePaymentFailed(Request $req)
protected function handlePaymentFailed($event)
{
// TODO: do something about failed payments?
return $this->json([

View file

@ -58,17 +58,20 @@ class GatewayTransactionListener
'gateway_transaction' => $gt_obj,
]);
if (!empty($obj)) {
// make sure the object exists and has not been processed yet
if (!empty($obj) && $obj->getStatus() === InsuranceApplicationStatus::CREATED) {
// mark as paid
$obj->setDatePay(new DateTime());
$obj->setStatus(InsuranceApplicationStatus::PAID);
$this->em->flush();
}
// flag on api as paid
$result = $this->ic->tagApplicationPaid($obj->getExtTransactionId());
if (!$result['success'] || $result['response']['transaction_code'] !== 'GR004') {
error_log("INSURANCE MARK AS PAID FAILED FOR " . $obj->getID() . ": " . $result['error']['message']);
// flag on api as paid
$result = $this->ic->tagApplicationPaid($obj->getExtTransactionId());
// something went wrong with insurance api
if (!$result['success'] || $result['response']['transaction_code'] !== 'GR004') {
error_log("INSURANCE MARK AS PAID FAILED FOR " . $obj->getID() . ": " . $result['error']['message']);
}
}
}
}

View file

@ -74,6 +74,11 @@ class PayMongoConnector
return $this->doRequest('/v1/checkout_sessions/' . $checkout_id, 'GET');
}
public function getWebhook($id)
{
return $this->doRequest('/v1/webhooks/'. $id, 'GET');
}
protected function generateHash()
{
return base64_encode($this->secret_key);