Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump v3.4.0 #451

Merged
merged 3 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# CHANGELOG

## [v3.4.0 _(Oct, 11, 2023)_](https://github.com/omise/omise-magento/releases/tag/v3.4.0)
- Added dynamic webhooks with feature flag. (PR: [#450](https://github.com/omise/omise-magento/pull/450))

## [v3.3.1 _(Oct, 03, 2023)_](https://github.com/omise/omise-magento/releases/tag/v3.3.1)
- Added Promptpay QR payment instructions. (PR: [#447](https://github.com/omise/omise-magento/pull/447))
- Bug fixed on Alipay. (PR: [#446](https://github.com/omise/omise-magento/pull/446))
Expand Down
24 changes: 15 additions & 9 deletions Gateway/Request/PaymentDataBuilder.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<?php

namespace Omise\Payment\Gateway\Request;

use Magento\Payment\Gateway\Helper\SubjectReader;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Omise\Payment\Helper\OmiseHelper;
use Omise\Payment\Helper\OmiseMoney;
use Omise\Payment\Observer\InstallmentDataAssignObserver;
use Omise\Payment\Model\Config\Installment;
use Omise\Payment\Model\Config\Cc;
use Omise\Payment\Model\Config\Config;
use Omise\Payment\Block\Adminhtml\System\Config\Form\Field\Webhook;

class PaymentDataBuilder implements BuilderInterface
{
Expand All @@ -20,7 +22,7 @@ class PaymentDataBuilder implements BuilderInterface
* @var string
*/
const CURRENCY = 'currency';

/**
* @var string
*/
Expand All @@ -37,15 +39,15 @@ class PaymentDataBuilder implements BuilderInterface
const ZERO_INTEREST_INSTALLMENTS = 'zero_interest_installments';

/**
* @var \Omise\Payment\Helper\OmiseHelper
* @var string
*/
private $omiseHelper;
const WEBHOOKS_ENDPOINT = 'webhook_endpoints';

/**
* @var \Omise\Payment\Model\Config\Cc
*/
private $ccConfig;

/**
* @var OmiseMoney
*/
Expand All @@ -55,9 +57,8 @@ class PaymentDataBuilder implements BuilderInterface
* @param \Omise\Payment\Helper\OmiseHelper $omiseHelper
* @param Omise\Payment\Model\Config\Cc $ccConfig
*/
public function __construct(OmiseHelper $omiseHelper, Cc $ccConfig, OmiseMoney $money)
public function __construct(Cc $ccConfig, OmiseMoney $money)
{
$this->omiseHelper = $omiseHelper;
$this->money = $money;
$this->ccConfig = $ccConfig;
}
Expand All @@ -76,7 +77,7 @@ public function build(array $buildSubject)
$store_id = $order->getStoreId();
$om = \Magento\Framework\App\ObjectManager::getInstance();
$manager = $om->get(\Magento\Store\Model\StoreManagerInterface::class);
$store_name = $manager->getStore($store_id)->getName();
$store = $manager->getStore($store_id);
$currency = $order->getCurrencyCode();

$requestBody = [
Expand All @@ -89,10 +90,15 @@ public function build(array $buildSubject)
self::METADATA => [
'order_id' => $order->getOrderIncrementId(),
'store_id' => $order->getStoreId(),
'store_name' => $store_name
'store_name' => $store->getName()
]
];

if ($this->ccConfig->isDynamicWebhooksEnabled()) {
$webhookUrl = $store->getBaseUrl() . Webhook::URI;
$requestBody[self::WEBHOOKS_ENDPOINT] = [$webhookUrl];
}

if (Installment::CODE === $method->getMethod()) {
$requestBody[self::ZERO_INTEREST_INSTALLMENTS] = $this->isZeroInterestInstallment($method);
}
Expand Down
10 changes: 10 additions & 0 deletions Model/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ public function isWebhookEnabled()
return $this->getValue('webhook_status');
}

/**
* Check if using dynamic webhooks or not
*
* @return bool
*/
public function isDynamicWebhooksEnabled()
{
return $this->isWebhookEnabled() && $this->getValue('dynamic_webhooks');
}

/**
* Retrieve the order status in which to generate invoice at
*
Expand Down
91 changes: 91 additions & 0 deletions Test/Unit/ConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

namespace Omise\Payment\Test\Unit;

use Mockery as m;
use PHPUnit\Framework\TestCase;
use Omise\Payment\Model\Config\Config;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;

class ConfigTest extends TestCase
{
private $storeManagerMock;
private $scopeConfigMock;
private $storeMock;

protected function setUp(): void
{
$this->scopeConfigMock = m::mock(ScopeConfigInterface::class);
$this->storeManagerMock = m::mock(StoreManagerInterface::class);
$this->storeMock = m::mock(StoreInterface::class);
}

/**
* @dataProvider isDynamicWebhooksEnabledProvider
* @covers Omise\Payment\Model\Config\Config
*/
public function testIsDynamicWebhooksEnabled($webhookEnabled, $dynamicWebhooksEnabled, $expected)
{
$this->scopeConfigMock->shouldReceive('getValue')
->with('general/locale/code', m::any(), m::any())
->andReturn('en');

$this->scopeConfigMock->shouldReceive('getValue')
->with('payment/omise/sandbox_status', m::any(), m::any())
->andReturn(1);

$this->scopeConfigMock->shouldReceive('getValue')
->with('payment/omise/test_public_key', m::any(), m::any())
->andReturn('pkey_test_xx');

$this->scopeConfigMock->shouldReceive('getValue')
->with('payment/omise/test_secret_key', m::any(), m::any())
->andReturn('pkey_test_xx');

$this->scopeConfigMock->shouldReceive('getValue')
->with('payment/omise/dynamic_webhooks', m::any(), m::any())
->andReturn($dynamicWebhooksEnabled);

$this->scopeConfigMock->shouldReceive('getValue')
->with('payment/omise/webhook_status', m::any(), m::any())
->andReturn($webhookEnabled);

$this->storeMock->shouldReceive('getId')->andReturn(1);
$this->storeManagerMock->shouldReceive('getStore')->andReturn($this->storeMock);

$config = new Config($this->scopeConfigMock, $this->storeManagerMock);
$result = $config->isDynamicWebhooksEnabled();
$this->assertEquals($result, $expected);
}

/**
* Data provider for testIsDynamicWebhooksEnabled method
*/
public function isDynamicWebhooksEnabledProvider()
{
return [
[
'webhookEnabled' => 1,
'dynamicWebhooksEnabled' => 1,
'expected' => true,
],
[
'webhookEnabled' => 1,
'dynamicWebhooksEnabled' => 1,
'expected' => true,
],
[
'webhookEnabled' => 0,
'dynamicWebhooksEnabled' => 1,
'expected' => false,
],
[
'webhookEnabled' => 0,
'dynamicWebhooksEnabled' => 0,
'expected' => false,
],
];
}
}
148 changes: 148 additions & 0 deletions Test/Unit/PaymentDataBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php

namespace Omise\Payment\Test\Unit;

use Magento\Framework\App\ObjectManager;
use Magento\Payment\Gateway\Data\PaymentDataObjectInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Mockery as m;
use PHPUnit\Framework\TestCase;
use Omise\Payment\Gateway\Request\PaymentDataBuilder;
use Omise\Payment\Helper\OmiseHelper;
use Omise\Payment\Helper\OmiseMoney;
use Omise\Payment\Model\Config\Cc;
use Magento\Framework\ObjectManager\ConfigInterface;
use Magento\Framework\ObjectManager\FactoryInterface;
use Magento\Sales\Api\Data\OrderPaymentInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\Api\Data\StoreInterface;
use Omise\Payment\Model\Config\Installment;
use Omise\Payment\Model\Config\Promptpay;

class PaymentDataBuilderTest extends TestCase
{
private $omiseMoneyMock;
private $ccConfigMock;
private $paymentDataMock;
private $paymentMock;
private $orderMock;
private $factoryMock;
private $configMock;
private $storeManagerMock;
private $storeMock;

protected function setUp(): void
{
$this->factoryMock = m::mock(FactoryInterface::class);
$this->configMock = m::mock(ConfigInterface::class);
$this->omiseMoneyMock = m::mock(OmiseMoney::class);
$this->ccConfigMock = m::mock(Cc::class);
$this->paymentMock = m::mock(OrderPaymentInterface::class);
$this->paymentDataMock = m::mock(PaymentDataObjectInterface::class);
$this->orderMock = m::mock(OrderInterface::class);
$this->storeManagerMock = m::mock(StoreManagerInterface::class);
$this->storeMock = m::mock(StoreInterface::class);
}

/**
* @covers Omise\Payment\Gateway\Request\PaymentDataBuilder
*/
public function testConstants()
{
$this->assertEquals('webhook_endpoints', PaymentDataBuilder::WEBHOOKS_ENDPOINT);
$this->assertEquals('amount', PaymentDataBuilder::AMOUNT);
$this->assertEquals('currency', PaymentDataBuilder::CURRENCY);
$this->assertEquals('description', PaymentDataBuilder::DESCRIPTION);
$this->assertEquals('metadata', PaymentDataBuilder::METADATA);
$this->assertEquals('zero_interest_installments', PaymentDataBuilder::ZERO_INTEREST_INSTALLMENTS);
}

/**
* @dataProvider buildDataProvider
* @covers Omise\Payment\Gateway\Request\PaymentDataBuilder
*/
public function testBuild($paymentMethod, $expectedMetadata)
{
$amount = 1000;
$currency = 'THB';
$orderId = 123;
$storeId = 1;
$storeName = 'opn-store';
$storeBaseUrl = 'https://omise.co/';
$secureFormEnabled = true;

new ObjectManager($this->factoryMock, $this->configMock);

$this->paymentMock->shouldReceive('getMethod')->andReturn($paymentMethod);
$this->paymentMock->shouldReceive('getAdditionalInformation')->andReturn('installment_mbb');

$this->ccConfigMock->shouldReceive('getSecureForm')->andReturn($secureFormEnabled);
$this->ccConfigMock->shouldReceive('isDynamicWebhooksEnabled')->andReturn(true);

$this->omiseMoneyMock->shouldReceive('setAmountAndCurrency')->andReturn($this->omiseMoneyMock);
$this->omiseMoneyMock->shouldReceive('toSubunit')->andReturn($amount * 100);

$this->storeMock->shouldReceive('getName')->andReturn($storeName);
$this->storeMock->shouldReceive('getBaseUrl')->andReturn($storeBaseUrl);

$this->storeManagerMock->shouldReceive('getStore')->andReturn($this->storeMock);
$this->configMock->shouldReceive('getPreference');
$this->factoryMock->shouldReceive('create')->andReturn($this->storeManagerMock);

$this->orderMock->shouldReceive('getCurrencyCode')->andReturn($currency);
$this->orderMock->shouldReceive('getGrandTotalAmount')->andReturn($amount);
$this->orderMock->shouldReceive('getOrderIncrementId')->andReturn($orderId);
$this->orderMock->shouldReceive('getStoreId')->andReturn($storeId);

$this->paymentDataMock->shouldReceive('getOrder')->andReturn($this->orderMock);
$this->paymentDataMock->shouldReceive('getPayment')->andReturn($this->paymentMock);

$model = new PaymentDataBuilder($this->ccConfigMock, $this->omiseMoneyMock);
$result = $model->build(['payment' => $this->paymentDataMock]);

$this->assertEquals(100000, $result['amount']);
$this->assertEquals('THB', $result['currency']);
$this->assertEquals([
'https://omise.co/omise/callback/webhook'
], $result['webhook_endpoints']);
$this->assertEquals($expectedMetadata, $result['metadata']);

if ($paymentMethod === Installment::CODE) {
$this->assertEquals(true, $result['zero_interest_installments']);
}
}

/**
* Data provider for testBuild method
*/
public function buildDataProvider()
{
return [
[
'paymentMethod' => Cc::CODE,
'expectedMetadata' => [
'order_id' => 123,
'store_id' => 1,
'store_name' => 'opn-store',
'secure_form_enabled' => true
],
],
[
'paymentMethod' => Installment::CODE,
'expectedMetadata' => [
'order_id' => 123,
'store_id' => 1,
'store_name' => 'opn-store',
],
],
[
'paymentMethod' => Promptpay::CODE,
'expectedMetadata' => [
'order_id' => 123,
'store_id' => 1,
'store_name' => 'opn-store',
],
],
];
}
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"email": "[email protected]"
}
],
"version": "3.3.1",
"version": "3.4.0",
"minimum-stability": "stable",
"type": "magento2-module",
"license": "MIT",
Expand Down
23 changes: 17 additions & 6 deletions etc/adminhtml/system.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,30 @@
<comment>The "Live" mode secret key can be found in Opn Payments Dashboard.</comment>
</field>
<field id="webhook_status" translate="label comment" type="select" sortOrder="61" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Webhook</label>
<label>Enable webhooks</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<comment>Use webhook.</comment>
<comment>
<![CDATA[Enable webhooks to receive updates when charge and refund events occur. To learn more, see <a href="https://docs.opn.ooo/api-webhooks">our webhooks documentation</a>. </br></br>
Unless dynamic webhooks are enabled, you must add the URL below as a new endpoint on your <a href="https://dashboard.omise.co/v2/settings/webhooks">Opn Payments dashboard</a> (HTTPS only).
]]>
</comment>
</field>
<field id="webhook" translate="label" type="label" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Webhook endpoint</label>
<field id="dynamic_webhooks" translate="label comment" type="select" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enable dynamic webhooks</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<depends>
<field id="webhook_status">1</field>
</depends>
<comment>
<![CDATA[To enable <a href="https://www.omise.co/api-webhooks">WebHooks</a> feature, you must copy the above url to setup an endpoint at <a href="https://dashboard.omise.co/test/webhooks/edit"><strong>Opn Payments dashboard</strong></a> <em>(HTTPS only)</em>.]]>
<![CDATA[If enabled, charge and refund events will be automatically set to be received at the URL below. This can be useful when you need multiple webhook endpoints on the same account.]]>
</comment>
</field>
<field id="webhook" translate="label" type="label" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Webhook endpoint</label>
<frontend_model>Omise\Payment\Block\Adminhtml\System\Config\Form\Field\Webhook</frontend_model>
</field>
<field id="enable_cron_autoexpirysync" translate="label comment" type="select" sortOrder="71" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enable Cron Autosync Order Status</label>
<label>Enable cron autosync order status</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<comment>Enabling cron allows Magento to check orders with expired charge status and mark them as canceled.</comment>
</field>
Expand Down
Loading
Loading