This is a revised article initially written in September 2014 once I began to play with Magento 2 for the primary time. Since there have been a variety of modifications in Magento 2, this article additionally needed some refreshment. We will look over the backend, and proceed with the development of a easy module. For this process, I picked up the payment gateway API integration. This comparatively easy process will assist us exhibit some key modifications in Magento 2.
More exactly, we will likely be specializing in implementation of Stripe payment gateway. Even although Stripe has a wealthy set of options, right here we will likely be focusing solely on most simple functionalities to get you began with Magento 2 extension development.
NOTE: Magento 2 source code is below fixed modifications. Although code was examined on early Magento 2.1., it’s now outdated. Stay tuned for an replace.
Module setup
As you in all probability already know, loads has modified in Magento 2, and Module setup and code structure is just not an exception. One of the primary issues you will discover is absence of code pools that we’re used to from earlier version. Inside the app/code folder we’ll create our namespace folder. In the tip, our file/folder structure would appear like this:
Before we proceed, there may be yet another factor that must be taken care of. Stripe comes with its own set of PHP libraries for integration, and so they should be included as nicely. This, nonetheless, ought to be managed by Composer. If you check out composer.json, you will discover require line. By putting in this pattern extension by composer, Stripe library will likely be positioned below vendor/stripe folder, and will likely be out there by autoloader in our code.
Next huge change has been launched in XML configuration files. Magento 2 has launched XML scheme for every configuration type that must be adopted, or in any other case the module won’t work. First one that we are going to create, is module.xml that replaces configuration which was beforehand positioned below app/etc/modules/namespace_modulename.xml. As in magento 1, it’s used to declare module and its dependencies:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
<module name="Simplemagento_Stripe" setup_version="1.0.0">
<sequence>
<module name="Magento_Sales" />
<module name="Magento_Payment" />
<module name="Magento_Directory" />
<module name="Magento_Config" />
</sequence>
</module>
</config>
There are additionally some modifications in different config files, however I’ll let you have enjoyable exploring them yourselves.
Payment implementation
So far, we have now created our module file structure, we have now created module configuration files and we have now built-in our library files. It is now time to proceed with the payment integration.
Anyone who built-in payment gateway in Magento is aware of the significance of implementing correct admin settings, because the system will deal with many of the issues routinely. Lets check out our etc/adminhtml/system.xml file: ‘
"<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../Magento/Config/etc/system_file.xsd">
<system>
<section id="payment">
<group id="Simplemagento_stripe" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Stripe</label>
<comment>
<![CDATA[<a href="https://stripe.com/" target="_blank">Click here to sign up for Stripe account</a>]]>
</comment>
<field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" presentInWebsite="1" showInStore="0">
<label>Enabled</label>
<source_model>MagentoConfigModelConfigSourceYesno</source_model>
</field>
<field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Title</label>
</field>
<field id="api_key" translate="label" type="obscure" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Api Key</label>
<backend_model>MagentoConfigModelConfigBackendEncrypted</backend_model>
</field>
<field id="debug" translate="label" type="select" sortOrder="4" showInDefault="1" presentInWebsite="1" showInStore="0">
<label>Debug</label>
<source_model>MagentoConfigModelConfigSourceYesno</source_model>
</field>
<field id="cctypes" translate="label" type="multiselect" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Credit Card Types</label>
<source_model>SimplemagentoStripeModelSourceCctype</source_model>
</field>
<field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Sort Order</label>
</field>
<field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Payment from Applicable Countries</label>
<source_model>MagentoPaymentModelConfigSourceAllspecificcountries</source_model>
</field>
<field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Payment from Specific Countries</label>
<source_model>MagentoDirectoryModelConfigSourceCountry</source_model>
</field>
<field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Minimum Order Total</label>
</field>
<field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Maximum Order Total</label>
<comment>Leave empty to disable limit</comment>
</field>
</group>
</section>
</system>
</config>(*2*)
namespace SimplemagentoStripeModel;
class Payment extends MagentoPaymentModelMethodCc
{
const CODE = 'Simplemagento_stripe';
protected $_code = self::CODE;
protected $_isGateway = true;
protected $_canCapture = true;
protected $_canCapturePartial = true;
protected $_canRefund = true;
protected $_canRefundInvoicePartial = true;
protected $_stripeApi = false;
protected $_countryFactory;
protected $_minAmount = null;
protected $_maxAmount = null;
protected $_supportedCurrencyCodes = array('USD');
protected $_debugReplacePrivateDataKeys = ['number', 'exp_month', 'exp_year', 'cvc'];
public function __construct(
MagentoFrameworkModelContext $context,
MagentoFrameworkRegistry $registry,
MagentoFrameworkApiExtensionAttributesFactory $extensionFactory,
MagentoFrameworkApiAttributeValueFactory $customAttributeFactory,
MagentoPaymentHelperData $paymentData,
MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig,
MagentoPaymentModelMethodLogger $logger,
MagentoFrameworkModuleModuleListInterface $moduleList,
MagentoFrameworkStdlibDateTimeTimezoneInterface $localeDate,
MagentoDirectoryModelCountryFactory $countryFactory,
StripeStripe $stripe,
array $data = array()
)
”
At this point, we have covered almost all differences in module development between Magento 2 and 1. From this point on, most of the code would be the same between those two versions, as far as our Stripe integration is concerned.
Lets proceed with implementation of our most important function, capture():
/**
* Payment capturing
*
* @param MagentoPaymentModelInfoInterface $payment
* @param float $amount
* @return $this
* @throws MagentoFrameworkValidatorException
*/
public function capture(MagentoPaymentModelInfoInterface $payment, $amount)
{
//throw new MagentoFrameworkValidatorException(__('Inside Stripe, throwing donuts :]'));
/** @var MagentoSalesModelOrder $order */
$order = $payment->getOrder();
/** @var MagentoSalesModelOrderAddress $billing */
$billing = $order->getBillingAddress();
try catch (Exception $e) {
$this->debugData(['request' => $requestData, 'exception' => $e->getMessage()]);
$this->_logger->error(__('Payment capturing error.'));
throw new MagentoFrameworkValidatorException(__('Payment capturing error.'));
}
return $this;
}
As normal, we’ll fetch billing data by payment object. Credit card information is than handed to Stripes API which handles the remainder. In case of success, we’ll add this transaction to Magento’s list of transactions, and we’re mainly executed right here. It is vital to notice right here, that transaction id ought to be set to transaction ID obtained by the payment gateway, since this will likely be used afterward.
Another vital characteristic for payment method is capacity to subject a refund from Magento admin. So lets proceed and implement our refund() function:
/**
* Payment refund
*
* @param MagentoPaymentModelInfoInterface $payment
* @param float $amount
* @return $this
* @throws MagentoFrameworkValidatorException
*/
public function refund(MagentoPaymentModelInfoInterface $payment, $amount)
{
$transactionId = $payment->getParentTransactionId();
try
StripeCharge::retrieve($transactionId)->refund();
catch (Exception $e) {
$this->debugData(['transaction_id' => $transactionId, 'exception' => $e->getMessage()]);
$this->_logger->error(__('Payment refunding error.'));
throw new MagentoFrameworkValidatorException(__('Payment refunding error.'));
}
$payment
->setTransactionId($transactionId . '-' . MagentoSalesModelOrderPaymentTransaction::TYPE_REFUND)
->setParentTransactionId($transactionId)
->setIsTransactionClosed(1)
->setShouldCloseParentTransaction(1);
return $this;
}
Basically, we’re fetching transaction ID, which is then handed to API which handles refund communications. All we need to do right here is to correctly deal with errors and mark transactions. And sure, by dealing with errors I do imply throwing Exception from inside catch block, in order to inform Magento of an error. Reason for try..catch block in the primary place was to sanitize data, since response from server may need delicate data. This additionally applies to capture performance.
In Magento 2, checkout has been rewritten as JS shopper aspect application, that’s speaking with core system by API. Considering that, PHP half itself is just not sufficient for integration to work. We will proceed by including two extra JS files by layout update XML(test hyperlink, it’s to massive to be listed right here). Following files have been added:
- view/frontend/web/js/view/payment/stripe-payments.js
- view/frontend/web/js/view/payment/method-renderer/stripe-method.js
Their objective is to supply configuration for UI element used in checkout. For now, we’ll go away like that, explaining how this works, requires its own article.
And that’s mainly it. There could also be a couple of extra issues to tune up, however our module is prepared. Full code may be retrieved at our GIT repository here. An don’t neglect to regulate our repository. We plan to implement integration with tokens to keep away from sending bank card information to server.
So far we have now covered many of the staple items which are required for module development. However, our module is basically easy: it lacks any template files, controllers, blocks, composer config, etc. But we’ll cover all that in our future articles. This is only a warmup to get you began. I hope it was helpful, and that you simply loved studying it.