src/Controller/PaymentController.php line 363
<?php/** The Payment controller contains methods for the following payment gateways:* BarclaysSmart Pay - http://www.barclaycard.com/smartpay/documentation/pdf/SmartPay_HPP_IntegrationGuide.pdf* Authorize.net - https://developer.authorize.net/guides/DPM/wwhelp/wwhimpl/js/html/wwhelp.htm*/namespace App\Controller;use Doctrine\Persistence\ManagerRegistry;use Symfony\Bridge\Twig\Mime\TemplatedEmail;use Symfony\Bridge\Twig\Mime\WrappedTemplatedEmail;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\RedirectResponse;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Mailer\Mailer;use Symfony\Component\Mailer\MailerInterface;use Symfony\Component\Mailer\Transport;use Symfony\Component\Mime\Email;use Symfony\Component\Routing\Annotation\Route;use Symfony\Component\Routing\Generator\UrlGeneratorInterface;use Symfony\Contracts\Translation\TranslatorInterface;use Psr\Log\LoggerInterface;use Twig\Environment;use App\Entity\Booking as Booking;class PaymentController extends AbstractController{private $loggedUser;public function __construct(private ManagerRegistry $doctrine, private LoggerInterface $logger, private TranslatorInterface $trans, private MailerInterface $mailer, private Environment $twig){}#[\Symfony\Component\Routing\Attribute\Route('/customer/booking-confirmation/{ref}', name: 'bookingLink')]public function confirmAction($ref){$em = $this->doctrine->getManager();$booking = $this->decodeRef(urldecode($ref));$this->loggedUser = $this->getUser();$this->logger->info('confirmAction: Confirm booking for ref '.$ref);if ($booking){$this->logger->info('confirmAction: Found booking Id: '.$booking->getBookingid());$paid = false;$payment = $this->getPayment($em, $booking, $paid);$this->logger->info("confirmAction: paid: ".$paid);if (!$payment){$payment = new Payment();$payment->setPaymentgateway($booking->getBookingoffice()->getPaymentgateway());$payment->setBooking($booking);$payment->setUpdated( new \DateTime() );$em->persist($payment);$em->flush();$paymentArray = $em->getRepository('App\Entity\Payment')->findByBooking($booking->getBookingid());$payment = array_pop($paymentArray);}else if ($paid){$redirect = $this->generateUrl('successPayment', array('payment' => $this->createPaymentRef($payment)), UrlGeneratorInterface::ABSOLUTE_URL);return new RedirectResponse($redirect);}$paymentTemplate = $booking->getBookingoffice()->getPaymentgateway()->getTemplate();$this->logger->info('confirmAction: paymentTemplate: '.$paymentTemplate);$func = 'appendData_'.$paymentTemplate;$this->logger->info("confirmAction: Looking for appendData func: ".$func);if (method_exists($this, $func))$data = $this->$func($booking, $payment);else$data = array();$this->logger->info('confirmAction: Got payment Id: '.$payment->getPaymentid());$data['bookingRef'] = $ref;$data['booking'] = $booking;$data['payment'] = $payment;$data['user'] = $this->loggedUser;$data['resURL'] = $this->generateURL('processBooking', array('gateway' => $payment->getPaymentgateway()->getPaymentgatewayid()), UrlGeneratorInterface::ABSOLUTE_URL);$data['paid'] = $paid;$data['footer'] = 'booking.confirmation.footer.'.$paymentTemplate;$data['template'] = $paymentTemplate;$response = $this->render('booking/confirm.html.twig', $data);$date = new \DateTime(); // Set expire date to +2 seconds to prevent caching$date->modify('+2 seconds');$response->setExpires($date);return $response;}elsereturn $this->render('booking/notfound.html.twig', array("user" => $this->loggedUser));}#[\Symfony\Component\Routing\Attribute\Route('/customer/payment-failure/{payment}', name: 'failurePayment')]public function failureAction($payment, Request $request){$payment = $this->decodePaymentRef($payment);if ($payment){$notificationData = explode('\n', $payment->getNotification());$this->logger->info('$notificationData: '.end($notificationData));$notificationData = json_decode(end($notificationData));return $this->render('payment/failure.html.twig', array('payment' => $payment, 'failureReason' => $notificationData->code.': '.$notificationData->status));}elsereturn $this->render('booking/notfound.html.twig', array("user"=>$this->loggedUser ));}#[\Symfony\Component\Routing\Attribute\Route('/customer/worldpay/getRedirectUrl/{ref}', name: 'getWorldpayRedirectUrl')]public function getWorldpayRedirectUrlAction($ref){//BE 190702: this is for Worldpay XML payments only$em = $this->doctrine->getManager();$booking = $this->decodeRef(urldecode($ref));$this->loggedUser = $this->getUser();$this->logger->info("getWorldpayRedirectUrlAction: getRedirectUrlAction for ref ".$ref);if ($booking){$this->logger->info("getWorldpayRedirectUrlAction: Found booking Id: ".$booking->getBookingid());$paid = false;$payment = $this->getPayment($em, $booking, $paid);$this->logger->info("getWorldpayRedirectUrlAction: Found payment Id: ".$payment->getPaymentid());$office = $booking->getBookingoffice();//$data = $this->appendData_worldpay($booking, $payment);$cost = number_format($booking->getTotalprice(), 2, '', '');$currency = $booking->getBookingcurrency()->getCode();$officeSalt = $office->getMerchantsalt();$merchantCode = $this->decrypt($office->getMerchantcode(), $officeSalt).$currency;//$merchantCode = $office->getMerchantcode().$currency;$xml = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE paymentService PUBLIC "-//Worldpay//DTD Worldpay PaymentService v1//EN" "http://dtd.worldpay.com/paymentService_v1.dtd"><paymentService version="1.4" merchantCode="'.$merchantCode.'"><submit><order orderCode="'.$payment->getPaymentid().'" installationId="1356990"><description>'.$booking->__toString().'</description><amount currencyCode="'.$currency.'" exponent="2" value="'.$cost.'" /><orderContent><![CDATA[]]></orderContent><paymentMethodMask><include code="ALL" /></paymentMethodMask><shopper><shopperEmailAddress>'.$booking->getCustomer()->getEmail().'</shopperEmailAddress></shopper><billingAddress><address><address1>'.$booking->getCustomer()->getAddress().'</address1><address2></address2><address3></address3><postalCode>'.$booking->getCustomer()->getPostcode().'</postalCode><city>'.$booking->getCustomer()->getCity().'</city><state>'.$booking->getCustomer()->getState().'</state><countryCode>'.$booking->getCustomer()->getCustomercountry()->getCountry2Code().'</countryCode></address></billingAddress></order></submit></paymentService>';$this->logger->info("getWorldpayRedirectUrlAction: Xml: ".$xml);$username = $merchantCode;//$this->decrypt($office->getMerchantusername(), $officeSalt);$pwd = $this->decrypt($office->getMerchantpassword(), $officeSalt);$test_mode = $this->getParameter('app.worldpayxml_test_mode') == 'TRUE';$basicAuth = $username . ':' . $pwd;//$this->logger->info("Using Auth: ".$basicAuth);$basicAuthBase64 = base64_encode($basicAuth);$basicAuthUrlEncoded = urlencode($username) . ":" . urlencode($pwd);if ($test_mode)$url = "https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp";else$url = "https://secure.worldpay.com/jsp/merchant/xml/paymentService.jsp";$this->logger->info("Using Url: ".$url);//$this->logger->info("Using Url: ".str_replace($basicAuth, "user:pwd", $url));$ch = curl_init();curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);curl_setopt($ch, CURLOPT_USERPWD, $basicAuth);//curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml', 'Authorization: ' . $basicAuthBase64));curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);$response = curl_exec($ch);if ($response == false){$this->logger->error('Error code: '.curl_error($ch));$url = $this->generateUrl('failurePayment', array('payment'=>$this->createPaymentRef($payment)), UrlGeneratorInterface::ABSOLUTE_URL);$err = array('date' => date('Y-m-d H:i:s'),'code' => 999, 'status' => 'XML Response empty - contact Support');$payment->setNotification($payment->getNotification().'\n'.json_encode($err));$em->persist($payment);$em->flush();$this->mailSalesFailure($payment);//$this->logger->info("failurePayment: ".$serializedData);return new RedirectResponse($url);}else{$this->logger->info("getWorldpayRedirectUrlAction: response: ".$response);$responseXml = simplexml_load_string($response);//$this->logger->info("responseXml: ".print_r($responseXml, true));if (property_exists($responseXml->reply, 'error') || strpos($response, '<html') !== false){$url = $this->generateUrl('failurePayment', array('payment'=>$this->createPaymentRef($payment)), UrlGeneratorInterface::ABSOLUTE_URL);if (property_exists($responseXml->reply, 'error')){$code = (int)$responseXml->reply->error['code'];$error = (string)$responseXml->reply->error;if ($error == 'Order has already been paid')$status = $error;else$status = 'There was an unknown problem with the gateway - Contact Support, and give them this info: '.(string)$responseXml->reply->error;}else if (strpos($response, '<html') !== false && strpos($response, 'Status: 401') !== false){$code = '401';$status = 'There was an authentication problem with the gateway - contact Support, and give them this info: Status 401: Authentication Problem';}else{$code = '?';$status = 'There was an unknown problem with the gateway - Contact Support, and give them this info: '.htmlentities($response);}$err = array('date' => date('Y-m-d H:i:s'), 'code' => $code, 'status' => $status);$payment->setNotification($payment->getNotification().'\n'.json_encode($err));$em->persist($payment);$em->flush();$this->mailSalesFailure($payment);//$this->logger->info("failurePayment: ".$serializedData);return new RedirectResponse($url);}else if (property_exists($responseXml->reply, 'orderStatus') && property_exists($responseXml->reply->orderStatus, 'reference')){$refUrl = (string)$responseXml->reply->orderStatus->reference[0];$this->logger->info("getWorldpayRedirectUrlAction: reference: ".$refUrl);//$this->logger->info("reference: ".print_r($refUrl, true));//$this->logger->info("reference type: ".gettype($refUrl));//return $this->redirect($refUrl);$successURL = urlencode($this->generateURL("processWorldpay", array("pid" => $payment->getPaymentID() ), UrlGeneratorInterface::ABSOLUTE_URL));$refUrl .= '&successURL='.$successURL.'&failureURL='.$successURL.'&cancelURL='.$successURL.'&errorURL='.$successURL;$this->logger->info("getWorldpayRedirectUrlAction: reference: ".$refUrl);return new RedirectResponse($refUrl);}}curl_close($ch);}else$this->logger->info("getWorldpayRedirectUrlAction: No booking found!");}public function paymentNotificationAction($template, Request $request){$this->logger->info("PaymentController.paymentNotificationAction started template:$template");if ($template == "barclays"){$dataChk=array();$dataChk['live'] = $request->get("live"); // live or test$dataChk["eventCode"] = $request->get("eventCode"); // event code - will be AUTHORISATION$dataChk["pspReference"] = $request->get("pspReference"); // Barclay ref code$dataChk["originalReference"] = $request->get("originalReference"); // blank$dataChk["merchantReference"] = $request->get("merchantReference"); // Our ref code$dataChk["merchantAccountCode"] = $request->get("merchantAccountCode"); // Merchant account code$dataChk["success"] = $request->get("success");$dataChk["paymentMethod"] = $request->get("paymentMethod");$dataChk["operations"] = $request->get("operations");$dataChk["reason"] = $request->get("reason");$dataChk["amount"] = $request->get("amount");$this->logger->info("PaymentController.paymentNotificationAction Request Data: ".print_r($dataChk,true));$em = $this->doctrine->getManager();$payment = false;if ($dataChk["merchantReference"] > 0){$this->logger->info("PaymentController.paymentNotificationAction Retrieving Payment: ".$dataChk['merchantReference']);$payment = $em->getRepository('App\Entity\Payment')->find($dataChk["merchantReference"]);} else {$this->logger->info("PaymentController.paymentNotificationAction No Asscociated Payment");}if ($payment){$this->logger->info("PaymentController.paymentNotificationAction Payment Successfully Retreived: ".$dataChk['merchantReference']);$payment->setNotification(serialize($dataChk));$em->persist($payment);$em->flush();if ($dataChk["success"]=='false'){$this->logger->info("PaymentController.paymentNotificationAction Payment Unsuccessful, Mailing Sales Rep: ".$dataChk['merchantReference']);$this->mailSalesFailure($payment);} else{$this->logger->info("PaymentController.paymentNotificationAction Payment Successful: ".$dataChk['merchantReference']);}}$this->logger->info("PaymentController.paymentNotificationAction Returning 200 Response");return new Response('[accepted]',200,array('content-type' => 'text/html'));}}#[\Symfony\Component\Routing\Attribute\Route('/customer/worldpay/redirect/{pid}', name: 'processWorldpay')]public function processWorldpayAction($pid, MailerInterface $mailer, Request $request){$em = $this->doctrine->getManager();$payment = $this->saveReturnData_worldpay($pid, $request);$msg = 'NONE';if ($payment){$paymentRef = $this->createPaymentRef($payment);$booking = $payment->getBooking();if ($payment->getSuccessful() == 1){$redirect = $this->generateUrl('successPayment', array("payment"=>$paymentRef), UrlGeneratorInterface::ABSOLUTE_URL);$this->mailCustomerConfirmation($booking, $payment);$this->mailSalesConfirmation($booking, $payment, $request);$booking->setPaid(1);$em->persist($booking);$em->flush();} else {$redirect = $this->generateUrl('failurePayment', array("payment"=>$paymentRef), UrlGeneratorInterface::ABSOLUTE_URL);$this->mailSalesFailure($payment);}} else$redirect = $this->generateUrl('failurePayment', array("payment"=>$paymentRef), UrlGeneratorInterface::ABSOLUTE_URL);//$redirect = $redirect===false ? $request->get("MC_failure") : $redirect;//$params = $request->query->all();//return $this->render("ACSBundle:Payment:process_worldpay.html.twig", array("redirect"=>$redirect, "payment"=>$payment, 'params'=>$params, 'msg'=>$msg) );//return new Response("done");return new RedirectResponse($redirect);}#[\Symfony\Component\Routing\Attribute\Route('/customer/payment-success/{payment}', name: 'successPayment')]public function successAction($payment){$payment = $this->decodePaymentRef($payment);if ($payment)return $this->render("payment/success.html.twig", array('payment' => $payment));elsereturn $this->render("booking/notfound.html.twig", array("user"=>$this->loggedUser ));}public function testAction(){$test = 'MYADMINCODE^MYMERCHANT^T0211010:1400:GBP:AUTHORISED';$result = hash_hmac('sha256', $test, '@p-p1epie');return new Response($result);/*$em = $this->getDoctrine()->getManager();$payment = $em->getRepository('App\Entity\Payment')->find(57);$booking = $payment->getBooking();$this->mailCustomerConfirmation($booking, $payment);$customer = $booking->getCustomer();$user = $booking->getCreatedby();$recipient = trim($customer->getFirstname()." ".$customer->getSurname());$sender = trim($user->getFirstname()." ".$user->getSurname());return $this->render("emails/CustomerConfirmation.html.twig",array('recipient' => $recipient,'sender'=> $sender,'booking'=>$booking,'payment'=>$payment,));*/}/*** PRIVATE HELPER METHODS ***/private function appendData_worldpay($booking, $payment){// See docs for implementation : https://beta.developer.worldpay.com/docs/wpg/hostedintegration/quickstart$test_mode = $this->getParameter('app.worldpayxml_test_mode') == 'TRUE';// MD5 hash params may be chosen from the merchant interface. Currently set to// md5_secret:instId:currency:amount:paymentid$cost = number_format($booking->getTotalprice(), 2, '.', '');$currency = $booking->getBookingcurrency()->getCode();$sig_params = array();$sig_params[] = null; //$md5_secret;$sig_params[] = null; //$instId;$sig_params[] = $currency;$sig_params[] = $cost;$sig_params[] = $payment->getPaymentid();$bookingOffice = $booking->getBookingoffice();$bookingAgentEmail = $booking->getCreatedby()->getEmail();$bookingAgentName = trim($booking->getCreatedby()->getFirstname()." ".$booking->getCreatedby()->getSurname());$contactDetails = $bookingOffice->getAddress()."<br />".$bookingOffice->getCity()."<br />".$bookingOffice->getZipcode()."<br />".$bookingOffice->getOfficecountry()->getName()."<br />Reg. No.: ".$bookingOffice->getRegnumber()."<br />Phone: ".$bookingOffice->getPhone()."<br />Booking agent: ".$bookingAgentName." - <a href='mailto:".$bookingAgentEmail."'>".$bookingAgentEmail."</a>";$data = array("orderId" => $payment->getPaymentid(),"testMode" => $test_mode,"orderCurrency" => $currency,"orderAmount" => $cost,"description" => $booking->__toString(),"shopperEmail" => $booking->getCustomer()->getEmail(),"name" => $booking->getCustomer()->getFirstname()." ".$booking->getCustomer()->getSurname(),"address" => $booking->getCustomer()->getAddress(),"city" => $booking->getCustomer()->getCity(),"zip" => $booking->getCustomer()->getPostcode(),"state" => $booking->getCustomer()->getState(),"country" => $booking->getCustomer()->getCustomercountry()->getCountry2Code(),"phone" => preg_replace("/^![0-9]$/","",$booking->getCustomer()->getPhone()),"successURL" => $this->generateURL("successPayment", array( "payment"=>$payment->getPaymentID() ), UrlGeneratorInterface::ABSOLUTE_URL),"failureURL" => $this->generateURL("failurePayment", array( "payment"=>$payment->getPaymentID() ), UrlGeneratorInterface::ABSOLUTE_URL),// Following fields required by VISA for payments in GBP"shopperAdditionalAccountNumber" => "","shopperAdditionalLastName" => "","shopperAdditionalBirthDate" => "","shopperAdditionalPostalCode" => "","contactDetails" => $contactDetails);$data['MD5sig'] = md5(implode(":", $sig_params));return $data;}private function createPaymentRef($payment){$booking = $payment->getBooking();$ref=array();$ref[]=$payment->getPaymentid();$ref[]=$booking->getBookingid();$ref[]=$booking->getCustomer()->getCustomerid();$ref[]=$booking->getBookingoffice()->getOfficeid();/*$finalRef = $ref[0].$ref[1].$ref[2].$ref[3];$next = $this->getEverySecondDigit($finalRef);$finalRef = $finalRef / $next;$next = $this->getEverySecondDigit($finalRef);$finalRef = $finalRef / $next;$next = $this->getEverySecondDigit($finalRef);$finalRef = $finalRef * $next;$decimalPos = strrpos($finalRef, '.');$finalRef = str_replace('.', '', $finalRef);$finalRef .= $decimalPos;$this->logger->info('$finalRef: '.$finalRef);return urlencode($finalRef);*/return urlencode(bin2hex(implode(Booking::URL_SECRET, $ref)));}private function decodePaymentRef($ref){$this->logger->info('decodePaymentRef: '.$ref);//$decimalPos = (string)$ref[strlen((string)$ref) - 1];//$this->logger->info('decimalPos: '.$decimalPos);$data = explode(Booking::URL_SECRET, pack("H*", $ref));$this->logger->info('Data: '.print_r($data, true));$id = array_shift($data);$em = $this->doctrine->getManager();$payment = $em->getRepository('App\Entity\Payment')->find($id);if ($payment){$this->logger->info('Got payment: '.$payment->getPaymentid());$booking = $payment->getBooking();if ($booking->getBookingid() == $data[0] && $booking->getCustomer()->getCustomerid() == $data[1] && $booking->getBookingoffice()->getOfficeid() == $data[2])return $payment;else {$this->logger->info('Did not match criteria: bid: '.$booking->getBookingid().' $data[0]: '.$data[0].', cid: '.$booking->getCustomer()->getCustomerid().' $data[1]: '.$data[1].', oid: '.$booking->getBookingoffice()->getOfficeid().' $data[2]: '.$data[2]);return false;}} else {$this->logger->info('Could not find payment from id: '.$id);return false;}}private function decodeRef($ref){$data = explode(Booking::URL_SECRET, pack("H*", $ref));//$decimalPos = (string)$ref[(strlen($ref) - 1];$id = array_shift($data);$em = $this->doctrine->getManager();$booking = $em->getRepository('App\Entity\Booking')->find($id);if ($booking){if ($booking->getCustomer()->getCustomerid() == $data[0] && $booking->getBookingoffice()->getOfficeid()==$data[1])return $booking;elsereturn false;} elsereturn false;}private function decrypt($encrypted, $salt) {//https://www.the-art-of-web.com/php/two-way-encryption/$encKey = $this->getParameter('app.worldpayxml_enckey').$salt;$method = 'AES-256-CTR';list($encrypted, $encIV) = explode("::", $encrypted);$decrypted = openssl_decrypt($encrypted, $method, $encKey, 0, hex2bin($encIV));return $decrypted;}private function encrypt($token, $salt) {//https://www.the-art-of-web.com/php/two-way-encryption/$encKey = $this->getParameter('app.worldpayxml_enckey').$salt;$method = 'AES-256-CTR';$encIV = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method));$encrypted = openssl_encrypt($token, $method, $encKey, 0, $encIV) . "::" . bin2hex($encIV);return $encrypted;}public static function getEverySecondDigit($input) {//$this->logger = Logger::getLogger(__CLASS__);$input = (string)$input;$len = strlen($input);//$this->logger->info('Length of '.$input.' is '.$len);$final = '';for ($x = 0; $x < $len; $x++) {if ($input[$x] != '.') {if (($x + 1) % 2 === 0) {$final .= $input[$x];}} else {$final .= '.';}}//$this->logger->info('From $input: '.$input.', calculated: '.$final);return $final;}private function getPayment($em, $booking, &$paid){$this->logger->info('getPayment for booking: '.$booking->getBookingid());$paymentArray = $em->getRepository('App\Entity\Payment')->findByBooking($booking->getBookingid());$payment = false;foreach ($paymentArray as $p){$this->logger->info('Found payment: '.$p->getPaymentid().', is successful: '.($p->getSuccessful() == 1));$payment = $p;if ($p->getSuccessful() == 1){$paid = true;break;}//else if ($p->getResulttoken() == null && $p->getAuthcode() == null && $p->getSuccessful() == null) {// $payment = $p;// break;//}}return $payment;}private function mailCustomerConfirmation($booking, $payment){$customer = $booking->getCustomer();$user = $booking->getCreatedby();$office = $booking->getBookingoffice();$recipient = trim($customer->getFirstname().' '.$customer->getSurname());$sender = trim($user->getFirstname().' '.$user->getSurname());$from = $office->getEmailaddress();$message = (new TemplatedEmail())->subject('Booking Payment Received')->from($from)->to($customer->getEmail());//https://github.com/symfony/symfony/issues/42407#issuecomment-1006995400$html = $this->render('emails/CustomerConfirmation.html.twig',['recipient' => $recipient,'sender' => $sender,'booking' => $booking,'payment' => $payment,'email' => new WrappedTemplatedEmail($this->twig, $message),])->getContent();$message->html($html);$this->mailer = new Mailer(Transport::fromDsn('sendmail://default'));$sent = $this->mailer->send($message);//https://stackoverflow.com/q/71496164/206852// if ($sent !== null)// dd($sent->getDebug());}private function mailSalesConfirmation($booking, $payment, $request) {$customer = $booking->getCustomer();$user = $booking->getCreatedby();$office = $booking->getBookingoffice();$recipient = trim($user->getFirstname().' '.$user->getSurname());$sender = 'System';$from = $office->getEmailaddress();$message = (new TemplatedEmail())->subject('Booking Payment Received')->from($from)->to($user->getEmail())->cc($this->getParameter('app.accountsemail'));//https://github.com/symfony/symfony/issues/42407#issuecomment-1006995400$html = $this->render('emails/SalesConfirmation.txt.twig',['recipient' => $recipient,'sender' => $sender,'booking' => $booking,'payment' => $payment,'cardUsed' => $request->get('cardType'),'email' => new WrappedTemplatedEmail($this->twig, $message),])->getContent();$message->html($html);$this->mailer = new Mailer(Transport::fromDsn('sendmail://default'));$sent = $this->mailer->send($message);//https://stackoverflow.com/q/71496164/206852// if ($sent !== null)// dd($sent->getDebug());}private function mailSalesFailure($payment){$this->logger->info('mailSalesFailure');if ($payment){$booking = $payment->getBooking();$customer = $booking->getCustomer();$user = $booking->getCreatedby();$office = $booking->getBookingoffice();$recipient = trim($user->getFirstname().' '.$user->getSurname());$sender = 'System';$name = trim($customer->getFirstname().' '.$customer->getSurname());$from = $office->getEmailaddress();$template = $booking->getBookingoffice()->getPaymentgateway()->getTemplate();$notificationData = explode('\n', $payment->getNotification());$notificationData = json_decode(end($notificationData));//$this->logger->info('$payment->getNotification(): '.$payment->getNotification());$this->logger->info('$notificationData: '.print_r($notificationData, true));$this->logger->info('json_last_error: '.json_last_error());switch ($template){case 'authorize':$reason = $notificationData->response_reason_text;break;case 'barclays':$reason = $notificationData['reason'];break;case 'worldpay':$reason = 'Error code: '.$notificationData->code.', Error message: '.$notificationData->status;break;}$message = (new TemplatedEmail())->subject('Booking Payment Failed')->from($from)->to($user->getEmail());//https://github.com/symfony/symfony/issues/42407#issuecomment-1006995400$html = $this->render('emails/Failure.txt.twig',['recipient' => $recipient,'sender' => $sender,'booking' => $booking,'payment' => $payment,'name' => $name,'reason' => $reason,'email' => new WrappedTemplatedEmail($this->twig, $message),])->getContent();$message->html($html);$this->mailer = new Mailer(Transport::fromDsn('sendmail://default'));$sent = $this->mailer->send($message);//https://stackoverflow.com/q/71496164/206852// if ($sent !== null)// dd($sen}}private function saveReturnData_worldpay($pid, Request $request){$this->logger->info('saveReturnData_worldpay');$em = $this->doctrine->getManager();$payment = $em->getRepository('App\Entity\Payment')->find($pid);if ($payment){$this->logger->info("Found payment with id ".$pid);if ($request->query->has('errorRefNumber') && $request->query->has('errors')){$err = array('date' => date('Y-m-d H:i:s'), 'code' => $request->get('errorRefNumber'), 'status' => 'Worldpay error: '.$request->get('errors'));$payment->setNotification($payment->getNotification().'\n'.json_encode($err));$payment->setSuccessful(0);$em->persist($payment);$em->flush();return $payment;}//AUTHORISED EXAMPLE//https://dev.onlinepayments.aircharterservice.com/customer/worldpay/redirect/296?orderKey=AIRCHARTERSERVICES%5EAIRCHARTERUKECOMGBP%5E296&paymentStatus=AUTHORISED&paymentAmount=1234565&paymentCurrency=GBP&mac2=c975417ba3d7b63a29c1457802379b4f6b799a67750d9aab590dd277622ff92e//CANCELLED EXAMPLE//https://dev.onlinepayments.aircharterservice.com/customer/worldpay/redirect/309?orderKey=AIRCHARTERSERVICES%5EAIRCHARTERUKECOMGBP%5E309&orderAmount=65421&orderCurrency=GBP&mac2=5e3ee235214f9906c3869bb916f49477f126d076cb11c6497472f07b2528fdbf//REFUSED EXAMPLE//https://dev.onlinepayments.aircharterservice.com/customer/worldpay/redirect/309?orderKey=AIRCHARTERSERVICES%5EAIRCHARTERUKECOMGBP%5E309&paymentStatus=REFUSED&paymentAmount=65421&paymentCurrency=GBP&mac2=8911c0e65ad5d7bbd219db86581c84fe63a532a34023b2ac670e44f0d6e6c677//ERROR EXAMPLE//https://dev.onlinepayments.aircharterservice.com/customer/worldpay/redirect/309?orderKey=AIRCHARTERSERVICES%5EAIRCHARTERUKECOMGBP%5E309&errorRefNumber=D190710-T154806-M001-43&errors=Gateway+error//BE 190708: documentation at https://beta.developer.worldpay.com/docs/wpg/hostedintegration/securingpayments$booking = $payment->getBooking();$office = $booking->getBookingoffice();$officeSalt = $office->getMerchantsalt();$macSecret = $this->decrypt($office->getMerchantmacsecret(), $officeSalt);$test_mode = $this->getParameter('app.worldpayxml_test_mode') == 'TRUE';$currency = $booking->getBookingcurrency()->getCode();$merchantCode = $this->decrypt($office->getMerchantcode(), $officeSalt).$currency;// status can be "AUTHORISED" or "REFUSED"$status = $request->get('paymentStatus');$sig_params = array();$sig_params[] = $request->get('orderKey');$sig_params[] = $request->get('paymentAmount');$sig_params[] = $request->get('paymentCurrency');if ($status)$sig_params[] = $status;$sig_check = array();$sig_check[] = 'AIRCHARTERSERVICES^'.$merchantCode.'^'.$payment->getPaymentId();$sig_check[] = ltrim((string)number_format($booking->getTotalprice(), 2, '', ''), '0');$sig_check[] = $currency;if ($status)$sig_check[] = $status;$sigSent = implode(":", $sig_params);$sigCalculated = implode(":", $sig_check);$calculated_hash = hash_hmac('sha256', $sigCalculated, $macSecret);//$this->logger->info('Using secret: '.$macSecret);$hash_check = $calculated_hash == $request->get('mac2');$retVal = false;if ($hash_check){$statusCode = 1;if (!$status || $status == 'CANCELLED')$statusCode = 3;else if ($status == 'AUTHORISED')$statusCode = 1;else if ($status == 'REFUSED')$statusCode = 2;else$statusCode = 99;$this->logger->info('Hash check succeeded!');$payment->setResulttoken($request->get('mac2'));$payment->setAuthcode($request->get('mac2'));$payment->setSuccessful($statusCode == 1);$payment->setNotification($payment->getNotification().'\n'.json_encode(array('date' => date('Y-m-d H:i:s'), 'code' => $statusCode, 'status' => $status, 'params' => $request->query->all())));$retVal = $payment;}else{$this->logger->info('Sig params sent: '.$sigSent.', Sig params calculated: '.$sigCalculated.', are equal = '.($sigSent == $sigCalculated));$this->logger->info('Failed Hash Check: Calculated Value: '.$calculated_hash.', Sent Value (mac): '.$request->get('mac').', Sent Value (mac2): '.$request->get('mac2'));$payment->setNotification($payment->getNotification().'\n'.json_encode(array('date' => date('Y-m-d H:i:s'), 'code' => 4, 'status' => 'Failed Auth Hash Check - contact Support', 'params' => $request->query->all())));$retVal = $payment;}$em->persist($payment);$em->flush();return $retVal;} else {$this->logger->info("Payment with id ".$pid." was NOT found.");return false;}}/*** OLD CODE - KEPT FOR REFERENCE IF REQUIRED IN THE FUTURE ***/private function appendData_barclays($booking, $payment) {$hmac_key = $this->getParameter('barclays_hmac_key');$skin_code = $this->getParameter('barclays_skin_code');$merchant_acc = $this->getParameter('barclays_merchant_acc');$time = $payment->getUpdated()->format("U") + 60*60*24*30;$paymentexpires = gmdate("Y-m-d", $time) . 'T' . gmdate("H:i:s", $time) ."Z";//$paymentexpires=gmdate(DATE_ISO8601,$expTimestamp);$data = array('paymentAmount' => $booking->getTotalprice()*100,'currencyCode' => $booking->getBookingcurrency()->getCode(),'shipBeforeDate' => $payment->getUpdated()->format('Y-m-d'),'merchantReference' => $payment->getPaymentid(),'skinCode' => $skin_code,'merchantAccount' => $merchant_acc,'sessionValidity' => $paymentexpires,'shopperEmail' => $booking->getCustomer()->getEmail(),'shopperReference' => $booking->getCustomer()->getCustomerid(),'allowedMethods' => '','blockedMethods' => '','shopperStatement' => '','billingAddressType'=> '',);$summary = implode("",$data);$merchantSig = base64_encode(hash_hmac('sha1',$summary,$hmac_key,true));$data['orderData'] = base64_encode($booking->__toString());$data['merchantSig'] = $merchantSig;return $data;}private function appendData_authorize($booking,$payment) {$hmac_key = $this->getParameter('authorize_hmac_key');$api_login = $this->getParameter('authorize_api_login');/*added by vazquel on 2014-02-19*/$md5_hash = $this->getParameter('authorize_md5_hash');$gateway = new \Authorizenet_Authorizenet($api_login, $md5_hash);$response = $gateway->AuthorizeSIM;$fp_timestamp = gmdate("U");$fingerprint = \AuthorizeNetSIM_Form::getFingerprint($api_login, $hmac_key, $booking->getTotalprice(), $payment->getPaymentid(), $fp_timestamp, $booking->getBookingcurrency()->getCode());$data['x_login'] = $api_login;$data['x_test_request'] = $this->getParameter('authorize_test_mode');$data['x_fp_sequence'] = $payment->getPaymentid();$data['x_fp_timestamp'] = $fp_timestamp;$data['x_amount'] = $booking->getTotalprice();$data['x_currency_code']= $booking->getBookingcurrency()->getCode();$data['x_fp_hash'] = $fingerprint;$data['x_description'] = $booking->__toString();$data['x_first_name'] = $booking->getCustomer()->getFirstname();$data['x_last_name'] = $booking->getCustomer()->getSurname();$data['x_address'] = $booking->getCustomer()->getAddress();$data['x_city'] = $booking->getCustomer()->getCity();$data['x_zip'] = $booking->getCustomer()->getPostcode();$data['x_state'] = $booking->getCustomer()->getState();$data['x_country'] = $booking->getCustomer()->getCustomercountry()->getName();$data['x_phone'] = preg_replace("/^![0-9]$/","",$booking->getCustomer()->getPhone());$data['x_email'] = $booking->getCustomer()->getEmail();$data['booking'] = $booking;$data['payment'] = $payment;return $data;}private function appendData_moneyswap($booking, $payment) {$moneyswap_aid = $this->getParameter('moneyswap_aid');$moneyswap_md5_signature = $this->getParameter('moneyswap_md5_signature');$data = array("acqID" => $moneyswap_aid,//"acqID" => "04020826","backURL" => $this->generateURL("processBooking", array( "gateway"=>$payment->getPaymentgateway()->getPaymentgatewayid() ), UrlGeneratorInterface::ABSOLUTE_URL),"charSet" => "UTF-8","frontURL" => $this->generateURL("processBooking", array( "gateway"=>$payment->getPaymentgateway()->getPaymentgatewayid() ), UrlGeneratorInterface::ABSOLUTE_URL),"merID" => "846084045110001","merReserve" => $booking->__toString(),"orderAmount" => number_format($booking->getTotalprice(), 2, '.', ''),"orderCurrency" => $booking->getBookingcurrency()->getCode(),"orderNum" => $payment->getPaymentid(),"paymentSchema" => "UP","transTime" => date("YmdHis"),"transType" => "PURC","signType" => "MD5","version" => "VER000000002",);$signature = MoneySwapHelper::createSignature($moneyswap_md5_signature, $data);$data['signature'] = $signature;return $data;}private function getRelayResponseSnippet($redirect_url) {return "<html><head><script language=\"javascript\"><!--window.location1=\"{$redirect_url}\";//--></script></head><body><noscript><meta http-equiv=\"refresh\" content=\"1;url={$redirect_url}\"></noscript>Test</body></html>";}#[\Symfony\Component\Routing\Attribute\Route('/customer/process/{gateway}', name: 'processBooking')]public function processAction($gateway, MailerInterface $mailer, Request $request){$em = $this->getDoctrine()->getManager();$paymentGateway = $em->getRepository('App\Entity\Paymentgateway')->find($gateway);$func = "saveReturnData_".$paymentGateway->getTemplate();$api_login = $this->getParameter('authorize_api_login');$md5_hash = $this->getParameter('authorize_md5_hash');$fullDomain = ($request->server->get("https") ? "https://" : "http://") . $request->getHost();if (method_exists($this, $func))$payment = $this->$func($request);if ($payment){$booking=$payment->getBooking();if ($payment->getSuccessful()==1){if ($gateway == 3) {if (!$booking->getPaid()) {$this->mailCustomerConfirmation($booking,$payment);$this->mailSalesConfirmation($booking,$payment,$request);}} else {$this->mailCustomerConfirmation($booking,$payment);$this->mailSalesConfirmation($booking,$payment,$request);}$booking->setPaid(1);$em->persist($booking);$em->flush();//$this->mailCustomerConfirmation($booking,$payment);//$this->mailSalesConfirmation($booking,$payment);$fail = 0;$url = $this->generateUrl('successPayment', array("payment"=>$payment->getPaymentid()), UrlGeneratorInterface::ABSOLUTE_URL);if ($gateway == 2){//added by vazquel on 2014-02-19$auth_controller = new \Authorizenet_Authorizenet($api_login, $md5_hash);$response = $auth_controller->AuthorizeSIM;//added by vazquel on 2014-02-19 (Redirect to the payment success page)if ($response->isAuthorizeNet())return new Response($this->getRelayResponseSnippet($fullDomain.$url));elsereturn $this->redirect($url);}elsereturn $this->redirect($url);} else$fail=1;} else$fail=2;if ($fail == 1){$url = $this->generateUrl('failurePayment', array("payment"=>$payment->getPaymentid()), UrlGeneratorInterface::ABSOLUTE_URL);if ($gateway == 2){$this->mailSalesFailure($payment);$auth_controller = new \Authorizenet_Authorizenet($api_login, $md5_hash);$response = $auth_controller->AuthorizeSIM;if ($response->isAuthorizeNet())return new Response($this->getRelayResponseSnippet($fullDomain.$url));elsereturn $this->redirect($url);}elsereturn $this->redirect($url);}}private function saveReturnData_barclays(Request $request) {$hmac_key = $this->getParameter('barclays_hmac_key');$em = $this->getDoctrine()->getManager();$payment = $em->getRepository('App\Entity\Payment')->find($request->get("merchantReference"));if ($payment){$booking = $payment->getBooking();$data = $this->appendData_barclays($booking, $payment);$dataChk = array();$dataChk[] = $request->get("authResult");$dataChk[] = $request->get("pspReference");$dataChk[] = $request->get("merchantReference");$dataChk[] = $request->get("skinCode");$dataChk[] = $request->get("merchantReturnData");$sig = base64_encode(hash_hmac('sha1',implode("", $dataChk),$hmac_key,true));if ($sig==$request->get("merchantSig")){$payment->setResulttoken($request->get("pspReference"));$payment->setAuthcode($request->get("authResult"));$payment->setSuccessful("AUTHORISED"==$request->get("authResult"));$em->persist($payment);$em->flush();return $payment;}elsereturn false;}elsereturn false;}private function saveReturnData_authorize(Request $request) {$hmac_key = $this->getParameter('authorize_hmac_key');$api_login = $this->getParameter('authorize_api_login');$md5_hash = $this->getParameter('authorize_md5_hash');$em = $this->doctrine->getManager();$payment = $em->getRepository('App\Entity\Payment')->find($request->get("x_invoice_num"));$gateway = new \Authorizenet_Authorizenet($api_login, $md5_hash);$response = $gateway->AuthorizeSIM;if ($response->isAuthorizeNet() && $payment instanceof Payment){$payment->setResulttoken($response->authorization_code);$payment->setAuthcode($response->response_reason_code);$payment->setSuccessful($response->approved*1);$payment->setNotification(serialize($response));$em->persist($payment);$em->flush();return $payment;}elsedie( "Error. Check your MD5 Setting.<br>Received: $response->md5_hash<br>Calculated: ".$response->generateHash());}private function saveReturnData_moneyswap(Request $request) {$moneyswap_aid = $this->getParameter('moneyswap_aid');$moneyswap_md5_signature = $this->getParameter('moneyswap_md5_signature');$em = $this->getDoctrine()->getManager();$payment = $em->getRepository('App\Entity\Payment')->find($request->get("orderNum"));if ($payment){$booking = $payment->getBooking();$query = "version=".$request->get("version");$query .= "&charSet=".$request->get("charSet");$query .= "&transType=".$request->get("transType");$query .= "&orderNum=".$request->get("orderNum");$query .= "&orderAmount=".$request->get("orderAmount");$query .= "&orderCurrency=".$request->get("orderCurrency");$query .= "&settAmount=".$request->get("settAmount");$query .= "&settCurrency=".$request->get("settCurrency");$query .= "&rate=".$request->get("rate");$query .= "&merReserve=".$request->get("merReserve");$query .= "&transID=".$request->get("transID");$query .= "&merID=".$request->get("merID");$query .= "&acqID=".$request->get("acqID");$query .= "&paymentSchema=".$request->get("paymentSchema");$query .= "&RespCode=".$request->get("RespCode");$query .= "&RespMsg=".$request->get("RespMsg");$query .= "&transTime=".$request->get("transTime");$query .= "&GWTime=".$request->get("GWTime");$query .= "&signType=".$request->get("signType");$query .= "&signature=".$request->get("signature");$isValid = MoneySwapHelper::verifySignature($moneyswap_md5_signature, $query);if ($isValid){$payment->setResulttoken($request->get("RespMsg"));$payment->setAuthcode($request->get("RespCode"));$payment->setSuccessful("00"==$request->get("RespCode"));$payment->setNotification(serialize($query));$em->persist($payment);$em->flush();return $payment;}elsereturn false;}elsereturn false;}}