From f744b816b612bf6ddfd9d3e910fe79105ca4fbaa Mon Sep 17 00:00:00 2001 From: thisgun Date: Tue, 7 Jul 2026 16:38:32 +0900 Subject: [PATCH] =?UTF-8?q?fix(nhnkcp):=20=EC=A3=BC=EB=AC=B8=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EA=B2=BD=EA=B3=A0=20=ED=91=9C=EC=8B=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CHANGELOG.md | 1 + .../frontend/ja.json | 5 + .../_bundled/sirsoft-pay_nhnkcp/CHANGELOG.md | 1 + .../sirsoft-pay_nhnkcp/resources/lang/en.json | 5 + .../sirsoft-pay_nhnkcp/resources/lang/ko.json | 5 + .../AdminSettingsStatusController.php | 28 +++ ...tEcommercePaymentMethodsLayoutListener.php | 231 +++++++++++++++++- .../sirsoft-pay_nhnkcp/src/routes/api.php | 5 + .../AdminSettingsStatusControllerTest.php | 33 +++ ...mmercePaymentMethodsLayoutListenerTest.php | 39 +++ 10 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 plugins/_bundled/sirsoft-pay_nhnkcp/src/Controllers/AdminSettingsStatusController.php create mode 100644 plugins/_bundled/sirsoft-pay_nhnkcp/tests/Feature/Controllers/AdminSettingsStatusControllerTest.php diff --git a/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/CHANGELOG.md b/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/CHANGELOG.md index 4c3f5168..5d8e4a68 100644 --- a/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/CHANGELOG.md +++ b/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/CHANGELOG.md @@ -9,3 +9,4 @@ ### Added - 환불 진행 중 안내(`refund.in_progress`)와 KCP CLI 바이너리 누락·실행 권한 부족 안내(`errors.cli_binary_missing`, `errors.cli_binary_not_executable`) 일본어 번역 추가 — 환불 중복 요청·CLI 실행 환경 오류 안내가 일본어 로케일에서 자연스럽게 표시됩니다. +- 주문 설정 화면의 NHN KCP 테스트모드 경고(`nhnkcp_test_mode_status`, `test_mode_settings_warning_*`) 일본어 번역 추가 — 테스트결제 상태 안내와 실결제 설정 이동 버튼이 일본어 로케일에서 자연스럽게 표시됩니다. diff --git a/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/frontend/ja.json b/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/frontend/ja.json index e3311728..52e22b53 100644 --- a/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/frontend/ja.json +++ b/lang-packs/_bundled/g7-plugin-sirsoft-pay_nhnkcp-ja/frontend/ja.json @@ -5,6 +5,7 @@ "kcp_escrow_delivery": "エスクロー配送", "kcp_status": "決済状態", "nhnkcp_easy_pay_config": "簡便決済設定", + "nhnkcp_test_mode_status": "テストモード状態", "nhnkcp_test_map": "テスト決済マップ", "nicepay_test_map": "テスト決済マップ", "settings": "決済設定", @@ -48,6 +49,10 @@ "admin": { "test_mode_badge": "テスト決済", "test_mode_order_notice": "この注文はテスト決済として処理されました。", + "test_mode_settings_warning_plugin": "NHN KCP", + "test_mode_settings_warning_title": "(注意)NHN KCP決済設定が現在テスト決済になっています。", + "test_mode_settings_warning_body": "テスト決済では実際の決済は発生しません。ショップ運営中の場合は必ず実決済に設定して運営してください。", + "test_mode_settings_warning_action": "実決済に設定", "order_info_title": "NHN KCP決済情報", "order_info_description": "DBに保存されたKCP決済情報を表示します。", "pay_status_approved": "承認", diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/CHANGELOG.md b/plugins/_bundled/sirsoft-pay_nhnkcp/CHANGELOG.md index 16a3ab1c..9931ac07 100644 --- a/plugins/_bundled/sirsoft-pay_nhnkcp/CHANGELOG.md +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/CHANGELOG.md @@ -12,6 +12,7 @@ - 관리자 주문상세의 NHN KCP 결제 정보 패널이 원승인 정보와 현재 취소/환불 상태를 함께 표시하도록 보강했습니다. - PC 결제창 close-report와 승인 콜백이 경쟁할 때 결제 성공 주문이 실패 처리로 덮이지 않도록 주문번호 단위 콜백 락과 최신 결제 상태 가드를 적용했습니다. - NHN KCP 부분취소 요청에서 `rem_mny`를 취소 후 잔액이 아닌 현재 부분취소 가능 금액으로 전달해 큰 금액 부분취소가 거절될 수 있던 문제를 수정했습니다. +- 이커머스 주문설정 화면에서 NHN KCP 플러그인이 테스트 모드일 때 운영 전환 경고와 설정 이동 버튼을 표시하도록 보강했습니다. ## [1.0.0-beta.5] - 2026-06-23 diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/en.json b/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/en.json index a61e9d6d..8493b708 100644 --- a/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/en.json +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/en.json @@ -4,6 +4,7 @@ "health": "Health", "kcp_escrow_delivery": "Escrow Delivery", "kcp_status": "Payment Status", + "nhnkcp_test_mode_status": "Test Mode Status", "nhnkcp_easy_pay_config": "Easy Pay Config", "nhnkcp_easy_pay_display_map": "Easy Pay Display Map", "nhnkcp_test_map": "Test Payment Map", @@ -49,6 +50,10 @@ "admin": { "test_mode_badge": "Test Payment", "test_mode_order_notice": "This order was processed as a test payment.", + "test_mode_settings_warning_plugin": "NHN KCP", + "test_mode_settings_warning_title": "Warning: NHN KCP payments are currently set to test mode.", + "test_mode_settings_warning_body": "Test payments do not create real payments. If the shop is live, switch this plugin to live payment mode before operating.", + "test_mode_settings_warning_action": "Switch to live mode", "order_info_title": "NHN KCP Payment Info", "order_info_description": "Displays stored KCP original approval and current payment status.", "pay_status_approved": "Approved", diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/ko.json b/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/ko.json index 3a69bed3..2fbe7a66 100644 --- a/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/ko.json +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/resources/lang/ko.json @@ -4,6 +4,7 @@ "health": "연동 상태", "kcp_escrow_delivery": "에스크로 배송", "kcp_status": "결제 상태", + "nhnkcp_test_mode_status": "테스트 모드 상태", "nhnkcp_easy_pay_config": "간편결제 설정", "nhnkcp_easy_pay_display_map": "간편결제 표시 맵", "nhnkcp_test_map": "테스트 결제 맵", @@ -49,6 +50,10 @@ "admin": { "test_mode_badge": "테스트 결제", "test_mode_order_notice": "이 주문은 테스트 결제로 처리되었습니다.", + "test_mode_settings_warning_plugin": "NHN KCP", + "test_mode_settings_warning_title": "(주의!) NHN KCP 결제 설정이 현재 테스트결제로 되어 있습니다.", + "test_mode_settings_warning_body": "테스트결제 시 실제 결제가 되지 않으므로, 쇼핑몰 운영 중이면 반드시 실결제로 설정하여 운영하세요.", + "test_mode_settings_warning_action": "실결제로 설정", "order_info_title": "NHN KCP 결제 정보", "order_info_description": "DB에 저장된 KCP 원승인 및 현재 결제 상태를 표시합니다.", "pay_status_approved": "승인", diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/src/Controllers/AdminSettingsStatusController.php b/plugins/_bundled/sirsoft-pay_nhnkcp/src/Controllers/AdminSettingsStatusController.php new file mode 100644 index 00000000..5bf03d56 --- /dev/null +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/src/Controllers/AdminSettingsStatusController.php @@ -0,0 +1,28 @@ + (bool) $this->settingsService->get(self::PLUGIN_IDENTIFIER, 'is_test_mode', true), + ]); + } +} diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/src/Listeners/AdjustEcommercePaymentMethodsLayoutListener.php b/plugins/_bundled/sirsoft-pay_nhnkcp/src/Listeners/AdjustEcommercePaymentMethodsLayoutListener.php index b050d09a..ca173498 100644 --- a/plugins/_bundled/sirsoft-pay_nhnkcp/src/Listeners/AdjustEcommercePaymentMethodsLayoutListener.php +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/src/Listeners/AdjustEcommercePaymentMethodsLayoutListener.php @@ -13,6 +13,14 @@ class AdjustEcommercePaymentMethodsLayoutListener implements HookListenerInterfa { private const TARGET_LAYOUT = 'admin_ecommerce_settings'; + private const TEST_MODE_DATA_SOURCE_ID = 'nhnkcp_test_mode_status'; + + private const TEST_MODE_NOTICE_ID = 'payment_test_mode_order_settings_notice'; + + private const TEST_MODE_NOTICE_ROW_ID = 'nhnkcp_test_mode_order_settings_notice'; + + private const TEST_MODE_CONDITION = 'nhnkcp_test_mode_status.data?.is_test_mode === true'; + private const CORE_NO_PG_METHODS = ['point', 'deposit', 'free', 'dbank']; private const NHNKCP_NO_PG_METHODS = [ @@ -44,6 +52,7 @@ class AdjustEcommercePaymentMethodsLayoutListener implements HookListenerInterfa /** * @param array $layout + * @param int $templateId * @return array */ public function markEasyPayMethodsAsPgNotRequired(array $layout, int $templateId): array @@ -52,7 +61,9 @@ class AdjustEcommercePaymentMethodsLayoutListener implements HookListenerInterfa return $layout; } - return $this->replaceNoPgMethodExpressions($layout); + $layout = $this->replaceNoPgMethodExpressions($layout); + + return $this->ensureTestModeWarning($layout); } /** @@ -100,4 +111,222 @@ class AdjustEcommercePaymentMethodsLayoutListener implements HookListenerInterfa $expression ) ?? $expression; } + + /** + * @param array $layout + * @return array + */ + private function ensureTestModeWarning(array $layout): array + { + $layout = $this->ensureTestModeDataSource($layout); + + return $this->insertTestModeNotice($layout); + } + + /** + * @param array $layout + * @return array + */ + private function ensureTestModeDataSource(array $layout): array + { + $dataSources = is_array($layout['data_sources'] ?? null) ? $layout['data_sources'] : []; + + foreach ($dataSources as $source) { + if (is_array($source) && ($source['id'] ?? null) === self::TEST_MODE_DATA_SOURCE_ID) { + $layout['data_sources'] = $dataSources; + + return $layout; + } + } + + $dataSources[] = [ + 'id' => self::TEST_MODE_DATA_SOURCE_ID, + 'label_key' => '$t:sirsoft-pay_nhnkcp.editor.data_source.nhnkcp_test_mode_status', + 'type' => 'api', + 'endpoint' => '/api/plugins/sirsoft-pay_nhnkcp/admin/settings/test-mode-status', + 'method' => 'GET', + 'auto_fetch' => true, + 'auth_required' => true, + ]; + + $layout['data_sources'] = $dataSources; + + return $layout; + } + + /** + * @param array $node + * @return array + */ + private function insertTestModeNotice(array $node): array + { + if (($node['id'] ?? null) === 'tab_content_order_settings' && is_array($node['children'] ?? null)) { + $noticeIndex = $this->findChildIndexById($node['children'], self::TEST_MODE_NOTICE_ID); + + if ($noticeIndex === null) { + $insertAt = $this->findChildIndexById($node['children'], 'payment_methods_card') ?? count($node['children']); + array_splice($node['children'], $insertAt, 0, [$this->testModeNoticeContainerNode()]); + $noticeIndex = $insertAt; + } + + if (is_array($node['children'][$noticeIndex] ?? null)) { + $notice = $node['children'][$noticeIndex]; + $notice['if'] = $this->mergeTestModeCondition((string) ($notice['if'] ?? '')); + $node['children'][$noticeIndex] = $this->appendTestModeNoticeRow($notice); + } + + return $node; + } + + foreach ($node as $key => $value) { + if (is_array($value)) { + $node[$key] = $this->insertTestModeNotice($value); + } + } + + return $node; + } + + /** + * @param array $children + */ + private function findChildIndexById(array $children, string $id): ?int + { + foreach ($children as $index => $child) { + if (is_array($child) && ($child['id'] ?? null) === $id) { + return $index; + } + } + + return null; + } + + /** + * @param array $children + */ + private function hasChildWithId(array $children, string $id): bool + { + return $this->findChildIndexById($children, $id) !== null; + } + + private function mergeTestModeCondition(string $condition): string + { + if (str_contains($condition, self::TEST_MODE_CONDITION)) { + return $condition !== '' ? $condition : '{{' . self::TEST_MODE_CONDITION . '}}'; + } + + $inner = trim($condition); + if (str_starts_with($inner, '{{') && str_ends_with($inner, '}}')) { + $inner = trim(substr($inner, 2, -2)); + } + + if ($inner === '') { + return '{{' . self::TEST_MODE_CONDITION . '}}'; + } + + return '{{' . $inner . ' || ' . self::TEST_MODE_CONDITION . '}}'; + } + + /** + * @param array $notice + * @return array + */ + private function appendTestModeNoticeRow(array $notice): array + { + $children = is_array($notice['children'] ?? null) ? $notice['children'] : []; + + if (! $this->hasChildWithId($children, self::TEST_MODE_NOTICE_ROW_ID)) { + $children[] = $this->testModeNoticeRowNode(); + } + + $notice['children'] = $children; + + return $notice; + } + + /** + * @return array + */ + private function testModeNoticeContainerNode(): array + { + return [ + 'id' => self::TEST_MODE_NOTICE_ID, + 'type' => 'basic', + 'name' => 'Div', + 'if' => '{{' . self::TEST_MODE_CONDITION . '}}', + 'props' => [ + 'className' => 'mb-4 space-y-3 rounded-lg border border-orange-200 bg-orange-50 p-4 dark:border-orange-700 dark:bg-orange-900/20', + ], + 'children' => [ + $this->testModeNoticeRowNode(), + ], + ]; + } + + /** + * @return array + */ + private function testModeNoticeRowNode(): array + { + return [ + 'id' => self::TEST_MODE_NOTICE_ROW_ID, + 'type' => 'basic', + 'name' => 'Div', + 'if' => '{{' . self::TEST_MODE_CONDITION . '}}', + 'props' => [ + 'className' => 'flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between', + ], + 'children' => [ + [ + 'type' => 'basic', + 'name' => 'Div', + 'props' => [ + 'className' => 'min-w-0', + ], + 'children' => [ + [ + 'type' => 'basic', + 'name' => 'Div', + 'props' => [ + 'className' => 'text-sm font-medium text-orange-950 dark:text-orange-50', + ], + 'text' => '$t:sirsoft-pay_nhnkcp.admin.test_mode_settings_warning_plugin', + ], + [ + 'type' => 'basic', + 'name' => 'P', + 'props' => [ + 'className' => 'mt-0.5 text-sm leading-6 text-orange-800 dark:text-orange-200', + ], + 'text' => '$t:sirsoft-pay_nhnkcp.admin.test_mode_settings_warning_body', + ], + ], + ], + [ + 'type' => 'basic', + 'name' => 'Button', + 'props' => [ + 'type' => 'button', + 'className' => 'btn btn-outline btn-sm w-full flex-shrink-0 border-orange-300 text-orange-900 hover:bg-orange-100 sm:w-auto dark:border-orange-600 dark:text-orange-100 dark:hover:bg-orange-900/40', + ], + 'actions' => [ + [ + 'type' => 'click', + 'handler' => 'navigate', + 'params' => [ + 'path' => '/admin/plugins/sirsoft-pay_nhnkcp/settings', + ], + ], + ], + 'children' => [ + [ + 'type' => 'basic', + 'name' => 'Span', + 'text' => '$t:sirsoft-pay_nhnkcp.admin.test_mode_settings_warning_action', + ], + ], + ], + ], + ]; + } } diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/src/routes/api.php b/plugins/_bundled/sirsoft-pay_nhnkcp/src/routes/api.php index 64b6128c..6817d9c4 100644 --- a/plugins/_bundled/sirsoft-pay_nhnkcp/src/routes/api.php +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/src/routes/api.php @@ -3,6 +3,7 @@ use Illuminate\Support\Facades\Route; use Plugins\Sirsoft\PayNhnkcp\Controllers\AdminEscrowDeliveryController; use Plugins\Sirsoft\PayNhnkcp\Controllers\AdminOrderListController; +use Plugins\Sirsoft\PayNhnkcp\Controllers\AdminSettingsStatusController; use Plugins\Sirsoft\PayNhnkcp\Controllers\AdminTransactionController; use Plugins\Sirsoft\PayNhnkcp\Controllers\HealthCheckController; use Plugins\Sirsoft\PayNhnkcp\Controllers\MobileApprovalController; @@ -53,6 +54,10 @@ Route::prefix('admin')->name('admin.')->middleware(['auth:sanctum', 'admin'])->g ], ]))->name('vbank.notify.url'); + Route::get('/settings/test-mode-status', [AdminSettingsStatusController::class, 'testMode']) + ->middleware('permission:admin,sirsoft-ecommerce.settings.read') + ->name('settings.test-mode-status'); + // 테스트 모드 주문 맵 (관리자 주문목록 배지 표시용) Route::get('/orders/test-mode-map', [AdminOrderListController::class, 'testModeMap']) ->name('orders.test-mode-map'); diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Feature/Controllers/AdminSettingsStatusControllerTest.php b/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Feature/Controllers/AdminSettingsStatusControllerTest.php new file mode 100644 index 00000000..65531499 --- /dev/null +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Feature/Controllers/AdminSettingsStatusControllerTest.php @@ -0,0 +1,33 @@ +createMock(PluginSettingsService::class); + $settings->expects($this->once()) + ->method('get') + ->with('sirsoft-pay_nhnkcp', 'is_test_mode', true) + ->willReturn(true); + $this->app->instance(PluginSettingsService::class, $settings); + + $admin = $this->createAdminUser(['sirsoft-ecommerce.settings.read']); + + $response = $this->actingAs($admin) + ->getJson('/api/plugins/sirsoft-pay_nhnkcp/admin/settings/test-mode-status'); + + $response->assertOk() + ->assertJsonPath('data.is_test_mode', true); + + $data = $response->json('data'); + $this->assertArrayNotHasKey('test_site_cd', $data); + $this->assertArrayNotHasKey('live_site_cd', $data); + } +} diff --git a/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Unit/Listeners/AdjustEcommercePaymentMethodsLayoutListenerTest.php b/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Unit/Listeners/AdjustEcommercePaymentMethodsLayoutListenerTest.php index 22c0848d..1ad3458e 100644 --- a/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Unit/Listeners/AdjustEcommercePaymentMethodsLayoutListenerTest.php +++ b/plugins/_bundled/sirsoft-pay_nhnkcp/tests/Unit/Listeners/AdjustEcommercePaymentMethodsLayoutListenerTest.php @@ -58,6 +58,45 @@ class AdjustEcommercePaymentMethodsLayoutListenerTest extends TestCase $this->assertStringNotContainsString("['point','deposit','free','dbank'].includes", $json); } + public function test_adds_test_mode_warning_to_order_settings_tab(): void + { + $listener = new AdjustEcommercePaymentMethodsLayoutListener(); + + $layout = [ + 'layout_name' => 'admin_ecommerce_settings', + 'children' => [ + [ + 'id' => 'tab_content_order_settings', + 'children' => [ + ['id' => 'default_pg_card'], + ['id' => 'payment_methods_card'], + ], + ], + ], + ]; + + $result = $listener->markEasyPayMethodsAsPgNotRequired($layout, 1); + $result = $listener->markEasyPayMethodsAsPgNotRequired($result, 1); + $json = json_encode($result, JSON_UNESCAPED_SLASHES); + + $this->assertIsString($json); + $this->assertStringContainsString('nhnkcp_test_mode_status', $json); + $this->assertStringContainsString('/api/plugins/sirsoft-pay_nhnkcp/admin/settings/test-mode-status', $json); + $this->assertStringContainsString('payment_test_mode_order_settings_notice', $json); + $this->assertStringContainsString('nhnkcp_test_mode_order_settings_notice', $json); + $this->assertStringNotContainsString('sirsoft-pay_nhnkcp.admin.test_mode_settings_summary_title', $json); + $this->assertStringNotContainsString('sirsoft-pay_nhnkcp.admin.test_mode_settings_summary_body', $json); + $this->assertStringContainsString('sirsoft-pay_nhnkcp.admin.test_mode_settings_warning_plugin', $json); + $this->assertStringContainsString('sirsoft-pay_nhnkcp.admin.test_mode_settings_warning_body', $json); + $this->assertStringContainsString('/admin/plugins/sirsoft-pay_nhnkcp/settings', $json); + $this->assertSame(1, substr_count($json, 'payment_test_mode_order_settings_notice')); + $this->assertSame(1, substr_count($json, 'nhnkcp_test_mode_order_settings_notice')); + $this->assertLessThan( + strpos($json, 'payment_methods_card'), + strpos($json, 'payment_test_mode_order_settings_notice') + ); + } + public function test_leaves_other_layouts_unchanged(): void { $listener = new AdjustEcommercePaymentMethodsLayoutListener();