<?php
namespace App\Controller;
use App\Model\DataObject\User;
use App\Security\AppAuthenticator;
use App\Security\OauthAuthenticator;
use App\Service\CodelocksService;
use App\Service\PriceCalculator;
use App\Service\PromoService;
use Carbon\Carbon;
use Exception;
use Pimcore\Controller\FrontendController;
use Pimcore\Log\ApplicationLogger;
use Pimcore\Model\DataObject\Folder;
use Pimcore\Model\DataObject\Payment;
use Pimcore\Model\DataObject\PromoUser;
use Pimcore\Model\Document\Email;
use Pimcore\Model\WebsiteSetting;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Routing\Annotation\Route;
use Stripe\Exception\SignatureVerificationException;
use Stripe\Exception\UnexpectedValueException;
use Stripe\Stripe;
use Pimcore\Mail;
use Pimcore\Model\DataObject;
use Pimcore\Model\DataObject\Order;
use Pimcore\Model\DataObject\Price;
use Pimcore\Model\DataObject\Address;
use Pimcore\Model\DataObject\CreditHistory;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security as FrontSecurity;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
class StripeController extends FrontendController
{
private $stripe = null;
public function __construct()
{
\Stripe\Stripe::setApiKey(\Pimcore\Model\WebsiteSetting::getByName('stripeSecretKey')->getData());
$this->stripe = new \Stripe\StripeClient(\Pimcore\Model\WebsiteSetting::getByName('stripeSecretKey')->getData());
}
/**
* @route("/pay/{method}/{order}", name="pay_with_method", methods={"GET"})
*/
public function payWithMethod(Request $request, FrontSecurity $security)
{
$securityUser = $security->getUser();
if(!$securityUser){
return $this->json(["error" => "not authenticated"]);
}
$user = User::getByUsername($securityUser->getUserIdentifier())->load()[0];
$order = Order::getById($request->get("order"));
if(!$order) {
throw new Exception("Order doesn't exist", 1);
}
$amount = 0;
if($order->getOrderType() == "pack"){
$amount = $order->getPack()->getPrice();
}else{
foreach($order->getBookings() as $booking){
$amount += PriceCalculator::getTotal($booking);
}
}
$order->setTotalNoPromo($amount);
$promoService = new PromoService();
if($order->getPromo()){
$check = $promoService->submit($order->getPromo(), $order, false);
if(!$check["status"]){
$order->setTotalPrice($amount);
$order->setPromo(null);
$order->setPromoCode("");
$order->setPromoReduc("");
$order->setPromoReduc("");
$order->save();
}
$amount = $order->getTotalPrice();
}else{
$order->setTotalPrice($amount);
}
if($order->getUsingCredits() && $order->getOrderType() != "pack"){
if($amount){
if($user->getCredit() >= $amount){
$order->setCredits($amount);
$amount = 0;
}else{
$amount = $amount - $user->getCredit();
$order->setCredits($user->getCredit());
}
}else{
$order->setUsingCredits(false);
}
}
$order->setStatus("new");
$order->save();
$amount *= 100;
$user = User::getByUsername($securityUser->getUserIdentifier())->load()[0];
$url = $this->generateUrl('checkoutSuccess',["order"=>$order->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
$paymentIntent = \Stripe\PaymentIntent::create([
'customer' => $user->getStripeCustomerId(),
'payment_method' => $request->get('method'),
'amount' => $amount,
'currency' => 'eur',
'payment_method_types' => ['card'],
]);
$payment = new Payment();
$payment->setStripePaymentIntent($paymentIntent->id);
$payment->setTotalPrice($amount / 100.0);
$payment->setKey('Payment_'.uniqid());
$payment->setParent($order);
$payment->setPublished(true);
$payment->save();
$order->setPayment($payment);
$order->save();
return $this->json(["paymentIntent" => $paymentIntent]);
// return $this->redirectToRoute('checkoutSuccess', ["order"=>$order->getID()]);
}
/**
* @Template
* @param Request $request
* @route("/{_locale}/checkout-success/{studiotag}/{order}", name="checkoutSuccess", methods={"GET"})
* @return array
*/
public function successAction(Request $request, FrontSecurity $security,
GuardAuthenticatorHandler $guard,
AppAuthenticator $formAuthenticator)
{
$orderId = $request->get('order');
$order = Order::getById($request->get('order'));
if($order->getOrderType() == "pack"){
$prod = $order->getPack();
$duration = 0;
}else{
$prods = $order->getBookings();
$prod = $prods[0];
$duration = $prod->getBookedEnd()->diffInHours($prod->getBookedStart());
}
$token = $request->get("validation_token");
if($token){
$user = User::getByPaymentToken($token)->load();
if($user){
$user = $user[0];
//login automatique du user
$guard->authenticateUserAndHandleSuccess(
$user,
$request,
$formAuthenticator,
'app_admin'
);
}else{
return $this->redirectToRoute("studioList");
}
}else{
$securityUser = $security->getUser();
if (!$securityUser) {
return $this->redirectToRoute("studioList");
}
$user = User::getByUsername($securityUser->getUserIdentifier())->load()[0];
}
$order->setStripeReturnDate(Carbon::now());
$order->setStripeReturnUrl($request->getRequestUri());
if(($request->get("redirect_status") == "failed" && ($order->getStatus() != "validated" && $order->getStatus() != "waiting")) || $order->getStatus() == "abandonned" || $order->getStatus() == "canceled"){
$order->setStatus("canceled");
$order->setLogidriveStatus(true);
$order->save();
if($order->getOrderType() != "pack"){
$tag = "credit-pack";
$prod->setLogidriveStatus(true);
$prod->save();
}else{
$tag = $prod->getBookedStudio()->getCategorie()->getTagUrl();
}
return $this->redirectToRoute("checkoutFailure", ["order" => $orderId, "studiotag" => $tag]);
}
$linkedUser = $order->getLinkedUser();
if($user->getId() !== $linkedUser->getId()){
return $this->redirectToRoute("studioList");
}
$payment = $order->getPayment();
$checkPayment = Payment::getByStripePaymentIntent($request->get('payment_intent'))->load();
if($checkPayment){
$checkPayment = $checkPayment[0];
if($payment->getId() !== $checkPayment->getId()){
$order->setPayment($checkPayment);
$order->save();
}
}else{
if($payment->getStripePaymentIntent() !== $request->get('payment_intent')){
$payment->setStripePaymentIntent($request->get('payment_intent'));
$payment->save();
}
}
if($order->getStatus() == "new"){
$order->setStatus("waiting");
}
$order->save();
if($order->getOrderType() != "pack" && !$prod->getLockCode() && !$prod->getPtiCode()){
$clService = new CodelocksService();
$code = $clService->generateCode($prod);
if($code){
$prod->setLockCode($code);
$prod->setPtiCode($code);
$prod->save();
}
}
$session = $request->getSession();
$checkRefresh = $session->get("success_orderid");
$credFolder = \Pimcore\Model\DataObject\Folder::getByPath('/credit-history');
if (!$credFolder) {
$credFolder = new \Pimcore\Model\DataObject\Folder();
$credFolder->setKey('credit-history');
$credFolder->setParentId(1);
$credFolder->save();
}
if($checkRefresh != $orderId){
$session->set("success_orderid", $orderId);
$creditGifting = WebsiteSetting::getByName('credit_gifting')->getData();
$creditAmount = (float) WebsiteSetting::getByName('credit_gifting_amount')->getData();
if($order->getOrderType() != "pack" && $creditGifting){
$user = $order->getLinkedUser();
$userOrders = new Order\Listing();
$userOrders->setCondition("(status = 'validated' OR status = 'canceled') AND linkedUser__id = ".$user->getId()." AND oo_id != ".$order->getId());
$userOrders = $userOrders->load();
if(!$userOrders){
$history = new CreditHistory();
$history->setCredit($order->getCredits());
$history->setMouvement("add");
$history->setTransactionTime(Carbon::now());
$history->setOrder($order);
$history->setTransactionType("Credit Gift");
$history->setKey("History_gift".$user->getUsername()."_".Carbon::now()->format("YmdHis")."_".uniqid());
$history->setPublished(true);
$history->setParent($credFolder);
$history->save();
$histlist = $user->getCreditHistory();
$histlist[] = $history;
$user->setCreditHistory($histlist);
$user->setCredit(((float) $user->getCredit())+$creditAmount);
$user->save();
$data = [
"credit" => $creditAmount
];
$mail = new Mail();
$mail->setDocument('/emails/'.$prod->getOrder()->getLang().'/credit-gifting');
$mail->to($user->getEmail());
$mail->setParams($data);
$mail->send();
}
}
if($order->getUsingCredits()){
$credCheck = new CreditHistory\Listing();
$credCheck->setCondition("mouvement = 'remove' AND order__id = ".$order->getId());
$credCheck = $credCheck->load();
if(!$credCheck){
$creditsLeft = 0;
if(((float) $user->getCredit()) >= ((float) $order->getCredits())){
$creditsLeft = ((float) $user->getCredit()) - ((float) $order->getCredits());
}
$history = new CreditHistory();
$history->setCredit((float) $order->getCredits());
$history->setMouvement("remove");
$history->setTransactionTime(Carbon::now());
$history->setOrder($order);
$history->setTransactionType("Studio Reservation");
$history->setKey("History_".$user->getUsername()."_".Carbon::now()->format("YmdHis")."_".uniqid());
$history->setPublished(true);
$history->setParent($credFolder);
$history->save();
$histlist = $user->getCreditHistory();
$histlist[] = $history;
$user->setCreditHistory($histlist);
$user->setCredit($creditsLeft);
$user->save();
}
}
}
if($order->getOrderType() == "pack"){
return $this->render('stripe/order_pack_success.html.twig', ["order" => $order]);
}
return ["order" => $order, "payment" => $payment, "prod" => $prod, "duration" => $duration];
}
/**
* @Template
* @param Request $request
* @route("/{_locale}/checkout-failure/{studiotag}/{order}", name="checkoutFailure", methods={"GET"})
* @return array
*/
public function failureAction(Request $request)
{
$orderId = $request->get('order');
$order = Order::getById($orderId);
return $this->render('stripe/failure.html.twig', ["order" => $order]);
}
/**
*
* @route("/stripe/webhook",name="stripe_webhook",methods={"POST"})
* @return array
* @throws Exception
*/
public function webhookAction(Request $request)
{
$webhookSecret = \Pimcore\Model\WebsiteSetting::getByName('stripeWebhookSecret')->getData();
if ($webhookSecret) {
try {
$event = \Stripe\Webhook::constructEvent(
$request->getContent(),
$request->headers->get('stripe-signature'),
$webhookSecret
);
} catch (SignatureVerificationException $e) {
\Pimcore\Log\Simple::log("StripeWebhook", "Error ".$e->getMessage()." sig : ".$request->headers->get('stripe-signature')." at ".date("Y-m-d H:i:s"));
return $this->json([ 'error' => $e->getMessage() ], 401);
}catch (UnexpectedValueException $e) {
\Pimcore\Log\Simple::log("StripeWebhook", "Error ".$e->getMessage()." sig : ".$request->headers->get('stripe-signature')." at ".date("Y-m-d H:i:s"));
return $this->json([ 'error' => $e->getMessage() ], 402);
}catch(Exception $e){
\Pimcore\Log\Simple::log("StripeWebhook", "Error ".$e->getMessage()." sig : ".$request->headers->get('stripe-signature')." at ".date("Y-m-d H:i:s"));
return $this->json([ 'error' => $e->getMessage() ], 403);
}
} else {
$event = $request->request->all();
}
\Pimcore\Log\Simple::log("StripeWebhook", "Data Received, sig : ".$request->headers->get('stripe-signature')." at ".date("Y-m-d H:i:s"));
\Pimcore\Log\Simple::log("StripeWebhook", "Data Received, data : ".json_encode($event));
$object = $event->data->object;
switch ($event->type) {
case 'payment_intent.succeeded':
$payment = Payment::getByStripePaymentIntent($object->id);
$payment = $payment->load()[0];
if($payment){
$payment->setStatus($object->status);
if($object->charges->data[0]->payment_method_details->type == "card"){
$payment->setCardBrand($object->charges->data[0]->payment_method_details->card->brand);
$payment->setCardDigit($object->charges->data[0]->payment_method_details->card->last4);
}else{
$payment->setCardBrand("bancontact");
$payment->setCardDigit($object->charges->data[0]->payment_method_details->bancontact->iban_last4);
}
$payment->setStripePaymentMethod($object->charges->data[0]->payment_method);
$payment->save();
sleep(15);
$order = $payment->getOrder();
if(!$order){
$order = Order::getByPayment($payment);
$order = $order->load()[0];
$payment->setOrder($order);
$payment->save();
}else{
$order->setPayment($payment);
$order->save();
}
$user = $order->getLinkedUser();
$flagProcess = false;
switch($user->getStatus()){
case 'unverified':
$order->setStatus('waiting');
break;
case 'verified':
$flagProcess = true;
$order->setStatus('validated');
if($order->getOrderType() != "pack"){
$order->setBrevoFlag(true);
}
break;
}
$order->save();
$request->setLocale($order->getLang());
if ($flagProcess) {
if($order->getOrderType() == "pack"){
$creditAmount = $order->getPack()->getPrice() + $order->getPack()->getBonus();
$userCredit = (float) $user->getCredit();
$user->setCredit($creditAmount+$userCredit);
$credFolder = \Pimcore\Model\DataObject\Folder::getByPath('/credit-history');
if (!$credFolder) {
$credFolder = new \Pimcore\Model\DataObject\Folder();
$credFolder->setKey('credit-history');
$credFolder->setParentId(1);
$credFolder->save();
}
$history1 = new CreditHistory();
$history1->setCredit($creditAmount);
$history1->setMouvement("add");
$history1->setTransactionTime(Carbon::now());
$history1->setOrder($order);
$history1->setTransactionType($order->getPack()->getTitle($order->getLang()));
$history1->setKey("History_".$user->getUsername()."_".Carbon::now()->format("YmdHis")."_".uniqid());
$history1->setPublished(true);
$history1->setParent($credFolder);
$history1->save();
$histlist1 = $user->getCreditHistory();
$histlist1[] = $history1;
$user->setCreditHistory($histlist1);
$user->save();
}else{
$data['user'] = $user;
$booking = $order->getBookings();
$data['order'] = $order;
$data['lockCode'] = $booking[0]->getLockCode();
$data['ptiCode'] = $booking[0]->getPtiCode();
if(!$data['lockCode'] || !$data['ptiCode']){
$clService = new CodelocksService();
$code = $clService->generateCode($booking[0]);
if($code){
$booking[0]->setLockCode($code);
$booking[0]->setPtiCode($code);
$booking[0]->save();
$data['lockCode'] = $code;
$data['ptiCode'] = $code;
}
}
if($data['lockCode'] && $data['ptiCode'] && !$booking[0]->getMailSent()){
//Mail au client
$mail = new Mail();
$mail->setDocument('/emails/'.$order->getLang().'/payment-confirmation');
$mail->to($user->getEmail());
$mail->setParams($data);
$mail->send();
$booking[0]->setMailSent(true);
$booking[0]->save();
}
$onboarding = \Pimcore\Model\WebsiteSetting::getByName('studio_mail_onboarding')?->getData();
$studioCat = $booking[0]->getBookedStudio()->getCategorie();
$studios = explode("|", $user->getTypeStudios());
if($onboarding && $studioCat->getTagBrevo() && $studioCat->getMailOnboarding($order->getLang()) && !in_array($studioCat->getTagBrevo(), $studios)){
$mailDoc = $studioCat->getMailOnboarding($order->getLang());
$mail = new Mail();
$mail->setDocument($mailDoc->getId());
$mail->to($user->getEmail());
$mail->setParams($data);
$mail->send();
}
}
if($promo = $order->getPromo()){
$promoService = new PromoService();
$promoService->success($promo, $user, $order);
}
if($order->getUsingCredits()){
$credCheck = new CreditHistory\Listing();
$credCheck->setCondition("mouvement = 'remove' AND order__id = ".$order->getId());
$credCheck = $credCheck->load();
if(!$credCheck){
$creditsLeft = 0;
if(((float) $user->getCredit()) >= ((float) $order->getCredits())){
$creditsLeft = ((float) $user->getCredit()) - ((float) $order->getCredits());
}
$credFolder = \Pimcore\Model\DataObject\Folder::getByPath('/credit-history');
if (!$credFolder) {
$credFolder = new \Pimcore\Model\DataObject\Folder();
$credFolder->setKey('credit-history');
$credFolder->setParentId(1);
$credFolder->save();
}
$history = new CreditHistory();
$history->setCredit((float) $order->getCredits());
$history->setMouvement("remove");
$history->setTransactionTime(Carbon::now());
$history->setOrder($order);
$history->setTransactionType("Studio Reservation");
$history->setKey("History_".$user->getUsername()."_".Carbon::now()->format("YmdHis")."_".uniqid());
$history->setPublished(true);
$history->setParent($credFolder);
$history->save();
$histlist = $user->getCreditHistory();
$histlist[] = $history;
$user->setCreditHistory($histlist);
$user->setCredit($creditsLeft);
$user->save();
}
}
$orderList = Order::getList();
$orderList->setCondition("status = 'validated' AND linkedUser__id = ? ", [$user->getId()]);
$orderL = $orderList->load();
$reservationCount = count($orderL);
if ($user->getStaffStatus() !== 'monitor' && $user->getStaffStatus() !== 'approved') {
if ($reservationCount == null || $reservationCount <= 1) {
$user->setStaffStatus('new');
} elseif ($reservationCount >= 3) {
$user->setStaffStatus('approved');
} else {
$user->setStaffStatus(null);
}
$user->save();
}
}
}
break;
case 'payment_intent.payment_failed':
$payment = Payment::getByStripePaymentIntent($object->id);
$payment = $payment->load()[0];
if($payment){
$payment->setStatus($object->status);
$payment->save();
$order = Order::getByPayment($payment);
$order = $order->load()[0];
$order->setStatus('new');
$order->save();
$booking = $order->getBookings()[0];
if($booking){
$booking->setLockCode(null);
$booking->setPtiCode(null);
$booking->save();
}
}
break;
default:
break;
}
return $this->json([ 'status' => 'success' ]);
}
/**
* @route("/stripe/deposit/capture", name="capture_deposit", methods={"POST"})
*/
public function captureDeposit(Request $request){
if(!$request->get('amount') || !$request->get('message') || !$request->get('order')){
return $this->json([ 'error' => 'missing parameters' ]);
}
$order = Order::getById($request->get('order'));
if($order->getStatusDeposit() !== null && $order->getStatusDeposit() !== ''){
return $this->json([ 'error' => 'deposit already '.$order->getStatusDeposit() ]);
}
$deposit = $order->getCaution();
if( $request->get('amount') > $deposit){
return $this->json([ 'error' => 'capture amount is more than deposit' ]);
}
$payment = $order->getPaymentDeposit();
$intent = \Stripe\PaymentIntent::retrieve($payment->getStripePaymentIntent());
try{
$intent->capture(['amount_to_capture' => $request->get('amount')*100]);
}catch(Exception $e){
return $this->json([ 'error' => $e->getMessage() ]);
}
$order->setStatusDeposit('captured');
$order->save();
$data['order'] = $order;
$data['user'] = $order->getLinkedUser();
//Mail au client
$mail = new Mail();
$mail->setDocument('/emails/'.$request->getLocale().'/payment-released');
$mail->to($order->getLinkedUser()->getEmail());
$mail->setParams($data);
$mail->send();
return $this->json(["status" => "success"]);
}
/**
* @route("/stripe/deposit/release", name="release_deposit", methods={"POST"})
*/
public function releaseDeposit(Request $request){
if(!$request->get('order')){
return $this->json([ 'error' => 'missing parameters' ]);
}
$order = Order::getById($request->get('order'));
if($order->getStatusDeposit() !== null){
return $this->json([ 'error' => 'deposit already '.$order->getStatusDeposit() ]);
}
$payment = $order->getPaymentDeposit();
try{
$this->stripe->paymentIntents->cancel($payment->getStripePaymentIntent(),[]);
}catch(Exception $e){
return $this->json([ 'error' => $e->getMessage() ]);
}
$order->setStatusDeposit('released');
$order->save();
//TODO - Done: faire une mail vers le client
$data['order'] = $order;
$data['user'] = $order->getLinkedUser();
//Mail au client
$mail = new Mail();
$mail->setDocument('/emails/'.$request->getLocale().'/payment-released');
$mail->to($order->getLinkedUser()->getEmail());
$mail->setParams($data);
$mail->send();
return $this->json(["status" => "success"]);
}
}