Merge pull request #282 from roadster31/basemodulecontroller

Standard pages and enhancements for payment modules
This commit is contained in:
Manuel Raynaud
2014-04-11 09:34:51 +02:00
11 changed files with 271 additions and 15 deletions

View File

@@ -48,9 +48,9 @@ class BaseFrontController extends BaseController
/** /**
* Redirect to à route ID related URL * Redirect to à route ID related URL
* *
* @param unknown $routeId the route ID, as found in Config/Resources/routing/admin.xml * @param string $routeId the route ID, as found in Config/Resources/routing/admin.xml
* @param array|\Thelia\Controller\Front\unknown $urlParameters the URL parametrs, as a var/value pair array * @param array $urlParameters the URL parametrs, as a var/value pair array
* @param bool $referenceType * @param bool $referenceType
*/ */
public function redirectToRoute($routeId, $urlParameters = array(), $referenceType = Router::ABSOLUTE_PATH) public function redirectToRoute($routeId, $urlParameters = array(), $referenceType = Router::ABSOLUTE_PATH)
{ {
@@ -104,7 +104,7 @@ class BaseFrontController extends BaseController
/** /**
* Render the given template, and returns the result as an Http Response. * Render the given template, and returns the result as an Http Response.
* *
* @param $templateName the complete template name, with extension * @param string $templateName the complete template name, with extension
* @param array $args the template arguments * @param array $args the template arguments
* @param int $status http code status * @param int $status http code status
* @return \Thelia\Core\HttpFoundation\Response * @return \Thelia\Core\HttpFoundation\Response

View File

@@ -38,6 +38,13 @@ interface ParserInterface
public function setStatus($status); public function setStatus($status);
/**
* Setup the parser with a template definition, which provides a template description.
*
* @param TemplateDefinition $templateDefinition
*/
public function setTemplateDefinition(TemplateDefinition $templateDefinition);
/** /**
* Add a template directory to the current template list * Add a template directory to the current template list
* *
@@ -54,7 +61,7 @@ interface ParserInterface
* Return the registeted template directories for a givent template type * Return the registeted template directories for a givent template type
* *
* @param unknown $templateType * @param unknown $templateType
* @throws InvalidArgumentException if the tempmateType is not defined * @throws \InvalidArgumentException if the templateType is not defined
* @return array: an array of defined templates directories for the given template type * @return array: an array of defined templates directories for the given template type
*/ */
public function getTemplateDirectories($templateType); public function getTemplateDirectories($templateType);

View File

@@ -92,11 +92,11 @@ class SmartyParser extends Smarty implements ParserInterface
/** /**
* Add a template directory to the current template list * Add a template directory to the current template list
* *
* @param unknown $templateType the template type (a TemplateDefinition type constant) * @param int $templateType the template type (a TemplateDefinition type constant)
* @param string $templateName the template name * @param string $templateName the template name
* @param string $templateDirectory path to the template dirtectory * @param string $templateDirectory path to the template dirtectory
* @param unknown $key ??? * @param unknown $key ???
* @param string $unshift ??? Etienne ? * @param boolean $unshift ??? Etienne ?
*/ */
public function addTemplateDirectory($templateType, $templateName, $templateDirectory, $key, $unshift = false) public function addTemplateDirectory($templateType, $templateName, $templateDirectory, $key, $unshift = false)
{ {
@@ -116,9 +116,9 @@ class SmartyParser extends Smarty implements ParserInterface
/** /**
* Return the registeted template directories for a givent template type * Return the registeted template directories for a givent template type
* *
* @param unknown $templateType * @param int $templateType
* @throws InvalidArgumentException * @throws InvalidArgumentException
* @return multitype: * @return mixed:
*/ */
public function getTemplateDirectories($templateType) public function getTemplateDirectories($templateType)
{ {

View File

@@ -124,6 +124,19 @@ class Tlog Implements LoggerInterface
return self::$instance; return self::$instance;
} }
/**
* Create a new Tlog instance, that could be configured without interfering with the "main" instance
*
* @return Tlog a new Tlog instance.
*/
public static function getNewInstance() {
$instance = new Tlog();
$instance->init();
return $instance;
}
/** /**
* initialize default configuration * initialize default configuration
*/ */

View File

@@ -0,0 +1,132 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Module;
use Symfony\Component\Routing\Router;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Template\TemplateHelper;
use Thelia\Exception\TheliaProcessException;
use Thelia\Model\Order;
use Thelia\Tools\URL;
abstract class AbstractPaymentModule extends BaseModule implements PaymentModuleInterface
{
/**
* This method is called when the payement gateway needs to be invoked.
*
* If this method return a Response instance, this response is sent to the browser. Return null if you don't want to
* send a response and process the payment yourself.
*
* In many cases, it's necessary to send a form to the payment gateway. On your response you can return this form already
* completed, ready to be sent, instead of redirecting. The generateGatewayFormResponse() may help you in this case :)
*
* @param Order $order processed order
* @return null|Response
*/
abstract public function pay(Order $order);
/**
* This method is called by the Payment loop, to check if the current module has to be displayed to the customer
*
* If you return true, the payment method will de displayed to the customed
* If you return false, the payment method will not be displayed
*
* @return boolean
*/
abstract public function isValidPayment();
/**
* Render the payment gateway template. The module should provide the gateway URL and the form fields names and values.
*
* @param Order $order the order
* @param string $gateway_url the payment gateway URL
* @param array $form_data an associative array of form data, that will be rendered as hiddent fields
*
* @return Response the HTTP response.
*/
public function generateGatewayFormResponse($order, $gateway_url, $form_data)
{
/** @var ParserInterface $parser */
$parser = $this->container->get("thelia.parser");
$parser->setTemplateDefinition(TemplateHelper::getInstance()->getActiveFrontTemplate());
$renderedTemplate = $parser->render(
"order-payment-gateway.html",
array(
"order_id" => $order->getId(),
"cart_count" => $this->getRequest()->getSession()->getCart()->getCartItems()->count(),
"gateway_url" => $gateway_url,
"payment_form_data" => $form_data
)
);
return Response::create($renderedTemplate);
}
/**
* Return the order payment success page URL
*
* @param int $order_id the order ID
* @return string the order payment success page URL
*/
public function getPayementSuccessPageUrl($order_id)
{
$frontOfficeRouter = $this->container->get('router.front');
return URL::getInstance()->absoluteUrl(
$frontOfficeRouter->generate(
"order.placed",
array("order_id" => $order_id),
Router::ABSOLUTE_URL
)
);
}
/**
* Redirect the customer to the failure payment page. if $message is null, a generic message is displayed.
*
* @param int $order_id the order ID
* @param string|null $message an error message.
*
* @return string the order payment failure page URL
*/
public function getPayementFailurePageUrl($order_id, $message)
{
$frontOfficeRouter = $this->container->get('router.front');
return URL::getInstance()->absoluteUrl(
$frontOfficeRouter->generate(
"order.failed",
array(
"order_id" => $order_id,
"message" => $message
),
Router::ABSOLUTE_URL
)
);
}
}

View File

@@ -158,6 +158,11 @@
<default key="_controller">Front\Controller\OrderController::orderPlaced</default> <default key="_controller">Front\Controller\OrderController::orderPlaced</default>
<default key="_view">order-placed</default> <default key="_view">order-placed</default>
</route> </route>
<route id="order.failed" path="/order/failed/{order_id}/{message}">
<default key="_controller">Front\Controller\OrderController::orderFailed</default>
<default key="_view">order-failed</default>
</route>
<!-- end order management process --> <!-- end order management process -->
<!-- contact management --> <!-- contact management -->

View File

@@ -234,6 +234,29 @@ class OrderController extends BaseFrontController
$this->getParserContext()->set("placed_order_id", $placedOrder->getId()); $this->getParserContext()->set("placed_order_id", $placedOrder->getId());
} }
public function orderFailed($order_id, $message)
{
/* check if the placed order matched the customer */
$failedOrder = OrderQuery::create()->findPk(
$this->getRequest()->attributes->get('order_id')
);
if (null === $failedOrder) {
throw new TheliaProcessException("No failed order", TheliaProcessException::NO_PLACED_ORDER, $failedOrder);
}
$customer = $this->getSecurityContext()->getCustomerUser();
if (null === $customer || $failedOrder->getCustomerId() !== $customer->getId()) {
throw new TheliaProcessException("Received failed order id does not belong to the current customer", TheliaProcessException::PLACED_ORDER_ID_BAD_CURRENT_CUSTOMER, $placedOrder);
}
$this->getParserContext()
->set("failed_order_id", $failedOrder->getId())
->set("failed_order_message", $message)
;
}
protected function getOrderEvent() protected function getOrderEvent()
{ {
$order = $this->getOrder($this->getRequest()); $order = $this->getOrder($this->getRequest());

View File

@@ -13,7 +13,7 @@
<div class="clearfix"> <div class="clearfix">
<ul class="breadcrumb pull-left"> <ul class="breadcrumb pull-left">
<li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li> <li><a href="{url path='/admin/home'}">{intl l="Home"}</a></li>
<li><a href="#">{intl l="Search"}</a></li> <li><a href="#">{intl l="Search for '%term'" term=trim($smarty.get.search_term)}</a></li>
</ul> </ul>
</div> </div>
@@ -54,7 +54,7 @@
</thead> </thead>
<tbody> <tbody>
{loop name="customer_list" type="customer" current="false" visible="*" backend_context="1" search_term=$smarty.get.search_term search_in="ref,firstname,lastname,email"} {loop name="customer_list" type="customer" current="false" visible="*" backend_context="1" search_term=trim($smarty.get.search_term) search_in="ref,firstname,lastname,email"}
{assign "lastOrderDate" ''} {assign "lastOrderDate" ''}
{assign "lastOrderAmount" ''} {assign "lastOrderAmount" ''}
{assign "lastOrderCurrency" ''} {assign "lastOrderCurrency" ''}
@@ -131,7 +131,7 @@
</thead> </thead>
<tbody> <tbody>
{loop type="order" name="order-search" backend_context=1 customer="*" search_term=$smarty.get.search_term search_in="ref,customer_ref,customer_firstname,customer_lastname,customer_email"} {loop type="order" name="order-search" backend_context=1 customer="*" search_term=trim($smarty.get.search_term) search_in="ref,customer_ref,customer_firstname,customer_lastname,customer_email"}
{loop type="order_address" name="order-invoice-address" id=$INVOICE_ADDRESS} {loop type="order_address" name="order-invoice-address" id=$INVOICE_ADDRESS}
{assign "orderInvoiceFirstName" $FIRSTNAME} {assign "orderInvoiceFirstName" $FIRSTNAME}
{assign "orderInvoiceLastName" $LASTNAME} {assign "orderInvoiceLastName" $LASTNAME}
@@ -187,7 +187,7 @@
</thead> </thead>
<tbody> <tbody>
{loop type="product" name="product-search" visible="*" search_mode="sentence" search_term=$smarty.get.search_term search_in="ref,title"} {loop type="product" name="product-search" visible="*" search_mode="sentence" search_term=trim($smarty.get.search_term) search_in="ref,title"}
<tr> <tr>
<td>{$ID}</td> <td>{$ID}</td>

View File

@@ -23,7 +23,7 @@
<div class="main"> <div class="main">
<article id="cart" class="col-main" role="main" aria-labelledby="main-label"> <article id="cart" class="col-main" role="main" aria-labelledby="main-label">
<h1 id="main-label" class="page-header">{intl l="Your Cart"}</h1> <h1 id="main-label" class="page-header">{intl l="Billing and delivery"}</h1>
{include file="misc/checkout-progress.tpl" step="delivery"} {include file="misc/checkout-progress.tpl" step="delivery"}

View File

@@ -23,7 +23,7 @@
<div class="main"> <div class="main">
<article class="col-main" role="main" aria-labelledby="main-label"> <article class="col-main" role="main" aria-labelledby="main-label">
<h1 id="main-label" class="page-header">{intl l="Your Cart"}</h1> <h1 id="main-label" class="page-header">{intl l="Check my order"}</h1>
{include file="misc/checkout-progress.tpl" step="invoice"} {include file="misc/checkout-progress.tpl" step="invoice"}

View File

@@ -0,0 +1,76 @@
{extends file="layout.tpl"}
{* Security *}
{block name="no-return-functions" prepend}
{check_auth role="CUSTOMER" login_tpl="login"}
{/block}
{* Body Class *}
{block name="body-class"}page-order-payment{/block}
{* Breadcrumb *}
{block name='no-return-functions' append}
{$breadcrumbs = [
['title' => {intl l="Cart"}, 'url'=>{url path="/cart"}],
['title' => {intl l="Secure Payment"}, 'url'=>{url path="/order/pay"}]
]}
{/block}
{block name="main-content"}
<div class="main">
<article class="col-main clearfix" role="main" aria-labelledby="main-label">
<h1 id="main-label" class="page-header">{intl l="Secure payment"}</h1>
{include file="misc/checkout-progress.tpl" step="last"}
{loop type="order" name="placed-order" id=$order_id}
<div id="payment-success" class="panel">
<div class="panel-heading">
<h3 class="panel-title">{intl l="You choose to pay by"} : <span class="payment-method">{loop name="payment-module" type="module" id=$PAYMENT_MODULE}{$TITLE}{/loop}</span></h3>
</div>
<div class="panel-body">
{if $cart_count > 0}
<div class="panel-heading clearfix">
{intl l="Connecting to the secure payment server, please wait a few seconds..."}
</div>
<div class="panel-body">
<form action="{$gateway_url}" method="post" id="payement_gateway_form">
{foreach from=$payment_form_data key='name' item='value'}
<input type="hidden" name="{$name}" value="{$value}" />
{/foreach}
<p>{intl l='If nothing happens within 10 seconds, <a id="force-submit-payment-form" href="#">please click here</a>.'}</p>
</form>
</div>
{else}
{intl l="Sorry, your cart is empty. There's nothing to pay."}
{/if}
</div>
</div>
{/loop}
</article>
</div>
{/block}
{block name="javascript-initialization"}
<script type="text/javascript">
jQuery(function($) {
$('#payement_gateway_form').submit();
$('#force-submit-payment-form').click(function(ev) {
$('#payement_gateway_form').submit();
ev.preventDefault();
});
});
</script>
{/block}