<?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 Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
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)
{
}
#[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('Confirm booking for ref '.$ref);
if ($booking)
{
$this->logger->info('Found booking Id: '.$booking->getBookingid());
$paid = false;
$payment = $this->getPayment($em, $booking, $paid);
$this->logger->info("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('paymentTemplate: '.$paymentTemplate);
$func = 'appendData_'.$paymentTemplate;
$this->logger->info("Looking for appendData func: ".$func);
if (method_exists($this, $func))
$data = $this->$func($booking, $payment);
else
$data = array();
$this->logger->info('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;
}
else
return $this->render('booking/notfound.html.twig', array("user" => $this->loggedUser));
}
#[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));
}
else
return $this->render('booking/notfound.html.twig', array("user"=>$this->loggedUser ));
}
#[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("getRedirectUrlAction for ref ".$ref);
if ($booking)
{
$this->logger->info("Found booking Id: ".$booking->getBookingid());
$paid = false;
$payment = $this->getPayment($em, $booking, $paid);
$this->logger->info("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("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("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("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("reference: ".$refUrl);
return new RedirectResponse($refUrl);
}
}
curl_close($ch);
}
else
$this->logger->info("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'));
}
}
#[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);
}
#[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));
else
return $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;
else
return false;
} else
return 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());
$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>";
}
#[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));
else
return $this->redirect($url);
}
else
return $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));
else
return $this->redirect($url);
}
else
return $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;
}
else
return false;
}
else
return 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;
}
else
die( "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;
}
else
return false;
}
else
return false;
}
}