You can obtain free community support for example through stackoverflow, or also through the Symfony2 mailing list.
If you think you found a bug, please create a ticket in the bug tracker.
If you take code quality seriously, try out the new continuous inspection service.
scrutinizer-ci.com
In this chapter, we will explore how you can integrate JMSPaymentCoreBundle into your application. We will assume that you already have created an order object or equivalent. This could look like:
<?php
use Doctrine\ORM\Mapping as ORM;
use JMS\Payment\CoreBundle\Entity\PaymentInstruction;
class Order
{
/** @ORM\OneToOne(targetEntity="JMSPaymentCore:PaymentInstruction") */
private $paymentInstruction;
/** @ORM\Column(type="string", unique = true) */
private $orderNumber;
/** @ORM\Column(type="decimal", precision = 2) */
private $amount;
// ...
public function __construct($amount, $orderNumber)
{
$this->amount = $amount;
$this->orderNumber = $orderNumber;
}
public function getOrderNumber()
{
return $this->orderNumber;
}
public function getAmount()
{
return $this->amount;
}
public function getPaymentInstruction()
{
return $this->paymentInstruction;
}
public function setPaymentInstruction(PaymentInstruction $instruction)
{
$this->paymentInstruction = $instruction;
}
// ...
}
Usually, you want to give a potential customer some options on how to pay. For this, JMSPaymentCoreBundle ships with a special form type, jms_choose_payment_method, which we will leverage.
<?php
use JMS\DiExtraBundle\Annotation as DI;
use JMS\Payment\CoreBundle\Entity\Payment;
use JMS\Payment\CoreBundle\PluginController\Result;
use JMS\Payment\CoreBundle\Plugin\Exception\ActionRequiredException;
use JMS\Payment\CoreBundle\Plugin\Exception\Action\VisitUrl;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* @Route("/payments")
*/
class PaymentController
{
/** @DI\Inject */
private $request;
/** @DI\Inject */
private $router;
/** @DI\Inject("doctrine.orm.entity_manager") */
private $em;
/** @DI\Inject("payment.plugin_controller") */
private $ppc;
/**
* @Route("/{orderNumber}/details", name = "payment_details")
* @Template
*/
public function detailsAction(Order $order)
{
$form = $this->getFormFactory()->create('jms_choose_payment_method', null, array(
'amount' => $order->getAmount(),
'currency' => 'EUR',
'default_method' => 'payment_paypal', // Optional
'predefined_data' => array(
'paypal_express_checkout' => array(
'return_url' => $this->router->generate('payment_complete', array(
'orderNumber' => $order->getOrderNumber(),
), true),
'cancel_url' => $this->router->generate('payment_cancel', array(
'orderNumber' => $order->getOrderNumber(),
), true)
),
),
));
if ('POST' === $this->request->getMethod()) {
$form->bindRequest($this->request);
if ($form->isValid()) {
$this->ppc->createPaymentInstruction($instruction = $form->getData());
$order->setPaymentInstruction($instruction);
$this->em->persist($order);
$this->em->flush($order);
return new RedirectResponse($this->router->generate('payment_complete', array(
'orderNumber' => $order->getOrderNumber(),
)));
}
}
return array(
'form' => $form->createView()
);
}
// ...
/** @DI\LookupMethod("form.factory") */
protected function getFormFactory() { }
}
The jms_choose_payment_method form type will automatically render a form with all available payment methods. Upon binding, the form type will validate the data for the chosen payment method, and on success will give us a valid PaymentInstruction instance back.
You might want to add extra costs for a specific payment method. You can easily handle this by passing on a closure to the amount of the form:
<?php
use JMS\Payment\CoreBundle\Entity\ExtendedData;
$form = $this->getFormFactory()->create('jms_choose_payment_method', null, array(
'amount' => function($currency, $paymentSystemName, ExtendedData $data) use ($order) {
if ('paypal_express_checkout' == $paymentSystemName) {
return $order->getAmount() * 1.05;
}
return $order->getAmount();
},
// ...
));
In the previous section, we have created our PaymentInstruction. Now, we will see how we can actually deposit money in our account. As you saw above in the detailsAction, we redirected the user to the payment_complete route for which we will now create the corresponding action in our controller:
<?php
use JMS\DiExtraBundle\Annotation as DI;
use JMS\Payment\CoreBundle\Entity\Payment;
use JMS\Payment\CoreBundle\PluginController\Result;
use JMS\Payment\CoreBundle\Plugin\Exception\ActionRequiredException;
use JMS\Payment\CoreBundle\Plugin\Exception\Action\VisitUrl;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* @Route("/payments")
*/
class PaymentController
{
/** @DI\Inject */
private $request;
/** @DI\Inject */
private $router;
/** @DI\Inject("doctrine.orm.entity_manager") */
private $em;
/** @DI\Inject("payment.plugin_controller") */
private $ppc;
// ... see previous section
/**
* @Route("/{orderNumber}/complete", name = "payment_complete")
*/
public function completeAction(Order $order)
{
$instruction = $order->getPaymentInstruction();
if (null === $pendingTransaction = $instruction->getPendingTransaction()) {
$payment = $this->ppc->createPayment($instruction->getId(), $instruction->getAmount() - $instruction->getDepositedAmount());
} else {
$payment = $pendingTransaction->getPayment();
}
$result = $this->ppc->approveAndDeposit($payment->getId(), $payment->getTargetAmount());
if (Result::STATUS_PENDING === $result->getStatus()) {
$ex = $result->getPluginException();
if ($ex instanceof ActionRequiredException) {
$action = $ex->getAction();
if ($action instanceof VisitUrl) {
return new RedirectResponse($action->getUrl());
}
throw $ex;
}
} else if (Result::STATUS_SUCCESS !== $result->getStatus()) {
throw new \RuntimeException('Transaction was not successful: '.$result->getReasonCode());
}
// payment was successful, do something interesting with the order
}
}