Commit ea2d4f27 by liumengfei

增加Oceanpayment模块

parent c8bbb3e8
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Block;
class Form extends \Magento\Payment\Block\Form
{
/**
* Checkmo template
*
* @var string
*/
protected $_supportedInfoLocales = array('fr');
protected $_defaultInfoLocale = 'en';
protected $_template = 'Oceanpayment_Creditcard::form.phtml';
}
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Block;
class Info extends \Magento\Payment\Block\Info
{
/**
* @var string
*/
protected $_payableTo;
/**
* @var string
*/
protected $_mailingAddress;
/**
* @var string
*/
protected $_template = 'Oceanpayment_Creditcard::info.phtml';
public function getMethodCode()
{
return $this->getInfo()->getMethodInstance()->getCode();
}
/**
* @return string
*/
public function toPdf()
{
//$this->setTemplate('Oceanpayment_Creditcard::info/pdf/checkmo.phtml');
$this->setTemplate('Oceanpayment_Creditcard::pdf/info.phtml');
return $this->toHtml();
}
}
<?php
/**
* ######
* ######
* ########### ########### ########### ########### ########### ############ ########### ###### ###### ########### ########### ########### #############
* ############# ############# ############# ############# ############# ############# ############# ###### ###### ############# ############# ############# #############
* ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### #### ### #### ###### ###### ###### ###### #############
* ###### ###### ###### ############# ############# ###### ###### ###### ###### ############# ###### ###### #### ### #### ############# ###### ###### ######
* ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### #### ### #### ###### ###### ###### ######
* ############# ############# ############# ############# ###### ###### ############# ############# ############# #### ### #### ############# ###### ###### ##########
* ########### ########### ########### ########### ###### ###### ############ ########### ############# #### ### #### ########### ###### ###### ##########
* ###### ######
* ###### #############
* ###### ############
*
*
*
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Block\Payment;
class Redirect extends \Magento\Framework\View\Element\Template
{
/**
* @var \Magento\Checkout\Model\Session
*/
protected $_checkoutSession;
protected $_paymentMethod;
/**
* @param \Magento\Framework\View\Element\Template\Context $context
* @param \Magento\Checkout\Model\Session $checkoutSession
* @param array $data
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Oceanpayment\Creditcard\Model\PaymentMethod $paymentMethod,
\Magento\Checkout\Model\Session $checkoutSession,
array $data = []
) {
$this->_checkoutSession = $checkoutSession;
parent::__construct($context, $data);
$this->_paymentMethod = $paymentMethod;
$this->_isScopePrivate = true;
}
public function getCheckoutForm(){
$parameter = $this->_paymentMethod->getCheckoutParameter();
$gatewayUrl = $this->_paymentMethod->getGatewayUrl();
$formHTML = '';
$formHTML .= '<form action="'.$gatewayUrl.'" name="payment_checkout" id="payment_checkout">';
foreach ($parameter as $field => $value) {
$formHTML .= '<input type="hidden" name="'.$field.'" value="'.$value.'" >';
}
$formHTML .= '</form>';
return $formHTML;
}
public function getPayMode(){
return $this->_paymentMethod->getConfigData('pay_mode');
}
}
<?php
namespace Oceanpayment\Creditcard\Controller\Payment;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\Controller\ResultFactory;
use Magento\Quote\Api\CartManagementInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\RequestInterface;
class Back extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface
{
const PUSH = "[PUSH]";
const BrowserReturn = "[Browser Return]";
protected $_processingArray = array('processing', 'complete');
/**
* Customer session model
*
* @var \Magento\Customer\Model\Session
*/
protected $_customerSession;
protected $resultPageFactory;
protected $checkoutSession;
protected $orderRepository;
protected $_scopeConfig;
protected $_orderFactory;
protected $creditmemoSender;
protected $orderSender;
protected $urlBuilder;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Customer\Model\Session $customerSession
*/
public function __construct(
\Magento\Customer\Model\Session $customerSession,
\Magento\Framework\App\Action\Context $context,
\Oceanpayment\Creditcard\Model\PaymentMethod $paymentMethod,
\Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Sales\Model\Order\Email\Sender\CreditmemoSender $creditmemoSender,
\Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender,
\Magento\Framework\Url $urlBuilder,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
) {
$this->_customerSession = $customerSession;
$this->checkoutSession = $checkoutSession;
$this->urlBuilder = $urlBuilder;
$this->orderRepository = $orderRepository;
parent::__construct($context);
$this->_scopeConfig = $scopeConfig;
$this->_orderFactory = $orderFactory;
$this->_paymentMethod = $paymentMethod;
$this->creditmemoSender = $creditmemoSender;
$this->orderSender = $orderSender;
}
protected function _createInvoice($order)
{
if (!$order->canInvoice()) {
return;
}
$invoice = $order->prepareInvoice();
if (!$invoice->getTotalQty()) {
throw new \RuntimeException("Cannot create an invoice without products.");
}
$invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
$invoice->register();
$order->addRelatedObject($invoice);
}
public function execute()
{
//交易推送类型
$this->returnLog(self::BrowserReturn, $_REQUEST);
//载入模块
$model = $this->_paymentMethod;
$order = $this->_orderFactory->create()->loadByIncrementId($_REQUEST['order_number']);
$history = ' (payment_id:'.$_REQUEST['payment_id'].' | order_number:'.$_REQUEST['order_number'].' | '.$_REQUEST['order_currency'].':'.$_REQUEST['order_amount'].' | payment_details:'.$_REQUEST['payment_details'].')';
switch($this->validated($order)){
case 1:
//支付成功
$order->setState($model->getConfigData('success_order_status'));
$order->setStatus($model->getConfigData('success_order_status'));
$order->addStatusToHistory($model->getConfigData('success_order_status'), __(self::BrowserReturn.'Payment Success!'.$history));
//发送邮件
// $this->orderSender->send($order);
//自动Invoice
// if ($model->getConfigData('invoice')){
// $this->_createInvoice($order);
// }
$order->save();
$url = 'checkout/onepage/success';
break;
case 0:
//支付失败
$order->setState($model->getConfigData('failure_order_status'));
$order->setStatus($model->getConfigData('failure_order_status'));
$order->addStatusToHistory($model->getConfigData('failure_order_status'), __(self::BrowserReturn.'Payment Failed!'.$history));
$order->save();
$this->messageManager->addError(__('Payment Failed! '.$_REQUEST['payment_details']));
$url = 'checkout/onepage/failure';
break;
case -1:
//交易待处理
$order->setState($model->getConfigData('pre_auth_order_status'));
$order->setStatus($model->getConfigData('pre_auth_order_status'));
$order->addStatusToHistory($model->getConfigData('pre_auth_order_status'), __(self::BrowserReturn.'(Pre-auth)Payment Pending!'.$history));
$order->save();
$url = 'checkout/onepage/success';
break;
case 2:
//在网站中已经是支付成功
$url = 'checkout/onepage/success';
break;
case '10000':
//10000:Payment is declined 高风险订单
$order->setState($model->getConfigData('high_risk_order_status'));
$order->setStatus($model->getConfigData('high_risk_order_status'));
$order->addStatusToHistory($model->getConfigData('high_risk_order_status'), __(self::BrowserReturn.'(High Risk)Payment Failed!'.$history));
$order->save();
$this->messageManager->addError(__('Payment Failed! '.$_REQUEST['payment_details']));
$url = 'checkout/onepage/failure';
break;
case '20061':
//订单号重复
$url = 'checkout/onepage/failure';
break;
case 999:
//加密值错误或系统异常
$url = 'checkout/onepage/failure';
break;
default:
}
$url = $this->urlBuilder->getUrl($url);
$this->getParentLocationReplace($url);
}
private function validated($order)
{
//载入模块
$model = $this->_paymentMethod;
//获取账号
$account = $model->getConfigData('account');
//返回终端号
$terminal = $_REQUEST['terminal'];
//匹配终端号 判断是否3D交易
if($terminal == $model->getConfigData('terminal')){
$securecode = $model->getConfigData('securecode');
}elseif($terminal == $model->getConfigData('secure/secure_terminal')){
//3D
$securecode = $model->getConfigData('secure/secure_securecode');
}else{
$securecode = '';
}
//返回Oceanpayment的支付唯一号
$payment_id = $_REQUEST['payment_id'];
//返回网站订单号
$order_number = $_REQUEST['order_number'];
//返回交易币种
$order_currency = $_REQUEST['order_currency'];
//返回交易金额
$order_amount = $_REQUEST['order_amount'];
//返回交易状态
$payment_status = $_REQUEST['payment_status'];
//返回支付详情
$payment_details = $_REQUEST['payment_details'];
//用于支付结果页面显示
$_SESSION['payment_details'] = $payment_details;
//用于支付结果页面显示响应代码
$getErrorCode = explode(':', $payment_details);
$_SESSION['errorCode'] = $getErrorCode[0];
//返回解决办法
$_SESSION['payment_solutions']= $_REQUEST['payment_solutions'];
//返回备注
$order_notes = $_REQUEST['order_notes'];
//未通过的风控规则
$payment_risk = $_REQUEST['payment_risk'];
//返回支付信用卡卡号
$card_number = $_REQUEST['card_number'];
//返回交易类型
$payment_authType = $_REQUEST['payment_authType'];
//返回数据签名
$back_signValue = $_REQUEST['signValue'];
//SHA256加密
$local_signValue = hash("sha256",$account.$terminal.$order_number.$order_currency.$order_amount.$order_notes.$card_number.
$payment_id.$payment_authType.$payment_status.$payment_details.$payment_risk.$securecode);
//加密校验
if(strtoupper($local_signValue) == strtoupper($back_signValue)){
//在网站中已经是支付成功
if(in_array($order->getState(), $this->_processingArray)){
return 2;
}
//支付状态
if ($payment_status == 1) {
return 1;
} elseif ($payment_status == -1) {
return -1;
} elseif ($payment_status == 0) {
//10000:Payment is declined 高风险订单
if($getErrorCode[0] == '10000'){
return '10000';
}
//是否点击浏览器后退造成订单号重复 20061
if($getErrorCode[0] == '20061'){
return '20061';
}
return 0;
}
}else{
return 999;
}
}
/**
* return log
*/
public function returnLog($logType, $data){
$filedate = date('Y-m-d');
$newfile = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "a+" );
$return_log = date('Y-m-d H:i:s') . $logType . "\r\n";
foreach ($data as $k=>$v){
$return_log .= $k . " = " . $v . "\r\n";
}
$return_log .= '*****************************************' . "\r\n";
$return_log = $return_log.file_get_contents( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log");
$filename = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "r+" );
fwrite($filename,$return_log);
fclose($filename);
fclose($newfile);
}
/**
* JS
*
*/
public function getParentLocationReplace($url)
{
echo '<script type="text/javascript">parent.location.replace("'.$url.'");</script>';
}
public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
{
return null;
}
public function validateForCsrf(RequestInterface $request): ?bool
{
return true;
}
}
<?php
namespace Oceanpayment\Creditcard\Controller\Payment;
use Magento\Framework\Controller\ResultFactory;
use Magento\Quote\Api\CartManagementInterface;
class Notice extends \Magento\Framework\App\Action\Action
{
const PUSH = "[PUSH]";
const BrowserReturn = "[Browser Return]";
protected $_processingArray = array('processing', 'complete');
/**
* Customer session model
*
* @var \Magento\Customer\Model\Session
*/
protected $_customerSession;
protected $resultPageFactory;
protected $checkoutSession;
protected $orderRepository;
protected $_scopeConfig;
protected $_orderFactory;
protected $creditmemoSender;
protected $orderSender;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Customer\Model\Session $customerSession
*/
public function __construct(
\Magento\Customer\Model\Session $customerSession,
\Magento\Framework\App\Action\Context $context,
\Oceanpayment\Creditcard\Model\PaymentMethod $paymentMethod,
\Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Sales\Model\Order\Email\Sender\CreditmemoSender $creditmemoSender,
\Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
) {
$this->_customerSession = $customerSession;
$this->checkoutSession = $checkoutSession;
$this->orderRepository = $orderRepository;
parent::__construct($context);
$this->_scopeConfig = $scopeConfig;
$this->_orderFactory = $orderFactory;
$this->_paymentMethod = $paymentMethod;
$this->creditmemoSender = $creditmemoSender;
$this->orderSender = $orderSender;
}
protected function _createInvoice($order)
{
if (!$order->canInvoice()) {
return;
}
$invoice = $order->prepareInvoice();
if (!$invoice->getTotalQty()) {
throw new \RuntimeException("Cannot create an invoice without products.");
}
$invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
$invoice->register();
$order->addRelatedObject($invoice);
}
public function execute()
{
//获取推送输入流XML
$xml_str = file_get_contents("php://input");
//判断返回的输入流是否为xml
if($this->xml_parser($xml_str)){
$xml = simplexml_load_string($xml_str);
//把推送参数赋值到$_REQUEST
$_REQUEST['response_type'] = (string)$xml->response_type;
$_REQUEST['account'] = (string)$xml->account;
$_REQUEST['terminal'] = (string)$xml->terminal;
$_REQUEST['payment_id'] = (string)$xml->payment_id;
$_REQUEST['order_number'] = (string)$xml->order_number;
$_REQUEST['order_currency'] = (string)$xml->order_currency;
$_REQUEST['order_amount'] = (string)$xml->order_amount;
$_REQUEST['payment_status'] = (string)$xml->payment_status;
$_REQUEST['payment_details'] = (string)$xml->payment_details;
$_REQUEST['signValue'] = (string)$xml->signValue;
$_REQUEST['order_notes'] = (string)$xml->order_notes;
$_REQUEST['card_number'] = (string)$xml->card_number;
$_REQUEST['payment_authType'] = (string)$xml->payment_authType;
$_REQUEST['payment_risk'] = (string)$xml->payment_risk;
$_REQUEST['methods'] = (string)$xml->methods;
$_REQUEST['payment_country'] = (string)$xml->payment_country;
$_REQUEST['payment_solutions']= (string)$xml->payment_solutions;
//交易推送类型
$this->returnLog(self::PUSH, $xml_str);
//载入模块
$model = $this->_paymentMethod;
$order = $this->_orderFactory->create()->loadByIncrementId($_REQUEST['order_number']);
$history = ' (payment_id:'.$_REQUEST['payment_id'].' | order_number:'.$_REQUEST['order_number'].' | '.$_REQUEST['order_currency'].':'.$_REQUEST['order_amount'].' | payment_details:'.$_REQUEST['payment_details'].')';
//预授权结果推送
$authType = '';
if($_REQUEST['payment_authType'] == 1){
if($_REQUEST['payment_status'] == 1){
$authType = '(Capture)';
}elseif($_REQUEST['payment_status'] == 0){
$authType = '(Void)';
}
}
switch($this->validated($order)){
case 1:
//支付成功
$order->setState($model->getConfigData('success_order_status'));
$order->setStatus($model->getConfigData('success_order_status'));
$order->addStatusToHistory($model->getConfigData('success_order_status'), __(self::PUSH.$authType.'Payment Success!'.$history));
//发送邮件
$this->orderSender->send($order, true);
//自动Invoice
if ($model->getConfigData('invoice')){
$this->_createInvoice($order);
}
$order->save();
break;
case 0:
//支付失败
$order->setState($model->getConfigData('failure_order_status'));
$order->setStatus($model->getConfigData('failure_order_status'));
$order->addStatusToHistory($model->getConfigData('failure_order_status'), __(self::PUSH.$authType.'Payment Failed!'.$history));
$order->save();
break;
case -1:
//交易待处理
$order->setState($model->getConfigData('pre_auth_order_status'));
$order->setStatus($model->getConfigData('pre_auth_order_status'));
$order->addStatusToHistory($model->getConfigData('pre_auth_order_status'), __(self::PUSH.'(Pre-auth)Payment Pending!'.$history));
$order->save();
break;
case 2:
//在网站中已经是支付成功
$order->setState($model->getConfigData('success_order_status'));
$order->setStatus($model->getConfigData('success_order_status'));
$order->addStatusToHistory($model->getConfigData('success_order_status'), __(self::PUSH.'Payment Success!'.$history));
$order->save();
break;
case '10000':
//10000:Payment is declined 高风险订单
$order->setState($model->getConfigData('high_risk_order_status'));
$order->setStatus($model->getConfigData('high_risk_order_status'));
$order->addStatusToHistory($model->getConfigData('high_risk_order_status'), __(self::PUSH.'(High Risk)Payment Failed!'.$history));
$order->save();
case '20061':
//订单号重复
break;
case 999:
//加密值错误或系统异常
break;
default:
}
echo "receive-ok";
exit;
}
}
private function validated($order)
{
//载入模块
$model = $this->_paymentMethod;
//获取账号
$account = $model->getConfigData('account');
//返回终端号
$terminal = $_REQUEST['terminal'];
//匹配终端号 判断是否3D交易
if($terminal == $model->getConfigData('terminal')){
$securecode = $model->getConfigData('securecode');
}elseif($terminal == $model->getConfigData('secure/secure_terminal')){
//3D
$securecode = $model->getConfigData('secure/secure_securecode');
}else{
$securecode = '';
}
//返回Oceanpayment的支付唯一号
$payment_id = $_REQUEST['payment_id'];
//返回网站订单号
$order_number = $_REQUEST['order_number'];
//返回交易币种
$order_currency = $_REQUEST['order_currency'];
//返回交易金额
$order_amount = $_REQUEST['order_amount'];
//返回交易状态
$payment_status = $_REQUEST['payment_status'];
//返回支付详情
$payment_details = $_REQUEST['payment_details'];
//获取响应代码
$getErrorCode = explode(':', $payment_details);
//返回解决办法
$payment_solutions = $_REQUEST['payment_solutions'];
//返回备注
$order_notes = $_REQUEST['order_notes'];
//未通过的风控规则
$payment_risk = $_REQUEST['payment_risk'];
//返回支付信用卡卡号
$card_number = $_REQUEST['card_number'];
//返回交易类型
$payment_authType = $_REQUEST['payment_authType'];
//返回数据签名
$back_signValue = $_REQUEST['signValue'];
//SHA256加密
$local_signValue = hash("sha256",$account.$terminal.$order_number.$order_currency.$order_amount.$order_notes.$card_number.
$payment_id.$payment_authType.$payment_status.$payment_details.$payment_risk.$securecode);
//加密校验
if(strtoupper($local_signValue) == strtoupper($back_signValue)){
//是否是预授权交易
if($payment_authType == 0){
//在网站中已经是支付成功
if(in_array($order->getState(), $this->_processingArray)){
return 1;
}
}
//支付状态
if ($payment_status == 1) {
return 1;
} elseif ($payment_status == -1) {
return -1;
} elseif ($payment_status == 0) {
//10000:Payment is declined 高风险订单
if($getErrorCode[0] == '10000'){
return '10000';
}
//是否点击浏览器后退造成订单号重复 20061
if($getErrorCode[0] == '20061'){
return '20061';
}
return 0;
}
}else{
return 999;
}
}
/**
* notice log
*/
public function returnLog($logType, $xml){
$filedate = date('Y-m-d');
$newfile = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "a+" );
$return_log = date('Y-m-d H:i:s') . $logType . "\r\n";
$return_log .= $xml;
// foreach ($_REQUEST as $k=>$v){
// $return_log .= $k . " = " . $v . "\r\n";
// }
$return_log .= '*****************************************' . "\r\n";
$return_log = $return_log.file_get_contents( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log");
$filename = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "r+" );
fwrite($filename,$return_log);
fclose($filename);
fclose($newfile);
}
/**
* 判断是否为xml
*
*/
function xml_parser($str){
$xml_parser = xml_parser_create();
if(!xml_parse($xml_parser,$str,true)){
xml_parser_free($xml_parser);
return false;
}else {
return true;
}
}
}
<?php
namespace Oceanpayment\Creditcard\Controller\Payment;
use Magento\Framework\Controller\ResultFactory;
use Magento\Customer\Api\Data\GroupInterface;
class Redirect extends \Magento\Framework\App\Action\Action
{
/**
* Customer session model
*
* @var \Magento\Customer\Model\Session
*/
protected $_customerSession;
protected $resultPageFactory;
protected $_paymentMethod;
protected $_checkoutSession;
protected $checkout;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Customer\Model\Session $customerSession
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Customer\Model\Session $customerSession,
\Oceanpayment\Creditcard\Model\PaymentMethod $paymentMethod,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->_customerSession = $customerSession;
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
$this->_paymentMethod = $paymentMethod;
$this->_checkoutSession = $checkoutSession;
}
public function execute()
{
$resultPage = $this->resultPageFactory->create();
$resultPage->getConfig()->getTitle()->set(__('Credit/Debit Card'));
return $resultPage;
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Model;
use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Framework\Locale\ResolverInterface;
use Magento\Customer\Helper\Session\CurrentCustomer;
use Magento\Payment\Helper\Data as PaymentHelper;
class CreditcardConfigProvider implements ConfigProviderInterface
{
/**
* @var ResolverInterface
*/
protected $localeResolver;
/**
* @var Config
*/
protected $config;
/**
* @var \Magento\Customer\Helper\Session\CurrentCustomer
*/
protected $currentCustomer;
/**
* @var string[]
*/
/*protected $methodCodes = [
Config::METHOD_WPP_BML,
Config::METHOD_WPP_PE_EXPRESS,
Config::METHOD_WPP_EXPRESS,
Config::METHOD_WPP_PE_BML
];*/
/**
* @var \Magento\Payment\Model\Method\AbstractMethod[]
*/
protected $methods = [];
/**
* @var PaymentHelper
*/
protected $paymentHelper;
protected $checkoutSession;
/**
* @param ConfigFactory $configFactory
* @param ResolverInterface $localeResolver
* @param CurrentCustomer $currentCustomer
* @param PaymentHelper $paymentHelper
*/
public function __construct(
//ConfigFactory $configFactory,
ResolverInterface $localeResolver,
CurrentCustomer $currentCustomer,
\Magento\Checkout\Model\Session $checkoutSession,
PaymentHelper $paymentHelper
) {
$this->localeResolver = $localeResolver;
//$this->config = $configFactory->create();
$this->currentCustomer = $currentCustomer;
$this->paymentHelper = $paymentHelper;
$this->checkoutSession = $checkoutSession;
$code = 'oceanpaymentcreditcard';
$this->methods[$code] = $this->paymentHelper->getMethodInstance($code);
}
/**
* {@inheritdoc}
*/
public function getConfig()
{
$code = 'oceanpaymentcreditcard';
$config = [];
if ($this->methods[$code]->isAvailable($this->checkoutSession->getQuote())) {
$config = [];
$config['payment'] = [];
$config['payment']['creditcard']['redirectUrl'] = [];
$config['payment']['creditcard']['redirectUrl'][$code] = $this->getMethodRedirectUrl($code);
}
return $config;
}
/**
* Return redirect URL for method
*
* @param string $code
* @return mixed
*/
protected function getMethodRedirectUrl($code)
{
return $this->methods[$code]->getOrderPlaceRedirectUrl();
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Model;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Payment\Model\Method\AbstractMethod;
use Magento\Sales\Model\Order;
class PaymentMethod extends AbstractMethod
{
const CODE = 'oceanpaymentcreditcard';
const POST = "[POST to Oceanpayment]";
protected $_code = self::CODE;
protected $_isInitializeNeeded = true;
protected $_formBlockType = 'Oceanpayment\Creditcard\Block\Form';
protected $_infoBlockType = 'Oceanpayment\Creditcard\Block\Info';
protected $_isGateway = false;
protected $_canAuthorize = false;
protected $_canCapture = false;
protected $_canCapturePartial = false;
protected $_canRefund = false;
protected $_canRefundInvoicePartial = false;
protected $_canVoid = false;
protected $_canUseInternal = false;
protected $_canUseCheckout = true;
protected $_canUseForMultishipping = false;
protected $_canSaveCc = false;
protected $urlBuilder;
protected $_moduleList;
protected $checkoutSession;
protected $_orderFactory;
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\Sales\Model\OrderFactory $orderFactory,
\Magento\Framework\Url $urlBuilder,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []){
$this->urlBuilder = $urlBuilder;
$this->_moduleList = $moduleList;
$this->checkoutSession = $checkoutSession;
$this->_orderFactory = $orderFactory;
parent::__construct($context,
$registry,
$extensionFactory,
$customAttributeFactory,
$paymentData,
$scopeConfig,
$logger,
$resource,
$resourceCollection,
$data);
}
/**
* Redirect URL
*
* @return string Redirect URL
*/
public function getOrderPlaceRedirectUrl()
{
return $this->urlBuilder->getUrl('oceanpaymentcreditcard/payment/redirect', ['_secure' => true]);
}
/**
* Gateway URL
*
* @return string Gateway URL
*/
public function getGatewayUrl()
{
return $this->getConfigData('gateway_url');
}
public function canUseForCurrency($currencyCode)
{
return true;
}
public function initialize($paymentAction, $stateObject)
{
$payment = $this->getInfoInstance();
//$order = $payment->getOrder();
$state = $this->getConfigData('new_order_status');
//$state = Mage_Sales_Model_Order::STATE_PENDING_PAYMENT;
$stateObject->setState($state);
$stateObject->setStatus('pending_payment');
$stateObject->setIsNotified(false);
}
public function getCheckoutParameter()
{
$orderIncrementId = $this->checkoutSession->getLastRealOrderId();
$order = $this->_orderFactory->create()->loadByIncrementId($orderIncrementId);
$billing = $order->getBillingAddress();
$shipping = $order->getShippingAddress();
$productDetails = $this->getProductItems($order->getAllItems());
//支付币种
$order_currency = $order->getOrderCurrencyCode();
//金额
$order_amount = sprintf('%.2f', $order->getGrandTotal());
//判断是否启用3D功能
if($this->getConfigData('secure/secure_mode') == 1){
//检验是否需要3D验证
$validate_arr = $this->validate3D($order_currency, $order_amount, $billing, $shipping);
}else{
$validate_arr['terminal'] = $this->getConfigData('terminal');
$validate_arr['securecode'] = $this->getConfigData('securecode');
}
//账户
$account = $this->getConfigData('account');
//终端号
$terminal = $validate_arr['terminal'];
//securecode
$securecode = $validate_arr['securecode'];
//支付方式
$methods = 'Credit Card';
//订单号
$order_number = $orderIncrementId;
//返回地址
$backUrl = $this->urlBuilder->getUrl('oceanpaymentcreditcard/payment/back', ['_secure' => true,'_nosid' => true]);
//服务器响应地址
$noticeUrl = $this->urlBuilder->getUrl('oceanpaymentcreditcard/payment/notice', ['_secure' => true,'_nosid' => true]);
//备注
$order_notes = $orderIncrementId;
//账单人名
$billing_firstName = substr(urlencode($this->OceanHtmlSpecialChars($billing->getFirstname())),0,50);
//账单人姓
$billing_lastName = substr(urlencode($this->OceanHtmlSpecialChars($billing->getLastname())),0,50);
//账单人email
$billing_email = $this->OceanHtmlSpecialChars($order->getCustomerEmail());
//账单人电话
$billing_phone = $billing->getTelephone();
//账单人国家
$billing_country = $billing->getCountryId();
//账单人州(可不提交)
$billing_state = $billing->getRegionCode();
//账单人城市
$billing_city = $billing->getCity();
//账单人地址
$billing_address = implode(' ', $billing->getStreet());
//账单人邮编
$billing_zip = $billing->getPostcode();
//收货人地址信息
//收货人名
$ship_firstName = substr(urlencode($this->OceanHtmlSpecialChars($shipping->getFirstname())),0,50);
//收货人姓
$ship_lastName = substr(urlencode($this->OceanHtmlSpecialChars($shipping->getLastname())),0,50);
//收货人手机
$ship_phone = $shipping->getTelephone();
//收货人国家
$ship_country = $shipping->getCountryId();
//收货人州
$ship_state = $shipping->getRegionCode();
//收货人城市
$ship_city = $shipping->getCity();
//收货人地址
$ship_addr = implode(' ', $shipping->getStreet());
//收货人邮编
$ship_zip = $shipping->getPostcode();
//产品名称
$productName = $productDetails['productName'];
//产品SKU
$productSku = $productDetails['productSku'];
//产品数量
$productNum = $productDetails['productNum'];
//产品单价
$productPrice = $productDetails['productPrice'];
//网店程序类型
$cart_info = 'Magento 2.x';
//接口版本
$cart_api = 'V1.1.0';
//校验源字符串
$signsrc = $account.$terminal.$backUrl.$order_number.$order_currency.$order_amount.$billing_firstName.$billing_lastName.$billing_email.$securecode;
//sha256加密结果
$signValue = hash("sha256",$signsrc);
//支付页面类型
$pages = $this->isMobile();
$parameter = array('account'=>$account,
'terminal'=>$terminal,
'order_number'=>$order_number,
'order_currency'=>$order_currency,
'order_amount'=>$order_amount,
'backUrl'=>$backUrl,
'noticeUrl'=>$noticeUrl,
'order_notes'=>$order_notes,
'methods'=>$methods,
'signValue'=>$signValue,
'billing_firstName'=>$billing_firstName,
'billing_lastName'=>$billing_lastName,
'billing_email'=>$billing_email,
'billing_phone'=>$billing_phone,
'billing_country'=>$billing_country,
'billing_state'=>$billing_state,
'billing_city'=>$billing_city,
'billing_address'=>$billing_address,
'billing_zip'=>$billing_zip,
'ship_firstName'=>$ship_firstName,
'ship_lastName'=>$ship_lastName,
'ship_phone'=>$ship_phone,
'ship_country'=>$ship_country,
'ship_state'=>$ship_state,
'ship_city'=>$ship_city,
'ship_addr'=>$ship_addr,
'ship_zip'=>$ship_zip,
'productName'=>$productName,
'productSku'=>$productSku,
'productNum'=>$productNum,
'productPrice'=>$productPrice,
'cart_info'=>$cart_info,
'cart_api'=>$cart_api,
'pages'=>$pages,
);
//记录提交日志
$this->postLog(self::POST, $parameter);
return $parameter;
}
public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null)
{
if (parent::isAvailable($quote) && $quote){
return true;
}
return false;
}
/**
* post log
*/
public function postLog($logType, $data){
$filedate = date('Y-m-d');
$newfile = fopen( dirname(dirname(__FILE__)) . "/oceanpayment_log/" . $filedate . ".log", "a+" );
$return_log = date('Y-m-d H:i:s') . $logType . "\r\n";
foreach ($data as $k=>$v){
$return_log .= $k . " = " . $v . "\r\n";
}
$return_log .= '*****************************************' . "\r\n";
$return_log = $return_log.file_get_contents( dirname(dirname(__FILE__)) . "/oceanpayment_log/" . $filedate . ".log");
$filename = fopen( dirname(dirname(__FILE__)) . "/oceanpayment_log/" . $filedate . ".log", "r+" );
fwrite($filename,$return_log);
fclose($filename);
fclose($newfile);
}
/**
* 判断是否手机设备
*/
public function isMobile(){
$useragent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$useragent_commentsblock = preg_match('|\(.*?\)|', $useragent, $matches) > 0 ? $matches[0] : '';
function CheckSubstrs($substrs, $text){
foreach($substrs as $substr){
if(false !== strpos($text, $substr)){
return true;
}
}
return false;
}
$mobile_os_list = array('Google Wireless Transcoder','Windows CE','WindowsCE','Symbian','Android','armv6l','armv5','Mobile','CentOS','mowser','AvantGo','Opera Mobi','J2ME/MIDP','Smartphone','Go.Web','Palm','iPAQ');
$mobile_token_list = array('Profile/MIDP','Configuration/CLDC-','160×160','176×220','240×240','240×320','320×240','UP.Browser','UP.Link','SymbianOS','PalmOS','PocketPC','SonyEricsson','Nokia','BlackBerry','Vodafone','BenQ','Novarra-Vision','Iris','NetFront','HTC_','Xda_','SAMSUNG-SGH','Wapaka','DoCoMo','iPhone','iPod');
$found_mobile = CheckSubstrs($mobile_os_list, $useragent_commentsblock) || CheckSubstrs($mobile_token_list,$useragent);
if ($found_mobile){
return 1; //手机登录
}else{
return 0; //电脑登录
}
}
/**
* 检验是否需要3D验证
*/
public function validate3D($order_currency, $order_amount, $billing, $shipping){
//是否需要3D验证
$is_3d = 0;
//获取3D功能下各个的币种
$currencies_value_str = $this->getConfigData('secure/secure_currency');
$currencies_value = explode(';', $currencies_value_str);
//获取3D功能下各个的金额
$amount_value_str = $this->getConfigData('secure/secure_amount');
$amount_value = explode(';', $amount_value_str);
$amountValidate = array_combine($currencies_value, $amount_value);
if($amountValidate){
//判断金额是否为空
if(isset($amountValidate[$order_currency])){
//判断3D金额不为空
//判断订单金额是否大于3d设定值
if($order_amount >= $amountValidate[$order_currency]){
//需要3D
$is_3d = 1;
}
}else{
//其他币种是否需要3D
if($this->getConfigData('secure/secure_other_currency') == 1){
//需要3D
$is_3d = 1;
}
}
}
//获取3D功能下国家列表
$countries_3d_str = $this->getConfigData('secure/secure_country');
$countries_3d = explode(',', $countries_3d_str);
//账单国
$billing_country = $billing->getCountryId();
//收货国
$ship_country = $shipping->getCountryId();
//判断账单国是否处于3D国家列表
if (in_array($billing_country , $countries_3d)){
$is_3d = 1;
}
//判断收货国是否处于3D国家列表
if (in_array($ship_country , $countries_3d)){
$is_3d = 1;
}
if($is_3d == 0){
$validate_arr['terminal'] = $this->getConfigData('terminal');
$validate_arr['securecode'] = $this->getConfigData('securecode');
}elseif($is_3d == 1){
//3D
$validate_arr['terminal'] = $this->getConfigData('secure/secure_terminal');
$validate_arr['securecode'] = $this->getConfigData('secure/secure_securecode');
}
return $validate_arr;
}
/**
* 获取订单详情
*/
function getProductItems($AllItems){
$productDetails = array();
$productName = array();
$productSku = array();
$productNum = array();
$productPrice = array();
foreach ($AllItems as $item) {
$productName[] = $item->getName();
$productSku[] = $item->getSku();
$productNum[] = number_format($item->getQtyOrdered());
$productPrice[] = sprintf('%.2f', $item->getPrice());
}
$productDetails['productName'] = implode(';', $productName);
$productDetails['productSku'] = implode(';', $productSku);
$productDetails['productNum'] = implode(';', $productNum);
$productDetails['productPrice'] = implode(';', $productPrice);
return $productDetails;
}
/**
* 钱海支付Html特殊字符转义
*/
function OceanHtmlSpecialChars($parameter){
//去除前后空格
$parameter = trim($parameter);
//转义"双引号,<小于号,>大于号,'单引号
$parameter = str_replace(array("<",">","'","\""),array("&lt;","&gt;","&#039;","&quot;"),$parameter);
return $parameter;
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Model\Source;
use Magento\Framework\Option\ArrayInterface;
class OtherCurrency implements ArrayInterface {
/**
* @return array
*/
public function toOptionArray() {
return [
['value' => '1', 'label' => __('3D Secure')],
['value' => '0', 'label' =>__('Sale')]
];
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Creditcard\Model\Source;
use Magento\Framework\Option\ArrayInterface;
class PayMode implements ArrayInterface {
/**
* @return array
*/
public function toOptionArray() {
return [
['value' => 'iframe', 'label' => __('Iframe')],
['value' => 'redirect', 'label' =>__('Redirect')]
];
}
}
#Oceanpayment Creditcard
Installation
1 - unzip de module in app/code/Oceanpayment/Creditcard
2 - enable module: bin/magento module:enable --clear-static-content Oceanpayment_Creditcard
3 - upgrade database: bin/magento setup:upgrade
4 - re-run compile command: bin/magento setup:di:compile
In order to deactivate the module bin/magento module:disable --clear-static-content Oceanpayment_Creditcard
In order to update static files: bin/magento setup:static-content:deploy
Important: make sure that php path is correct in bin/magento file
{
"name": "oceanpayment/module-creditcard",
"description": "Accept payments using Oceanpayment Creditcard payment gateway",
"type": "magento2-module",
"version": "2.0.2",
"license": [
"OSL-3.0",
"AFL-3.0"
],
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Oceanpayment\\Creditcard\\": ""
}
}
}
<?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="oceanpaymentcreditcard" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Oceanpayment Debit/Credit Card</label>
<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="3" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Title</label>
</field>
<field id="account" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Account</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="terminal" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Terminal</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="securecode" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0">
<label>SecureCode</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="gateway_url" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Gateway URL</label>
</field>
<field id="pay_mode" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Pay Mode</label>
<source_model>Oceanpayment\Creditcard\Model\Source\PayMode</source_model>
</field>
<field id="new_order_status" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0">
<label>New Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="success_order_status" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Approved Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="failure_order_status" translate="label" type="select" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Failure Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="high_risk_order_status" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1" showInStore="0">
<label>High Risk Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="pre_auth_order_status" translate="label" type="select" sortOrder="13" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Pre-auth Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="invoice" translate="label" type="select" sortOrder="14" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Invoice When Complete</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="allowspecific" translate="label" type="allowspecific" sortOrder="14" 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="16" 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="sort_order" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Sort Order</label>
</field>
<include path="Oceanpayment_Creditcard::system/secure_mode.xml"/>
</group>
</section>
</system>
</config>
\ No newline at end of file
<?xml version="1.0"?>
<include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd">
<group id="secure" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
<label>3D Secure Mode</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model>
<field id="secure_mode" translate="label" sortOrder="1" type="select" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="secure_terminal" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Terminal</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_securecode" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure SecureCode</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_currency" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Currency</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_amount" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Amount</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_other_currency" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Other Currency</label>
<source_model>Oceanpayment\Creditcard\Model\Source\OtherCurrency</source_model>
</field>
<field id="secure_country" translate="label" type="multiselect" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Countries</label>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
</field>
</group>
</include>
\ No newline at end of file
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<payment>
<oceanpaymentcreditcard>
<title>Debit/Credit Card</title>
<model>Oceanpayment\Creditcard\Model\PaymentMethod</model>
<active>0</active>
<new_order_status>pending</new_order_status>
<success_order_status>processing</success_order_status>
<failure_order_status>canceled</failure_order_status>
<pre_auth_order_status>processing</pre_auth_order_status>
<invoice>0</invoice>
<sort_order>0</sort_order>
<allowspecific>0</allowspecific>
</oceanpaymentcreditcard>
</payment>
</default>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Checkout\Model\CompositeConfigProvider">
<arguments>
<argument name="configProviders" xsi:type="array">
<item name="creditcard_config_provider" xsi:type="object">Oceanpayment\Creditcard\Model\CreditcardConfigProvider</item>
</argument>
</arguments>
</type>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="oceanpaymentcreditcard" frontName="oceanpaymentcreditcard">
<module name="Oceanpayment_Creditcard" />
</route>
</router>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 Oceanpayment Design All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Oceanpayment_Creditcard" active="true" schema_version="2.0.2" setup_version="2.0.2">
<sequence>
<module name="Magento_Store"/>
<module name="Magento_Catalog"/>
</sequence>
</module>
</config>
*************************************
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
use \Magento\Framework\Component\ComponentRegistrar;
\Magento\Framework\Component\ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Oceanpayment_Creditcard',
__DIR__
);
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Creditcard\Block\Info
*/
?>
<?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?>
<?php if ($block->getInfo()->getLastTransId() == ''): ?>
<br /><?php echo __('No transaction ID recorded.') ?>
<?php else: ?>
<br /><?php echo __('CreditCard Transaction ID: %1', $this->escapeHtml($block->getInfo()->getLastTransId())) ?>
<?php endif; ?>
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Creditcard\Block\Info
*/
?>
<?php echo $this->escapeHtml($block->getMethod()->getTitle()) ?>
{{pdf_row_separator}}
<?php echo __('CreditCard Transaction ID: %1', $this->escapeHtml($block->getInfo()->getLastTransId())) ?>
{{pdf_row_separator}}
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Oceanpayment_Creditcard::css/styles.css"/>
</head>
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<item name="billing-step" xsi:type="array">
<item name="children" xsi:type="array">
<item name="payment" xsi:type="array">
<item name="children" xsi:type="array">
<item name="renders" xsi:type="array">
<!-- merge payment method renders here -->
<item name="children" xsi:type="array">
<item name="creditcard-payments" xsi:type="array">
<item name="component" xsi:type="string">Oceanpayment_Creditcard/js/view/payment/creditcard-method</item>
<item name="methods" xsi:type="array">
<item name="oceanpaymentcreditcard" xsi:type="array">
<item name="isBillingAddressRequired" xsi:type="boolean">true</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
\ No newline at end of file
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" layout="1column">
<body>
<referenceContainer name="content">
<block class="Oceanpayment\Creditcard\Block\Payment\Redirect" name="creditcard.redirect" template="payment/redirect.phtml" cacheable="false"/>
</referenceContainer>
</body>
</page>
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Creditcard\Block\Form
*/
?>
<?php $methodCode = $block->escapeHtml($block->getMethodCode());?>
<div class="items <?php /* @noEscape */ echo $methodCode ?> instructions agreement checkout-agreement-item-content" id="payment_form_<?php /* @noEscape */ echo $methodCode ?>" style="display: none;">
<?php /* @noEscape */ echo nl2br($block->escapeHtml($instructions)) ?>
<div style="padding:10px 15px 15px;">
<img src="<?php $block->escapeUrl($block->getViewFileUrl('Oceanpayment_Creditcard::icon_accepted_cards_creditcard.gif'));?>" alt="<?php echo __('Payment by visa or mastercard'); ?>"><br>
</div>
</div>
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Creditcard\Block\Info
*/
?>
<dl class="payment-method oceanpaymentcreditcard">
<dt class="title"><?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?></dt>
<dd class="content">
<strong><?php echo $block->escapeHtml(__('You will be redirected to our secure payment page when you place an order.')) ?></strong>
</dd>
</dl>
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
?>
<div id="checkout-loader" data-role="checkout-loader" class="loading-mask" data-mage-init='{"checkoutLoader": {}}'>
<div class="loader">
<img src="<?php echo $block->getViewFileUrl('images/loader-1.gif'); ?>"
alt="<?php echo __('Loading...'); ?>"
style="position: absolute;">
</div>
</div>
<div>
<?php echo $block->getCheckoutForm(); ?>
<?php if($block->getPayMode() == 'iframe'){?>
<iframe width="100%" height="800px" scrolling="auto" name="iframe_checkout" id="iframe_checkout" style="border:none; margin: 0 auto; overflow:auto;"></iframe>
<script type="text/javascript">
if (window.XMLHttpRequest) {
document.payment_checkout.target="iframe_checkout";
}
document.payment_checkout.submit();
</script>
<?php }elseif($block->getPayMode() == 'redirect'){?>
<p style="font: 16px; padding: 0px; text-align: center; margin: 100px auto 0px; width: 90%;"><?php echo __('Processing, please do not click the refresh button.'); ?></p>
<script type="text/javascript">
document.payment_checkout.submit();
</script>
<?php } ?>
</div>
\ No newline at end of file
.payment-method div.payment_logo_oceanpaymentcreditcard {
background:url(../images/payment_logo.png) no-repeat;
}
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
define(
[
'jquery',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/url-builder',
'mage/storage',
'Magento_Checkout/js/model/error-processor',
'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/full-screen-loader'
],
function ($, quote, urlBuilder, storage, errorProcessor, customer, fullScreenLoader) {
'use strict';
return function (messageContainer) {
var serviceUrl,
payload,
paymentData = quote.paymentMethod();
if (Object.prototype.hasOwnProperty.call(paymentData, '__disableTmpl')) { delete paymentData.__disableTmpl; }
/**
* Checkout for guest and registered customer.
*/
if (!customer.isLoggedIn()) {
serviceUrl = urlBuilder.createUrl('/guest-carts/:cartId/payment-information', {
cartId: quote.getQuoteId()
});
payload = {
cartId: quote.getQuoteId(),
email: quote.guestEmail,
paymentMethod: paymentData,
billingAddress: quote.billingAddress()
};
} else {
serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {});
payload = {
cartId: quote.getQuoteId(),
paymentMethod: paymentData,
billingAddress: quote.billingAddress()
};
}
fullScreenLoader.startLoader();
return storage.post(
serviceUrl, JSON.stringify(payload)
).done(
function () {
//$.mage.redirect(window.checkoutConfig.payment.paypalExpress.redirectUrl[quote.paymentMethod().method]);
$.mage.redirect(window.checkoutConfig.payment.creditcard.redirectUrl[quote.paymentMethod().method]);
}
).fail(
function (response) {
errorProcessor.process(response, messageContainer);
fullScreenLoader.stopLoader();
}
);
};
}
);
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
/*browser:true*/
/*global define*/
define(
[
'uiComponent',
'Magento_Checkout/js/model/payment/renderer-list'
],
function (
Component,
rendererList
) {
'use strict';
rendererList.push(
{
type: 'oceanpaymentcreditcard',
component: 'Oceanpayment_Creditcard/js/view/payment/method-renderer/creditcard-method'
}
);
/** Add view logic here if needed */
return Component.extend({});
}
);
\ No newline at end of file
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
/*browser:true*/
/*global define*/
define(
[
'jquery',
'Magento_Checkout/js/view/payment/default',
'Oceanpayment_Creditcard/js/action/set-payment-method',
'Magento_Checkout/js/model/payment/additional-validators'
],
function ($, Component, setPaymentMethodAction) {
'use strict';
return Component.extend({
defaults: {
template: 'Oceanpayment_Creditcard/payment/creditcard'
},
/** Redirect to creditcard */
continueToCreditCard: function () {
//update payment method information if additional data was changed
this.selectPaymentMethod();
setPaymentMethodAction(this.messageContainer);
return false;
}
});
}
);
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<div class="payment-method" data-bind="css: {'_active': (getCode() == isChecked())}">
<div class="payment-method-title field choice">
<input type="radio"
name="payment[method]"
class="radio"
data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/>
<label data-bind="attr: {'for': getCode()}" class="label">
<span data-bind="text: getTitle()"></span>
<div alt="Logo" data-bind="attr: {'class': 'payment_logo_' + getCode()}"></div>
</label>
</div>
<div class="payment-method-content">
<!-- ko foreach: getRegion('messages') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
<div class="payment-method-billing-address">
<!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
</div>
<div class="checkout-agreements-block">
<!-- ko foreach: $parent.getRegion('before-place-order') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
</div>
<div class="actions-toolbar">
<div class="primary">
<button class="action primary checkout"
type="submit"
data-bind="click: continueToCreditCard, enable: (getCode() == isChecked())"
disabled>
<span data-bind="i18n: 'Place Order'"></span>
</button>
</div>
</div>
</div>
</div>
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Block;
class Form extends \Magento\Payment\Block\Form
{
/**
* Checkmo template
*
* @var string
*/
protected $_supportedInfoLocales = array('fr');
protected $_defaultInfoLocale = 'en';
protected $_template = 'Oceanpayment_Klarna::form.phtml';
}
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Block;
class Info extends \Magento\Payment\Block\Info
{
/**
* @var string
*/
protected $_payableTo;
/**
* @var string
*/
protected $_mailingAddress;
/**
* @var string
*/
protected $_template = 'Oceanpayment_Klarna::info.phtml';
public function getMethodCode()
{
return $this->getInfo()->getMethodInstance()->getCode();
}
/**
* @return string
*/
public function toPdf()
{
//$this->setTemplate('Oceanpayment_Klarna::info/pdf/checkmo.phtml');
$this->setTemplate('Oceanpayment_Klarna::pdf/info.phtml');
return $this->toHtml();
}
}
<?php
/**
* ######
* ######
* ########### ########### ########### ########### ########### ############ ########### ###### ###### ########### ########### ########### #############
* ############# ############# ############# ############# ############# ############# ############# ###### ###### ############# ############# ############# #############
* ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### #### ### #### ###### ###### ###### ###### #############
* ###### ###### ###### ############# ############# ###### ###### ###### ###### ############# ###### ###### #### ### #### ############# ###### ###### ######
* ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### ###### #### ### #### ###### ###### ###### ######
* ############# ############# ############# ############# ###### ###### ############# ############# ############# #### ### #### ############# ###### ###### ##########
* ########### ########### ########### ########### ###### ###### ############ ########### ############# #### ### #### ########### ###### ###### ##########
* ###### ######
* ###### #############
* ###### ############
*
*
*
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Block\Payment;
class Redirect extends \Magento\Framework\View\Element\Template
{
/**
* @var \Magento\Checkout\Model\Session
*/
protected $_checkoutSession;
protected $_paymentMethod;
/**
* @param \Magento\Framework\View\Element\Template\Context $context
* @param \Magento\Checkout\Model\Session $checkoutSession
* @param array $data
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Oceanpayment\Klarna\Model\PaymentMethod $paymentMethod,
\Magento\Checkout\Model\Session $checkoutSession,
array $data = []
) {
$this->_checkoutSession = $checkoutSession;
parent::__construct($context, $data);
$this->_paymentMethod = $paymentMethod;
$this->_isScopePrivate = true;
}
public function getCheckoutForm(){
$parameter = $this->_paymentMethod->getCheckoutParameter();
$gatewayUrl = $this->_paymentMethod->getGatewayUrl();
$formHTML = '';
$formHTML .= '<form action="'.$gatewayUrl.'" name="payment_checkout" id="payment_checkout">';
foreach ($parameter as $field => $value) {
$formHTML .= "<input type='hidden' name='".$field."' value='".$value."' >";
}
$formHTML .= '</form>';
// echo "<pre>";
// print_r($formHTML);die();
return $formHTML;
}
public function getPayMode(){
return $this->_paymentMethod->getConfigData('pay_mode');
}
}
<?php
namespace Oceanpayment\Klarna\Controller\Payment;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\Controller\ResultFactory;
use Magento\Quote\Api\CartManagementInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\RequestInterface;
class Back extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface
{
const PUSH = "[PUSH]";
const BrowserReturn = "[Browser Return]";
protected $_processingArray = array('processing', 'complete');
/**
* Customer session model
*
* @var \Magento\Customer\Model\Session
*/
protected $_customerSession;
protected $resultPageFactory;
protected $checkoutSession;
protected $orderRepository;
protected $_scopeConfig;
protected $_orderFactory;
protected $creditmemoSender;
protected $orderSender;
protected $urlBuilder;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Customer\Model\Session $customerSession
*/
public function __construct(
\Magento\Customer\Model\Session $customerSession,
\Magento\Framework\App\Action\Context $context,
\Oceanpayment\Klarna\Model\PaymentMethod $paymentMethod,
\Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Sales\Model\Order\Email\Sender\CreditmemoSender $creditmemoSender,
\Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender,
\Magento\Framework\Url $urlBuilder,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
) {
$this->_customerSession = $customerSession;
$this->checkoutSession = $checkoutSession;
$this->urlBuilder = $urlBuilder;
$this->orderRepository = $orderRepository;
parent::__construct($context);
$this->_scopeConfig = $scopeConfig;
$this->_orderFactory = $orderFactory;
$this->_paymentMethod = $paymentMethod;
$this->creditmemoSender = $creditmemoSender;
$this->orderSender = $orderSender;
}
protected function _createInvoice($order)
{
if (!$order->canInvoice()) {
return;
}
$invoice = $order->prepareInvoice();
if (!$invoice->getTotalQty()) {
throw new \RuntimeException("Cannot create an invoice without products.");
}
$invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
$invoice->register();
$order->addRelatedObject($invoice);
}
public function execute()
{
//交易推送类型
$this->returnLog(self::BrowserReturn, $_REQUEST);
//载入模块
$model = $this->_paymentMethod;
$order = $this->_orderFactory->create()->loadByIncrementId($_REQUEST['order_number']);
$history = ' (payment_id:'.$_REQUEST['payment_id'].' | order_number:'.$_REQUEST['order_number'].' | '.$_REQUEST['order_currency'].':'.$_REQUEST['order_amount'].' | payment_details:'.$_REQUEST['payment_details'].')';
switch($this->validated($order)){
case 1:
//支付成功
$order->setState($model->getConfigData('success_order_status'));
$order->setStatus($model->getConfigData('success_order_status'));
$order->addStatusToHistory($model->getConfigData('success_order_status'), __(self::BrowserReturn.'Payment Success!'.$history));
//发送邮件
// $this->orderSender->send($order);
//自动Invoice
if ($model->getConfigData('invoice')){
$this->_createInvoice($order);
}
$order->save();
$url = 'checkout/onepage/success';
break;
case 0:
//支付失败
$order->setState($model->getConfigData('failure_order_status'));
$order->setStatus($model->getConfigData('failure_order_status'));
$order->addStatusToHistory($model->getConfigData('failure_order_status'), __(self::BrowserReturn.'Payment Failed!'.$history));
$order->save();
$this->messageManager->addError(__('Payment Failed! '.$_REQUEST['payment_details']));
$url = 'checkout/onepage/failure';
break;
case -1:
//交易待处理
$order->setState($model->getConfigData('pre_auth_order_status'));
$order->setStatus($model->getConfigData('pre_auth_order_status'));
$order->addStatusToHistory($model->getConfigData('pre_auth_order_status'), __(self::BrowserReturn.'(Pre-auth)Payment Pending!'.$history));
$order->save();
$url = 'checkout/onepage/success';
break;
case 2:
//在网站中已经是支付成功
$url = 'checkout/onepage/success';
break;
case '10000':
//10000:Payment is declined 高风险订单
$order->setState($model->getConfigData('high_risk_order_status'));
$order->setStatus($model->getConfigData('high_risk_order_status'));
$order->addStatusToHistory($model->getConfigData('high_risk_order_status'), __(self::BrowserReturn.'(High Risk)Payment Failed!'.$history));
$order->save();
$this->messageManager->addError(__('Payment Failed! '.$_REQUEST['payment_details']));
$url = 'checkout/onepage/failure';
break;
case '20061':
//订单号重复
$url = 'checkout/onepage/failure';
break;
case 999:
//加密值错误或系统异常
$url = 'checkout/onepage/failure';
break;
default:
}
$url = $this->urlBuilder->getUrl($url);
$this->getParentLocationReplace($url);
}
private function validated($order)
{
//载入模块
$model = $this->_paymentMethod;
//获取账号
$account = $model->getConfigData('account');
//返回终端号
$terminal = $_REQUEST['terminal'];
//匹配终端号 判断是否3D交易
if($terminal == $model->getConfigData('terminal')){
$securecode = $model->getConfigData('securecode');
}elseif($terminal == $model->getConfigData('secure/secure_terminal')){
//3D
$securecode = $model->getConfigData('secure/secure_securecode');
}else{
$securecode = '';
}
//返回Oceanpayment的支付唯一号
$payment_id = $_REQUEST['payment_id'];
//返回网站订单号
$order_number = $_REQUEST['order_number'];
//返回交易币种
$order_currency = $_REQUEST['order_currency'];
//返回交易金额
$order_amount = $_REQUEST['order_amount'];
//返回交易状态
$payment_status = $_REQUEST['payment_status'];
//返回支付详情
$payment_details = $_REQUEST['payment_details'];
//用于支付结果页面显示
$_SESSION['payment_details'] = $payment_details;
//用于支付结果页面显示响应代码
$getErrorCode = explode(':', $payment_details);
$_SESSION['errorCode'] = $getErrorCode[0];
//返回解决办法
$_SESSION['payment_solutions']= $_REQUEST['payment_solutions'];
//返回备注
$order_notes = $_REQUEST['order_notes'];
//未通过的风控规则
$payment_risk = $_REQUEST['payment_risk'];
//返回支付信用卡卡号
$card_number = $_REQUEST['card_number'];
//返回交易类型
$payment_authType = $_REQUEST['payment_authType'];
//返回数据签名
$back_signValue = $_REQUEST['signValue'];
//SHA256加密
$local_signValue = hash("sha256",$account.$terminal.$order_number.$order_currency.$order_amount.$order_notes.$card_number.
$payment_id.$payment_authType.$payment_status.$payment_details.$payment_risk.$securecode);
//加密校验
if(strtoupper($local_signValue) == strtoupper($back_signValue)){
//在网站中已经是支付成功
if(in_array($order->getState(), $this->_processingArray)){
return 2;
}
//支付状态
if ($payment_status == 1) {
return 1;
} elseif ($payment_status == -1) {
return -1;
} elseif ($payment_status == 0) {
//10000:Payment is declined 高风险订单
if($getErrorCode[0] == '10000'){
return '10000';
}
//是否点击浏览器后退造成订单号重复 20061
if($getErrorCode[0] == '20061'){
return '20061';
}
return 0;
}
}else{
return 999;
}
}
/**
* return log
*/
public function returnLog($logType, $data){
$filedate = date('Y-m-d');
$newfile = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "a+" );
$return_log = date('Y-m-d H:i:s') . $logType . "\r\n";
foreach ($data as $k=>$v){
$return_log .= $k . " = " . $v . "\r\n";
}
$return_log .= '*****************************************' . "\r\n";
$return_log = $return_log.file_get_contents( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log");
$filename = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "r+" );
fwrite($filename,$return_log);
fclose($filename);
fclose($newfile);
}
/**
* JS
*
*/
public function getParentLocationReplace($url)
{
echo '<script type="text/javascript">parent.location.replace("'.$url.'");</script>';
}
public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
{
return null;
}
public function validateForCsrf(RequestInterface $request): ?bool
{
return true;
}
}
<?php
namespace Oceanpayment\Klarna\Controller\Payment;
use Magento\Framework\Controller\ResultFactory;
use Magento\Quote\Api\CartManagementInterface;
class Notice extends \Magento\Framework\App\Action\Action
{
const PUSH = "[PUSH]";
const BrowserReturn = "[Browser Return]";
protected $_processingArray = array('processing', 'complete');
/**
* Customer session model
*
* @var \Magento\Customer\Model\Session
*/
protected $_customerSession;
protected $resultPageFactory;
protected $checkoutSession;
protected $orderRepository;
protected $_scopeConfig;
protected $_orderFactory;
protected $creditmemoSender;
protected $orderSender;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Customer\Model\Session $customerSession
*/
public function __construct(
\Magento\Customer\Model\Session $customerSession,
\Magento\Framework\App\Action\Context $context,
\Oceanpayment\Klarna\Model\PaymentMethod $paymentMethod,
\Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Sales\Model\Order\Email\Sender\CreditmemoSender $creditmemoSender,
\Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
) {
$this->_customerSession = $customerSession;
$this->checkoutSession = $checkoutSession;
$this->orderRepository = $orderRepository;
parent::__construct($context);
$this->_scopeConfig = $scopeConfig;
$this->_orderFactory = $orderFactory;
$this->_paymentMethod = $paymentMethod;
$this->creditmemoSender = $creditmemoSender;
$this->orderSender = $orderSender;
}
protected function _createInvoice($order)
{
if (!$order->canInvoice()) {
return;
}
$invoice = $order->prepareInvoice();
if (!$invoice->getTotalQty()) {
throw new \RuntimeException("Cannot create an invoice without products.");
}
$invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
$invoice->register();
$order->addRelatedObject($invoice);
}
public function execute()
{
//获取推送输入流XML
$xml_str = file_get_contents("php://input");
//判断返回的输入流是否为xml
if($this->xml_parser($xml_str)){
$xml = simplexml_load_string($xml_str);
//把推送参数赋值到$_REQUEST
$_REQUEST['response_type'] = (string)$xml->response_type;
$_REQUEST['account'] = (string)$xml->account;
$_REQUEST['terminal'] = (string)$xml->terminal;
$_REQUEST['payment_id'] = (string)$xml->payment_id;
$_REQUEST['order_number'] = (string)$xml->order_number;
$_REQUEST['order_currency'] = (string)$xml->order_currency;
$_REQUEST['order_amount'] = (string)$xml->order_amount;
$_REQUEST['payment_status'] = (string)$xml->payment_status;
$_REQUEST['payment_details'] = (string)$xml->payment_details;
$_REQUEST['signValue'] = (string)$xml->signValue;
$_REQUEST['order_notes'] = (string)$xml->order_notes;
$_REQUEST['card_number'] = (string)$xml->card_number;
$_REQUEST['payment_authType'] = (string)$xml->payment_authType;
$_REQUEST['payment_risk'] = (string)$xml->payment_risk;
$_REQUEST['methods'] = (string)$xml->methods;
$_REQUEST['payment_country'] = (string)$xml->payment_country;
$_REQUEST['payment_solutions']= (string)$xml->payment_solutions;
//交易推送类型
$this->returnLog(self::PUSH, $xml_str);
//载入模块
$model = $this->_paymentMethod;
$order = $this->_orderFactory->create()->loadByIncrementId($_REQUEST['order_number']);
$history = ' (payment_id:'.$_REQUEST['payment_id'].' | order_number:'.$_REQUEST['order_number'].' | '.$_REQUEST['order_currency'].':'.$_REQUEST['order_amount'].' | payment_details:'.$_REQUEST['payment_details'].')';
//预授权结果推送
$authType = '';
if($_REQUEST['payment_authType'] == 1){
if($_REQUEST['payment_status'] == 1){
$authType = '(Capture)';
}elseif($_REQUEST['payment_status'] == 0){
$authType = '(Void)';
}
}
switch($this->validated($order)){
case 1:
//支付成功
$order->setState($model->getConfigData('success_order_status'));
$order->setStatus($model->getConfigData('success_order_status'));
$order->addStatusToHistory($model->getConfigData('success_order_status'), __(self::PUSH.$authType.'Payment Success!'.$history));
//发送邮件
$this->orderSender->send($order, true);
//自动Invoice
if ($model->getConfigData('invoice')){
$this->_createInvoice($order);
}
$order->save();
break;
case 0:
//支付失败
$order->setState($model->getConfigData('failure_order_status'));
$order->setStatus($model->getConfigData('failure_order_status'));
$order->addStatusToHistory($model->getConfigData('failure_order_status'), __(self::PUSH.$authType.'Payment Failed!'.$history));
$order->save();
break;
case -1:
//交易待处理
$order->setState($model->getConfigData('pre_auth_order_status'));
$order->setStatus($model->getConfigData('pre_auth_order_status'));
$order->addStatusToHistory($model->getConfigData('pre_auth_order_status'), __(self::PUSH.'(Pre-auth)Payment Pending!'.$history));
$order->save();
break;
case 2:
//在网站中已经是支付成功
$order->setState($model->getConfigData('success_order_status'));
$order->setStatus($model->getConfigData('success_order_status'));
$order->addStatusToHistory($model->getConfigData('success_order_status'), __(self::PUSH.'Payment Success!'.$history));
$order->save();
break;
case '10000':
//10000:Payment is declined 高风险订单
$order->setState($model->getConfigData('high_risk_order_status'));
$order->setStatus($model->getConfigData('high_risk_order_status'));
$order->addStatusToHistory($model->getConfigData('high_risk_order_status'), __(self::PUSH.'(High Risk)Payment Failed!'.$history));
$order->save();
case '20061':
//订单号重复
break;
case 999:
//加密值错误或系统异常
break;
default:
}
echo "receive-ok";
exit;
}
}
private function validated($order)
{
//载入模块
$model = $this->_paymentMethod;
//获取账号
$account = $model->getConfigData('account');
//返回终端号
$terminal = $_REQUEST['terminal'];
//匹配终端号 判断是否3D交易
if($terminal == $model->getConfigData('terminal')){
$securecode = $model->getConfigData('securecode');
}elseif($terminal == $model->getConfigData('secure/secure_terminal')){
//3D
$securecode = $model->getConfigData('secure/secure_securecode');
}else{
$securecode = '';
}
//返回Oceanpayment的支付唯一号
$payment_id = $_REQUEST['payment_id'];
//返回网站订单号
$order_number = $_REQUEST['order_number'];
//返回交易币种
$order_currency = $_REQUEST['order_currency'];
//返回交易金额
$order_amount = $_REQUEST['order_amount'];
//返回交易状态
$payment_status = $_REQUEST['payment_status'];
//返回支付详情
$payment_details = $_REQUEST['payment_details'];
//获取响应代码
$getErrorCode = explode(':', $payment_details);
//返回解决办法
$payment_solutions = $_REQUEST['payment_solutions'];
//返回备注
$order_notes = $_REQUEST['order_notes'];
//未通过的风控规则
$payment_risk = $_REQUEST['payment_risk'];
//返回支付信用卡卡号
$card_number = $_REQUEST['card_number'];
//返回交易类型
$payment_authType = $_REQUEST['payment_authType'];
//返回数据签名
$back_signValue = $_REQUEST['signValue'];
//SHA256加密
$local_signValue = hash("sha256",$account.$terminal.$order_number.$order_currency.$order_amount.$order_notes.$card_number.
$payment_id.$payment_authType.$payment_status.$payment_details.$payment_risk.$securecode);
//加密校验
if(strtoupper($local_signValue) == strtoupper($back_signValue)){
//是否是预授权交易
if($payment_authType == 0){
//在网站中已经是支付成功
if(in_array($order->getState(), $this->_processingArray)){
return 1;
}
}
//支付状态
if ($payment_status == 1) {
return 1;
} elseif ($payment_status == -1) {
return -1;
} elseif ($payment_status == 0) {
//10000:Payment is declined 高风险订单
if($getErrorCode[0] == '10000'){
return '10000';
}
//是否点击浏览器后退造成订单号重复 20061
if($getErrorCode[0] == '20061'){
return '20061';
}
return 0;
}
}else{
return 999;
}
}
/**
* notice log
*/
public function returnLog($logType, $xml){
$filedate = date('Y-m-d');
$newfile = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "a+" );
$return_log = date('Y-m-d H:i:s') . $logType . "\r\n";
$return_log .= $xml;
// foreach ($_REQUEST as $k=>$v){
// $return_log .= $k . " = " . $v . "\r\n";
// }
$return_log .= '*****************************************' . "\r\n";
$return_log = $return_log.file_get_contents( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log");
$filename = fopen( dirname(dirname(dirname(__FILE__))) . "/oceanpayment_log/" . $filedate . ".log", "r+" );
fwrite($filename,$return_log);
fclose($filename);
fclose($newfile);
}
/**
* 判断是否为xml
*
*/
function xml_parser($str){
$xml_parser = xml_parser_create();
if(!xml_parse($xml_parser,$str,true)){
xml_parser_free($xml_parser);
return false;
}else {
return true;
}
}
}
<?php
namespace Oceanpayment\Klarna\Controller\Payment;
use Magento\Framework\Controller\ResultFactory;
use Magento\Customer\Api\Data\GroupInterface;
class Redirect extends \Magento\Framework\App\Action\Action
{
/**
* Customer session model
*
* @var \Magento\Customer\Model\Session
*/
protected $_customerSession;
protected $resultPageFactory;
protected $_paymentMethod;
protected $_checkoutSession;
protected $checkout;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Customer\Model\Session $customerSession
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Customer\Model\Session $customerSession,
\Oceanpayment\Klarna\Model\PaymentMethod $paymentMethod,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->_customerSession = $customerSession;
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
$this->_paymentMethod = $paymentMethod;
$this->_checkoutSession = $checkoutSession;
}
public function execute()
{
$resultPage = $this->resultPageFactory->create();
$resultPage->getConfig()->getTitle()->set(__('Klarna'));
return $resultPage;
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Model;
use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Framework\Locale\ResolverInterface;
use Magento\Customer\Helper\Session\CurrentCustomer;
use Magento\Payment\Helper\Data as PaymentHelper;
class KlarnaConfigProvider implements ConfigProviderInterface
{
/**
* @var ResolverInterface
*/
protected $localeResolver;
/**
* @var Config
*/
protected $config;
/**
* @var \Magento\Customer\Helper\Session\CurrentCustomer
*/
protected $currentCustomer;
/**
* @var string[]
*/
/*protected $methodCodes = [
Config::METHOD_WPP_BML,
Config::METHOD_WPP_PE_EXPRESS,
Config::METHOD_WPP_EXPRESS,
Config::METHOD_WPP_PE_BML
];*/
/**
* @var \Magento\Payment\Model\Method\AbstractMethod[]
*/
protected $methods = [];
/**
* @var PaymentHelper
*/
protected $paymentHelper;
protected $checkoutSession;
/**
* @param ConfigFactory $configFactory
* @param ResolverInterface $localeResolver
* @param CurrentCustomer $currentCustomer
* @param PaymentHelper $paymentHelper
*/
public function __construct(
//ConfigFactory $configFactory,
ResolverInterface $localeResolver,
CurrentCustomer $currentCustomer,
\Magento\Checkout\Model\Session $checkoutSession,
PaymentHelper $paymentHelper
) {
$this->localeResolver = $localeResolver;
//$this->config = $configFactory->create();
$this->currentCustomer = $currentCustomer;
$this->paymentHelper = $paymentHelper;
$this->checkoutSession = $checkoutSession;
$code = 'oceanpaymentklarna';
$this->methods[$code] = $this->paymentHelper->getMethodInstance($code);
}
/**
* {@inheritdoc}
*/
public function getConfig()
{
$code = 'oceanpaymentklarna';
$config = [];
if ($this->methods[$code]->isAvailable($this->checkoutSession->getQuote())) {
$config = [];
$config['payment'] = [];
$config['payment']['klarna']['redirectUrl'] = [];
$config['payment']['klarna']['redirectUrl'][$code] = $this->getMethodRedirectUrl($code);
}
return $config;
}
/**
* Return redirect URL for method
*
* @param string $code
* @return mixed
*/
protected function getMethodRedirectUrl($code)
{
return $this->methods[$code]->getOrderPlaceRedirectUrl();
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Model;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Payment\Model\Method\AbstractMethod;
use Magento\Sales\Model\Order;
class PaymentMethod extends AbstractMethod
{
const CODE = 'oceanpaymentklarna';
const POST = "[POST to Oceanpayment]";
protected $_code = self::CODE;
protected $_isInitializeNeeded = true;
protected $_formBlockType = 'Oceanpayment\Klarna\Block\Form';
protected $_infoBlockType = 'Oceanpayment\Klarna\Block\Info';
protected $_isGateway = false;
protected $_canAuthorize = false;
protected $_canCapture = false;
protected $_canCapturePartial = false;
protected $_canRefund = false;
protected $_canRefundInvoicePartial = false;
protected $_canVoid = false;
protected $_canUseInternal = false;
protected $_canUseCheckout = true;
protected $_canUseForMultishipping = false;
protected $_canSaveCc = false;
protected $urlBuilder;
protected $_moduleList;
protected $checkoutSession;
protected $_orderFactory;
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\Sales\Model\OrderFactory $orderFactory,
\Magento\Framework\Url $urlBuilder,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []){
$this->urlBuilder = $urlBuilder;
$this->_moduleList = $moduleList;
$this->checkoutSession = $checkoutSession;
$this->_orderFactory = $orderFactory;
parent::__construct($context,
$registry,
$extensionFactory,
$customAttributeFactory,
$paymentData,
$scopeConfig,
$logger,
$resource,
$resourceCollection,
$data);
}
/**
* Redirect URL
*
* @return string Redirect URL
*/
public function getOrderPlaceRedirectUrl()
{
return $this->urlBuilder->getUrl('oceanpaymentklarna/payment/redirect', ['_secure' => true]);
}
/**
* Gateway URL
*
* @return string Gateway URL
*/
public function getGatewayUrl()
{
return $this->getConfigData('gateway_url');
}
public function canUseForCurrency($currencyCode)
{
return true;
}
public function initialize($paymentAction, $stateObject)
{
$payment = $this->getInfoInstance();
//$order = $payment->getOrder();
$state = $this->getConfigData('new_order_status');
//$state = Mage_Sales_Model_Order::STATE_PENDING_PAYMENT;
$stateObject->setState($state);
$stateObject->setStatus('pending_payment');
$stateObject->setIsNotified(false);
}
public function replace_specialChar($strParam){
$regex = "/\_|\-|\——|/";
return preg_replace($regex,"",$strParam);
}
public function getCheckoutParameter()
{
$orderIncrementId = $this->checkoutSession->getLastRealOrderId();
$order = $this->_orderFactory->create()->loadByIncrementId($orderIncrementId);
// echo "<pre>";
// print_r($order->getBaseShippingTaxAmount());exit;
$billing = $order->getBillingAddress();
$shipping = $order->getShippingAddress();
$productDetails = $this->getProductItems($order->getAllItems());
//支付币种
$order_currency = $order->getOrderCurrencyCode();
//金额
$order_amount = round($order->getGrandTotal(),2);
// $order_amount = sprintf('%.2f', $order->getGrandTotal());
//判断是否启用3D功能
if($this->getConfigData('secure/secure_mode') == 1){
//检验是否需要3D验证
$validate_arr = $this->validate3D($order_currency, $order_amount, $billing, $shipping);
}else{
$validate_arr['terminal'] = $this->getConfigData('terminal');
$validate_arr['securecode'] = $this->getConfigData('securecode');
}
//账户
$account = $this->getConfigData('account');
//终端号
$terminal = $validate_arr['terminal'];
//securecode
$securecode = $validate_arr['securecode'];
//支付方式
// $methods = 'Klarna';
//订单号
$order_number = $orderIncrementId;
//返回地址
$backUrl = $this->urlBuilder->getUrl('oceanpaymentklarna/payment/back', ['_secure' => true,'_nosid' => true]);
//服务器响应地址
$noticeUrl = $this->urlBuilder->getUrl('oceanpaymentklarna/payment/notice', ['_secure' => true,'_nosid' => true]);
//备注
$order_notes = $orderIncrementId;
//账单人名
$billing_firstName = $this->OceanHtmlSpecialChars($billing->getFirstname());
//账单人姓
$billing_lastName = $this->OceanHtmlSpecialChars($billing->getLastname());
//账单人email
$billing_email = $this->OceanHtmlSpecialChars($order->getCustomerEmail());
//账单人电话
$billing_phone = $billing->getTelephone();
//账单人国家
$billing_country = $billing->getCountryId();
//账单人州(可不提交)
$billing_state = $billing->getRegionCode();
//账单人城市
$billing_city = $billing->getCity();
//账单人地址
$billing_address = implode(' ', $billing->getStreet());
//账单人邮编
$billing_zip = $billing->getPostcode();
//收货人邮箱
$ship_email = $shipping->getFirstname();
//收货人名
$ship_firstName = $shipping->getFirstname();
//收货人姓
$ship_lastName = $shipping->getLastname();
//收货人手机
$ship_phone = $shipping->getTelephone();
//收货人国家
$ship_country = $shipping->getCountryId();
//收货人州
$ship_state = $shipping->getRegionCode();
//收货人城市
$ship_city = $shipping->getCity();
//收货人地址
$ship_addr = implode(' ', $shipping->getStreet());
//收货人邮编
$ship_zip = $shipping->getPostcode();
//产品名称
$productName = $productDetails['productName'];
//产品SKU
$productSku = $productDetails['productSku'];
//产品数量
$productNum = $productDetails['productNum'];
//产品单价
$productPrice = $productDetails['productPrice'];
//网店程序类型
$cart_info = 'PC';
//接口版本
$cart_api = 'V2.0';
//校验源字符串
$signsrc = $account.$terminal.$backUrl.$order_number.$order_currency.$order_amount.$billing_firstName.$billing_lastName.$billing_email.$securecode;
//sha256加密结果
$signValue = hash("sha256",$signsrc);
//支付页面类型
$pages = $this->isMobile();
$itemList = '{
"0": {
"type": "1",
"title": "'.substr($productName,0,100).'",
"sku": "'.substr($productSku,0,100).'",
"price": "'.($order_amount-$order->getTaxAmount()-$order->getShippingAmount()).'",
"quantity": "1",
"total_amount": "'.($order_amount-$order->getTaxAmount()-$order->getShippingAmount()).'",
"taxRate": "'.round(($order->getTaxAmount() / ($order_amount-$order->getTaxAmount()-$order->getShippingAmount())),2).'",
"taxPrice": "'.round($order->getTaxAmount(),2).'",
"image_url": "",
"product_url": "",
"remark": ""
},
"1": {
"type": "3",
"title": "折扣",
"sku": "'.substr($productSku,0,100).'",
"price": "0",
"quantity": "0",
"total_amount": "0",
"taxRate": "0",
"taxPrice": "0",
"image_url": "",
"product_url": "",
"remark": ""
},
"2": {
"type": "4",
"title": "运费",
"sku": "'.substr($productSku,0,100).'",
"price": "'.round($order->getShippingAmount(),2).'",
"quantity": "1",
"total_amount": "'.round($order->getShippingAmount(),2).'",
"taxRate": "0",
"taxPrice": "'.round($order->getShippingAmount(),2).'",
"image_url": "",
"product_url": "",
"remark": ""
},
"3": {
"type": "5",
"title": "税费",
"sku": "'.substr($productSku,0,100).'",
"price": "'.round($order->getTaxAmount(),2).'",
"quantity": "1",
"total_amount": "'.round($order->getTaxAmount(),2).'",
"taxRate": "0",
"taxPrice": "'.round($order->getTaxAmount(),2).'",
"image_url": "",
"product_url": "",
"remark": ""
}
}';
$parameter = array('account'=>$account,
'terminal'=>$terminal,
'order_number'=>$order_number,
'order_currency'=>$order_currency,
'order_amount'=>$order_amount,
'backUrl'=>$backUrl,
'noticeUrl'=>$noticeUrl,
'order_notes'=>$order_notes,
'itemList'=>$itemList,
// 'methods'=>$methods,
'signValue'=>$signValue,
'billing_firstName'=>$billing_firstName,
'billing_lastName'=>$billing_lastName,
'billing_email'=>$billing_email,
'billing_phone'=>$billing_phone,
'billing_country'=>$billing_country,
'billing_state'=>$billing_state,
'billing_city'=>$billing_city,
'billing_address'=>$billing_address,
'billing_zip'=>$billing_zip,
'ship_firstName'=>$ship_firstName,
'ship_lastName'=>$ship_lastName,
'ship_phone'=>$ship_phone,
'ship_country'=>$ship_country,
'ship_state'=>$ship_state,
'ship_city'=>$ship_city,
'ship_addr'=>$ship_addr,
'ship_zip'=>$ship_zip,
'productName'=>$productName,
'productSku'=>$productSku,
'productNum'=>$productNum,
'productPrice'=>$productPrice,
'cart_info'=>$cart_info,
'cart_api'=>$cart_api,
'pages'=>$pages,
);
// echo "<pre>";
// print_r($parameter);die();
//记录提交日志
$this->postLog(self::POST, $parameter);
return $parameter;
}
public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null)
{
if (parent::isAvailable($quote) && $quote){
return true;
}
return false;
}
/**
* post log
*/
public function postLog($logType, $data){
$filedate = date('Y-m-d');
$newfile = fopen( dirname(dirname(__FILE__)) . "/oceanpayment_log/" . $filedate . ".log", "a+" );
$return_log = date('Y-m-d H:i:s') . $logType . "\r\n";
foreach ($data as $k=>$v){
$return_log .= $k . " = " . $v . "\r\n";
}
$return_log .= '*****************************************' . "\r\n";
$return_log = $return_log.file_get_contents( dirname(dirname(__FILE__)) . "/oceanpayment_log/" . $filedate . ".log");
$filename = fopen( dirname(dirname(__FILE__)) . "/oceanpayment_log/" . $filedate . ".log", "r+" );
fwrite($filename,$return_log);
fclose($filename);
fclose($newfile);
}
/**
* 判断是否手机设备
*/
public function isMobile(){
$useragent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$useragent_commentsblock = preg_match('|\(.*?\)|', $useragent, $matches) > 0 ? $matches[0] : '';
function CheckSubstrs($substrs, $text){
foreach($substrs as $substr){
if(false !== strpos($text, $substr)){
return true;
}
}
return false;
}
$mobile_os_list = array('Google Wireless Transcoder','Windows CE','WindowsCE','Symbian','Android','armv6l','armv5','Mobile','CentOS','mowser','AvantGo','Opera Mobi','J2ME/MIDP','Smartphone','Go.Web','Palm','iPAQ');
$mobile_token_list = array('Profile/MIDP','Configuration/CLDC-','160×160','176×220','240×240','240×320','320×240','UP.Browser','UP.Link','SymbianOS','PalmOS','PocketPC','SonyEricsson','Nokia','BlackBerry','Vodafone','BenQ','Novarra-Vision','Iris','NetFront','HTC_','Xda_','SAMSUNG-SGH','Wapaka','DoCoMo','iPhone','iPod');
$found_mobile = CheckSubstrs($mobile_os_list, $useragent_commentsblock) || CheckSubstrs($mobile_token_list,$useragent);
if ($found_mobile){
return 1; //手机登录
}else{
return 0; //电脑登录
}
}
/**
* 检验是否需要3D验证
*/
public function validate3D($order_currency, $order_amount, $billing, $shipping){
//是否需要3D验证
$is_3d = 0;
//获取3D功能下各个的币种
$currencies_value_str = $this->getConfigData('secure/secure_currency');
$currencies_value = explode(';', $currencies_value_str);
//获取3D功能下各个的金额
$amount_value_str = $this->getConfigData('secure/secure_amount');
$amount_value = explode(';', $amount_value_str);
$amountValidate = array_combine($currencies_value, $amount_value);
if($amountValidate){
//判断金额是否为空
if(isset($amountValidate[$order_currency])){
//判断3D金额不为空
//判断订单金额是否大于3d设定值
if($order_amount >= $amountValidate[$order_currency]){
//需要3D
$is_3d = 1;
}
}else{
//其他币种是否需要3D
if($this->getConfigData('secure/secure_other_currency') == 1){
//需要3D
$is_3d = 1;
}
}
}
//获取3D功能下国家列表
$countries_3d_str = $this->getConfigData('secure/secure_country');
$countries_3d = explode(',', $countries_3d_str);
//账单国
$billing_country = $billing->getCountryId();
//收货国
$ship_country = $shipping->getCountryId();
//判断账单国是否处于3D国家列表
if (in_array($billing_country , $countries_3d)){
$is_3d = 1;
}
//判断收货国是否处于3D国家列表
if (in_array($ship_country , $countries_3d)){
$is_3d = 1;
}
if($is_3d == 0){
$validate_arr['terminal'] = $this->getConfigData('terminal');
$validate_arr['securecode'] = $this->getConfigData('securecode');
}elseif($is_3d == 1){
//3D
$validate_arr['terminal'] = $this->getConfigData('secure/secure_terminal');
$validate_arr['securecode'] = $this->getConfigData('secure/secure_securecode');
}
return $validate_arr;
}
/**
* 获取订单详情
*/
function getProductItems($AllItems){
$productDetails = array();
$productName = array();
$productSku = array();
$productNum = array();
$productPrice = array();
foreach ($AllItems as $item) {
$productName[] = $item->getName();
$productSku[] = $item->getSku();
$productNum[] = number_format($item->getQtyOrdered());
$productPrice[] = sprintf('%.2f', $item->getPrice());
}
$productDetails['productName'] = implode(';', $productName);
$productDetails['productSku'] = implode(';', $productSku);
$productDetails['productNum'] = implode(';', $productNum);
$productDetails['productPrice'] = implode(';', $productPrice);
return $productDetails;
}
/**
* 钱海支付Html特殊字符转义
*/
function OceanHtmlSpecialChars($parameter){
//去除前后空格
$parameter = trim($parameter);
//转义"双引号,<小于号,>大于号,'单引号
$parameter = str_replace(array("<",">","'","\""),array("&lt;","&gt;","&#039;","&quot;"),$parameter);
return $parameter;
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Model\Source;
use Magento\Framework\Option\ArrayInterface;
class OtherCurrency implements ArrayInterface {
/**
* @return array
*/
public function toOptionArray() {
return [
['value' => '1', 'label' => __('3D Secure')],
['value' => '0', 'label' =>__('Sale')]
];
}
}
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Oceanpayment\Klarna\Model\Source;
use Magento\Framework\Option\ArrayInterface;
class PayMode implements ArrayInterface {
/**
* @return array
*/
public function toOptionArray() {
return [
['value' => 'iframe', 'label' => __('Iframe')],
['value' => 'redirect', 'label' =>__('Redirect')]
];
}
}
#Oceanpayment Klarna
Installation
1 - unzip de module in app/code/Oceanpayment/Klarna
2 - enable module: bin/magento module:enable --clear-static-content Oceanpayment_Klarna
3 - upgrade database: bin/magento setup:upgrade
4 - re-run compile command: bin/magento setup:di:compile
In order to deactivate the module bin/magento module:disable --clear-static-content Oceanpayment_Klarna
In order to update static files: bin/magento setup:static-content:deploy
Important: make sure that php path is correct in bin/magento file
{
"name": "oceanpayment/module-klarna",
"description": "Accept payments using Oceanpayment Klarna payment gateway",
"type": "magento2-module",
"version": "1.0.0",
"license": [
"OSL-3.0",
"AFL-3.0"
],
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Oceanpayment\\Klarna\\": ""
}
}
}
<?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="oceanpaymentklarna" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Klarna</label>
<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="3" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Title</label>
</field>
<field id="account" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Account</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="terminal" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Terminal</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="securecode" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0">
<label>SecureCode</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="gateway_url" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Gateway URL</label>
</field>
<field id="pay_mode" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Pay Mode</label>
<source_model>Oceanpayment\Klarna\Model\Source\PayMode</source_model>
</field>
<field id="new_order_status" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0">
<label>New Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="success_order_status" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Approved Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="failure_order_status" translate="label" type="select" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Failure Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="high_risk_order_status" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1" showInStore="0">
<label>High Risk Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="pre_auth_order_status" translate="label" type="select" sortOrder="13" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Pre-auth Order Status</label>
<source_model>Magento\Sales\Model\Config\Source\Order\Status</source_model>
</field>
<field id="invoice" translate="label" type="select" sortOrder="14" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Invoice When Complete</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="allowspecific" translate="label" type="allowspecific" sortOrder="14" 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="16" 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="sort_order" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Sort Order</label>
</field>
</group>
</section>
</system>
</config>
\ No newline at end of file
<?xml version="1.0"?>
<include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd">
<group id="secure" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
<label>3D Secure Mode</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model>
<field id="secure_mode" translate="label" sortOrder="1" type="select" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="secure_terminal" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Terminal</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_securecode" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure SecureCode</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_currency" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Currency</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_amount" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Amount</label>
<can_be_empty>0</can_be_empty>
</field>
<field id="secure_other_currency" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Other Currency</label>
<source_model>Oceanpayment\Klarna\Model\Source\OtherCurrency</source_model>
</field>
<field id="secure_country" translate="label" type="multiselect" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0">
<label>3D Secure Countries</label>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
</field>
</group>
</include>
\ No newline at end of file
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<payment>
<oceanpaymentklarna>
<title>Klarna</title>
<model>Oceanpayment\Klarna\Model\PaymentMethod</model>
<active>0</active>
<new_order_status>pending</new_order_status>
<success_order_status>processing</success_order_status>
<failure_order_status>canceled</failure_order_status>
<pre_auth_order_status>processing</pre_auth_order_status>
<invoice>0</invoice>
<sort_order>0</sort_order>
<allowspecific>0</allowspecific>
</oceanpaymentklarna>
</payment>
</default>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Checkout\Model\CompositeConfigProvider">
<arguments>
<argument name="configProviders" xsi:type="array">
<item name="klarna_config_provider" xsi:type="object">Oceanpayment\Klarna\Model\KlarnaConfigProvider</item>
</argument>
</arguments>
</type>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="oceanpaymentklarna" frontName="oceanpaymentklarna">
<module name="Oceanpayment_Klarna" />
</route>
</router>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © 2016 Oceanpayment Design All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Oceanpayment_Klarna" active="true" schema_version="2.0.2" setup_version="2.0.2">
<sequence>
<module name="Magento_Store"/>
<module name="Magento_Catalog"/>
</sequence>
</module>
</config>
*************************************
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
use \Magento\Framework\Component\ComponentRegistrar;
\Magento\Framework\Component\ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Oceanpayment_Klarna',
__DIR__
);
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Klarna\Block\Info
*/
?>
<?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?>
<?php if ($block->getInfo()->getLastTransId() == ''): ?>
<br /><?php echo __('No transaction ID recorded.') ?>
<?php else: ?>
<br /><?php echo __('Klarna Transaction ID: %1', $this->escapeHtml($block->getInfo()->getLastTransId())) ?>
<?php endif; ?>
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Klarna\Block\Info
*/
?>
<?php echo $this->escapeHtml($block->getMethod()->getTitle()) ?>
{{pdf_row_separator}}
<?php echo __('Klarna Transaction ID: %1', $this->escapeHtml($block->getInfo()->getLastTransId())) ?>
{{pdf_row_separator}}
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Oceanpayment_Klarna::css/styles.css"/>
</head>
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<item name="billing-step" xsi:type="array">
<item name="children" xsi:type="array">
<item name="payment" xsi:type="array">
<item name="children" xsi:type="array">
<item name="renders" xsi:type="array">
<!-- merge payment method renders here -->
<item name="children" xsi:type="array">
<item name="klarna-payments" xsi:type="array">
<item name="component" xsi:type="string">Oceanpayment_Klarna/js/view/payment/klarna-method</item>
<item name="methods" xsi:type="array">
<item name="oceanpaymentklarna" xsi:type="array">
<item name="isBillingAddressRequired" xsi:type="boolean">true</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
\ No newline at end of file
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" layout="1column">
<body>
<referenceContainer name="content">
<block class="Oceanpayment\Klarna\Block\Payment\Redirect" name="klarna.redirect" template="payment/redirect.phtml" cacheable="false"/>
</referenceContainer>
</body>
</page>
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Klarna\Block\Form
*/
?>
<?php $methodCode = $block->escapeHtml($block->getMethodCode());?>
<div class="items <?php /* @noEscape */ echo $methodCode ?> instructions agreement checkout-agreement-item-content" id="payment_form_<?php /* @noEscape */ echo $methodCode ?>" style="display: none;">
<?php /* @noEscape */ echo nl2br($block->escapeHtml($instructions)) ?>
<div style="padding:10px 15px 15px;">
<img src="<?php $block->escapeUrl($block->getViewFileUrl('Oceanpayment_Klarna::icon_accepted_cards_klarna.gif'));?>" alt="<?php echo __('Payment by visa or mastercard'); ?>"><br>
</div>
</div>
\ No newline at end of file
<?php
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/**
* @var $block \Oceanpayment\Klarna\Block\Info
*/
?>
<dl class="payment-method oceanpaymentklarna">
<dt class="title"><?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?></dt>
<dd class="content">
<strong><?php echo $block->escapeHtml(__('You will be redirected to our secure payment page when you place an order.')) ?></strong>
</dd>
</dl>
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
?>
<div>
<?php echo $block->getCheckoutForm(); ?>
<?php if($block->getPayMode() == 'iframe'){?>
<iframe width="100%" height="800px" scrolling="auto" name="iframe_checkout" id="iframe_checkout" style="border:none; margin: 0 auto; overflow:auto;"></iframe>
<script type="text/javascript">
if (window.XMLHttpRequest) {
document.payment_checkout.target="iframe_checkout";
}
document.payment_checkout.submit();
</script>
<?php }elseif($block->getPayMode() == 'redirect'){?>
<p style="font: 16px; padding: 0px; text-align: center; margin: 100px auto 0px; width: 90%;"><?php echo __('Processing, please do not click the refresh button.'); ?></p>
<script type="text/javascript">
document.payment_checkout.submit();
</script>
<?php } ?>
</div>
\ No newline at end of file
.payment-method div.payment_logo_oceanpaymentklarna {
background:url(../images/payment_logo.png) no-repeat;
height:80px;
}
/**
* Copyright © 2016 Oceanpayment Design. All rights reserved.
* See COPYING.txt for license details.
*/
define(
[
'jquery',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/url-builder',
'mage/storage',
'Magento_Checkout/js/model/error-processor',
'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/full-screen-loader'
],
function ($, quote, urlBuilder, storage, errorProcessor, customer, fullScreenLoader) {
'use strict';
return function (messageContainer) {
var serviceUrl,
payload,
paymentData = quote.paymentMethod();
if (Object.prototype.hasOwnProperty.call(paymentData, '__disableTmpl')) { delete paymentData.__disableTmpl; }
/**
* Checkout for guest and registered customer.
*/
if (!customer.isLoggedIn()) {
serviceUrl = urlBuilder.createUrl('/guest-carts/:cartId/payment-information', {
cartId: quote.getQuoteId()
});
payload = {
cartId: quote.getQuoteId(),
email: quote.guestEmail,
paymentMethod: paymentData,
billingAddress: quote.billingAddress()
};
} else {
serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {});
payload = {
cartId: quote.getQuoteId(),
paymentMethod: paymentData,
billingAddress: quote.billingAddress()
};
}
fullScreenLoader.startLoader();
return storage.post(
serviceUrl, JSON.stringify(payload)
).done(
function () {
//$.mage.redirect(window.checkoutConfig.payment.paypalExpress.redirectUrl[quote.paymentMethod().method]);
$.mage.redirect(window.checkoutConfig.payment.klarna.redirectUrl[quote.paymentMethod().method]);
}
).fail(
function (response) {
errorProcessor.process(response, messageContainer);
fullScreenLoader.stopLoader();
}
);
};
}
);
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
/*browser:true*/
/*global define*/
define(
[
'uiComponent',
'Magento_Checkout/js/model/payment/renderer-list'
],
function (
Component,
rendererList
) {
'use strict';
rendererList.push(
{
type: 'oceanpaymentklarna',
component: 'Oceanpayment_Klarna/js/view/payment/method-renderer/klarna-method'
}
);
/** Add view logic here if needed */
return Component.extend({});
}
);
\ No newline at end of file
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
/*browser:true*/
/*global define*/
define(
[
'jquery',
'Magento_Checkout/js/view/payment/default',
'Oceanpayment_Klarna/js/action/set-payment-method',
'Magento_Checkout/js/model/payment/additional-validators'
],
function ($, Component, setPaymentMethodAction) {
'use strict';
return Component.extend({
defaults: {
template: 'Oceanpayment_Klarna/payment/klarna'
},
/** Redirect to klarna */
continueToKlarna: function () {
//update payment method information if additional data was changed
this.selectPaymentMethod();
setPaymentMethodAction(this.messageContainer);
return false;
}
});
}
);
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<div class="payment-method" data-bind="css: {'_active': (getCode() == isChecked())}">
<div class="payment-method-title field choice">
<input type="radio"
name="payment[method]"
class="radio"
data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/>
<label data-bind="attr: {'for': getCode()}" class="label">
<span data-bind="text: getTitle()"></span>
<div alt="Logo" data-bind="attr: {'class': 'payment_logo_' + getCode()}"></div>
</label>
</div>
<div class="payment-method-content">
<!-- ko foreach: getRegion('messages') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
<div class="payment-method-billing-address">
<!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
</div>
<div class="checkout-agreements-block">
<!-- ko foreach: $parent.getRegion('before-place-order') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
</div>
<div class="actions-toolbar">
<div class="primary">
<button class="action primary checkout"
type="submit"
data-bind="click: continueToKlarna, enable: (getCode() == isChecked())"
disabled>
<span data-bind="i18n: 'Place Order'"></span>
</button>
</div>
</div>
</div>
</div>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment