취소가 채널에 반영이 안 된다는 문의가 왔다. 우리 쪽 상태부터 봤다.
Table of contents
Open Table of contents
증상 — 우리는 완료, 전파는 실패
상태 컬럼 셋을 꺼냈다.
SELECT cancel_status, distribution_cancel_status, distribution_claim_id
FROM order_cancels WHERE order_id = ?;
cancel_status complete
distribution_cancel_status fail
distribution_claim_id NULL
cancel_status 는 complete 인데 distribution_cancel_status 만 fail 이다.
전파 작업을 찾아봤더니 그 행 자체가 없었다.
SELECT * FROM distribution_jobs WHERE ... AND job = 'cancel';
distribution_jobs 에 실패한 행이 남은 것이 아니라 행이 아예 안 만들어진 상태였다. 다시 돌릴 대상이 없으니 재시도로 해결될 일이 아니었다.
원인 — 발행 지점이 환불에 있었다
이 작업을 누가 만드는지 역추적했다.
grep -rn "JobCompleteCancel" app/
한 곳이 나왔다.
// Order/Refund/Helper.php
public function completeRefund($refund)
{
...
JobCompleteCancel::dispatch($order);
}
JobCompleteCancel::dispatch 가 취소 처리부가 아니라 completeRefund 안에 있었다.
[예상] 취소 처리 → 전파 작업 생성
[실제] 환불 완료 → 전파 작업 생성
전파가 취소가 아니라 환불에 매달려 있는 구조였다. 취소만 하고 completeRefund 를 안 지나가면 그 작업은 만들어지지 않는다.
환불이 없었다
그 주문에 환불이 있는지 셌다.
SELECT COUNT(*) FROM order_refunds WHERE order_id = ?;
0
환불이 없으니 completeRefund 를 지날 일이 없었다.
왜 환불이 없는지도 따라갔다.
SELECT provider_id FROM orders WHERE id = ?;
SELECT provider_id FROM distribution_orders WHERE order_id = ?;
NULL
NULL
퇴점한 입점사의 주문이라 orders 와 distribution_orders 양쪽의 provider_id 가 비어 있었다.
제약 — 구조적으로 불가능한 경로
퇴점 처리되면서 provider_id 가 가리키던 결제 정보와의 연결이 끊긴 것으로 보인다.
환불하려면 결제 정보가 필요
↓
없음
↓
환불 레코드 안 생김
↓
전파 작업 안 생김
코드나 자료를 손봐서 될 문제가 아니라 이 경로로는 도달할 수 없는 상태였다.
몇 번을 다시 돌려도 결과가 같은 종류다. order_refunds 가 안 생기는 한 이 주문에서는 연동이 성립하지 않는다.
남은 길은 외부 관리 화면에서 직접 취소하는 것이고 그것은 운영이 손으로 해야 한다. 고칠 수 있는 문제와 다른 경로로 넘겨야 하는 문제를 가르는 것이 여기서 필요했다.
조치 — 바뀐 확인 순서
이 건으로 같은 문의가 왔을 때의 순서가 정해졌다.
[전] 전파 작업 확인 → 없음 → 왜 없지 → 클레임 확인 → ...
[후] 환불 레코드 존재 확인 → 없으면 여기서 끝
distribution_jobs 부터 보면 없다는 사실만 알고 왜 없는지는 모른다.
SELECT COUNT(*) FROM order_refunds WHERE order_id = ?;
이 한 줄로 구조적으로 불가한 건인지 아닌지가 갈린다.
조사에서 먼저 볼 것을 고르는 기준이 거기 있었다. 결과를 가장 크게 가르는 조회를 앞에 두면 나머지 단계를 안 밟게 된다.
검증 — 미발생을 찾는 점검
왜 환불에 매달았는지도 짚어 봤다.
[취소 시점에 전파] 취소했지만 환불이 안 될 수도 있음
[환불 완료에 전파] 돈이 돌아간 뒤에 밖에 알림
돈 처리가 끝난 뒤에 알리는 것은 그 자체로 안전한 순서다.
먼저 알리고 환불이 실패하면 밖에서는 complete 로 보이는데 돈은 안 돌아간 상태가 된다. 다만 이 설계는 환불이 항상 있다는 전제 위에 서 있고 이번 건이 그 전제가 깨지는 경우였다.
전제가 깨지면 조용히 빠지는 것이 이 구조의 성질이다.
[에러] 작업이 실패로 남음 → 눈에 띔
[미생성] 작업이 없음 → 안 보임
distribution_jobs 의 실패 목록에 안 뜨니 문의가 오기 전까지 아무도 모른다.
없는 것을 찾으려면 있어야 할 것과 대조하는 수밖에 없다.
SELECT o.id FROM orders o
JOIN order_cancels c ON c.order_id = o.id
WHERE c.cancel_status = 'complete'
AND c.distribution_cancel_status <> 'success'
AND NOT EXISTS (SELECT 1 FROM order_refunds r WHERE r.order_id = o.id);
cancel_status 는 완료인데 전파가 안 됐고 order_refunds 도 없는 건을 찾는다.
이 수가 0이 아니면 같은 케이스가 쌓이고 있다는 뜻이다. 앞서 본 것들도 같은 모양이었다. 실패한 중지 위에 삭제가 얹혀 사라진 건이나 승인이 필터에 걸린 건이 전부 오류가 아니라 미발생이었다.
정리
- 전파 작업이 취소가 아니라 환불 완료 시점에 발행된다
- 환불이 없으면 그 발행 지점을 안 지난다
- 실패한 작업이 아니라 작업 자체가 없으면 재시도할 대상이 없다
- 결제 정보가 없는 주문은 구조적으로 그 흐름을 못 탄다
- 고칠 수 있는 문제와 다른 경로로 넘길 문제를 가른다
- 확인은 환불 레코드가 있는지부터 한다
- 결과를 가장 크게 가르는 조회를 앞에 둔다
- 돈 처리 뒤에 알리는 순서 자체는 안전하다
- 다만 환불이 항상 있다는 전제 위에 있다
- 오류는 목록에 남고 미발생은 안 남는다
- 미발생은 있어야 할 것과 대조하는 점검으로만 보인다