Some checks failed
ci / 정본·보안·린트·타입·테스트 (push) Failing after 1m13s
ci / 워크스페이스 빌드 검증 (push) Has been skipped
ci / 모바일 린트·타입·Jest (push) Failing after 1m4s
ci / Supabase Edge Functions + Cloudflare Worker (push) Successful in 37s
ci / .NET API 서버 테스트 (push) Successful in 27s
deploy-site / deploy (push) Failing after 20s
Stripe is not used. Keeping its checkout, portal and webhook paths meant a
second payment provider, a second return-URL format and dead UI.
- Delete the stripe-checkout, stripe-portal and stripe-webhook functions and
their config; billing-catalog serves Payple prices only, and the web parser
rejects a catalog that still mixes in Stripe prices.
- Web: drop the Stripe checkout/portal buttons, provider toggle and return
notices; billing shows Payple only. Past rows with provider='stripe' are
still displayed ("Stripe (종료)") with a support contact instead of a portal.
- Desktop: delete the Stripe checkout modal, payment IPC channels, preload
namespace and their types; "Remove ads with Pro" opens the web billing page
via license.openBilling. Support/refund copy names Payple.
- billingUrl() loses the Stripe-only success/canceled result option; the
Deno contract is regenerated.
- Migrations and the DB's accepted provider values are untouched (history).
- Docs and the backlog record the removal (MON-04, EXT-STRIPE-01, GAP-BILL-03).
Verified: typecheck (desktop/web/admin/api-client/mobile), contract:check,
deno check all functions, deno test 80/80, desktop 1478/1480 on the Electron
runtime (2 known environment failures), web and admin builds, release
metadata and mobile boundary self-tests, eslint on changed files.
76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
import {
|
|
formatBillingPrice,
|
|
formatPlanCatalogPrice,
|
|
parseBillingCatalog,
|
|
} from '../src/lib/billing-catalog'
|
|
|
|
const catalogPayload = {
|
|
schema_version: '1',
|
|
plans: [
|
|
{
|
|
tier: 'pro',
|
|
prices: [
|
|
{ provider: 'payple', unit_amount: 9900, currency: 'KRW', interval: 'month', interval_count: 1 },
|
|
],
|
|
},
|
|
{
|
|
tier: 'pro_plus',
|
|
prices: [
|
|
{ provider: 'payple', unit_amount: 29900, currency: 'KRW', interval: 'month', interval_count: 1 },
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
test.describe('billing catalog strict client contract', () => {
|
|
test('parses provider prices and formats minor currency units', () => {
|
|
const catalog = parseBillingCatalog(catalogPayload)
|
|
expect(catalog).not.toBeNull()
|
|
expect(formatBillingPrice(catalog!.plans.pro[0])).toContain('9,900')
|
|
expect(formatPlanCatalogPrice('pro', catalog)).toContain('9,900')
|
|
expect(formatPlanCatalogPrice('pro_plus', catalog)).toContain('29,900')
|
|
expect(formatPlanCatalogPrice('free', catalog)).toBe('무료')
|
|
})
|
|
|
|
test('rejects duplicate tiers/providers, malformed amounts, and unknown values', () => {
|
|
const invalid = [
|
|
null,
|
|
{},
|
|
{ ...catalogPayload, schema_version: '2' },
|
|
{ ...catalogPayload, plans: [catalogPayload.plans[0], catalogPayload.plans[0]] },
|
|
{
|
|
...catalogPayload,
|
|
plans: [{
|
|
tier: 'pro',
|
|
prices: [catalogPayload.plans[0].prices[0], catalogPayload.plans[0].prices[0]],
|
|
}, catalogPayload.plans[1]],
|
|
},
|
|
{
|
|
...catalogPayload,
|
|
plans: [{ tier: 'pro', prices: [{ ...catalogPayload.plans[0].prices[0], unit_amount: 0 }] }, catalogPayload.plans[1]],
|
|
},
|
|
{
|
|
...catalogPayload,
|
|
plans: [{ tier: 'pro', prices: [{ ...catalogPayload.plans[0].prices[0], currency: 'KRW<script>' }] }, catalogPayload.plans[1]],
|
|
},
|
|
{
|
|
...catalogPayload,
|
|
plans: [{ tier: 'pro', prices: [{ ...catalogPayload.plans[0].prices[0], provider: 'google_play' }] }, catalogPayload.plans[1]],
|
|
},
|
|
]
|
|
for (const value of invalid) expect(parseBillingCatalog(value)).toBeNull()
|
|
})
|
|
|
|
test('keeps an unavailable provider absent instead of inventing a price', () => {
|
|
const catalog = parseBillingCatalog({
|
|
schema_version: '1',
|
|
plans: [
|
|
{ tier: 'pro', prices: [] },
|
|
{ tier: 'pro_plus', prices: [] },
|
|
],
|
|
})
|
|
expect(catalog).not.toBeNull()
|
|
expect(formatPlanCatalogPrice('pro', catalog)).toBeNull()
|
|
})
|
|
})
|