이슈 본연: API 레퍼런스의 미채움 마커 5종(실측 제외/TODO/필드 없음/대표 에러 없음)
1,194건을 코드에서 읽어 전수 채우고(0건), api:docgen 재생성이 사람이 채운 내용을
손실·열화시키던 멱등성 결함 4종(CRLF, 표 파이프 이스케이프, 에러 서술 보존, 중복
라우트명 키)을 근본 수정. --check 를 실측 제외와 드리프트가 구분되도록 재정의.
ParameterDescriber/ResourceFieldDescriber 에 leaf 폴백·SEO 스코프·공통 사전 확장.
파생 결함(문서 실측 중 발견): ResponseHelper 기본 메시지 키 6종이 존재하지 않는
messages.* 를 가리켜 응답 message 가 번역문 대신 키 문자열로 노출되던 문제를 common.*
으로 정정하고, 코어 7 + gdpr/marketing/pay_kginicis/verification/ecommerce 확장의
호출부와 누락 lang 키(게시판 10키 + 이커머스 category_images 4키 포함)를 ko/en/ja
전수 정정. 중복 그룹 키 2건은 기존 키로 호출부 통합.
보안: GET /api/identity/challenges/{id} 는 권한 가드 없는 공개 폴링 엔드포인트인데
Service::getStatus 가 attempts/max_attempts 를 응답에 담아 남은 시도 횟수를 추론할 수
있었다. 두 필드를 제거하고, 챌린지 화면(admin_basic/basic)의 서버 attempts 참조를
제거(남은 횟수 UI 는 query fallback + verify 실패 시 로컬 증가로 유지).
부수 정리(변경셋에 들어온 board 컨트롤러의 audit 사전 결함): FormRequest 4개 신설로
base Request 주입 제거, CommentController 의 Board 직접 호출을 BoardService 위임으로
전환, PHPDoc @return 보강. board 스위트에서 발견한 stale test 3건(seed SSoT 불일치,
admin 계정 user 역할 누락)도 같은 세션에서 정정.
버전 정렬(공개 release 기준): 코어 7.0.3→7.0.4, sirsoft-board 1.0.1→1.0.2,
verification_kginicis 1.0.0→1.0.1, admin_basic 1.0.1→1.0.4.
152 lines
5.0 KiB
PHP
152 lines
5.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Plugins\Sirsoft\PayKginicis\Controllers;
|
|
|
|
use App\Helpers\ResponseHelper;
|
|
use App\Http\Controllers\Api\Base\AdminBaseController;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Plugins\Sirsoft\PayKginicis\Concerns\SanitizesPgResponse;
|
|
use Plugins\Sirsoft\PayKginicis\Services\KgInicisApiService;
|
|
|
|
/**
|
|
* KG 이니시스 에스크로 구매거절확인 관리자 컨트롤러
|
|
*
|
|
* 구매자가 구매거절 선택 후 판매자(관리자)가 INIAPI v1 type=Dncf로 거절 확인 처리.
|
|
* 메뉴얼: https://manual.inicis.com/pay/escrow_pc.html#dncf
|
|
*/
|
|
class AdminEscrowDenyConfirmController extends AdminBaseController
|
|
{
|
|
use SanitizesPgResponse;
|
|
|
|
/** 에스크로 구매거절확인 PG 응답 저장 허용 필드 */
|
|
private const ESCROW_DENY_CONFIRM_RESPONSE_KEYS = [
|
|
'resultCode',
|
|
'resultMsg',
|
|
'tid',
|
|
'TID',
|
|
'originalTid',
|
|
'mid',
|
|
'MID',
|
|
'type',
|
|
];
|
|
|
|
public function __construct(
|
|
private readonly KgInicisApiService $apiService,
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* confirm
|
|
*
|
|
* @param Request $request
|
|
* @param string $orderNumber
|
|
* @return JsonResponse
|
|
*/
|
|
public function confirm(Request $request, string $orderNumber): JsonResponse
|
|
{
|
|
$payment = $this->findEscrowPayment($orderNumber);
|
|
|
|
if (! $payment) {
|
|
return ResponseHelper::error('common.failed', 404, null);
|
|
}
|
|
|
|
$meta = $payment->payment_meta ? json_decode($payment->payment_meta, true) : [];
|
|
|
|
// 이미 구매거절확인 완료된 경우
|
|
if (isset($meta['escrow_deny_confirm'])) {
|
|
return ResponseHelper::error('common.failed', 422, [
|
|
'message' => ['이미 구매거절확인이 완료되었습니다.'],
|
|
]);
|
|
}
|
|
|
|
$dcnfName = trim((string) $request->input('dcnf_name', '관리자'));
|
|
|
|
Log::info('KG Inicis: escrow deny confirm requested', [
|
|
'order_number' => $orderNumber,
|
|
'tid' => $payment->transaction_id,
|
|
]);
|
|
|
|
try {
|
|
$this->apiService->useEscrowCredentials(true);
|
|
|
|
$pgResponse = $this->apiService->denyConfirmEscrow([
|
|
'originalTid' => $payment->transaction_id,
|
|
'dcnfName' => $dcnfName,
|
|
]);
|
|
|
|
$resultCode = $pgResponse['resultCode'] ?? '';
|
|
$sanitizedPgResponse = $this->sanitizePgResponse($pgResponse, self::ESCROW_DENY_CONFIRM_RESPONSE_KEYS);
|
|
|
|
if ($resultCode !== '00') {
|
|
Log::warning('KG Inicis: escrow deny confirm failed', [
|
|
'order_number' => $orderNumber,
|
|
'result_code' => $resultCode,
|
|
'result_msg' => $pgResponse['resultMsg'] ?? '',
|
|
'pg_response' => $sanitizedPgResponse,
|
|
]);
|
|
|
|
return ResponseHelper::error('common.failed', 502, [
|
|
'message' => [$pgResponse['resultMsg'] ?? '구매거절확인에 실패했습니다.'],
|
|
]);
|
|
}
|
|
|
|
// payment_meta에 구매거절확인 이력 저장
|
|
$meta['pg_response_sanitized'] = true;
|
|
$meta['escrow_deny_confirm'] = [
|
|
'confirmed_at' => now()->toDateTimeString(),
|
|
'dcnf_name' => $dcnfName,
|
|
'pg_response' => $sanitizedPgResponse,
|
|
];
|
|
|
|
DB::table('ecommerce_order_payments')
|
|
->where('id', $payment->id)
|
|
->update([
|
|
'payment_meta' => json_encode($meta, JSON_UNESCAPED_UNICODE),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
Log::info('KG Inicis: escrow deny confirm completed', [
|
|
'order_number' => $orderNumber,
|
|
'tid' => $payment->transaction_id,
|
|
]);
|
|
|
|
return ResponseHelper::success('common.success', [
|
|
'result_code' => $resultCode,
|
|
'result_msg' => $pgResponse['resultMsg'] ?? 'OK',
|
|
]);
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error('KG Inicis: escrow deny confirm exception', [
|
|
'order_number' => $orderNumber,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return ResponseHelper::error('common.failed', 500, [
|
|
'message' => [$e->getMessage()],
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function findEscrowPayment(string $orderNumber): ?object
|
|
{
|
|
return DB::table('ecommerce_order_payments as p')
|
|
->join('ecommerce_orders as o', 'o.id', '=', 'p.order_id')
|
|
->where('o.order_number', $orderNumber)
|
|
->where('p.pg_provider', 'kginicis')
|
|
->where('p.is_escrow', true)
|
|
->whereNotNull('p.transaction_id')
|
|
->select([
|
|
'p.id',
|
|
'p.transaction_id',
|
|
'p.payment_meta',
|
|
])
|
|
->first();
|
|
}
|
|
}
|