feat(verification_kginicis): 라이브 MID 자동완성 방지 + SRB 프리픽스 전환

- 라이브 자격증명 입력칸 브라우저 자동완성 자동 채움 차단
 (live_mid: one-time-code / live_api_key: new-password —
 크롬이 autoComplete=off 를 무시함을 실측 후 강화)
- 라이브 가맹점 MID 프리픽스를 이니시스 정책 변경에 맞춰 SRA→SRB 전환.
 런타임 프리픽스를 InicisIdentityProvider::LIVE_MID_PREFIX 상수로 추출,
 표시 문자열(라벨·Span·힌트 ko/en/ja)은 값 교체.
- 설정 저장 테스트가 실제 스토리지 설정 파일을 오염시키던 결함 격리
 (PluginTestCase 에서 plugins 디스크를 테스트 전용 경로로 격리)
- 설정 화면 배너 아이콘 크기 시맨틱 정정 (w-N h-N → text-*, 정렬 회귀 해결)
- 프리픽스 상수 ↔ 표시 문자열 정합 audit 룰 추가 (이중관리 불일치 차단)
This commit is contained in:
chym1217
2026-07-13 15:18:10 +09:00
parent cd3f6ad2b7
commit 57ac2b0b3b
21 changed files with 331 additions and 32 deletions
@@ -4,6 +4,12 @@
형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)를 따르며,
[Semantic Versioning](https://semver.org/lang/ko/)을 준수합니다.
## [1.0.1] - 2026-07-09
### Changed
- 라이브 가맹점 MID 프리픽스 표기를 이니시스 정책 변경에 맞춰 SRB 로 갱신했습니다 (라이브 MID 라벨·입력 안내 문구).
## [1.0.0] - 2026-07-01
### Added
@@ -8,7 +8,7 @@ return [
],
'settings' => [
'test_mode' => 'テストモード',
'live_mid' => 'ライブMID(SRAプレフィックス)',
'live_mid' => 'ライブMID(SRBプレフィックス)',
'live_api_key' => 'ライブAPIキー',
'duplicate_field' => '同一人識別基準',
'duplicate_block_enabled' => [
@@ -14,7 +14,7 @@
"live_mode_warning_body": "ライブモードは実際の加盟店MIDで本人認証を実行します。ライブMIDとAPIキーを入力してから運用してください。",
"section_live_keys": "ライブ資格情報",
"live_mid": "ライブMID",
"live_mid_hint": "イニシスライブ加盟店MIDはSRAで始まります (例:SRA1234567)。運用モード使用前に必須入力です。",
"live_mid_hint": "イニシスライブ加盟店MIDはSRBで始まります (例:SRB1234567)。運用モード使用前に必須入力です。",
"live_api_key": "ライブAPIキー",
"section": {
"duplicate_field": "同一人識別"
@@ -12,7 +12,7 @@
"en": "G7 plugin (sirsoft-verification_kginicis) Japanese language pack (bundled)",
"ja": "G7 プラグイン (sirsoft-verification_kginicis) 日本語 言語パック(バンドル)"
},
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"scope": "plugin",
"target_identifier": "sirsoft-verification_kginicis",
@@ -4,6 +4,16 @@ All notable changes to this plugin will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [1.0.1] - 2026-07-09
### Changed
- 라이브 가맹점 MID 프리픽스를 이니시스 정책 변경에 맞춰 SRB 로 갱신했습니다 (설정 화면 고정 프리픽스 표시·입력 안내 문구 포함).
### Fixed
- 라이브 자격증명 입력칸(라이브 MID·라이브 API 키)에 브라우저 자동완성으로 계정 이메일 등 의도치 않은 값이 자동으로 채워지던 문제를 수정했습니다. 잘못 채워진 값이 저장되어 라이브 본인인증이 실패하는 상황을 방지합니다.
## [1.0.0] - 2026-07-01
### Changed
@@ -1,7 +1,7 @@
{
"name": "plugins/sirsoft-verification_kginicis",
"description": "KG Inicis Identity Verification provider for G7",
"version": "1.0.0",
"version": "1.0.1",
"type": "library",
"authors": [
{
@@ -12,9 +12,9 @@ return [
'settings' => [
'test_mode' => 'Test Mode',
'live_mid' => 'Live MID (SRA prefix)',
'live_mid' => 'Live MID (SRB prefix)',
'live_api_key' => 'Live API Key',
// Short field labels for validation error messages (without "(SRA prefix)" hint).
// Short field labels for validation error messages (without "(SRB prefix)" hint).
'live_mid_attribute' => 'Live MID',
'live_api_key_attribute' => 'Live API Key',
'duplicate_field' => 'Duplicate Identifier',
@@ -12,9 +12,9 @@ return [
'settings' => [
'test_mode' => '테스트 모드',
'live_mid' => '라이브 MID (SRA 프리픽스)',
'live_mid' => '라이브 MID (SRB 프리픽스)',
'live_api_key' => '라이브 API 키',
// 검증 에러 메시지의 필드 이름용 짧은 라벨 (화면 라벨의 부가 설명 "(SRA 프리픽스)" 제외)
// 검증 에러 메시지의 필드 이름용 짧은 라벨 (화면 라벨의 부가 설명 "(SRB 프리픽스)" 제외)
'live_mid_attribute' => '라이브 MID',
'live_api_key_attribute' => '라이브 API 키',
'duplicate_field' => '동일인 식별 기준',
@@ -1,6 +1,6 @@
{
"name": "@g7/sirsoft-verification_kginicis",
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"private": true,
"scripts": {
@@ -5,7 +5,7 @@
"ko": "KG이니시스 본인인증",
"en": "KG Inicis Identity Verification"
},
"version": "1.0.0",
"version": "1.0.1",
"license": "MIT",
"description": {
"ko": "KG이니시스 통합인증의 본인확인(reqSvcCd=03)을 G7 코어 IDV 인프라에 Provider 로 등록하는 플러그인",
@@ -0,0 +1,76 @@
/**
* @file pluginSettingsLiveMid.test.tsx
* @description sirsoft-verification_kginicis 플러그인 환경설정 라이브 MID 입력칸 회귀 테스트 (#458)
*
* 작업 A — 라이브 MID 입력칸(live_mid)에 autoComplete="off" 가 적용되어 브라우저 자동완성
* (계정 이메일 등)이 채워지지 않도록 한다.
* 작업 B — 라이브 MID 고정 프리픽스 Span 이 이니시스 정책 변경값 SRB 로 표시된다.
*/
import { describe, it, expect } from 'vitest';
import pluginSettingsLayout from '../../../layouts/admin/plugin_settings.json';
/** 레이아웃 트리에서 props.name 이 일치하는 첫 노드를 찾는다. */
function findByPropName(node: unknown, name: string): Record<string, unknown> | undefined {
if (!node || typeof node !== 'object') {
return undefined;
}
const value = node as Record<string, unknown>;
const props = (value.props ?? {}) as Record<string, unknown>;
if (props.name === name) {
return value;
}
for (const child of Object.values(value)) {
const found = findByPropName(child, name);
if (found) {
return found;
}
}
return undefined;
}
/** 레이아웃 트리에서 text 가 일치하는 첫 노드를 찾는다. */
function findByText(node: unknown, text: string): Record<string, unknown> | undefined {
if (!node || typeof node !== 'object') {
return undefined;
}
const value = node as Record<string, unknown>;
if (value.text === text) {
return value;
}
for (const child of Object.values(value)) {
const found = findByText(child, text);
if (found) {
return found;
}
}
return undefined;
}
describe('plugin_settings 라이브 MID 입력칸 (#458)', () => {
it('live_mid input 에 autoComplete="one-time-code" 가 적용되어 있어야 한다 (크롬 autofill/제안 차단)', () => {
// 크롬은 autoComplete="off" 를 무시하고 저장된 계정(이메일)을 채운다. live_mid 는 비밀번호가
// 아닌 일반 텍스트 MID 칸이므로, autofill/제안을 차단하되 "비밀번호 생성" 제안이 뜨지 않는
// one-time-code 토큰을 사용한다 (new-password 는 live_api_key 처럼 실제 비번 칸에만) (#458).
const liveMid = findByPropName(pluginSettingsLayout, 'live_mid');
expect(liveMid).toBeDefined();
const props = (liveMid?.props ?? {}) as Record<string, unknown>;
expect(props.autoComplete).toBe('one-time-code');
});
it('live_api_key input 에 autoComplete="new-password" 가 적용되어 있어야 한다 (저장 비번 autofill 차단)', () => {
// live_api_key 는 실제 새 API 키(비밀번호 성격) 입력칸이므로 의미상으로도 정확한 new-password 사용.
const liveApiKey = findByPropName(pluginSettingsLayout, 'live_api_key');
expect(liveApiKey).toBeDefined();
const props = (liveApiKey?.props ?? {}) as Record<string, unknown>;
expect(props.autoComplete).toBe('new-password');
});
it('라이브 MID 고정 프리픽스 Span 이 SRB 로 표시되어야 한다', () => {
// 정책 전환 후 SRA 는 존재하지 않아야 한다.
expect(findByText(pluginSettingsLayout, 'SRA')).toBeUndefined();
expect(findByText(pluginSettingsLayout, 'SRB')).toBeDefined();
});
});
@@ -20,7 +20,7 @@
"live_mode_warning_body": "Live mode performs verification with your real merchant MID. Enter your Live MID and API key before going live.",
"section_live_keys": "Live Credentials",
"live_mid": "Live MID",
"live_mid_hint": "Inicis live merchant MIDs start with SRA (e.g., SRA1234567). Required before switching to Live mode.",
"live_mid_hint": "Inicis live merchant MIDs start with SRB (e.g., SRB1234567). Required before switching to Live mode.",
"live_api_key": "Live API Key",
"section": {
"duplicate_field": "Duplicate Check Basis"
@@ -20,7 +20,7 @@
"live_mode_warning_body": "라이브 모드는 실제 가맹점 MID 로 본인인증을 수행합니다. 라이브 MID 와 API 키를 입력한 뒤 운영하세요.",
"section_live_keys": "라이브 자격증명",
"live_mid": "라이브 MID",
"live_mid_hint": "이니시스 라이브 가맹점 MID 는 SRA 로 시작합니다 (예: SRA1234567). 운영 모드 사용 전 필수 입력.",
"live_mid_hint": "이니시스 라이브 가맹점 MID 는 SRB 로 시작합니다 (예: SRB1234567). 운영 모드 사용 전 필수 입력.",
"live_api_key": "라이브 API 키",
"section": {
"duplicate_field": "동일인 식별"
@@ -63,7 +63,7 @@
"name": "Icon",
"props": {
"name": "circle-info",
"className": "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5"
"className": "text-xl text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5"
}
},
{
@@ -98,7 +98,7 @@
"name": "Icon",
"props": {
"name": "triangle-exclamation",
"className": "w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5"
"className": "text-xl text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5"
}
},
{
@@ -203,7 +203,7 @@
"name": "Icon",
"props": {
"name": "triangle-exclamation",
"className": "w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
"className": "text-xl text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
}
},
{
@@ -297,7 +297,7 @@
"props": {
"className": "inline-flex items-center px-3 bg-gray-100 dark:bg-gray-600 border-r border-gray-300 dark:border-gray-600 text-body font-mono font-bold select-none"
},
"text": "SRA"
"text": "SRB"
},
{
"type": "basic",
@@ -305,6 +305,7 @@
"props": {
"type": "text",
"name": "live_mid",
"autoComplete": "one-time-code",
"placeholder": "1234567",
"className": "flex-1 font-mono outline-none border-0"
}
@@ -353,7 +354,7 @@
"props": {
"type": "password",
"name": "live_api_key",
"autoComplete": "off",
"autoComplete": "new-password",
"placeholder": "••••••••••••••••",
"className": "{{_local.errors?.live_api_key ? 'dark:focus:ring-red-400 font-mono border border-red-500 dark:border-red-500 focus:ring-red-500' : 'dark:focus:ring-blue-400 focus:border-transparent font-mono'}}"
}
@@ -36,6 +36,15 @@ class InicisIdentityProvider implements IdentityVerificationInterface
/** Provider 식별자 (코어 표준) */
public const PROVIDER_ID = 'inicis';
/**
* 라이브 가맹점 MID 프리픽스 (이니시스 정책값).
*
* 라이브 MID = 이 프리픽스 + 운영자 입력값 (예: SRB1234567).
* 이니시스 프리픽스 정책 변경 시 이 상수 1곳만 갱신하면 런타임 로직 전체에 반영된다.
* (UI Span·언어팩 힌트 같은 정적 표시 문자열은 상수 참조가 불가하므로 값만 함께 교체)
*/
public const LIVE_MID_PREFIX = 'SRB';
/**
* 성인인증 purpose 키 (Plugin::getIdentityPurposes 선언값과 동일 — 단일 출처).
*
@@ -143,7 +152,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
}
/**
* 운영 가능 여부 — 모드별 MID/API key + 라이브 모드의 SRA 프리픽스 검증.
* 운영 가능 여부 — 모드별 MID/API key + 라이브 모드의 프리픽스 검증.
*
* @return bool
*/
@@ -157,7 +166,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
return $liveMid !== ''
&& ! empty($this->config['live_api_key'])
&& str_starts_with($liveMid, 'SRA');
&& str_starts_with($liveMid, self::LIVE_MID_PREFIX);
}
/**
@@ -500,7 +509,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
'default' => 'TGdxb2l3enJDWFRTbTgvREU3MGYwUT09',
],
'live_mid' => [
'label' => 'Live MID (SRA prefix)',
'label' => 'Live MID (SRB prefix)',
'type' => 'text',
'default' => '',
],
@@ -568,13 +577,13 @@ class InicisIdentityProvider implements IdentityVerificationInterface
}
/**
* 라이브 MID 에 SRA 프리픽스를 보장한다.
* 라이브 MID 에 프리픽스(self::LIVE_MID_PREFIX)를 보장한다.
*
* 운영자가 settings UI 에서 SRA 분리형 input 으로 입력하므로 일반적으로 SRA 미포함 값이
* 들어오지만, 직접 SRA 포함하여 입력한 경우에도 안전하게 처리한다.
* 운영자가 settings UI 에서 프리픽스 분리형 input 으로 입력하므로 일반적으로 프리픽스
* 미포함 값이 들어오지만, 직접 프리픽스를 포함하여 입력한 경우에도 안전하게 처리한다.
*
* @param string $value 운영자 입력 값
* @return string SRA 가 보장된 라이브 MID
* @return string 프리픽스가 보장된 라이브 MID
*/
protected function buildLiveMid(string $value): string
{
@@ -583,7 +592,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
return '';
}
return str_starts_with($value, 'SRA') ? $value : 'SRA'.$value;
return str_starts_with($value, self::LIVE_MID_PREFIX) ? $value : self::LIVE_MID_PREFIX.$value;
}
/**
@@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Lang;
* 자격증명에 required 규칙을 동적 부여한다.
*
* 검증 기준은 InicisIdentityProvider::isAvailable() 의 라이브 운영 가능 조건
* (live_mid 비어있지 않음 + live_api_key 비어있지 않음) 과 일치시킨다. live_mid 의 SRA
* (live_mid 비어있지 않음 + live_api_key 비어있지 않음) 과 일치시킨다. live_mid 의 SRB
* 프리픽스는 buildLiveMid() 가 자동 부착하므로 별도 형식 검증은 두지 않는다.
*
* @since 1.0.0-beta.1
@@ -8,6 +8,7 @@ use App\Models\Role;
use App\Models\User;
use App\Services\PluginSettingsService;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Storage;
use Plugins\Sirsoft\VerificationKginicis\Plugin;
use Plugins\Sirsoft\VerificationKginicis\Tests\PluginTestCase;
@@ -219,9 +220,11 @@ class InicisLiveModeSettingsValidationTest extends PluginTestCase
])->assertStatus(200);
// 저장 파일에 평문이 남지 않아야 한다 (sensitive 암호화).
$settingsPath = storage_path('app/plugins/'.self::IDENTIFIER.'/settings/setting.json');
$this->assertFileExists($settingsPath);
$raw = file_get_contents($settingsPath);
// PluginTestCase 가 'plugins' 디스크를 임시 디렉토리로 격리하므로 실제 storage_path 를
// 하드코딩하지 않고 격리된 디스크를 통해 파일을 확인한다.
$settingsFile = self::IDENTIFIER.'/settings/setting.json';
$this->assertTrue(Storage::disk('plugins')->exists($settingsFile));
$raw = Storage::disk('plugins')->get($settingsFile);
$this->assertStringNotContainsString('live-secret-key', $raw);
// 복호화 왕복은 원문과 일치해야 한다.
@@ -4,7 +4,9 @@ namespace Plugins\Sirsoft\VerificationKginicis\Tests;
use App\Extension\HookListenerRegistrar;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Plugins\Sirsoft\VerificationKginicis\Listeners\AssertNoDuplicateInicisIdentity;
use Plugins\Sirsoft\VerificationKginicis\Listeners\CleanInicisRecordOnUserDelete;
use Plugins\Sirsoft\VerificationKginicis\Listeners\CleanInicisRecordOnUserWithdraw;
@@ -25,6 +27,9 @@ abstract class PluginTestCase extends TestCase
{
use RefreshDatabase;
/** 테스트 격리용 임시 plugins 스토리지 루트 (setUp 에서 생성, tearDown 에서 제거). */
private ?string $isolatedPluginsRoot = null;
protected function migrateFreshUsing(): array
{
return [
@@ -42,6 +47,8 @@ abstract class PluginTestCase extends TestCase
{
parent::setUp();
$this->isolatePluginStorage();
$this->registerPluginAutoload();
// 플러그인 lang 네임스페이스 등록 — 실 환경에서는 코어 TranslationServiceProvider::boot()
@@ -84,6 +91,54 @@ abstract class PluginTestCase extends TestCase
$this->registerPluginHookListeners();
}
protected function tearDown(): void
{
$this->restorePluginStorage();
parent::tearDown();
}
/**
* 플러그인 스토리지('plugins' 디스크)를 테스트 전용 임시 디렉토리로 격리한다.
*
* 설정 저장 테스트(PluginSettingsService::save)는 코어 'plugins' 디스크
* (root = storage_path('app/plugins'))에 setting.json 을 쓴다. 격리하지 않으면
* 테스트가 실제 로컬 런타임 설정 파일을 덮어써 라이브 모드/자격증명이 오염된다
* (RefreshDatabase 는 DB 만 롤백하고 파일시스템은 되돌리지 않음). 디스크 root 를
* 임시 경로로 바꾸고 resolved 인스턴스를 purge 하여 실제 파일을 원천적으로 못
* 건드리게 한다.
*/
private function isolatePluginStorage(): void
{
// Laravel 이 테스트용 쓰기 공간으로 보장하는 storage/framework/testing 하위를 사용한다.
// sys_get_temp_dir() 은 CI/컨테이너/open_basedir 제약 환경에서 위치가 다르거나
// 쓰기 불가일 수 있어 프로젝트 내부의 격리 경로를 쓴다 (Laravel 규약 준수).
$this->isolatedPluginsRoot = storage_path(
'framework/testing/plugin-storage-'.uniqid('', true)
);
File::ensureDirectoryExists($this->isolatedPluginsRoot);
config(['filesystems.disks.plugins.root' => $this->isolatedPluginsRoot]);
// 이미 resolve 된 'plugins' 디스크 인스턴스를 폐기해 새 root 로 재생성되게 한다.
Storage::forgetDisk('plugins');
}
/**
* 격리 임시 디렉토리를 제거한다 (테스트 간 잔여 파일 격리).
*/
private function restorePluginStorage(): void
{
if ($this->isolatedPluginsRoot !== null && is_dir($this->isolatedPluginsRoot)) {
File::deleteDirectory($this->isolatedPluginsRoot);
}
$this->isolatedPluginsRoot = null;
Storage::forgetDisk('plugins');
}
/**
* Plugin 의 hook listener 들을 코어 HookManager 에 등록.
*
@@ -99,7 +99,7 @@ class InicisIdentityProviderWithConfigTest extends TestCase
['is_test_mode' => true, 'test_mid' => 'INIiasTest', 'duplicate_field' => 'di'],
);
$cloned = $provider->withConfig(['is_test_mode' => false, 'live_mid' => 'SRA1234567']);
$cloned = $provider->withConfig(['is_test_mode' => false, 'live_mid' => 'SRB1234567']);
$reflection = new \ReflectionClass($cloned);
foreach (['gateway', 'mappingRepository', 'recordRepository', 'cache', 'config'] as $prop) {
@@ -113,7 +113,7 @@ class InicisIdentityProviderWithConfigTest extends TestCase
$this->assertSame(false, $injectedConfig['is_test_mode']);
$this->assertSame('INIiasTest', $injectedConfig['test_mid']);
$this->assertSame('SRA1234567', $injectedConfig['live_mid']);
$this->assertSame('SRB1234567', $injectedConfig['live_mid']);
$this->assertSame('di', $injectedConfig['duplicate_field']);
$gatewayProp = $reflection->getProperty('gateway');
@@ -0,0 +1,139 @@
<?php
namespace Plugins\Sirsoft\VerificationKginicis\Tests\Unit\Identity;
use App\Contracts\Extension\CacheInterface;
use Mockery;
use PHPUnit\Framework\TestCase;
use Plugins\Sirsoft\VerificationKginicis\Identity\InicisIdentityProvider;
use Plugins\Sirsoft\VerificationKginicis\Repositories\InicisChallengeMappingRepositoryInterface;
use Plugins\Sirsoft\VerificationKginicis\Repositories\InicisIdentityRecordRepositoryInterface;
use Plugins\Sirsoft\VerificationKginicis\Services\InicisGatewayInterface;
/**
* 라이브 MID 프리픽스(LIVE_MID_PREFIX) 회귀 테스트.
*
* 배경 (#458):
* 이니시스 정책 변경으로 라이브 가맹점 MID 프리픽스가 SRA → SRB 로 전환된다.
* 프리픽스를 InicisIdentityProvider::LIVE_MID_PREFIX 클래스 상수 1곳으로 추출하여
* 런타임 로직(buildLiveMid / isAvailable)이 상수를 참조하도록 했다. 본 테스트는
* 상수값이 실제 프리픽스 부착·검증에 일관되게 적용되는지 고정한다 (다음 정책 변경 시
* 상수만 바꾸면 이 스위트가 전 지점을 함께 검증).
*/
class InicisLiveMidPrefixTest extends TestCase
{
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
/**
* 프리픽스 상수는 현재 정책값 SRB 여야 한다.
*
* @return void
*/
public function test_live_mid_prefix_constant_is_srb(): void
{
$this->assertSame('SRB', InicisIdentityProvider::LIVE_MID_PREFIX);
}
/**
* buildLiveMid 는 프리픽스 미포함 입력값 앞에 상수 프리픽스를 부착해야 한다.
*
* @return void
*/
public function test_build_live_mid_prepends_prefix_when_absent(): void
{
$result = $this->invokeBuildLiveMid('1234567');
$this->assertSame(InicisIdentityProvider::LIVE_MID_PREFIX.'1234567', $result);
$this->assertSame('SRB1234567', $result);
}
/**
* buildLiveMid 는 이미 프리픽스를 포함한 입력값에 중복 부착하지 않아야 한다.
*
* @return void
*/
public function test_build_live_mid_does_not_double_prepend_prefix(): void
{
$result = $this->invokeBuildLiveMid('SRB1234567');
$this->assertSame('SRB1234567', $result);
}
/**
* buildLiveMid 는 빈 입력값(공백 포함)을 빈 문자열로 반환해야 한다.
*
* @return void
*/
public function test_build_live_mid_returns_empty_for_blank_input(): void
{
$this->assertSame('', $this->invokeBuildLiveMid(''));
$this->assertSame('', $this->invokeBuildLiveMid(' '));
}
/**
* 라이브 모드에서 프리픽스가 부착된 라이브 MID + API 키가 있으면 운영 가능해야 한다.
*
* @return void
*/
public function test_is_available_true_in_live_mode_with_prefixed_mid(): void
{
$provider = $this->makeProvider([
'is_test_mode' => false,
'live_mid' => '1234567',
'live_api_key' => 'live-api-key',
]);
$this->assertTrue($provider->isAvailable());
}
/**
* 라이브 모드에서 API 키가 비어 있으면 운영 불가여야 한다.
*
* @return void
*/
public function test_is_available_false_in_live_mode_without_api_key(): void
{
$provider = $this->makeProvider([
'is_test_mode' => false,
'live_mid' => '1234567',
'live_api_key' => '',
]);
$this->assertFalse($provider->isAvailable());
}
/**
* 라이브 MID 를 리플렉션으로 protected buildLiveMid() 에 통과시킨다.
*
* @param string $value
* @return string
*/
protected function invokeBuildLiveMid(string $value): string
{
$provider = $this->makeProvider(['is_test_mode' => true, 'test_mid' => 'INIiasTest']);
$method = new \ReflectionMethod($provider, 'buildLiveMid');
$method->setAccessible(true);
return $method->invoke($provider, $value);
}
/**
* @param array<string, mixed> $config
* @return InicisIdentityProvider
*/
protected function makeProvider(array $config): InicisIdentityProvider
{
return new InicisIdentityProvider(
Mockery::mock(InicisGatewayInterface::class),
Mockery::mock(InicisChallengeMappingRepositoryInterface::class),
Mockery::mock(InicisIdentityRecordRepositoryInterface::class),
Mockery::mock(CacheInterface::class),
$config,
);
}
}
@@ -59,7 +59,7 @@ design_rationale: |
- 채택: (a) 스키마를 plugin.php 로 이전(결제 플러그인 동형 + sensitive) + (b) 라이브 조건부
required 는 코어가 이미 발행하는 core.plugin_settings.update_rules 필터로 주입. 코어 무수정.
- 라이브 검증 기준은 InicisIdentityProvider::isAvailable()(live_mid + live_api_key 비어있지 않음)
과 일치. live_mid 의 SRA 프리픽스는 buildLiveMid() 가 자동 부착하므로 별도 형식 검증 미도입.
과 일치. live_mid 의 SRB 프리픽스는 buildLiveMid() 가 자동 부착하므로 별도 형식 검증 미도입.
- (라벨 로케일 보강) ServiceProvider 의 validation.attributes 라벨 등록이 ['ko','en'] 하드코딩이라
ja 로케일에서 일본어 라벨이 노출되지 않던 결함을 config('app.supported_locales') 동적 처리로 정정.
ja 번역(ライブ MID 등)은 이미 언어팩에 존재했으나 등록 로케일 누락으로 미사용 상태였다.