현금영수증 발급사를 PG 결제사와 독립적으로 선택할 수 있도록 공용 훅 축 (registered_providers/issue/cancel)에 토스·KG 프로바이더를 등록한다. KG 결제로 받으면서 영수증만 토스로 발급하는 구성이 가능해진다. KG 전용 발행 컨트롤러· 라우트·관리자 패널은 이 축으로 이전하며 제거했다 — 이중 발행 경로를 남기지 않기 위해서다. 토스 환불은 네 가지를 바로잡았다. 전액취소를 부분취소로 보내던 문제(R1), 면세금액 미전달로 복합과세 상점의 부가세가 틀어지던 문제(R2), 가상계좌 환불계좌를 싣지 않아 취소가 실패하던 문제(R3), 에스크로 부분취소를 PG 호출 전에 차단하지 않던 문제(E4). 취소·발급 요청에는 멱등키를 부착해 네트워크 재시도가 국세청에 이중 신고되거나 이중 환불되는 것을 막는다. 코어 활동로그 가드가 \Exception 만 잡아 채널 해석 실패(\Error)를 놓치고 있었다. 결제 입금통보 같은 외부 콜백이 로그 문제만으로 500을 반환하던 문제를 \Throwable 로 수정한다. 활동로그는 부수 기록이므로 그것을 유발한 요청까지 죽여서는 안 된다. 토스 API 서비스는 베이스 \Exception 을 직접 던지고 있어 도메인 예외 (TossPaymentsApiException)를 신설했다. KG의 KgInicisApiException 선례와 동형이며 RuntimeException 상속이라 기존 소비자의 catch 는 그대로 동작한다.
235 lines
7.6 KiB
PHP
235 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Requests;
|
|
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Modules\Sirsoft\Ecommerce\Http\Requests\Admin\StoreBanksRequest;
|
|
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
|
|
|
|
/**
|
|
* 은행 목록 저장 요청 검증 테스트
|
|
*
|
|
* - banks 배열 검증
|
|
* - banks.*.code 필수/문자열/최대길이
|
|
* - banks.*.name 필수/다국어 배열
|
|
* - 현재 로케일 은행명만 필수
|
|
* - 빈 배열 허용
|
|
*/
|
|
class StoreBanksRequestTest extends ModuleTestCase
|
|
{
|
|
/**
|
|
* 은행명 검증은 "현재 앱 로케일" 필드만 필수로 본다. 테스트 데이터가 ko/en 을 제공하므로
|
|
* 환경 로케일(예: ja)에 관계없이 결정적이도록 ko 로 고정한다.
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
app()->setLocale('ko');
|
|
}
|
|
|
|
/**
|
|
* 검증 수행
|
|
*
|
|
* @param array $data 검증 대상 데이터
|
|
*/
|
|
protected function validate(array $data): \Illuminate\Validation\Validator
|
|
{
|
|
$request = new StoreBanksRequest;
|
|
|
|
return Validator::make($data, $request->rules());
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// banks 배열 검증
|
|
// ──────────────────────────────────────────────
|
|
|
|
public function test_banks_accepts_valid_data(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => ['ko' => '국민은행', 'en' => 'Kookmin Bank']],
|
|
['code' => '088', 'name' => ['ko' => '신한은행', 'en' => 'Shinhan Bank']],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->passes(),
|
|
'유효한 은행 데이터를 허용해야 합니다. 오류: '.$validator->errors()->toJson()
|
|
);
|
|
}
|
|
|
|
public function test_banks_accepts_empty_array(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->passes(),
|
|
'빈 배열을 허용해야 합니다'
|
|
);
|
|
}
|
|
|
|
public function test_banks_accepts_null(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => null,
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->passes(),
|
|
'null 값을 허용해야 합니다'
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// banks.*.code 검증
|
|
// ──────────────────────────────────────────────
|
|
|
|
public function test_bank_code_required_when_banks_present(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['name' => ['ko' => '국민은행', 'en' => 'Kookmin Bank']],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.code'),
|
|
'은행코드는 필수 항목이어야 합니다'
|
|
);
|
|
}
|
|
|
|
public function test_bank_code_max_length(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => 'VERY_LONG_CODE', 'name' => ['ko' => '테스트', 'en' => 'Test']],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.code'),
|
|
'은행코드는 최대 10자까지 허용해야 합니다'
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// banks.*.name 검증
|
|
// ──────────────────────────────────────────────
|
|
|
|
public function test_bank_name_required_when_banks_present(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004'],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.name'),
|
|
'은행명은 필수 항목이어야 합니다'
|
|
);
|
|
}
|
|
|
|
public function test_bank_name_must_be_array(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => '국민은행'],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.name'),
|
|
'은행명은 다국어 배열 형식이어야 합니다'
|
|
);
|
|
}
|
|
|
|
public function test_bank_name_locale_max_length(): void
|
|
{
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => ['ko' => str_repeat('가', 101)]],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.name.ko'),
|
|
'은행명 로케일 값은 최대 100자까지 허용해야 합니다'
|
|
);
|
|
}
|
|
|
|
// ──────────────────────────────────────────────
|
|
// 현재 로케일 기준 은행명 필수 검증
|
|
// ──────────────────────────────────────────────
|
|
|
|
public function test_bank_name_current_locale_only_is_valid(): void
|
|
{
|
|
// 기본 로케일(ko)만 입력해도 통과해야 함
|
|
app()->setLocale('ko');
|
|
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => ['ko' => '국민은행']],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->passes(),
|
|
'현재 로케일(ko)만 입력해도 유효해야 합니다. 오류: '.$validator->errors()->toJson()
|
|
);
|
|
}
|
|
|
|
public function test_bank_name_other_locale_null_is_valid(): void
|
|
{
|
|
// 현재 로케일(ko) 입력, 다른 로케일은 null (ConvertEmptyStringsToNull 시뮬레이션)
|
|
app()->setLocale('ko');
|
|
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => ['ko' => '국민은행', 'en' => null]],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->passes(),
|
|
'다른 로케일이 null이어도 유효해야 합니다. 오류: '.$validator->errors()->toJson()
|
|
);
|
|
}
|
|
|
|
public function test_bank_name_current_locale_missing_fails(): void
|
|
{
|
|
// 현재 로케일(ko)이 없고 다른 로케일만 있으면 실패해야 함
|
|
app()->setLocale('ko');
|
|
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => ['en' => 'Kookmin Bank']],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.name.ko'),
|
|
'현재 로케일(ko) 은행명이 없으면 실패해야 합니다'
|
|
);
|
|
}
|
|
|
|
public function test_bank_name_en_locale_required_when_locale_is_en(): void
|
|
{
|
|
// 로케일이 en일 때는 영문 은행명이 필수
|
|
app()->setLocale('en');
|
|
|
|
$validator = $this->validate([
|
|
'banks' => [
|
|
['code' => '004', 'name' => ['ko' => '국민은행']],
|
|
],
|
|
]);
|
|
|
|
$this->assertTrue(
|
|
$validator->errors()->has('banks.0.name.en'),
|
|
'로케일이 en일 때 영문 은행명이 없으면 실패해야 합니다'
|
|
);
|
|
}
|
|
}
|