diff --git a/config/services.yaml b/config/services.yaml
index 00085811..12c32894 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -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:
diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php
new file mode 100644
index 00000000..f3bc0312
--- /dev/null
+++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php
@@ -0,0 +1,124 @@
+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('Webhook is enabled, no need to do anything.');
+
+ return 0;
+ } else {
+ $output->writeln('Webhook is disabled! Logging event and proceeding...');
+
+ $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('Insufficient payment amount (' . $amount_paid . '/' . $trans->getAmount() . ') for this transaction: ' . $trans->getID() . '');
+ }
+ } else {
+ $output->writeln('No payments found for transaction: ' . $trans->getID() . '');
+ }
+ } else {
+ $output->writeln('Checkout not found: ' . $checkout['error']['message'] . '');
+ }
+ }
+
+ $output->writeln('Done! Processed ' . $x . ' rows.');
+
+ return 0;
+ }
+}
diff --git a/src/Controller/CAPI/RiderAppController.php b/src/Controller/CAPI/RiderAppController.php
index 694ffdd0..bb9c8546 100644
--- a/src/Controller/CAPI/RiderAppController.php
+++ b/src/Controller/CAPI/RiderAppController.php
@@ -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)
diff --git a/src/Controller/PayMongoController.php b/src/Controller/PayMongoController.php
index 21cd864c..af698acd 100644
--- a/src/Controller/PayMongoController.php
+++ b/src/Controller/PayMongoController.php
@@ -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([
diff --git a/src/EntityListener/GatewayTransactionListener.php b/src/EntityListener/GatewayTransactionListener.php
index c9007aaf..530f0093 100644
--- a/src/EntityListener/GatewayTransactionListener.php
+++ b/src/EntityListener/GatewayTransactionListener.php
@@ -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']);
+ }
}
}
}
diff --git a/src/Service/PayMongoConnector.php b/src/Service/PayMongoConnector.php
index c34a94e7..25f380ca 100644
--- a/src/Service/PayMongoConnector.php
+++ b/src/Service/PayMongoConnector.php
@@ -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);