Skip to content
isdnetworks
Go back

결제를 주문 시점으로 당기며 빌링키는 버리지 않고 남겨, 결제 시점만 옮기는 쪽을 택했다

넘겨받은 커머스는 신선식품을 중량으로 판다. 그래서 결제도 출고 뒤에 실제 중량이 정해지면 청구하는 후결제였다. 이것을 주문 시점에 승인하는 선결제로 바꾸면서, 빌링키 방식을 버리는 대신 남겨 두고 청구 시점만 당기기로 했다. 대가는 시스템 곳곳에 깔린 「돈은 출고 뒤에 받는다」는 전제였다. 그 전제 넷을 하나씩 고치는 것이 이 결정의 비용이었다.

Table of contents

Open Table of contents

배경 — 빌링키는 두고 결제 시점만 당긴 이유

전환 방식은 두 가지가 있었다.

  1. 빌링키 방식 자체를 버리고 주문할 때마다 카드 승인을 받는다
  2. 빌링키와 카드 정보 수집은 그대로 두고, 그 키로 청구하는 시점만 출고 뒤에서 주문 시점으로 당긴다

궁극의 목표는 1번이지만 빌링키 방식은 당장 버릴 수 없다는 판단이라 2번으로 선택했고, 카드 등록·보관 화면은 그대로 둔다. 대가는 빌링키를 보관하는 부담이 당분간 남는다는 점이다. 「선결제면 빌링키가 필요 없다」는 목표이지 현재 상태가 아니라는 것을 처음부터 분명히 해 두어야, 이 화면을 없앨지 말지가 결정 항목으로 거듭 올라오지 않는다.

원인 — 깨진 전제 네 개

① 배치가 이미 결제된 주문을 다시 청구한다. 후결제에서는 출고 배치가 청구 주체였다. 선결제 주문도 그 배치를 그대로 지나가면 한 번 더 청구된다. 주문 시점에 이미 결제됐는지를 가리는 가드 isPaidAtOrder() 를 배치에 넣었고, 결제 예정 알림톡 3종도 같은 가드로 감쌌다. 배송 알림톡 4종은 가드 밖에 두어야 하므로, 가드 범위를 AST 로 검증하고 범위를 일부러 틀린 대조군이 검출되는지도 확인했다. GKE 에 배포한 뒤 컨테이너 안의 isPaidAtOrder 가 0건에서 4건이 된 것과 이미지 digest 가 바뀐 것을 되읽었다. 이 배포로 시험 주문 1건의 이중 청구가 실제로 막혔다.

② 주문을 취소해도 돈이 돌아가지 않는다. 기존 취소 함수들은 환불이 아니라 정기결제 해지였다. 후결제에서는 취소 시점에 아직 청구 전이라 돌려줄 돈이 없었기 때문이다. 선결제에서는 취소하면 주문만 지워지고 결제가 남는다. 결제대행 환불 분기를 넣었고, 환불이 실패하면 주문을 지우지 않고 refund_failed 로 남긴 뒤 CS 채널에 알린다. 판정식은 실제 주문 51건(환불 대상 카드 28건, 무통장 5건과 다른 배송일 18건은 대조군)과 합성 사례 9건(선물 주문·키 누락·TID 없음 등)으로 돌려 9건 모두 통과시켰다.

③ 결제완료 판정이 세 군데에서 다르다. 백오피스는 목록 라벨, 필터, Excel 내보내기가 각자 다른 필드로 결제완료를 판정한다. 새 결제대행 경로는 옛 경로의 결과 필드를 쓰지 않으므로, 필터와 Excel 에서는 카드 결제가 전부 「미완료」로 보였다. 그 Excel 의 결제상태 열이 실제로 미수금과 회계 집계에 쓰이고 있어, 결제를 마친 카드 주문이 미수금으로 잡히는 상태였다. 수단·시기와 무관하게 결제완료를 가르는 식을 totalPrice > 0 && payment.price >= totalPrice 하나로 정했고, 원장 실측(일괄 34 · 무통장 8 · 간편결제 6)과 미결제 대조군에서 모두 맞았다.

④ 결제 게이트웨이 규격이 바뀌었다. 전환 배포와 함께 카드 키 발급 요청의 파라미터가 소문자(cardNo·expYear…)에서 대문자(CardNo·ExpYear…)로 바뀌고 BuyerName 이 더해졌다. 소문자로 부르면 HTTP 400 과 resultCd 8001(필수 파라미터 누락)이 온다. 이 응답은 네트워크 경로 문제(403·8011)와 증상이 달라, 둘을 섞어 읽으면 IP 허용이나 프록시를 엉뚱하게 의심하게 된다.

검증 — 배포를 되읽은 방법

Cloud Functions 의 취소 함수는 firebase deploy --only functions:<이름> 으로 그 함수 하나만 배포했다. 프로젝트의 npm run deploy 를 쓰지 않은 이유는 대상 지정이 없으면 함수가 지워졌다가 새로 생기기 때문이다. 반면 --only 는 지정한 함수만 갱신한다. 배포 뒤 그 함수의 updateTime 이 바뀌고 versionId 가 1에서 2로 오른 것, 프로젝트 전체 함수 수가 51에서 51로 그대로인 것을 확인했다. 배포 명령이 성공했다는 것과 의도한 함수만 바뀌었다는 것은 다른 사실이다.

결과 — 꺼졌던 간편결제가 다시 살아났다

결제대행사를 옮길 때, 새 쪽이 정기결제형 간편결제를 지원하지 않아 간편결제 두 종을 미리 꺼 두었다. 주문 시점 승인으로 바뀌면 정기결제가 필요 없으므로 간편결제를 다시 받을 수 있다. 첫 선결제 배송일의 주문 9건 가운데 3건이 간편결제로 주문 시점에 승인됐다. 전환의 목적은 결제 시점이었지만, 막혀 있던 결제 수단이 돌아온 것이 지금까지 보인 가장 분명한 효과다.

결과 — 결제 시점만 옮긴 선택

정리


Share this post on:

Previous Post
130만 명에게 보낼 법정 통지 메일은 발송 시스템을 새로 짓지 않고 기존 큐에 채널 하나를 붙였다
Next Post
브라우저 코드에 박힌 Slack 토큰은 바꿔 끼우지 않고 경유지를 세워 없애기로 했다