在这篇博客中,我们将学习如何创建在线支付方式。我们将Stripe付款作为参考,你可以根据你的需求进行修改,这将取决于网关提供商提供的库。

Stripe Payment Gateway有多个功能,但我们将实现最基本的功能。我们将使用官方提供的PHP库,即https://github.com/stripe/stripe-php

应该注意到,Stripe库是通过Composer管理的,因此可以通过Composer安装或者也可以下载它,并将文件放在vendor/stripe中。如果我们通过composer管理整个过程,那么它可以通过自动加载器自动包含在代码中,但如果我们不这样做,那么我们必须将它包含在我们的代码中。

我们将创建多个文件,大多数文件将与以前的博客相同。所以,我们将只讨论重要文件。现在,让我们开始:

扩展的结构将是这样的:

1. System.xml

此文件用于为扩展创建店铺配置,以便可以从管理面板轻松完成付款方式的配置。我们将定义几个字段,如API密钥、支持的信用卡类型等。

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="payment">
<group id="magease_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" showInWebsite="1" showInStore="0">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</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>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
</field>
<field id="debug" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Debug</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</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>Magease\Stripe\Model\Source\Cctype</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>Magento\Payment\Model\Config\Source\Allspecificcountries</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>Magento\Directory\Model\Config\Source\Country</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. Model

我们将创建一个用于处理支付的模型,并将此模型在config.xml中定义,如上一篇博客中所述:

<?php
namespace Magease\Stripe\Model;

class Payment extends \Magento\Payment\Model\Method\Cc
{
const CODE = 'magease_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(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
\Magento\Framework\Api\AttributeValueFactory $customAttributeFactory,
\Magento\Payment\Helper\Data $paymentData,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Payment\Model\Method\Logger $logger,
\Magento\Framework\Module\ModuleListInterface $moduleList,
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
\Magento\Directory\Model\CountryFactory $countryFactory,
\Stripe\Stripe $stripe,
array $data = array()
) {
parent::__construct(
$context,
$registry,
$extensionFactory,
$customAttributeFactory,
$paymentData,
$scopeConfig,
$logger,
$moduleList,
$localeDate,
null,
null,
$data
);

$this->_countryFactory = $countryFactory;
$this->_stripeApi = $stripe;
$this->_stripeApi->setApiKey(
$this->getConfigData('api_key')
);

$this->_minAmount = $this->getConfigData('min_order_total');
$this->_maxAmount = $this->getConfigData('max_order_total');
}

/**
* Payment capturing
*
* @param \Magento\Payment\Model\InfoInterface $payment
* @param float $amount
* @return $this
* @throws \Magento\Framework\Validator\Exception
*/
public function capture(\Magento\Payment\Model\InfoInterface $payment, $amount)
{
//throw new \Magento\Framework\Validator\Exception(__('Inside Stripe, throwing donuts :]'));

/** @var \Magento\Sales\Model\Order $order */
$order = $payment->getOrder();

/** @var \Magento\Sales\Model\Order\Address $billing */
$billing = $order->getBillingAddress();

try {
$requestData = [
'amount' => $amount * 100,
'currency' => strtolower($order->getBaseCurrencyCode()),
'description' => sprintf('#%s, %s', $order->getIncrementId(), $order->getCustomerEmail()),
'card' => [
'number' => $payment->getCcNumber(),
'exp_month' => sprintf('%02d',$payment->getCcExpMonth()),
'exp_year' => $payment->getCcExpYear(),
'cvc' => $payment->getCcCid(),
'name' => $billing->getName(),
'address_line1' => $billing->getStreetLine(1),
'address_line2' => $billing->getStreetLine(2),
'address_city' => $billing->getCity(),
'address_zip' => $billing->getPostcode(),
'address_state' => $billing->getRegion(),
'address_country' => $billing->getCountryId(),
// To get full localized country name, use this instead:
// 'address_country' => $this->_countryFactory->create()->loadByCode($billing->getCountryId())->getName(),
]
];

$charge = \Stripe\Charge::create($requestData);
$payment
->setTransactionId($charge->id)
->setIsTransactionClosed(0);
} catch (\Exception $e) {
$this->debugData(['request' => $requestData, 'exception' => $e->getMessage()]);
$this->_logger->error(__('Payment capturing error.'));
throw new \Magento\Framework\Validator\Exception(__('Payment capturing error.'));
}

return $this;
}

/**
* Payment refund
*
* @param \Magento\Payment\Model\InfoInterface $payment
* @param float $amount
* @return $this
* @throws \Magento\Framework\Validator\Exception
*/
public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount)
{
$transactionId = $payment->getParentTransactionId();

try {
\Stripe\Charge::retrieve($transactionId)->refund(['amount' => $amount * 100]);
} catch (\Exception $e) {
$this->debugData(['transaction_id' => $transactionId, 'exception' => $e->getMessage()]);
$this->_logger->error(__('Payment refunding error.'));
throw new \Magento\Framework\Validator\Exception(__('Payment refunding error.'));
}

$payment
->setTransactionId($transactionId . '-' . \Magento\Sales\Model\Order\Payment\Transaction::TYPE_REFUND)
->setParentTransactionId($transactionId)
->setIsTransactionClosed(1)
->setShouldCloseParentTransaction(1);

return $this;
}

/**
* Determine method availability based on quote amount and config data
*
* @param \Magento\Quote\Api\Data\CartInterface|null $quote
* @return bool
*/
public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null)
{
if ($quote && (
$quote->getBaseGrandTotal() < $this->_minAmount
|| ($this->_maxAmount && $quote->getBaseGrandTotal() > $this->_maxAmount))
) {
return false;
}

if (!$this->getConfigData('api_key')) {
return false;
}

return parent::isAvailable($quote);
}

/**
* Availability for currency
*
* @param string $currencyCode
* @return bool
*/
public function canUseForCurrency($currencyCode)
{
if (!in_array($currencyCode, $this->_supportedCurrencyCodes)) {
return false;
}
return true;
}
}

在此文件中,有两个主要方法:

1.capture(): 此方法处理信用卡收费和保存交易的过程。我们只是从payment对象获取帐单邮寄地址的详细信息,并将其与卡详细信息和订单金额一起传递给stripe API。API将处理此数据并返回charge对象我们只是将其id保存为订单的交易ID。

2.refund(): 此方法将用于处理退款。之前保存的交易ID将发送到API,然后退款将被处理。

您可以在此处查看详细的文件和目录结构

 

创建沙箱帐户并获取API密钥:

要创建沙箱沙盒帐户,请转到https://dashboard.stripe.com/register并输入所需的详细信息并注册。创建帐户后您需要获得在付款配置中要输入的API密钥(在Magento管理面板中)。导航到Developers -> API Keys,您将获得两个键,Publishable keySecret key

 

您只用到Secret key复制并保存在某处。

现在,我们将看到放置API密钥的位置以及如何为我们创建的付款方式执行其他配置。为此,请导航到Store -> Configuration -> Payment Methods -> Stripe

您可以在此处输入API密钥并执行其他设置:

现在让我们看一下这些字段的的作用:

Enabled:用于启用/禁用付款方式

Title:付款方式可以在这里设置标题,它将显示在付款过程中

API Key:以前需要输入的API密钥

Debug:它是一个可选的字段,如果我们想在调试日志中记录这个付款方式完成结账过程,就需要使用这个字段

Credit Card Types:这里我们可以选择支持的信用卡类型

Payment from Applicable Countries/Payment from Specific Countries:这里我们可以选择哪些国家在结账时,可以使用该付款方式并显示在前台

Minimum Order Total/Maximum Order Total:这里我们可以定义基于订单总数的规则。如果订单总数小于或超过定义,则付款方式据此隐藏/显示

Sort Order:这个字段可以设置排序,在该排序中,该付款方式将显示在其他可用的前端支付方式前面