4

I have a Symfony 3.4 project, I wanted to integrate PayumBundle for Paypal Express Checkout, What I'm trying to achieve is the following: A user can add funds to their account using Paypal Express Checkout, once they fill in the amount they wanted to add, I check if the payment went through and I increase the user's balance with that amount. A user can buy "Orders" from the website as well, if they did that, the amount of the order will be deducted from their balance. and here is what I have set up for now:

Config.yml:

payum:
security:
    token_storage:
        AppBundle\Entity\PaymentToken: { doctrine: orm }
storages:
    AppBundle\Entity\Payment: { doctrine: orm }
    AppBundle\Entity\PaymentDetails: { doctrine: orm }
gateways:
    paypal_express_checkout_default_gateaway:
        factory: paypal_express_checkout
        username:  '**************.email.com'
        password:  '********************'
        signature: '***************'
        sandbox: true

My PaymentDetails class:

    /**
     * @ORM\Table(name="payum_payment_details")
     * @ORM\Entity
     */
    class PaymentDetails extends ArrayObject
    {
        /**
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="IDENTITY")
         */
        protected $id;
        /**
         * @return int
         */


        /**
         * One PaymentDetails has One AccountBalanceTransaction.
         * @ORM\OneToOne(targetEntity="AppBundle\Entity\AccountBalanceTransaction", mappedBy="paymentDetails")
         */
        private $accountBalanceTransaction;

        /**
         * Many PaymentDetails have One User.
         * @ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="paymentDetails")
         * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
         */
        private $user;

    // getters and setter removed

}

AccountBalanceTransaction class:

/**
 * @ORM\Entity
 * @ORM\Table(name="account_balance_transaction")
 */
class AccountBalanceTransaction
{
    public function __construct()
    {
        $this->setDate(new \DateTime());
        $this->paymentDetails = new ArrayCollection();
    }


    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="decimal", precision=7, scale=2, nullable=true)
     */
    private $credit;

    /**
     * @ORM\Column(type="decimal", precision=7, scale=2, nullable=true)
     */
    private $debit;

    /**
     * @ORM\Column(type="decimal", precision=7, scale=2)
     */
    private $balance;

    /**
     * @ORM\Column(type="datetime")
     */
    private $date;

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    private $description;

    /**
     * One AccountBalanceTransaction Might have One PaymentDetails.
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\PaymentDetails", inversedBy="accountBalanceTransaction")
     * @ORM\JoinColumn(name="payment_details_id", referencedColumnName="id", nullable=true)
     */
    private $paymentDetails;


    /**
     * One AccountBalanceTransaction Might have One Order.
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\Order", inversedBy="accountBalanceTransaction")
     * @ORM\JoinColumn(name="order_id", referencedColumnName="id", nullable=true)
     */
    private $order;

    /**
     * Many AccountBalanceTransactions have One User.
     * @ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="accountBalanceTransactions")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;
}

And finally, this is my PaymentController class:

class PaymentController extends Controller
{

 /**
     * @Extra\Route(
     *   "/prepare_add_funds",
     *   name="paypal_express_checkout_prepare_add_funds"
     * )
     *
     * @Extra\Template(":payment:prepare.html.twig")
     */
    public function prepareSimplePurchaseAndDoctrineOrmAction(Request $request)
    {
        $user = $this->getUser();
        if (!is_object($user) || !$user instanceof UserInterface) {
            throw new AccessDeniedException('This user does not have access to this section.');
        }

        $gatewayName = 'paypal_express_checkout_default_gateaway';
        $form = $this->createPurchaseForm();
        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
            $storage = $this->get('payum')->getStorage(PaymentDetails::class);
            /** @var $payment PaymentDetails */
            $payment = $storage->create();
            $amount = $data['amount'];
            $payment['PAYMENTREQUEST_0_CURRENCYCODE'] = 'USD';
            $payment['PAYMENTREQUEST_0_AMT'] = $amount;
            $storage->update($payment);
            $captureToken = $this->get('payum')->getTokenFactory()->createCaptureToken(
                $gatewayName,
                $payment,
                'paypal_express_checkout_done_add_funds'
            );
            $payment['INVNUM'] = $payment->getId();
            $payment->setUser($user);
            $storage->update($payment);

            return $this->redirect($captureToken->getTargetUrl());
        }

        return array(
            'form' => $form->createView(),
            'gatewayName' => $gatewayName
        );
    }

    /**
     * @Extra\Route(
     *   "/payment/details",
     *   name="paypal_express_checkout_done_add_funds"
     * )
     */

    public function viewAction(Request $request)
    {
        $user = $this->getUser();
        if (!is_object($user) || !$user instanceof UserInterface) {
            throw new AccessDeniedException('This user does not have access to this section.');
        }



        $token = $this->get('payum')->getHttpRequestVerifier()->verify($request);
        $gateway = $this->get('payum')->getGateway($token->getGatewayName());
        try {
            $gateway->execute(new Sync($token));
        } catch (RequestNotSupportedException $e) {}

        $gateway->execute($status = new GetHumanStatus($token));
        $payment = $status->getFirstModel();

        if ($user->getId() != || $payment->getUser()->getId()) {
            throw new AccessDeniedException('This user does not have access to this section.');
        }

        if ($status->isCaptured()) {
            $entityManager = $this->getDoctrine()->getManager();
            $amount = $details['AMT'];

            // check if this paymentDetails already has an AccountBalanceTransaction
            $accountBalanceTransaction = $this->getDoctrine()
                ->getRepository(AccountBalanceTransaction::class)
                ->findOneBy(['paymentDetails' => $payment->getId()]);
            // if not then create a new one and increase the user's balance
            if($accountBalanceTransaction === null){
                $accountBalanceTransaction = new AccountBalanceTransaction();
                $accountBalanceTransaction->setUser($user);
                $accountBalanceTransaction->setPaymentDetails($payment);
                $accountBalanceTransaction->setCredit($amount);
                $accountBalanceTransaction->setDescription('User added funds with Paypal.');
                $accountBalanceTransaction->setBalance($user->getBalance() + $amount);

                $entityManager->persist($accountBalanceTransaction);
                $entityManager->flush();


                $user->setBalance($user->getBalance() + $amount);
                $entityManager->persist($user);
                $entityManager->flush();
            }

            $refundToken = $this->get('payum')->getTokenFactory()->createRefundToken(
                $token->getGatewayName(),
                $status->getFirstModel(),
                $request->getUri()
            );
        }

        return $this->render('payment/view.html.twig', array(
            'status' => $status->getValue(),
            'refundToken' => $refundToken
        ));
    }


    /**
     * @return \Symfony\Component\Form\FormInterface
     */
    protected function createPurchaseForm()
    {
        return $this->createFormBuilder()
            ->add('amount', null, array(
                'data' => 100,
                'constraints' => array(new Range(array('max' => 10000)))
            ))
            ->getForm()
            ;
    }
}

I'm using FosUserBundle for users management.

This kind of works, but I'm not really happy with it, I think there is a better way to do it, but since I'm new to Payum and payments integration in general, so I'll need someone else's help.

Also, I'm not sure if I should be creating the Entity AccountBalanceTransaction in the viewAction, Maybe I should create it in the prepareAction along with PaymentDetails. But again, what if I did that and the payment failed should I delete it in the view action?

Ayoub_B
  • 702
  • 7
  • 19

0 Answers0