Integrating a New Payment Provider into Coreshop
Coreshop uses Payum to handle all payments done by Coreshop. It is a PHP payment processing library that handles all payment logic. Out of the box Payum supports many payment providers; you can find the list of all supported official payment providers here.
When we want to add a new payment provider for Coreshop, we first need to check whether or not the payment provider that we need is supported by Payum. If Payum supports a required payment provider, it can be added to Coreshop easily in a few steps. If Payum does not support it, we need to create our own Payum extension for that specific provider and connect it to Coreshop. In this guide, we will cover both of those cases.
Integrating Payum-supported Payment Provider into Coreshop
When you want to implement a payment provider into Coreshop — which Payum already supports via official support or community package — you just have to follow a couple of steps.
1. First, create a configuration form type (a Symfony form type), which will contain all the fields that are necessary for your payment provider to work. Let's say our payment provider needs a username, api_key, and secret fields.
2. Register configuration form type inside services.yaml
Now, all that is left is to enable that payment provider inside the administration. If everything is done correctly, you should be able to see the payment_provider type when you try to add a new payment provider, and after selecting it, a configuration form will appear at the bottom with all the fields we defined.
Implementing Payum Gateway for Payment Provider and Integrating it into Coreshop
Although Payum supports a lot of payment providers and there are a lot of community packages that integrate payment providers that Payum does not officially support, it can happen that you will have to implement a payment provider for which there is no official or community support.
In order to implement a Payum gateway for a payment provider, we first need to understand the basics of Payum integration. Basically, the whole Payum logic is based on these three concepts:
- Request - contains payment model information. We define multiple requests that are needed for specific payment providers (such as a request for getting payment details, a request for executing the payment, and so on), and we execute them when we need them.
- Action - a class that handles the specific request. In each action, we define which request that particular action handles and the logic that has to be executed for that request.
- Gateway - class that connects requests and actions.
Start by creating a gateway factory and register it inside services.yaml. For now, we will just create an empty class.
Make sure that factory has the same value as the type that is used when registering the payment provider configuration form.
This class is the place where you will define which actions your payment gateway supports and define the necessary configuration. When actions are added to the gateway, they will be added as a stack.
Keep in mind that both Coreshop and Payum define some default actions supported by every gateway (you can find those inside yaml files — they are tagged with payum.action tag), and all actions we add will be added to the back of that stack.
That is important because of the way the gateway works. When executing a request, the gateway will try to find the first action that supports a given request and triggers it, and once that action is done, the gateway is also done.
So if you want to trigger multiple actions, you will have to execute another request inside an original action, and then the gateway will start the process for that request. When it is done, it will continue with the logic from the original action.
Coreshop Payment Process using Payum - How Does it Work
Now, let's see how the Coreshop payment process using Payum works. For the purposes of this guide, let's assume that you are using a default Coreshop installation, but this guide can easily be applied to any custom Coreshop checkout process.
So, first, you create a cart, add a couple of products to it, and you go through all the checkout steps, and are ready to make your payment. When you click a button on the last checkout step, you start the payment process, first CoreShop/Bundle/FrontendBundle/Controller/CheckoutController::doCheckoutAction triggers, and here you are redirected to the coreshop_payment route where all of the logic starts.
Coreshop_payment route is defined in CoreShop/Bundle/PayumBundle/Controller/PaymentController::prepareCaptureAction, and here a special token is created based on the selected payment provider.
The token contains the gateway name, model (Coreshop payment interface), target path (URL which should start the process of payment), and after path (URL to which we should redirect after payment is done).
Two types of the token can be created: authorize token and capture token (the only difference between those two is the target path). The last thing that prepareCaptureAction does is redirect to the target path from the token. We will focus on capture token in this guide, but the same logic applies to authorize token.
The target path of the capture token is the route defined inside payum/payum-bundle/Controller/CaptureController::doAction. Here Payum first gets our gateway based on the name of the gateway defined inside the token, and then the first Payum request (Capture request) is executed by the gateway.
As mentioned above, the gateway will try to find the first action supporting that Capture request, which is CapturePaymentAction from Coreshop.
As you can see, that action also tries to execute the GetStatus request, which contains the Coreshop payment. Now, the gateway will again go from the top of the stack and try to find the first action that supports the GetStatus request.
Here comes the second Coreshop action that will trigger — and that is ExecuteSameRequestWithPaymentDetailsAction — this action will only change the request model a bit. It will only use payment details instead of the whole payment object, and then it will execute the same GetStatus request.
There are no default actions that support the GetStatus request, so we will have to define one for our gateway.
Make sure to add it inside PaymentProviderGatewayFactory.
StatusAction will just check in which status is payment. Keep in mind that the request model is actually Coreshop payment details due to changes in ExecuteSameRequestWithPaymentDetailsAction. So, in our case, if the model is empty, that means that payment was not yet created and the status is new.
StatusAction is now done, so we go back to action, and that is ExecuteSameRequestWithPaymentDetailsAction. That action is also done, so we go to CapturePaymentAction; there, we go inside and if condition because our payment status is now new.
Here a ConvertPayment request will be executed, and if we don't support that request, it will default to the code inside catch. At the end of the CapturePaymentAction, the same original Capture request that was executed inside CaptureController will be executed again but now with a different request model, and here again, we have to define an action that supports it. So let's create our CaptureAction.
This time we can do a bit more logic. Let's say that our CaptureAction will execute an API call using the configuration we defined (username, api_key, secret) and after the API call is done, make a new request. For that, we need to define an API class that CaptureAction will use.
In CaptureAction, we have to implement ApiAwareInterface and use ApiAwareTrait so we can use our API class, and we can also implement GatewayAwareInterface and use GatewayAwareTrait so we have access to the gateway so we can execute another request.
Once we've added CaptureAction and Api class, we have to update PaymentProviderGatewayFactory, so our payment provider knows about the new action and API class.
CaptureAction will now do an API call executePayment, and that will execute the payment and return a response that will be set inside a request model. After, we can execute the GetStatus request because now our request model will have a status variable, but first, we have to make some adjustments in StatusAction to handle the new request model.
And that's it. After StatusAction is done, all of our actions are finished, and Payum will redirect us to the after-payment URL that was defined inside the capture token. Coreshop will do the rest of the logic, such as updating order, sending order confirmation mail, and so on.
One thing we still need to mention in this guide is the creation of a custom request. That is pretty simple to do; just create a PHP class. Your request can extend the Generic request from Payum to get some basic structure of the request, but that is totally optional because the request is basically any PHP class. After you create a request, you can send that request inside the gateway execute() method. Below you can see an example of a request.
Following this guide, you will be able to implement a new payment provider using Payum and Coreshop. The guide doesn't go into details about implementing a specific payment provider; instead, it shows you a generic implementation example, but that logic applies to any payment provider. For additional examples, you can always reference Payum and coreshop documentation.
Do you have more questions or issues while implementing this? Reach out to us; we'll be glad to help!