feat(billing): indirim kodu sistemi (admin panel + Stripe promo + ödeme)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

Admin paneli (admin@sase.tr) kod üretir/takip eder, ödeme sayfası kodu
uygular. Kodlar Stripe Coupon + Promotion Code olarak yaşar (limit/süre/
kullanım Stripe redemption'da enforce edilir); yerel discount_codes aynası
panel listeleme + checkout-anı preview içindir.

- migration 0022_discount_codes + schema (discount_codes)
- DiscountCodesModule: admin CRUD (@Roles admin) + /discount-codes/validate (login)
- StripeService: createPromotion / deactivatePromotion / getPromotionRedemptions
  / previewDiscount + resolvePlanAmount; createCheckoutSession opsiyonel
  discountCode -> discounts[{promotion_code}] + metadata.discount_code; webhook
  gerçek tahsilatı (session.amount_total) yazar + ayna times_redeemed++
- web: /dashboard/admin/discount-codes paneli + ödeme adımı indirim alanı
  (validate preview -> indirimli özet -> kod checkout'a); tr/en çeviriler
- 7 birim test (previewDiscount: % / sabit / süre / geçersiz/pasif/dolu/limit)

Not: 2026-04-22 Stripe API promotionCodes.create `promotion:{type:'coupon'}`
kullanıyor (üst-seviye `coupon` değil). dev'de Stripe key yok -> CREATE dev'de
503; E2E için dev'e Stripe TEST key gerekir.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 03:40:18 +03:00
parent 3de7fa4989
commit 346ebaa0e6
17 changed files with 1309 additions and 19 deletions

View File

@@ -43,6 +43,7 @@ import { Route as DashboardOemCodeRouteImport } from './routes/dashboard/oem.$co
import { Route as DashboardBlogSlugRouteImport } from './routes/dashboard/blog_/$slug'
import { Route as DashboardAdminUsersRouteImport } from './routes/dashboard/admin/users'
import { Route as DashboardAdminReferralsRouteImport } from './routes/dashboard/admin/referrals'
import { Route as DashboardAdminDiscountCodesRouteImport } from './routes/dashboard/admin/discount-codes'
import { Route as DashboardAdminCopyLogsRouteImport } from './routes/dashboard/admin/copy-logs'
import { Route as DashboardAdminAnalyticsRouteImport } from './routes/dashboard/admin/analytics'
import { Route as DashboardVehiclesIdIndexRouteImport } from './routes/dashboard/vehicles_/$id/index'
@@ -229,6 +230,12 @@ const DashboardAdminReferralsRoute = DashboardAdminReferralsRouteImport.update({
path: '/admin/referrals',
getParentRoute: () => DashboardRoute,
} as any)
const DashboardAdminDiscountCodesRoute =
DashboardAdminDiscountCodesRouteImport.update({
id: '/admin/discount-codes',
path: '/admin/discount-codes',
getParentRoute: () => DashboardRoute,
} as any)
const DashboardAdminCopyLogsRoute = DashboardAdminCopyLogsRouteImport.update({
id: '/admin/copy-logs',
path: '/admin/copy-logs',
@@ -340,6 +347,7 @@ export interface FileRoutesByFullPath {
'/dashboard/': typeof DashboardIndexRoute
'/dashboard/admin/analytics': typeof DashboardAdminAnalyticsRoute
'/dashboard/admin/copy-logs': typeof DashboardAdminCopyLogsRoute
'/dashboard/admin/discount-codes': typeof DashboardAdminDiscountCodesRoute
'/dashboard/admin/referrals': typeof DashboardAdminReferralsRoute
'/dashboard/admin/users': typeof DashboardAdminUsersRoute
'/dashboard/blog/$slug': typeof DashboardBlogSlugRoute
@@ -388,6 +396,7 @@ export interface FileRoutesByTo {
'/dashboard': typeof DashboardIndexRoute
'/dashboard/admin/analytics': typeof DashboardAdminAnalyticsRoute
'/dashboard/admin/copy-logs': typeof DashboardAdminCopyLogsRoute
'/dashboard/admin/discount-codes': typeof DashboardAdminDiscountCodesRoute
'/dashboard/admin/referrals': typeof DashboardAdminReferralsRoute
'/dashboard/admin/users': typeof DashboardAdminUsersRoute
'/dashboard/blog/$slug': typeof DashboardBlogSlugRoute
@@ -439,6 +448,7 @@ export interface FileRoutesById {
'/dashboard/': typeof DashboardIndexRoute
'/dashboard/admin/analytics': typeof DashboardAdminAnalyticsRoute
'/dashboard/admin/copy-logs': typeof DashboardAdminCopyLogsRoute
'/dashboard/admin/discount-codes': typeof DashboardAdminDiscountCodesRoute
'/dashboard/admin/referrals': typeof DashboardAdminReferralsRoute
'/dashboard/admin/users': typeof DashboardAdminUsersRoute
'/dashboard/blog_/$slug': typeof DashboardBlogSlugRoute
@@ -490,6 +500,7 @@ export interface FileRouteTypes {
| '/dashboard/'
| '/dashboard/admin/analytics'
| '/dashboard/admin/copy-logs'
| '/dashboard/admin/discount-codes'
| '/dashboard/admin/referrals'
| '/dashboard/admin/users'
| '/dashboard/blog/$slug'
@@ -538,6 +549,7 @@ export interface FileRouteTypes {
| '/dashboard'
| '/dashboard/admin/analytics'
| '/dashboard/admin/copy-logs'
| '/dashboard/admin/discount-codes'
| '/dashboard/admin/referrals'
| '/dashboard/admin/users'
| '/dashboard/blog/$slug'
@@ -588,6 +600,7 @@ export interface FileRouteTypes {
| '/dashboard/'
| '/dashboard/admin/analytics'
| '/dashboard/admin/copy-logs'
| '/dashboard/admin/discount-codes'
| '/dashboard/admin/referrals'
| '/dashboard/admin/users'
| '/dashboard/blog_/$slug'
@@ -866,6 +879,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DashboardAdminReferralsRouteImport
parentRoute: typeof DashboardRoute
}
'/dashboard/admin/discount-codes': {
id: '/dashboard/admin/discount-codes'
path: '/admin/discount-codes'
fullPath: '/dashboard/admin/discount-codes'
preLoaderRoute: typeof DashboardAdminDiscountCodesRouteImport
parentRoute: typeof DashboardRoute
}
'/dashboard/admin/copy-logs': {
id: '/dashboard/admin/copy-logs'
path: '/admin/copy-logs'
@@ -997,6 +1017,7 @@ interface DashboardRouteChildren {
DashboardIndexRoute: typeof DashboardIndexRoute
DashboardAdminAnalyticsRoute: typeof DashboardAdminAnalyticsRoute
DashboardAdminCopyLogsRoute: typeof DashboardAdminCopyLogsRoute
DashboardAdminDiscountCodesRoute: typeof DashboardAdminDiscountCodesRoute
DashboardAdminReferralsRoute: typeof DashboardAdminReferralsRoute
DashboardAdminUsersRoute: typeof DashboardAdminUsersRoute
DashboardBlogSlugRoute: typeof DashboardBlogSlugRoute
@@ -1030,6 +1051,7 @@ const DashboardRouteChildren: DashboardRouteChildren = {
DashboardIndexRoute: DashboardIndexRoute,
DashboardAdminAnalyticsRoute: DashboardAdminAnalyticsRoute,
DashboardAdminCopyLogsRoute: DashboardAdminCopyLogsRoute,
DashboardAdminDiscountCodesRoute: DashboardAdminDiscountCodesRoute,
DashboardAdminReferralsRoute: DashboardAdminReferralsRoute,
DashboardAdminUsersRoute: DashboardAdminUsersRoute,
DashboardBlogSlugRoute: DashboardBlogSlugRoute,