diff --git a/Gateway/Request/APMBuilder.php b/Gateway/Request/APMBuilder.php index 527cb21f..211ec941 100644 --- a/Gateway/Request/APMBuilder.php +++ b/Gateway/Request/APMBuilder.php @@ -375,16 +375,17 @@ private function getOrderItems($order) $itemArray = []; $items = $order->getItems(); $currency = $order->getCurrencyCode(); - foreach ($items as $itemObject) { - $item = $itemObject->toArray(); + + foreach ($items as $item) { + $price = $item->getPrice(); + if ((float) $price === 0.0) { + continue; + } $itemArray[] = [ - 'sku' => $item['sku'], - 'name' => $item['name'], - 'amount' => $this->money->setAmountAndCurrency( - $item['base_original_price'], - $currency - )->toSubunit(), - 'quantity' => $item['qty_ordered'], + 'sku' => $item->getSku(), + 'name' => $item->getName(), + 'amount' => $this->money->setAmountAndCurrency($price, $currency)->toSubunit(), + 'quantity' => $item->getQtyOrdered(), ]; } return $itemArray; diff --git a/Gateway/Validator/APMRequestValidator.php b/Gateway/Validator/APMRequestValidator.php index 06217b44..35b18d65 100644 --- a/Gateway/Validator/APMRequestValidator.php +++ b/Gateway/Validator/APMRequestValidator.php @@ -20,11 +20,12 @@ public function build(array $buildSubject) $paymentDataObject = SubjectReader::readPayment($buildSubject); $order = $paymentDataObject->getOrder(); $paymentInfo = $paymentDataObject->getPayment(); + $subTotal = $paymentInfo->getOrder()->getSubTotal(); switch ($paymentInfo->getMethod()) { case Atome::CODE: - $this->validateAtomePhoneNumber($order, $paymentInfo); - $this->validateAtomeAmount($order); + $this->validateAtomePhoneNumber($paymentInfo); + $this->validateAtomeAmount($order, $subTotal); break; default: break; @@ -41,7 +42,7 @@ public function build(array $buildSubject) * * @return void */ - private function validateAtomePhoneNumber($order, $info) + private function validateAtomePhoneNumber($info) { $number = $info->getAdditionalInformation(AtomeDataAssignObserver::PHONE_NUMBER); $phonePattern = "/(\+)?([0-9]{10,13})/"; @@ -61,7 +62,7 @@ private function validateAtomePhoneNumber($order, $info) * * @return void */ - private function validateAtomeAmount($order) + private function validateAtomeAmount($order, $subTotal) { $limits = [ 'THB' => [ @@ -81,6 +82,10 @@ private function validateAtomeAmount($order) $currency = strtoupper($order->getCurrencyCode()); $amount = $order->getGrandTotalAmount(); + if ((float) $subTotal === 0.0) { + throw new LocalizedException(__('Complimentary products cannot be billed')); + } + if (!isset($limits[$currency])) { throw new LocalizedException(__('Currency not supported')); } diff --git a/Test/Mock/InfoMock.php b/Test/Mock/InfoMock.php index 4f297213..be96c58f 100644 --- a/Test/Mock/InfoMock.php +++ b/Test/Mock/InfoMock.php @@ -41,4 +41,8 @@ public function getMethod() public function getPayment() { } + + public function getOrder() + { + } } diff --git a/Test/Mock/OrderMock.php b/Test/Mock/OrderMock.php new file mode 100644 index 00000000..f3bca49d --- /dev/null +++ b/Test/Mock/OrderMock.php @@ -0,0 +1,52 @@ +addressMock = $this->getMockBuilder(AddressInterface::class)->getMock(); + $this->addressMock = $this->getMockBuilder(AddressAdapterInterface::class)->getMock(); $this->addressMock->method('getCountryId')->willReturn('TH'); - $this->orderMock = $this->getMockBuilder(OrderAdapterInterface::class) - ->getMockForAbstractClass(); + $this->orderMock = $this->getMockBuilder(OrderMock::class)->getMock(); $this->orderMock->method('getShippingAddress')->willReturn($this->addressMock); $this->infoMock = $this->getMockBuilder(InfoMock::class)->getMock(); $this->infoMock->method('getMethod')->willReturn(Atome::CODE); + $this->infoMock->method('getOrder')->willReturn($this->orderMock); - $this->paymentDataObject = new PaymentDataObject( - $this->orderMock, - $this->infoMock - ); + $this->paymentDataObjectMock = $this->getMockBuilder(PaymentDataObjectInterface::class)->getMock(); + $this->paymentDataObjectMock->method('getOrder')->willReturn($this->orderMock); + $this->paymentDataObjectMock->method('getPayment')->willReturn($this->infoMock); $this->model = new APMRequestValidator(); } + /** + * @covers Omise\Payment\Gateway\Validator\APMRequestValidator + */ + public function testComplimentaryProducts() + { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('Complimentary products cannot be billed'); + $this->orderMock->method('getSubTotal')->willReturn(0.0); + $this->orderMock->method('getCurrencyCode')->willReturn("THB"); + $this->infoMock->method('getAdditionalInformation')->willReturn('0987654321'); + $this->model->build(['payment' => $this->paymentDataObjectMock]); + } + /** * @covers Omise\Payment\Gateway\Validator\APMRequestValidator */ @@ -51,11 +63,9 @@ public function testCurrencyNotSupported() $this->expectException(LocalizedException::class); $this->expectExceptionMessage('Currency not supported'); $this->orderMock->method('getCurrencyCode')->willReturn("USD"); - $this->orderMock->method('getGrandTotalAmount')->willReturn(100); + $this->orderMock->method('getSubTotal')->willReturn(100); $this->infoMock->method('getAdditionalInformation')->willReturn('0987654321'); - $this->model->build([ - 'payment' => $this->paymentDataObject, - ]); + $this->model->build(['payment' => $this->paymentDataObjectMock]); } /** @@ -67,10 +77,9 @@ public function testMinAmountShouldThrowError() $this->expectExceptionMessage('Amount must be greater than 20.00 THB'); $this->orderMock->method('getCurrencyCode')->willReturn("THB"); $this->orderMock->method('getGrandTotalAmount')->willReturn(10); + $this->orderMock->method('getSubTotal')->willReturn(10); $this->infoMock->method('getAdditionalInformation')->willReturn('0987654321'); - $this->model->build([ - 'payment' => $this->paymentDataObject, - ]); + $this->model->build(['payment' => $this->paymentDataObjectMock]); } /** @@ -81,10 +90,9 @@ public function testValidAmountShouldNotThrowError() $this->expectNotToPerformAssertions(); $this->orderMock->method('getCurrencyCode')->willReturn("THB"); $this->orderMock->method('getGrandTotalAmount')->willReturn(20); + $this->orderMock->method('getSubTotal')->willReturn(20); $this->infoMock->method('getAdditionalInformation')->willReturn('0987654321'); - $this->model->build([ - 'payment' => $this->paymentDataObject, - ]); + $this->model->build(['payment' => $this->paymentDataObjectMock]); } /** @@ -96,10 +104,9 @@ public function testMaxAmountShouldThrowError() $this->expectExceptionMessage('Amount must be less than 150,000.00 THB'); $this->orderMock->method('getCurrencyCode')->willReturn("THB"); $this->orderMock->method('getGrandTotalAmount')->willReturn(200000); + $this->orderMock->method('getSubTotal')->willReturn(200000); $this->infoMock->method('getAdditionalInformation')->willReturn('0987654321'); - $this->model->build([ - 'payment' => $this->paymentDataObject, - ]); + $this->model->build(['payment' => $this->paymentDataObjectMock]); } /** @@ -112,9 +119,8 @@ public function testInvalidAtomePhoneNumberValidation() $this->infoMock->method('getAdditionalInformation')->willReturn('0987'); $this->orderMock->method('getCurrencyCode')->willReturn("THB"); $this->orderMock->method('getGrandTotalAmount')->willReturn(100); - $this->model->build([ - 'payment' => $this->paymentDataObject, - ]); + $this->orderMock->method('getSubTotal')->willReturn(100); + $this->model->build(['payment' => $this->paymentDataObjectMock]); } /** @@ -126,8 +132,7 @@ public function testValidAtomePhoneNumberValidation() $this->infoMock->method('getAdditionalInformation')->willReturn('+66987654321'); $this->orderMock->method('getCurrencyCode')->willReturn("THB"); $this->orderMock->method('getGrandTotalAmount')->willReturn(100); - $this->model->build([ - 'payment' => $this->paymentDataObject, - ]); + $this->orderMock->method('getSubTotal')->willReturn(100); + $this->model->build(['payment' => $this->paymentDataObjectMock]); } } diff --git a/Test/Unit/AtomeAPMBuilderTest.php b/Test/Unit/AtomeAPMBuilderTest.php new file mode 100644 index 00000000..cac89952 --- /dev/null +++ b/Test/Unit/AtomeAPMBuilderTest.php @@ -0,0 +1,117 @@ +itemMock = $this->getMockBuilder(OrderItemInterface::class)->getMock(); + $this->addressMock = $this->getMockBuilder(AddressAdapterInterface::class)->getMock(); + $this->helper = $this->getMockBuilder(OmiseHelper::class)->disableOriginalConstructor()->getMock(); + $this->returnUrlHelper = $this->getMockBuilder(ReturnUrlHelper::class)->disableOriginalConstructor()->getMock(); + $this->config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); + $this->capabilities = $this->getMockBuilder(Capabilities::class)->disableOriginalConstructor()->getMock(); + $this->orderMock = $this->getMockBuilder(OrderAdapterInterface::class)->getMock(); + $this->orderMock->method('getShippingAddress')->willReturn($this->addressMock); + $this->orderMock->method('getItems')->willReturn([$this->itemMock]); + $this->orderMock->method('getCurrencyCode')->willReturn('THB'); + $this->infoMock = $this->getMockBuilder(InfoMock::class)->getMock(); + } + + /** + * @covers Omise\Payment\Gateway\Request\APMBuilder + * @covers Omise\Payment\Model\Config\Atome + */ + public function testApmBuilderWithItemPriceZero() + { + $this->itemMock->method('getPrice')->willReturn(0.0); + $this->infoMock->method('getMethod')->willReturn(Atome::CODE); + $this->returnUrlHelper->method('create')->willReturn([ + 'url' => 'https://omise.co/complete', + 'token' => '1234' + ]); + + $this->builder = new APMBuilder( + $this->helper, + $this->returnUrlHelper, + $this->config, + $this->capabilities, + new OmiseMoney(), + ); + + $result = $this->builder->build(['payment' => new PaymentDataObject( + $this->orderMock, + $this->infoMock + )]); + + $this->assertEquals(0, count($result['source']['items'])); + $this->assertEquals('atome', $result['source']['type']); + $this->assertEquals('https://omise.co/complete', $result['return_uri']); + } + + /** + * @covers Omise\Payment\Gateway\Request\APMBuilder + * @covers Omise\Payment\Model\Config\Atome + * @covers Omise\Payment\Helper\OmiseMoney + */ + public function testApmBuilderWithItemPriceGreaterThanZero() + { + $this->itemMock->method('getPrice')->willReturn(100.0); + $this->infoMock->method('getMethod')->willReturn(Atome::CODE); + $this->returnUrlHelper->method('create')->willReturn([ + 'url' => 'https://omise.co/complete', + 'token' => '1234' + ]); + + $this->builder = new APMBuilder( + $this->helper, + $this->returnUrlHelper, + $this->config, + $this->capabilities, + new OmiseMoney(), + ); + + $result = $this->builder->build(['payment' => new PaymentDataObject( + $this->orderMock, + $this->infoMock + )]); + + $this->assertEquals(1, count($result['source']['items'])); + $this->assertEquals('atome', $result['source']['type']); + $this->assertEquals('https://omise.co/complete', $result['return_uri']); + } + + /** + * @covers Omise\Payment\Model\Config\Atome + */ + public function testConstants() + { + $this->assertEquals('omise_offsite_atome', Atome::CODE); + $this->assertEquals('atome', Atome::ID); + } +} diff --git a/i18n/ja_JP.csv b/i18n/ja_JP.csv index bc607d6d..525a5e4c 100644 --- a/i18n/ja_JP.csv +++ b/i18n/ja_JP.csv @@ -14,3 +14,7 @@ "Card stolen or lost.", "このカードは紛失/盗難が報告されています" "Payer did not take action before charge expiration.", "有効期限前に操作がありませんでした。" "secure_form_banner_message", "Opn Payments : プラグインを最新バージョンにアップデートし、顧客情報の安全な管理に必要なSecure Formを有効にしてください。アップデート後、クレジットカードの決済フォームを再度カスタマイズする必要があります。Secure Formを有効にする方法はこちらをご確認ください。" +"Complimentary products cannot be billed", "無償提供商品は課金の対象外です" +"Currency not supported", "サポートされていない通貨です", +"Amount must be greater than %1 %2", "金額は %1 %2を超える必要があります" +"Amount must be less than %1 %2", "金額は %1 %2を下回る必要があります" diff --git a/i18n/th_TH.csv b/i18n/th_TH.csv index 7ef2562a..513831dc 100644 --- a/i18n/th_TH.csv +++ b/i18n/th_TH.csv @@ -37,3 +37,7 @@ "True Money Phone Number", "เบอร์โทรศัพท์ที่ใช้สมัครบัญชี TrueMoney Wallet" "Use a new card", "ใช้บัตรใหม่" "secure_form_banner_message", "Opn Payments : อัปเดตปลั๊กอินของคุณเป็นเวอร์ชันล่าสุดเพื่อเปิดใช้งาน Secure Form และมอบความปลอดภัยสูงสุดให้กับข้อมูลของลูกค้า โดยหลังจากทำการอัปเกรด คุณจะต้องปรับแต่งแบบฟอร์มการชำระเงินด้วยบัตรเครดิตใหม่อีกครั้ง เรียนรู้เพิ่มเติมเกี่ยวกับการเปิดใช้งาน Secure Form" +"Complimentary products cannot be billed", "สินค้าที่ระลึกไม่สามารถเรียกเก็บเงินได้" +"Currency not supported", "ระบบไม่รองรับสกุลเงินนี้", +"Amount must be greater than %1 %2", "จำนวนเงินจะต้องมากกว่า %1 %2" +"Amount must be less than %1 %2", "จำนวนเงินจะต้องไม่เกิน %1 %2" diff --git a/view/frontend/web/images/atome.png b/view/frontend/web/images/atome.png new file mode 100644 index 00000000..cc20d19c Binary files /dev/null and b/view/frontend/web/images/atome.png differ diff --git a/view/frontend/web/images/touchngo.png b/view/frontend/web/images/touchngo.png index f8306d56..b15a1cec 100644 Binary files a/view/frontend/web/images/touchngo.png and b/view/frontend/web/images/touchngo.png differ diff --git a/view/frontend/web/js/view/payment/method-renderer/omise-offsite-atome-method.js b/view/frontend/web/js/view/payment/method-renderer/omise-offsite-atome-method.js index 02e4e418..77ed88cc 100644 --- a/view/frontend/web/js/view/payment/method-renderer/omise-offsite-atome-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/omise-offsite-atome-method.js @@ -22,6 +22,12 @@ define( code: 'omise_offsite_atome', restrictedToCurrencies: ['thb', 'sgd', 'myr'], + logo: { + file: "images/atome.png", + width: "30", + height: "30", + name: "atome" + }, /** * Initiate observable fields diff --git a/view/frontend/web/js/view/payment/method-renderer/omise-offsite-touchngo-method.js b/view/frontend/web/js/view/payment/method-renderer/omise-offsite-touchngo-method.js index 30ed9a89..6a5db0cf 100644 --- a/view/frontend/web/js/view/payment/method-renderer/omise-offsite-touchngo-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/omise-offsite-touchngo-method.js @@ -24,7 +24,7 @@ define( restrictedToCurrencies: ['sgd', 'myr'], logo: { file: "images/touchngo.png", - width: "80", + width: "30", height: "30", name: "touchngo" }, diff --git a/view/frontend/web/template/payment/offsite-atome-form.html b/view/frontend/web/template/payment/offsite-atome-form.html index 69eaec0b..bb00cd15 100644 --- a/view/frontend/web/template/payment/offsite-atome-form.html +++ b/view/frontend/web/template/payment/offsite-atome-form.html @@ -9,8 +9,12 @@ click: selectPaymentMethod, visible: isRadioButtonVisible(), enable: isActive()" /> -