Files
Gnuboard7/plugins/_bundled/sirsoft-tosspayments/AGENTS.md
T
HeuJung 8328b1db77 docs(core,extensions): 확장 개발자 문서 9세트 집필과 생성기 정합 보강
S1 파일럿 3종(sirsoft-board · sirsoft-gdpr · sirsoft-admin_basic)에 이어
결제·본인인증 동형군 6종의 AGENTS.md · README.md · docs/ 5문서를 집필했다.
확장을 고치려는 쪽이 매번 src/ 를 훑어 구조를 재발견하지 않도록, 설계 의도와
확장점(발행·구독 훅)·수정 시 동반 의무·금지 패턴을 코드 근거로 서술했다.

생성기(ExtensionDocScaffolder)에서 표를 무의미하게 만들던 세 결함을 함께 고쳤다.
getLayoutExtensions 기본 구현이 돌려주는 절대경로가 파일 목록과 중복돼 로컬
머신 경로가 커밋 문서에 실리던 문제, getNotificationDefinitions 를 'key'/'event'
로 읽어 모든 행이 '-' 로 찍히던 문제, getSettingsLayout 절대경로가 정규화 없이
노출되던 문제다. 셋 다 예외를 남기지 않고 표만 조용히 망가뜨리므로
ExtensionDocContractTest 에 각각의 되돌림 red 를 확인한 단언을 두었다.
템플릿의 extensions/{id}/ 는 모듈·플러그인의 발행과 반대 방향(오버라이드)이라
별도 블록(template-overrides)으로 분리했다.

sirsoft-admin_basic 의 컴포넌트·핸들러·레이아웃 문서를 코어 docs/ 에서 그 템플릿
소유로 이관하고, 남은 참조 6축을 재는 가드를 추가했다. 이관 후 남은 옛 경로는
오류가 아니라 헛걸음으로만 나타나 드러나지 않는다. 의 상대 링크가
한 단계 얕아 공유 docs/ 대신 를 가리키던 문제도 함께 고쳤다.

README 상단의 확장명 이미지 배지를 평문 H1 로 바꿨다( 지시 2026-08-31).
루트 README.md · README.ko.md 도 같은 기준을 적용했다. 정보 배지는 유지한다.

sirsoft-gdpr: 회원탈퇴로 자동 철회된 동의가 관리자 동의 이력 화면의 출처 필터로
걸러지지 않던 문제를 고쳤다. Repository 가 'withdraw' 리터럴을 직접 UPDATE 에
싣는데 그 값이 ConsentSource enum 에 없어, 화면 필터 옵션·라벨 어느 쪽에도
도달하지 못했다. 어휘를 enum 단일 출처로 모으고 ko·en·ja 라벨과 필터 옵션을
함께 채웠으며, 어휘 대조 테스트의 모집단에 Repository 를 편입했다.
2026-08-31 15:57:44 +09:00

13 KiB

토스페이먼츠 — 에이전트 가이드

이 문서는 이 플러그인을 수정하는 에이전트·확장개발자를 위한 것입니다. 도입 검토·운영 관점은 README.md 를 보세요.

TL;DR (5초 요약)

1. 유형: 플러그인 (sirsoft-tosspayments) — 토스페이먼츠 PG 연동(통합결제창 SDK/가상계좌 웹훅 secret 대조/에스크로 3-상태). 소유 테이블 없음 — 상태는 sirsoft-ecommerce 소유
2. 확장 방식: `RegisterPgProviderListener`/`RegisterTossPaymentMethodsListener`/`RegisterCashReceiptProviderListener` 로 이커머스 레지스트리에 등록 — 이커머스 코드는 이 플러그인을 모른다
3. 건드리면 안 되는 것: 결제 승인 확인 시 서버가 재계산한 금액과 PG 콜백 금액 대조(amount mismatch 검사) 생략, 가상계좌 웹훅의 secret 대조(`webhook_secret_verify`) 우회 — 토스는 notify IP 목록·서명을 제공하지 않아 secret 대조가 유일한 위조 방지 수단
4. 작업 위치: `plugins/_bundled/sirsoft-tosspayments` — 활성 디렉토리 직접 수정 금지
5. 반영: `php artisan plugin:update sirsoft-tosspayments --force`

1. 이 확장은 무엇인가

토스페이먼츠 PG(결제 게이트웨이)를 sirsoft-ecommerce에 연결하는 어댑터입니다. 승인은 브라우저 리다이렉트 기반입니다 — 통합결제창 SDK(js.tosspayments.com/v2/standard)가 결제를 처리한 뒤 브라우저를 ?paymentKey&orderId&amount가 붙은 콜백 URL로 리다이렉트하고, 서버가 그 파라미터로 승인 확인 API를 호출합니다. 다른 PG 플러그인(iframe 팝업/CLI/SOAP)과 달리 프론트엔드 계층이 SDK 호출 하나로 끝나고 프로토콜 복잡도가 대부분 서버 쪽(확인·웹훅)에 있습니다.

이 플러그인은 결제창형과 주문서형(order_sheet_mode) 두 UI 모드를 지원합니다. 결제창형은 통합결제창이 결제수단을 전부 처리하는 카드 하나로 노출되고, 주문서형은 method_* 설정으로 활성화한 개별 토스 결제수단(카드/가상계좌/계좌이체/휴대폰/토스페이/ 카카오페이/네이버페이/페이코/삼성페이)이 체크아웃 화면에 개별 버튼으로 뜹니다 — 어느 쪽이든 최종 처리는 토스 결제창 하나로 귀결되므로 각 수단은 pg_provider 를 이 플러그인 자신으로 고정(pg_locked)합니다(§AGENTS.md "4. 확장점").

설계 원칙: 이 플러그인도 상태를 소유하지 않습니다(§data-model.md — 모델·테이블 0개). 가상계좌 웹훅 검증은 토스가 notify IP 목록이나 서명을 제공하지 않는다는 제약에서 비롯됩니다 — 승인 확인 응답에만 실리는 secret 값을 payment_meta에 저장해 두었다가 웹훅이 도착하면 대조하는 것이 토스 공식 문서가 제시하는 유일한 위조 방지 수단입니다.

의도적으로 하지 않는 것: 승인 확인 시 PG가 돌려준 금액과 서버가 재계산한 주문 금액이 다르면(amount_mismatch) 결제를 완료 처리하지 않고 실패로 되돌립니다 — 콜백 URL의 amount 쿼리 파라미터는 브라우저를 거치므로 신뢰할 수 없는 입력이기 때문입니다.

2. 디렉토리 지도

경로 역할 수정 시 필요한 절차
plugin.json manifest (버전 SSoT) version 변경 시 package.json·package-lock.json·composer.json 동기화
plugin.php 진입 클래스 (선언형 표면 SSoT) 표면 변경 시 ext:docgen 재실행 + 코어 최소 버전 검토
src/Controllers/ 컨트롤러 API 표면 변경 시 api:docgen 재실행
src/Http/Requests/ FormRequest (검증 SSoT) 검증 규칙은 Service 가 아니라 여기에 둔다
src/Services/ 비즈니스 로직 Repository 인터페이스 주입 (구체 클래스 금지)
src/Listeners/ 훅 리스너 Repository 경유 (Model·DB 파사드 직접 접근 금지)
src/routes/ 라우트 모든 라우트에 name() 필수
upgrades/ 업그레이드 스텝 DB·설정 구조 변경 시 작성 (모듈/플러그인 전용)
resources/layouts/ 레이아웃 JSON php artisan plugin:update sirsoft-tosspayments --force (빌드 불필요)
resources/js/ 프론트 엔트리·핸들러 php artisan plugin:build → php artisan plugin:update sirsoft-tosspayments --force
resources/extensions/ 다른 확장 레이아웃에 주입하는 조각 php artisan plugin:update sirsoft-tosspayments --force
dist/ 커밋되는 빌드 산출물 --production 으로 재빌드 (sourceMappingURL 잔존 금지)
config/ 확장 config 설정 기본값은 settings 스키마와 어긋나지 않게
tests/ 테스트 변경 범위만 필터 실행
CHANGELOG.md 변경 이력 버전 상향 시 항목 추가 (미기재 시 버전 상향 불가)
components.json 편집기 컴포넌트 선언 (레이아웃 저작자가 읽는 props 계약) php artisan plugin:update sirsoft-tosspayments --force
docs/ 개발자 문서 표면 변경 시 php artisan ext:docgen 재실행
lang/ 다국어 키 추가 시 ko·en 동시 반영 + 번들 ja 팩 동기화

3. 핵심 흐름

결제 승인: 통합결제창이 브라우저를 /payment/callback?paymentKey&orderId&amount로 리다이렉트 → PaymentCallbackController가 주문 조회 → sirsoft-tosspayments.payment.before_confirm 훅 → TossPaymentsApiService::confirmPayment() 가 토스 승인 확인 API 호출 → 응답 금액과 서버 재계산 금액 대조(불일치 시 실패 처리) → sirsoft-tosspayments.payment.after_confirm 훅 → 이커머스 주문 결제 완료 처리. 가상계좌가 발급된 경우 응답에 실린 secret을 payment_meta.toss_secret에 저장해 이후 웹훅 대조에 씁니다.

가상계좌 입금 웹훅: 토스가 /webhook/deposit으로 POST → 저장된 toss_secret과 웹훅 본문의 secret을 대조(webhook_secret_verify 설정이 꺼져 있지 않은 한 강제) → 일치하면 결제 완료 처리, 불일치하면 경고 로그만 남기고 처리하지 않습니다.

결제 취소(환불): 관리자가 주문 취소(cancel_pg=true) → 코어가 sirsoft-ecommerce.payment.refund 필터 발화 → PaymentRefundListener가 토스 취소 API 호출 → before_cancel/after_cancel 훅 발화.

설정 저장 검증: 관리자가 플러그인 설정을 저장 → core.plugin_settings.before_save (동기 훅, sync: true) → ValidateTossSettingsListener가 vbank_valid_hours(1~2160시간)와 use_escrow(off/on/buyer_choice 3-상태) 범위를 검증 → 위반 시 ValidationException으로 저장 자체를 막습니다.

4. 확장점

확장점 수 상세
발행 훅 4개 발행 훅
구독 훅 9개 구독 훅
훅 리스너 6개 훅 리스너
레이아웃 확장 3개 레이아웃 확장
미들웨어 0개 미들웨어
브로드캐스트 채널 0개 브로드캐스트 채널
스케줄 0개 스케줄
알림 정의 0개 알림 정의

before_confirm/before_cancel은 API 호출 전 개입 지점이라 예외를 던지면 실제 토스 호출이 일어나지 않습니다. core.plugin_settings.before_save를 ValidateTossSettingsListener 가 구독하는 것은 다른 PG 플러그인에는 없는 패턴입니다 — 이 훅은 코어 PluginSettingsService 가 발행하며, sync: true가 없으면 큐로 비동기 디스패치되어 ValidationException이 워커 안에서 죽고 저장은 그대로 진행됩니다(§CLAUDE.md "Listener 데이터 접근 규정" 의 sync 훅 규칙과 동일한 이유).

5. 수정 시 동반 의무

  • _bundled 에서만 수정하고 php artisan plugin:update sirsoft-tosspayments --force 로 반영
  • manifest version 상향 시 package.json · package-lock.json · composer.json 동기화 + CHANGELOG 기재
  • 발행 훅 추가·이름 변경 시 php artisan ext:docgen 재실행 (구독하는 확장의 계약이 바뀝니다)
  • API 표면 변경 시 php artisan api:docgen --scope=plugin:sirsoft-tosspayments 재실행 + docs/api/** 갱신
  • 레이아웃 JSON 변경 시 빌드 없이 update 만 — 신규 Tailwind 클래스는 빌드된 CSS 에 존재하는지 확인
  • TSX/TS 변경 시 --production 재빌드 후 dist/ 커밋 (sourceMappingURL 잔존 금지)
  • 다국어 키 추가 시 ko·en 동시 반영 + 번들 ja 언어팩 증분 동기화
  • 승인 확인에서 금액 대조(amount_mismatch) 로직을 우회하거나 완화하지 않는다
  • 가상계좌 웹훅의 secret 대조(webhook_secret_verify)를 기본값 true 이외로 바꾸지 않는다 — 끄면 토스 노티 위조를 막을 수단이 사라진다
  • order_sheet_mode 관련 로직을 고칠 때 RegisterPgProviderListener(enabled_methods)와 RegisterTossPaymentMethodsListener(builtin 결제수단 주입) 양쪽을 함께 갱신 — 한쪽만 고치면 설정과 노출 목록이 어긋난다
  • ValidateTossSettingsListener에 새 범위 검증을 추가하면 core.plugin_settings.before_save 의 sync: true를 유지

6. 금지 패턴

금지 올바른 사용 이유
콜백 URL의 amount 쿼리 파라미터를 그대로 신뢰해 결제 완료 처리 서버가 주문 금액을 재계산해 PG 응답 금액과 대조, 불일치 시 실패 처리 콜백은 브라우저를 거치므로 사용자가 쿼리 파라미터를 조작해 실제 결제 금액보다 낮은 금액으로 완료 처리를 유도할 수 있다
가상계좌 웹훅의 secret 대조를 생략하거나 항상 통과 payment_meta.toss_secret과 웹훅 본문의 secret을 항상 대조 토스는 notify IP 목록·서명을 제공하지 않아 secret 대조가 유일한 위조 방지 수단이다 — 생략하면 제3자가 임의 주문에 대해 위조 입금통보를 보낼 수 있다
core.plugin_settings.before_save 리스너에 sync: true 없이 등록 저장을 막아야 하는 검증 훅은 반드시 sync: true 기본값(비동기 큐)이면 ValidationException이 워커 안에서 죽고 저장이 그대로 진행되어 검증이 무력화된다
라이브 시크릿 키를 로그·에러 메시지에 노출 운영 키는 항상 마스킹하거나 로그 대상에서 제외 노출되면 제3자가 서버측 API를 위조 호출할 수 있다

7. 테스트 실행

종류 개수 위치
PHPUnit 12개 plugins/_bundled/sirsoft-tosspayments/tests
Vitest 5개 vitest.config.ts
Playwright 0개 —
시나리오 매니페스트 3개 tests/scenarios

기저 TestCase: tests/PluginTestCase.php — 확장 테스트는 이 클래스를 상속합니다 (Tests\TestCase 직접 상속 금지).

# PHPUnit (변경 범위만) (Bash)
php vendor/bin/phpunit plugins/_bundled/sirsoft-tosspayments/tests --filter='<대상클래스>'

# Vitest (확장 디렉토리에서) (PowerShell)
cd plugins/_bundled/sirsoft-tosspayments && powershell -Command "npm run test:run -- <대상>"

무필터 전체 실행은 금지되어 있습니다 — 변경 범위에 걸리는 대상만 지정해 실행합니다.

8. 문서 목차

문서 내용 상태
docs/README.md 문서 통합 목차와 실측 집계 ✅
docs/architecture.md 설계 의도·계층 지도·디렉토리 맵 ✅
docs/extension-points.md 발행/구독 훅·미들웨어·채널·스케줄 ✅
docs/data-model.md 모델·소유 테이블·마이그레이션·Enum ✅
docs/settings.md 설정 스키마·권한·메뉴·라우트·의존 관계 ✅
docs/frontend.md 레이아웃·액션 핸들러·전역 진입점·에셋 ✅
docs/api/ API 레퍼런스 (엔드포인트별 파라미터·응답 필드) ✅
CHANGELOG.md 변경 이력 ✅