KptivePaymentSipsBundle
, (*1)
The KptivePaymentSipsBundle
provides access to the Atos Worldline SIPS payment solution through
the JMSPaymentCoreBundle., (*2)
The following payment services are powered by Atos SIPS:
- Merc@net (BNP Parisbas)
- Cyberplus (Banque Populaire)
- Elys Net (HSBC)
- Scellius (La Banque Postale)
- SogenActif (Société Générale)
- Webaffaires (Crédit du Nord)
- Sherlocks (LCL)
- Citelis (Crédit Mutuel)
- ..., (*3)
This means that this bundle should work out of the box with any of them., (*4)
Installation
Step 1
Run:, (*5)
``` bash
$ php composer.phar require kptive/payment-sips-bundle, (*6)
Or add the following to your `composer.json` before updating your vendors:
``` js
{
"require": {
"kptive/payment-sips-bundle": "*@dev"
}
}
Step 2
Register the bundle in your AppKernel
class.
You will also have to register the JMSPaymentCoreBundle
and
configure it., (*7)
``` php
<?php
// app/AppKernel.php, (*8)
public function registerBundles()
{
$bundles = array(
// ...
new JMS\Payment\CoreBundle\JMSPaymentCoreBundle(),
new Kptive\PaymentSipsBundle\KptivePaymentSipsBundle(),
);
// ...
}
// ...
### Step 3
Copy the content of your SIPS folder into `app/sips/`. If you want to put it
elsewhere, just edit the config values of the `pathfile` and binaries locations
(see below).
You will also have to copy or put your own logo images in the right location
depending on what you specified in your `pathfile`.
Configuration
-------------
``` yaml
kptive_payment_sips:
config:
merchant_id: "082584341411111"
merchant_country: fr
normal_return_url: %base_url%/checkout/complete
cancel_return_url: %base_url%/checkout/cancel
automatic_response_url: %base_url%/checkout/notification
pathfile: %kernel.root_dir%/config/sips/param/pathfile
currency_code: 978
bin:
request_bin: %kernel.root_dir%/config/sips/bin/static/request
response_bin: %kernel.root_dir%/config/sips/bin/static/response
Usage
Let's assume that you have an AcmePaymentBundle
and that you handle your
orders with a Acme\PaymentBundle\Entity\Order
class:, (*9)
``` php
<?php, (*10)
namespace Acme\PaymentBundle\Entity;, (*11)
use Doctrine\ORM\Mapping as ORM;
use JMS\Payment\CoreBundle\Entity\PaymentInstruction;, (*12)
/**
* @ORM\Table(name="acme_order")
*/
class Order
{, (*13)
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="JMS\Payment\CoreBundle\Entity\PaymentInstruction")
*/
private $paymentInstruction;
/**
* @ORM\Column(type="decimal", precision=10, scale=2)
*/
private $amount;
/**
* @ORM\Column(type="datetime", name="payed_at", nullable=true)
*/
private $payedAt;
// ...
public function getId()
{
return $this->id;
}
public function getAmount()
{
return $this->amount;
}
public function getPaymentInstruction()
{
return $this->paymentInstruction;
}
public function setPaymentInstruction(PaymentInstruction $instruction)
{
$this->paymentInstruction = $instruction;
return $this;
}
public function getPayedAt()
{
return $this->payedAt;
}
public function setPayedAt($payedAt)
{
$this->payedAt = $payedAt;
return $this;
}
Create a controller with a `details` action.
This is where your customer can review their order and confirm it.
``` php
<?php
namespace Acme\PaymentBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use JMS\Payment\CoreBundle\Entity\PaymentInstruction;
use Acme\PaymentBundle\Entity\Order;
/**
* @Route("/checkout")
*/
class CheckoutController extends Controller
{
// ...
/**
* @Route("/details/{id}", name = "payment_details")
* @Template()
*/
public function detailsAction(Order $order)
{
$request = $this->get('request');
$em = $this->get('doctrine')->getEntityManager();
$router = $this->get('router');
$ppc = $this->get('payment.plugin_controller');
$confirm = new \StdClass();
$form = $this->createFormBuilder($confirm)
->add('save', 'submit', array('label' => 'confirmer'))
->getForm();
if ('POST' === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
$instruction = new PaymentInstruction($order->getAmount(), 978, 'sips');
$ppc->createPaymentInstruction($instruction);
$order->setPaymentInstruction($instruction);
$em->persist($order);
$em->flush($order);
return new RedirectResponse($router->generate('payment_gateway', array(
'id' => $order->getId(),
)));
}
}
return array(
'order' => $order,
'form' => $form->createView()
);
}
}
As you can see in the previous action, when the user confirms their order, we
create a new PaymentInstruction
(see the
JMSPaymentCoreBundle documentation
for more information on how it works).
They are then redirected to the payment_gateway
route.
This is where we'll make a call to the SIPS API so that we can display the SIPS
credit card choice form., (*14)
Let's implement the corresponding action:, (*15)
``` php
/**
* @Route("/gateway/{id}", name="payment_gateway")
* @Template()
*/
public function sipsGatewayAction(Order $order)
{
$client = $this->get('kptive_payment_sips.client');, (*16)
$config = array(
'amount' => $order->getAmount() * 100,
'order_id' => $order->getId(),
);
$sips = $client->request($config);
return array('sips' => $sips);
}
And in the corresponding view, display the form to the user:
``` jinja
{# src/Acme/PaymentBundle/Resources/views/Checkout/sipsGateway.html.twig #}
{{ sips|raw }}
When the user has completed the payment workflow on the SIPS platform, they will
be redirected to the normal_return_url
you configured earlier in the bundle
config section., (*17)
Let's implement the action :, (*18)
``` php
/**
* @Route("/complete", name="payment_complete")
* @Template()
*/
public function completeAction(Request $request)
{
$data = $request->request->get('DATA');
$em = $this->get('doctrine')->getEntityManager();
$client = $this->get('kptive_payment_sips.client');, (*19)
$response = $client->handleResponseData($data);
$order = $em->getRepository('KsPaymentBundle:Order')->find($response['order_id']);
$instruction = $order->getPaymentInstruction();
$result = $this->get('kptive_payment_sips.return_handler')->handle($instruction, $response);
return array('order' => $order);
}
For now, we didn't do anything with the Order, we just handled the bank response
and marked the payment as valid.
The JMSPaymentCoreBundle will trigger a `payment.state_change` event.
So we will listen to this event and do everything useful we want in a `PaymentListener`:
``` php
<?php
namespace Acme\PaymentBundle\EventListener;
use Doctrine\ORM\EntityManager;
use JMS\Payment\CoreBundle\PluginController\Event\PaymentStateChangeEvent;
use JMS\Payment\CoreBundle\Model\PaymentInterface;
class PaymentListener
{
protected $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function onPaymentStateChange(PaymentStateChangeEvent $event)
{
if (PaymentInterface::STATE_DEPOSITED === $event->getNewState()) {
$order = $this
->entityManager
->getRepository('AcmePaymentBundle:Order')
->findOneBy(array('paymentInstruction' => $event->getPaymentInstruction()));
$order->setPayedAt(new \DateTime());
// Do various things with the Order here
// ...
$this->entityManager->persist($order);
$this->entityManager->flush();
}
}
}
Register it as a service:, (*20)
xml
<service id="acme_payment.payment_listener" class="Acme\PaymentBundle\EventListener\PaymentListener">
<tag name="kernel.event_listener" event="payment.state_change" method="onPaymentStateChange" />
<argument type="service" id="doctrine.orm.entity_manager">
</service>
, (*21)
And voilĂ !, (*22)
If your customer doesn't click on the "Back" button on the bank platform,
a request will be automatically issued to the configured automatic_response_url
., (*23)
You can use the same URL as the normal_return_url
or implement your own., (*24)
Warning: those examples don't take security into account. Don't forget to
check the ownership of the order!, (*25)
Credits
A great thank you to Johannes M Schmitt for his awesome JMSPayementCoreBundle.
Thanks to https://github.com/Kitano/KitanoPaymentSipsBundle for the inspiration., (*26)
License
KptivePaymentSipsBundle is released under the MIT License.
See the bundled LICENSE file for details., (*27)