feat(billing): 구매 복원 + RTDN 수신 스캐폴드 + 만료 강등 정합성
CI / build (push) Failing after 12m15s

- 만료 강등 스케줄러: 해지(자동갱신 off) 건만 강등(자동갱신은 마켓/RTDN 관리)
- POST /api/billing/restore: 최근 결제 기준 멤버십 복원(기기변경·재설치)
- POST /api/billing/google/rtdn: Google Play 실시간 알림 수신 스캐폴드(비인증, 로깅)
- 실연동 지점(영수증 검증·RTDN 처리)은 TODO 로 표시

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-28 17:29:15 +09:00
parent 8142b8b518
commit a47b8405f4
6 changed files with 80 additions and 3 deletions
@@ -26,4 +26,12 @@
FROM iap_purchase WHERE purchase_token = #{purchaseToken}
</select>
<select id="findLatestByMember" parameterType="long" resultMap="iapResultMap">
SELECT id, member_id, platform, product_id, purchase_token, order_id, status, expires_at, created_at
FROM iap_purchase
WHERE member_id = #{memberId} AND status = 'VERIFIED'
ORDER BY expires_at DESC, id DESC
LIMIT 1
</select>
</mapper>
+3 -2
View File
@@ -117,10 +117,11 @@
UPDATE member SET plan_auto_renew = #{autoRenew}, updated_at = NOW() WHERE id = #{id}
</update>
<!-- 만료된 유료회원 자동 강등 (스케줄러) — 구독정보도 정리 -->
<!-- 만료된 '해지(자동갱신 off)' 유료회원만 강등 (자동갱신 건은 마켓 갱신/RTDN 이 관리) -->
<update id="downgradeExpired">
UPDATE member SET plan = 'FREE', plan_product = NULL, plan_auto_renew = 0, updated_at = NOW()
WHERE plan = 'PREMIUM' AND plan_expires_at IS NOT NULL AND plan_expires_at &lt; NOW()
WHERE plan = 'PREMIUM' AND plan_auto_renew = 0
AND plan_expires_at IS NOT NULL AND plan_expires_at &lt; NOW()
</update>
<update id="updateStatus">