Skip to content

Commit

Permalink
Merge pull request #451 from omise/release-v3.4.0
Browse files Browse the repository at this point in the history
Bump v3.4.0
  • Loading branch information
ajzkk authored Oct 11, 2023
2 parents 3324493 + 183aad5 commit 6e38874
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 17 deletions.
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

0 comments on commit 6e38874

Please sign in to comment.