<?php
namespace App\Controller\Mpesa;
use App\Entity\MpesaAuth;
use App\Entity\MpesaManual;
use App\Entity\MpesaTransaction;
use App\Entity\MpesaTransactionVerification;
use App\Entity\User;
use DateTime;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerBuilder;
use PDOException;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class MpesaController extends AbstractController
{
private $host = 'api.safaricom.co.ke';
private $callbackUrlRegister = '/mpesa/c2b/v2/registerurl';
private $tokenRequest = '/oauth/v1/generate?grant_type=client_credentials';
private $authType = 'PROD';
private LoggerInterface $logger;
private ObjectManager $em;
private HttpClientInterface $client;
private KernelInterface $kernel;
//Proxy:OAuth2Token - https://api.safaricom.co.ke/oauth/v1/generate
//Proxy:C2B_v1 - https://api.safaricom.co.ke/mpesa/c2b/v1/registerurl
public function __construct(ManagerRegistry $managerRegistry, LoggerInterface $logger, HttpClientInterface $client, KernelInterface $kernel)
{
$this->logger = $logger;
$this->em = $managerRegistry->getManager();
$this->client = $client;
$this->kernel = $kernel;
}
//composer update "symfony/*" --with-all-dependencies
//
/**
* @Route("/callback", methods={"POST"}, name="mpesaCallback")
* @param Request $request
* @return Response
*/
public function mpesaCallback(Request $request): Response
{
$em = $this->em;
$this->logger->debug('MPESa Callback', [$request->getContent()]);
$json = $request->getContent();
$response = json_decode($json, true);
// 20191122063845
$transactionId = $response['TransID'];
$mpesaTransactionVerification = $em->getRepository(MpesaTransactionVerification::class)->findOneBy([
'transactionId' => $transactionId
]);
if($mpesaTransactionVerification){
$mpesa = new MpesaTransaction();
$mpesa->setTransactionId($transactionId);
$mpesa->setTransactionType($mpesaTransactionVerification->getTransactionType());
$mpesa->setTransactionTime($mpesaTransactionVerification->getTransactionTime());
$mpesa->setTransactionAmount($mpesaTransactionVerification->getTransactionAmount());
$mpesa->setShortCode($mpesaTransactionVerification->getShortCode());
$mpesa->setBillReferenceNumber($mpesaTransactionVerification->getBillReferenceNumber());
$mpesa->setAccountBalance($response['OrgAccountBalance']);
$mpesa->setMsisdn($mpesaTransactionVerification->getMsisdn());
$mpesa->setFirstName($mpesaTransactionVerification->getFirstName());
$mpesa->setMiddleName($mpesaTransactionVerification->getMiddleName());
$mpesa->setLastName($mpesaTransactionVerification->getLastName());
$mpesa->setIsComplete(true);
// $mpesa->setCreatedAt(new \DateTime('+3 hours'));
$mpesa->setCreatedAt(new \DateTime());
$mpesa->setIsValidated(true);
$mpesa->setIsSeen(false);
$mpesa->setIsUsed(false);
$mpesa->setCompletedMpesaFromIp($request->getClientIp());
try {
$em->persist($mpesa);
$em->flush();
return new Response('OK', Response::HTTP_OK);
} catch (\PDOException $exception) {
return new Response('OK', Response::HTTP_OK);
}
}else{
$this->logger->debug('MPESa Validation', [$request->getContent()]);
$json = $request->getContent();
$response = json_decode($json, true);
$transactionId = $response['TransID'];
$transactionTime = $response['TransTime'];
$from = \DateTime::createFromFormat('YmdHis', $transactionTime);
$transactionTime = $from;
$transactionType = $response['TransactionType'];
$transactionAmount = $response['TransAmount'];
$shortCode = $response['BusinessShortCode'];
$billRefNumber = $response['BillRefNumber'];
$invoiceNumber = $response['InvoiceNumber'];
$accountBalance = $response['OrgAccountBalance'];
$thirdPartyTransactionId = $response['ThirdPartyTransID'];
$msisdn = $response['MSISDN'];
$firstName = $response['FirstName'];
$secondName = isset($response['MiddleName']) ? $response['MiddleName'] : '-';
$lastName = isset($response['LastName']) ? $response['LastName'] : '-';
$mpesa = new MpesaTransaction();
$mpesa->setTransactionId($transactionId);
$mpesa->setTransactionType($transactionType);
$mpesa->setTransactionTime($transactionTime);
$mpesa->setTransactionAmount($transactionAmount);
$mpesa->setShortCode($shortCode);
$mpesa->setBillReferenceNumber($billRefNumber);
$mpesa->setAccountBalance(($accountBalance ? $accountBalance : 0));
$mpesa->setThirdPartyTransactionId($thirdPartyTransactionId);
$mpesa->setMsisdn($msisdn);
$mpesa->setFirstName($firstName);
$mpesa->setMiddleName($secondName);
$mpesa->setLastName($lastName);
$mpesa->setIsComplete(false);
$mpesa->setCreatedAt(new \DateTime());
$mpesa->setIsValidated(false);
$mpesa->setIsSeen(false);
$mpesa->setIsUsed(false);
$mpesa->setCompletedMpesaFromIp($request->getClientIp());
try {
$em->persist($mpesa);
$em->flush();
// return new Response('OK', Response::HTTP_OK);
return new JsonResponse([
"ResultCode" => '0',
"ResultDesc" => "Accepted"
], Response::HTTP_OK);
} catch (\PDOException $exception) {
return new JsonResponse([
"ResultCode" => 'C2B00016',
"ResultDesc" => "Rejected"
], Response::HTTP_OK);
}
}
// return new Response('OK', Response::HTTP_OK);
}
/**
* @Route("/callback/verify", methods={"POST"}, name="mpesaCallbackVerify")
* @param Request $request
* @return Response
*/
public function mpesaCallbackVerification(Request $request): Response
{
$this->logger->debug($request->getContent());
$jsonData = json_decode($request->getContent(), true);
$result = $jsonData['Result'];
$resultCode = $result['ResultCode'];
if($resultCode == '0') {
$resultParameters = $result['ResultParameters']['ResultParameter'];
$firstName = null;
$middleName = null;
$lastName = null;
$mpesaTransaction = new MpesaTransaction();
foreach ($resultParameters as $index => $resultParameter) {
if ($index == 0) {
$phoneName = $resultParameter['Value'];
if ($phoneName) {
$phoneNameArray = explode('-', $phoneName);
$phoneNumber = trim($phoneNameArray[0]);
$name = $phoneNameArray[1];
$namesArray = explode(" ", trim($name));
foreach ($namesArray as $index => $item) {
if ($index == 0) {
$firstName = trim($item);
} elseif ($index == 1) {
$middleName = trim($item);
} elseif ($index >= 2) {
$lastName .= trim($item);
}
}
$mpesaTransaction->setMsisdn($phoneNumber);
$mpesaTransaction->setFirstName($firstName);
$mpesaTransaction->setMiddleName($middleName ? $middleName : '-');
$mpesaTransaction->setLastName($lastName ? $lastName : '-');
}
} else if ($index == 1) {
$values = explode('-', $resultParameter['Value']);
$mpesaTransaction->setShortCode($values[0]);
} else if ($index == 3) {
$transactionTime = explode('-', $resultParameter['Value']);
$from = \DateTime::createFromFormat('YmdHis', $transactionTime[0]);
$mpesaTransaction->setTransactionTime($from);
} else if ($index == 10) {
$amount = $resultParameter['Value'];
$mpesaTransaction->setTransactionAmount($amount);
} else if ($index == 12) {
$transactionID = $resultParameter['Value'];
$mpesaTransaction->setTransactionId($transactionID);
}
}
//
$mpesaTransaction->setTransactionType("Paybill");
$mpesaTransaction->setIsValidated(false);
$mpesaTransaction->setCreatedAt(new DateTime());
$mpesaTransaction->setBillReferenceNumber('billReference');
$mpesaTransaction->setAccountBalance(0);
$mpesaTransaction->setIsComplete(true);
$mpesaTransaction->setIsSeen(false);
$mpesaTransaction->setIsUsed(false);
$mpesaManual = new MpesaManual();
$mpesaManual->setMpesaTransaction($mpesaTransaction);
$mpesaManual->setCreatedBy(null);
$mpesaManual->setCreatedAt(new DateTime());
$mpesaAuth = $this->em->getRepository(MpesaAuth::class)->findOneBy([
'tillNumber' => $mpesaTransaction->getShortCode()
]);
$mpesaManual->setBranch($mpesaAuth->getBranch());
try {
$user = $this->em->getRepository(User::class)->findOneBy([
'id' => 17
]);
$mpesaManual->setCreatedBy($user);
$this->em->persist($mpesaTransaction);
$this->em->persist($mpesaManual);
$this->em->flush();
} catch (\Exception $error) {
$this->logger->debug($error->getMessage());
}
}
return new Response('OK', Response::HTTP_OK);
}
/**
* @Route("/callback/verify/timeout", methods={"POST"}, name="mpesaCallbackVerifyTimeout")
* @param Request $request
* @return Response
*/
public function mpesaCallbackVerificationTimeout(Request $request): Response
{
$this->logger->debug($request->getContent());
return new Response('OK', Response::HTTP_OK);
}
/**
* @Route("/callback/validation", methods={"POST"}, name="mpesaCallbackValidation")
* @param Request $request
* @return Response
*/
public function mpesaCallbackValidation(Request $request): JsonResponse|Response
{
$em = $this->em;
$this->logger->debug('MPESa Validation', [$request->getContent()]);
$json = $request->getContent();
$response = json_decode($json, true);
$transactionId = $response['TransID'];
$transactionTime = $response['TransTime'];
$from = \DateTime::createFromFormat('YmdHis', $transactionTime);
$transactionTime = $from;
$transactionType = $response['TransactionType'];
$transactionAmount = $response['TransAmount'];
$shortCode = $response['BusinessShortCode'];
$billRefNumber = $response['BillRefNumber'];
$invoiceNumber = $response['InvoiceNumber'];
$accountBalance = $response['OrgAccountBalance'];
$thirdPartyTransactionId = $response['ThirdPartyTransID'];
$msisdn = $response['MSISDN'];
$firstName = $response['FirstName'];
$secondName = isset($response['MiddleName']) ? $response['MiddleName'] : '-';
$lastName = isset($response['LastName']) ? $response['LastName'] : '-';
$mpesa = new MpesaTransactionVerification();
$mpesa->setTransactionId($transactionId);
$mpesa->setTransactionType($transactionType);
$mpesa->setTransactionTime($transactionTime);
$mpesa->setTransactionAmount($transactionAmount);
$mpesa->setShortCode($shortCode);
$mpesa->setBillReferenceNumber($billRefNumber);
$mpesa->setAccountBalance(($accountBalance ? $accountBalance : 0));
$mpesa->setThirdPartyTransactionId($thirdPartyTransactionId);
$mpesa->setMsisdn($msisdn);
$mpesa->setFirstName($firstName);
$mpesa->setMiddleName($secondName);
$mpesa->setLastName($lastName);
$mpesa->setIsComplete(false);
$mpesa->setCreatedAt(new \DateTime());
$mpesa->setIsValidated(false);
$mpesa->setIsSeen(false);
$mpesa->setIsUsed(false);
$mpesa->setCompletedMpesaFromIp($request->getClientIp());
try {
$em->persist($mpesa);
$em->flush();
// return new Response('OK', Response::HTTP_OK);
return new JsonResponse([
"ResultCode" => '0',
"ResultDesc" => "Accepted"
], Response::HTTP_OK);
} catch (\PDOException $exception) {
return new JsonResponse([
"ResultCode" => 'C2B00016',
"ResultDesc" => "Rejected"
], Response::HTTP_OK);
}
}
/**
* @Route("/callback/register/{shortCode}", name="register_callback_urls")
* @return Response|null
*/
function paymentUrlRegister(Request $request, $shortCode): ?Response
{
$em = $this->getDoctrine()->getManager();
/** @var MpesaAuth $credential */
$credential = $em->getRepository(MpesaAuth::class)->findOneBy([
'authType' => $this->authType,
'tillNumber' => $shortCode
]);
if (!$credential) {
return new Response('Credentials not found', Response::HTTP_OK, [
'content-type' => 'application/json'
]);
}
dump("Get credentials");
$response = [];
$data = [
'ShortCode' => $credential->getTillNumber(),
'ResponseType' => 'Completed',
'ConfirmationURL' => 'https://neema.ohau.co.ke/callback',
'ValidationURL' => 'https://neema.ohau.co.ke/callback/validation'
];
$url = "https://{$this->host}{$this->callbackUrlRegister}";
try {
$response = $this->client->request(
"POST",
$url,
[
'headers' => [
"Content-Type:application/json",
"Authorization:Bearer {$credential->getToken()}"
],
'json' => $data
],
);
$responseData = $response->getContent();
$this->logger->debug("ok - {$responseData}_");
return new Response($response->getContent(), Response::HTTP_OK);
} catch (ClientExceptionInterface|ClientException|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $e) {
$this->logger->debug($e->getMessage());
return new Response($e->getMessage(), Response::HTTP_OK);
}
}
/**
* @Route("/mpesa/mpesa_credentials/{shortCode}", name="renew_mpesa_authentication")
*/
function getMpesaAuthentication(Request $request, $shortCode)
{
$em = $this->em;
$ip = $request->getClientIp();
$date = new \DateTime();
$date = date_format($date, 'YmdHis');
/** @var MpesaAuth $credential */
$credential = $em->getRepository(MpesaAuth::class)->findOneBy([
'authType' => $this->authType,
'tillNumber' => $shortCode
]);
$date = new \DateTime();
$date = date_format($date, 'YmdHis');
$url = "https://{$this->host}{$this->tokenRequest}";
$password = base64_encode("{$credential->getConsumerKey()}:{$credential->getConsumerSecret()}");
try {
$response = $this->client->request(
"GET",
$url,
[
'headers' => [
"Content-Type:application/json",
"Authorization:Basic {$password}"
]
]
);
$responseData = $response->getContent();
// dump($responseData); die;
$responseDataArray = json_decode($responseData, true);
$credential->setToken($responseDataArray['access_token']);
$credential->setTokenUpdatedAt(new \DateTime());
$em->flush();
$this->logger->debug("ok - {$responseData}_");
return new Response($response->getContent(), Response::HTTP_OK);
} catch (ClientExceptionInterface|ClientException|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $e) {
$this->logger->debug($e->getMessage());
return new Response($e->getMessage(), Response::HTTP_OK);
}
}
#[Route('/imports', name: 'app_admin_imports_k')]
public function importMpesaTransactions(){
$path = $this->kernel->getProjectDir() .'/reports'. '/imports' . '/mpesa_18_19.csv';
$rowNo = 1;
$availableStudents = 0;
$sumAllocation = 0;
// $fp is file pointer to file sample.csv
$count = 0;
$total = 0;
if (($fp = fopen($path, "r")) !== FALSE) {
while (($row = fgetcsv($fp, 1000, ",")) !== FALSE) {
$num = count($row);
// dump("<p> $num fields in line $rowNo: <br /></p>\n");
$rowNo++;
$mpesaTransaction = $this->em->getRepository(MpesaTransaction::class)->findOneBy([
'transactionId' => $row[0]
]);
if($mpesaTransaction){
$mpesaTransaction->setMsisdn($row[7]);
}
if(!$mpesaTransaction) {
$k = $row[1].' '.$row[2];
// "18-06-2024 14:30:47"
// dump($k);
$dateTime = DateTime::createFromFormat('d-m-Y H:i:s',$k);
// $datetime = DateTime::createFromFormat('d/m/Y', $date_str);
// dump($dateTime);
$mpesaTransaction = new MpesaTransaction();
$mpesaTransaction->setCreatedAt(new DateTime());
$mpesaTransaction->setTransactionId($row[0]);
$mpesaTransaction->setTransactionType($row[6]);
$mpesaTransaction->setFirstName($row[8]);
$mpesaTransaction->setTransactionTime($dateTime);
$mpesaTransaction->setShortCode($row[3]);
$mpesaTransaction->setMsisdn($row[7]);
$mpesaTransaction->setIsUsed(false);
$mpesaTransaction->setIsComplete(true);
$mpesaTransaction->setTransactionAmount($row[4]);
$mpesaTransaction->setBillReferenceNumber($row[0]);
$mpesaTransaction->setAccountBalance($row[4]);
$mpesaTransaction->setIsSeen(false);
$mpesaTransaction->setIsValidated(false);
$this->em->persist($mpesaTransaction);
$count++;
}
}
dump($count);
dump($total);
fclose($fp);
$this->em->flush();
dump($availableStudents);
dump($sumAllocation);
// $this->em->flush();
}
die;
}
}