개발 문서
PayApp 정기결제 연결
Immercy의 Indie 요금은 월 9,900원입니다. 휴대전화 번호와 명시적인 정기결제 동의를 받은 뒤 PayApp의 신용카드 결제 화면으로 이동합니다. 카드번호, 유효기간, 카드 비밀번호는 Immercy에서 받거나 보관하지 않습니다. 현재 결제사 계정 개설, 심사, 실제 승인·취소 확인은 완료되지 않았습니다.
운영자가 준비할 항목
- PayApp 판매자 계정을 만들고 사업자 및 정산 정보를 등록합니다. 정기결제 이용 가능 여부와 필요한 심사를 PayApp에 확인합니다.
- 판매자 설정의 연동 정보를 아래 Vercel Production 환경 변수에 등록합니다. 실제 값은 Git, 이 문서, 브라우저 코드에 적지 않습니다.
- 도메인과 HTTPS를 먼저 연결합니다.
https://immercy.eturnalove.com/api/webhooks/payapp이 로그인 화면이나 다른 주소로 리디렉션되지 않도록 합니다. Vercel Deployment Protection, 방화벽의 대화형 챌린지로 이 경로가 막혀서도 안 됩니다. - 정기결제 약정의 최종 만료일을 정해
PAYAPP_REBILL_EXPIRE에YYYY-MM-DD로 설정합니다. 이는 API 필수 값이며 임의의 영구 구독으로 대체하지 않았습니다. 신규 신청은 이 날짜가 32일 이상 남아 있을 때만 열립니다. - 운영자 연락처·사업자 정보·해지/환불 조건을 사이트 운영 정보와 대조하고 공개합니다. 부분 환불 시 남은 이용 권한을 유지하는 현재 정책도 일치시킵니다.
| 환경 변수 | 값의 출처 |
|---|---|
PAYAPP_USER_ID | 판매자 로그인 아이디 |
PAYAPP_LINK_KEY | 판매자 설정의 연동 Key |
PAYAPP_LINK_VALUE | 판매자 설정의 연동 Value |
PAYAPP_REBILL_EXPIRE | 운영자가 결정하고 구매자에게 표시하는 약정 만료일 |
APP_URL | https://immercy.eturnalove.com |
위 설정이 없거나 부정확하면 결제 요청이 열리지 않습니다. 기존 결제 해지와 통보 수신은 신규 신청 만료일이 지났더라도 계속 처리하도록 분리했습니다.
서버 연동
공식 API는 https://api.payapp.kr/oapi/apiLoad.html에 UTF-8 form POST로 요청하고 URL 인코딩된 응답을 반환합니다. 이 구현은 rebillRegist로 신청하고 rebillCancel로 다음 청구를 중단합니다. 자세한 필드와 통보 규격은 PayApp 개발자센터를 기준으로 작성했습니다.
서버가 상품·금액·주기를 고정합니다. 등록 시 사용한 실제 필드는 userid, goodname, goodprice, recvphone, recvemail, rebillCycleType, rebillCycleMonth, rebillExpire, feedbackurl, failurl, returnurl, var1, var2, smsuse, openpaytype입니다. 공개 매뉴얼에 없는 결제 조회 API나 등록 요청의 멱등 키를 임의로 추가하지 않았습니다.
var1은 Immercy 주문 ID이고 var2는 주문마다 다른 무작위 값입니다. DB에는 var2의 해시만 남깁니다. 결제 통보는 판매자 아이디, 연동 Key/Value, 주문 식별자, 등록 번호, 금액을 모두 확인합니다. 미리 등록한 주문과 맞지 않으면 이용 권한을 바꾸지 않습니다.
returnurl은 화면 복귀에만 사용합니다. checkout=returned 쿼리나 결제 URL 생성 성공으로 Indie 권한을 지급하지 않습니다. DB 트랜잭션이 결제 기록, 중복 통보 기록, 해당 기간의 이용 권한을 함께 저장한 뒤에만 통보 응답을 SUCCESS로 반환합니다. 처리 실패는 FAIL입니다.
공개 매뉴얼에서 일반 결제의 서버 조회 API를 확인하지 못했으므로, 별도의 조회 API 검증을 했다고 표시하지 않습니다. 판매자·주문 인증을 통과한 결제 통보가 자동 반영의 근거입니다. 운영자는 누락된 통보를 판매자 관리 화면과 대조하고 PayApp에 재통보를 요청해야 합니다.
구독 기간과 상태
결제 신청일이 한국 시간 128일이면 해당 일이 월 결제일입니다. 2931일 신청은 말일 결제로 등록합니다. 승인 시각부터 승인 다음 달의 지정 결제일이 한국 시간으로 끝날 때까지 이용 권한을 부여하여 결제사의 당일 청구 처리 시간을 수용합니다. 예를 들어 9월 5일 승인분은 10월 5일이 끝날 때까지, 9월 29일의 말일 구독 승인분은 10월 31일이 끝날 때까지 유효합니다. 약정 만료일은 새로운 청구의 끝이며 이미 결제한 기간을 잘라내지 않습니다.
이는 Immercy의 월간 이용 기간 정책입니다. PayApp 공개 매뉴얼에는 최초 승인과 지정일이 다른 경우의 첫 자동 청구 시점을 세부적으로 명시하지 않았으므로, 29~31일 가입과 결제 화면에서 늦게 승인한 경우를 실제 가맹점 시험에서 대조한 뒤 결제를 개통합니다. 청구 시점이 위 정책과 다르면 PayApp 계약/등록 방식을 확인하여 조정하고 이 상태로 판매를 시작하지 않습니다.
| 상태 | 처리 |
|---|---|
registering | 결제사 신청 중. 동일 계정의 추가 신청 차단 |
pending | 만들어진 결제 URL을 다시 사용. 권한 부여 없음 |
active | 검증된 결제 승인으로 해당 기간 Indie 적용 |
past_due | 갱신 실패 또는 이용 기간 만료. 이미 결제한 기간은 유지 |
canceled | 결제사가 향후 청구 중단 확인. 기존 결제 기간까지 사용 |
requires_review | 신청 응답이 불확실하여 운영자 대조 필요 |
해지는 환불과 별개입니다. API에서 해지 확인을 받지 못한 경우 화면에 성공을 표시하지 않습니다. 해지 후 남은 유료 기간이 끝나면 새 구독을 신청할 수 있습니다. Enterprise는 관리자 계약을 따르므로 Indie 신청을 막습니다. 계정 정지 상태에서도 본인의 결제 상태 조회와 해지는 허용합니다.
승인 번호별 기간을 따로 저장합니다. 과거 결제의 환불이 최신 정상 결제 기간을 취소하지 않으며, 중복 승인으로 기간이 늘어나지 않습니다. 전체 환불은 해당 승인 건의 권한을 제거합니다. 부분 취소는 원거래 번호와 고유 취소 번호로 합산하고, 부분 취소 누계가 전액에 이르기 전까지 해당 기간을 유지합니다. 환불 후 늦게 들어온 승인 통보로 환불 기록이 되돌아가지 않습니다.
운영 중 대조와 복구
subscriptions, billingAccounts, billingPayments, billingRefunds, billingEvents가 결제 기록입니다. 원문 통보에는 연동 비밀과 개인정보가 포함될 수 있어 요청 본문 전체를 로그에 남기지 않습니다. 금융 원장은 TTL로 자동 삭제하지 않습니다.
신청 요청이 시간 초과되어 requires_review가 된 경우 새로운 정기결제를 자동으로 만들지 않습니다. PayApp 판매자 화면에서 주문 시각·상품·수신자와 주문 ID를 대조합니다. 등록된 약정이 있다면 PayApp에서 그 약정을 먼저 해지하거나 정상 통보를 복구합니다. 약정이 실제로 없거나 해지되었다는 확인 없이 DB의 차단을 풀지 않습니다. 확인 후 해당 주문만 status: 'canceled', cancelAtPeriodEnd: true로 정정하고, 확인한 담당자·시각·근거를 운영 기록에 남깁니다. billingAccounts나 승인 원장을 삭제해서 해결하지 않습니다.
환불은 판매자 관리 화면에서 원거래를 확인하고 처리합니다. 해지까지 필요한 요청이라면 정기결제 약정도 별도로 해지합니다. 처리 후 해당 승인 번호의 통보가 반영되었는지 원장을 대조합니다. 금융 내역을 임의로 만들어 유료 권한을 지급하지 않습니다. 결제사의 실제 통보 데이터가 문서와 다르면 원문을 안전하게 보관하여 필드 차이를 확인한 뒤 어댑터를 수정합니다.
개통 후 실제 확인할 항목
이 목록은 운영 담당자가 설정 후 수행할 실제 확인 절차이며, 이번 구현에서 실행한 결과가 아닙니다.
- PayApp이 허용하는 시험 방법으로 신규 신청, 취소한 결제 화면 복귀, 정상 승인, 중복 통보, 다음 회차 승인·실패를 확인합니다.
- 29~31일 신청, 12월→1월, 윤년/평년 2월, 신청 후 지연된 최초 승인에서 실제 첫 청구일과 다음 달 이용 종료일을 대조합니다. 같은 달 말일에 불필요하게 추가 청구되지 않는지 확인합니다.
pay_date의 한국 시간 형식, 부분 취소의price와orig_price, 실패 통보의 주문/판매자 인증 필드가 실제 계약의 통보와 맞는지 확인합니다. 인증 필드가 부족한 통보는 권한을 부여하지 않습니다.- 전체 취소, 부분 취소 여러 건, 과거 승인 취소, 승인 전에 도착한 취소를 대조합니다.
- 해지 후 남은 이용 기간, 기간 만료, 재가입을 확인합니다. 정지 계정에서도 해지할 수 있는지 확인합니다.
- 첫 회차·다음 회차·취소 모두 통보가 도착하는지 확인하고, 누락 시 판매자 화면과 대조할 담당자를 정합니다. 실결제 검증 시 실제 금액이 청구될 수 있으므로 PayApp의 시험 절차를 먼저 확인합니다.