무통장입금 주문의 현금영수증 발급을 관리자·회원·비회원 세 경로에서 제공하고,
입금이 확인되면 자동 발급하며, 부분환불로 금액이 바뀌면 전액취소 후 재발급한다.
발급 프로바이더에 종속되지 않도록 발급/취소는 필터 훅으로 위임한다.
자동발급 리스너는 결제완료 전이(after_payment_complete)와 입금만 기록
(after_deposit_recorded) 두 경로를 모두 구독한다 — 후자가 없으면 관리자가
"입금만 기록"했을 때 자동발급이 통째로 누락된다.
금액 동기화(syncFromOrder)는 트랜잭션이 닫힌 뒤에 부른다. 재발급 실패 이력은
국세청 신고 누락을 막는 원장이라 롤백에 휩쓸리면 안 되고, 프로바이더 왕복 2회
동안 DB 잠금을 붙들 수 없기 때문이다. 환불은 이미 확정됐으므로 어떤 실패도
호출부로 전파하지 않고 FAILED 이력 + 로그로 남겨 관리자 수동 복구에 맡긴다.
식별번호는 재발급용으로만 암호화 보관하고 구매확정 시점에 폐기한다.
그 시점을 잡기 위해 order.after_purchase_confirmed 훅을 신설했다.
함께 고친 사전 결함:
- 취소 시 total_cash_equivalent_amount 가 재계산되지 않아 syncFromOrder 가
"금액 변동 없음"으로 판정, 부분환불 후 재발급이 아예 동작하지 않았다.
산정 규칙을 PaymentMethodEnum::resolveCashEquivalentAmount 로 SSoT 화해
주문 생성 시점과 취소 재계산 시점이 같은 규칙을 쓰게 했다.
- 현금영수증 발급이 payment.receipt_url 을 덮어써 카드 매출전표 URL 이
유실됐다. 한 컬럼이 두 의미를 갖던 문제로, 현금영수증 URL 은 이력 테이블이
보관하도록 분리했다.
- OrderService::confirmOption 이 confirmed_at 을 무조건 now 로 덮어썼다
(v0.16.1 부터 존재). 재확정 시 최초 확정 시점이 소실되고 구매확정 훅이
중복 발화한다. OrderOptionService 와 대칭으로 멱등 가드를 넣었다.
- 코어 api:docgen 이 사람이 작성한 API 문서를 재생성 때마다 지웠다.
@generated 블록 안의 표 셀·응답 본문이 대상이었고, extractGeneratedBlock
이 앞의 `## ` 헤딩까지 거슬러 올라가 여러 엔드포인트를 한 덩어리로 반환하던
문제도 함께 고쳤다. 실측 산출 응답 예시에 @probed 출처 마커를 붙여, 마커가
없는 본문은 사람 작성분으로 보고 보존한다 — 실측 1회의 표본은 사람이 코드에서
읽어낸 사실보다 좁으므로 실측은 기본값이지 덮어쓰기 권한이 아니다.
ParameterDescriber 가 {order} 같은 path 파라미터를 "정렬 방향 asc/desc" 로
설명하던 오염도 정정했다.
58 lines
2.2 KiB
PHP
58 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Enums;
|
|
|
|
use Modules\Sirsoft\Ecommerce\Enums\PaymentMethodEnum;
|
|
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
/**
|
|
* 결제수단별 현금성 금액 산정 규칙 테스트 (A-3)
|
|
*
|
|
* 이 규칙은 주문 생성(OrderProcessingService)과 취소 재계산(OrderAdjustmentService)이
|
|
* 공유하는 SSoT 다. 한쪽만 바뀌면 부분환불 후 현금영수증 재발급액이 틀어진다.
|
|
*/
|
|
class PaymentMethodCashEquivalentTest extends ModuleTestCase
|
|
{
|
|
/**
|
|
* @return array<string, array{PaymentMethodEnum, int, int}>
|
|
*/
|
|
public static function cashEquivalentCases(): array
|
|
{
|
|
return [
|
|
// 무통장만 구매자가 직접 현금을 입금한다
|
|
'무통장 = 실결제액 전액' => [PaymentMethodEnum::DBANK, 11000, 11000],
|
|
// 가상계좌는 PG 가 현금영수증을 자동 발급한다
|
|
'가상계좌 = 0' => [PaymentMethodEnum::VBANK, 11000, 0],
|
|
'카드 = 0' => [PaymentMethodEnum::CARD, 11000, 0],
|
|
'마일리지 = 0' => [PaymentMethodEnum::POINT, 11000, 0],
|
|
'무료 = 0' => [PaymentMethodEnum::FREE, 0, 0],
|
|
];
|
|
}
|
|
|
|
#[Test]
|
|
#[DataProvider('cashEquivalentCases')]
|
|
public function 결제수단별_현금성_금액을_산정한다(
|
|
PaymentMethodEnum $method,
|
|
int $finalAmount,
|
|
int $expected,
|
|
): void {
|
|
$this->assertSame($expected, $method->resolveCashEquivalentAmount($finalAmount));
|
|
}
|
|
|
|
#[Test]
|
|
public function 음수_실결제액은_0_으로_보정된다(): void
|
|
{
|
|
$this->assertSame(0, PaymentMethodEnum::DBANK->resolveCashEquivalentAmount(-1));
|
|
}
|
|
|
|
#[Test]
|
|
public function 전액_마일리지_무통장주문의_현금성_금액은_0_이다(): void
|
|
{
|
|
// $isZeroPayable 판정은 결제수단과 무관하므로 "무통장 + 전액 마일리지" 0원 주문이 성립한다.
|
|
// 그 주문에 0원 현금영수증을 발급하지 않으려면 여기서 0이 나와야 한다.
|
|
$this->assertSame(0, PaymentMethodEnum::DBANK->resolveCashEquivalentAmount(0));
|
|
}
|
|
}
|