주요 구현 완료 기능: - 구독 관리 (추가/편집/삭제/카테고리 분류) - 이벤트 할인 시스템 (기본값 자동 설정) - SMS 자동 스캔 및 구독 정보 추출 - 알림 시스템 (타임존 처리 안정화) - 환율 변환 지원 (KRW/USD) - 반응형 UI 및 애니메이션 - 다국어 지원 (한국어/영어) 버그 수정: - NotificationService tz.local 초기화 오류 해결 - MainScreenSummaryCard 레이아웃 오버플로우 수정 - 구독 추가 시 LateInitializationError 완전 해결 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
477 lines
20 KiB
Dart
477 lines
20 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// 구독 서비스와 웹사이트 URL 매칭을 처리하는 서비스 클래스
|
|
class SubscriptionUrlMatcher {
|
|
// OTT 서비스
|
|
static final Map<String, String> ottServices = {
|
|
'netflix': 'https://www.netflix.com',
|
|
'넷플릭스': 'https://www.netflix.com',
|
|
'disney+': 'https://www.disneyplus.com',
|
|
'디즈니플러스': 'https://www.disneyplus.com',
|
|
'youtube premium': 'https://www.youtube.com/premium',
|
|
'유튜브 프리미엄': 'https://www.youtube.com/premium',
|
|
'watcha': 'https://watcha.com',
|
|
'왓챠': 'https://watcha.com',
|
|
'wavve': 'https://www.wavve.com',
|
|
'웨이브': 'https://www.wavve.com',
|
|
'apple tv+': 'https://tv.apple.com',
|
|
'애플 티비플러스': 'https://tv.apple.com',
|
|
'tving': 'https://www.tving.com',
|
|
'티빙': 'https://www.tving.com',
|
|
'prime video': 'https://www.primevideo.com',
|
|
'프라임 비디오': 'https://www.primevideo.com',
|
|
'amazon prime': 'https://www.amazon.com/prime',
|
|
'아마존 프라임': 'https://www.amazon.com/prime',
|
|
'coupang play': 'https://play.coupangplay.com',
|
|
'쿠팡 플레이': 'https://play.coupangplay.com',
|
|
'hulu': 'https://www.hulu.com',
|
|
'훌루': 'https://www.hulu.com',
|
|
};
|
|
|
|
// 음악 서비스
|
|
static final Map<String, String> musicServices = {
|
|
'spotify': 'https://www.spotify.com',
|
|
'스포티파이': 'https://www.spotify.com',
|
|
'apple music': 'https://music.apple.com',
|
|
'애플 뮤직': 'https://music.apple.com',
|
|
'melon': 'https://www.melon.com',
|
|
'멜론': 'https://www.melon.com',
|
|
'genie': 'https://www.genie.co.kr',
|
|
'지니': 'https://www.genie.co.kr',
|
|
'youtube music': 'https://music.youtube.com',
|
|
'유튜브 뮤직': 'https://music.youtube.com',
|
|
'bugs': 'https://music.bugs.co.kr',
|
|
'벅스': 'https://music.bugs.co.kr',
|
|
'flo': 'https://www.music-flo.com',
|
|
'플로': 'https://www.music-flo.com',
|
|
'vibe': 'https://vibe.naver.com',
|
|
'바이브': 'https://vibe.naver.com',
|
|
'tidal': 'https://www.tidal.com',
|
|
'타이달': 'https://www.tidal.com',
|
|
};
|
|
|
|
// AI 서비스
|
|
static final Map<String, String> aiServices = {
|
|
'chatgpt': 'https://chat.openai.com',
|
|
'챗GPT': 'https://chat.openai.com',
|
|
'openai': 'https://openai.com',
|
|
'오픈AI': 'https://openai.com',
|
|
'claude': 'https://claude.ai',
|
|
'클로드': 'https://claude.ai',
|
|
'anthropic': 'https://www.anthropic.com',
|
|
'앤트로픽': 'https://www.anthropic.com',
|
|
'midjourney': 'https://www.midjourney.com',
|
|
'미드저니': 'https://www.midjourney.com',
|
|
'perplexity': 'https://www.perplexity.ai',
|
|
'퍼플렉시티': 'https://www.perplexity.ai',
|
|
'copilot': 'https://copilot.microsoft.com',
|
|
'코파일럿': 'https://copilot.microsoft.com',
|
|
'gemini': 'https://gemini.google.com',
|
|
'제미니': 'https://gemini.google.com',
|
|
'google ai': 'https://ai.google',
|
|
'구글 AI': 'https://ai.google',
|
|
'bard': 'https://bard.google.com',
|
|
'바드': 'https://bard.google.com',
|
|
'dall-e': 'https://openai.com/dall-e',
|
|
'달리': 'https://openai.com/dall-e',
|
|
'stable diffusion': 'https://stability.ai',
|
|
'스테이블 디퓨전': 'https://stability.ai',
|
|
};
|
|
|
|
// 프로그래밍 / 개발 서비스
|
|
static final Map<String, String> programmingServices = {
|
|
'github': 'https://github.com',
|
|
'깃허브': 'https://github.com',
|
|
'cursor': 'https://cursor.com',
|
|
'커서': 'https://cursor.com',
|
|
'jetbrains': 'https://www.jetbrains.com',
|
|
'제트브레인스': 'https://www.jetbrains.com',
|
|
'intellij': 'https://www.jetbrains.com/idea',
|
|
'인텔리제이': 'https://www.jetbrains.com/idea',
|
|
'visual studio': 'https://visualstudio.microsoft.com',
|
|
'비주얼 스튜디오': 'https://visualstudio.microsoft.com',
|
|
'aws': 'https://aws.amazon.com',
|
|
'아마존 웹서비스': 'https://aws.amazon.com',
|
|
'azure': 'https://azure.microsoft.com',
|
|
'애저': 'https://azure.microsoft.com',
|
|
'google cloud': 'https://cloud.google.com',
|
|
'구글 클라우드': 'https://cloud.google.com',
|
|
'digitalocean': 'https://www.digitalocean.com',
|
|
'디지털오션': 'https://www.digitalocean.com',
|
|
'heroku': 'https://www.heroku.com',
|
|
'헤로쿠': 'https://www.heroku.com',
|
|
'codecademy': 'https://www.codecademy.com',
|
|
'코드아카데미': 'https://www.codecademy.com',
|
|
'udemy': 'https://www.udemy.com',
|
|
'유데미': 'https://www.udemy.com',
|
|
'coursera': 'https://www.coursera.org',
|
|
'코세라': 'https://www.coursera.org',
|
|
};
|
|
|
|
// 오피스 및 협업 툴
|
|
static final Map<String, String> officeTools = {
|
|
'microsoft 365': 'https://www.microsoft.com/microsoft-365',
|
|
'마이크로소프트 365': 'https://www.microsoft.com/microsoft-365',
|
|
'office 365': 'https://www.microsoft.com/microsoft-365',
|
|
'오피스 365': 'https://www.microsoft.com/microsoft-365',
|
|
'google workspace': 'https://workspace.google.com',
|
|
'구글 워크스페이스': 'https://workspace.google.com',
|
|
'slack': 'https://slack.com',
|
|
'슬랙': 'https://slack.com',
|
|
'notion': 'https://www.notion.so',
|
|
'노션': 'https://www.notion.so',
|
|
'trello': 'https://trello.com',
|
|
'트렐로': 'https://trello.com',
|
|
'asana': 'https://asana.com',
|
|
'아사나': 'https://asana.com',
|
|
'dropbox': 'https://www.dropbox.com',
|
|
'드롭박스': 'https://www.dropbox.com',
|
|
'figma': 'https://www.figma.com',
|
|
'피그마': 'https://www.figma.com',
|
|
'adobe creative cloud': 'https://www.adobe.com/creativecloud.html',
|
|
'어도비 크리에이티브 클라우드': 'https://www.adobe.com/creativecloud.html',
|
|
};
|
|
|
|
// 기타 유명 서비스
|
|
static final Map<String, String> otherServices = {
|
|
'google one': 'https://one.google.com',
|
|
'구글 원': 'https://one.google.com',
|
|
'icloud': 'https://www.icloud.com',
|
|
'아이클라우드': 'https://www.icloud.com',
|
|
'nintendo switch online': 'https://www.nintendo.com/switch/online-service',
|
|
'닌텐도 스위치 온라인': 'https://www.nintendo.com/switch/online-service',
|
|
'playstation plus': 'https://www.playstation.com/ps-plus',
|
|
'플레이스테이션 플러스': 'https://www.playstation.com/ps-plus',
|
|
'xbox game pass': 'https://www.xbox.com/xbox-game-pass',
|
|
'엑스박스 게임 패스': 'https://www.xbox.com/xbox-game-pass',
|
|
'ea play': 'https://www.ea.com/ea-play',
|
|
'EA 플레이': 'https://www.ea.com/ea-play',
|
|
'ubisoft+': 'https://ubisoft.com/plus',
|
|
'유비소프트+': 'https://ubisoft.com/plus',
|
|
'epic games': 'https://www.epicgames.com',
|
|
'에픽 게임즈': 'https://www.epicgames.com',
|
|
'steam': 'https://store.steampowered.com',
|
|
'스팀': 'https://store.steampowered.com',
|
|
};
|
|
|
|
// 해지 안내 페이지 URL 목록 (공식 해지 안내 페이지가 있는 서비스들)
|
|
static final Map<String, String> cancellationUrls = {
|
|
// OTT 서비스 해지 안내 페이지
|
|
'netflix': 'https://help.netflix.com/ko/node/407',
|
|
'넷플릭스': 'https://help.netflix.com/ko/node/407',
|
|
'disney+':
|
|
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
|
'디즈니플러스':
|
|
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
|
'youtube premium': 'https://support.google.com/youtube/answer/6308278',
|
|
'유튜브 프리미엄': 'https://support.google.com/youtube/answer/6308278',
|
|
'watcha': 'https://watcha.com/settings/payment',
|
|
'왓챠': 'https://watcha.com/settings/payment',
|
|
'wavve': 'https://www.wavve.com/my',
|
|
'웨이브': 'https://www.wavve.com/my',
|
|
'apple tv+': 'https://support.apple.com/ko-kr/HT202039',
|
|
'애플 티비플러스': 'https://support.apple.com/ko-kr/HT202039',
|
|
'tving': 'https://www.tving.com/my/cancelMembership',
|
|
'티빙': 'https://www.tving.com/my/cancelMembership',
|
|
'amazon prime': 'https://www.amazon.com/gp/primecentral/managemembership',
|
|
'아마존 프라임': 'https://www.amazon.com/gp/primecentral/managemembership',
|
|
|
|
// 음악 서비스 해지 안내 페이지
|
|
'spotify': 'https://support.spotify.com/us/article/cancel-premium/',
|
|
'스포티파이': 'https://support.spotify.com/us/article/cancel-premium/',
|
|
'apple music': 'https://support.apple.com/ko-kr/HT202039',
|
|
'애플 뮤직': 'https://support.apple.com/ko-kr/HT202039',
|
|
'melon':
|
|
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
|
'멜론':
|
|
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
|
'youtube music': 'https://support.google.com/youtubemusic/answer/6308278',
|
|
'유튜브 뮤직': 'https://support.google.com/youtubemusic/answer/6308278',
|
|
|
|
// AI 서비스 해지 안내 페이지
|
|
'chatgpt':
|
|
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
|
'챗GPT':
|
|
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
|
'claude':
|
|
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
|
'클로드':
|
|
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
|
'midjourney': 'https://docs.midjourney.com/docs/manage-subscription',
|
|
'미드저니': 'https://docs.midjourney.com/docs/manage-subscription',
|
|
|
|
// 프로그래밍 / 개발 서비스 해지 안내 페이지
|
|
'github':
|
|
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
|
'깃허브':
|
|
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
|
'jetbrains':
|
|
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
|
'제트브레인스':
|
|
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
|
|
|
// 오피스 및 협업 툴 해지 안내 페이지
|
|
'microsoft 365':
|
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
'마이크로소프트 365':
|
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
'office 365':
|
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
'오피스 365':
|
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
'slack':
|
|
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
|
'슬랙':
|
|
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
|
'notion':
|
|
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
|
'노션':
|
|
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
|
'dropbox': 'https://help.dropbox.com/accounts-billing/cancellation',
|
|
'드롭박스': 'https://help.dropbox.com/accounts-billing/cancellation',
|
|
'adobe creative cloud':
|
|
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
|
'어도비 크리에이티브 클라우드':
|
|
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
|
|
|
// 기타 유명 서비스 해지 안내 페이지
|
|
'google one': 'https://support.google.com/googleone/answer/9140429',
|
|
'구글 원': 'https://support.google.com/googleone/answer/9140429',
|
|
'icloud': 'https://support.apple.com/ko-kr/HT207594',
|
|
'아이클라우드': 'https://support.apple.com/ko-kr/HT207594',
|
|
'nintendo switch online':
|
|
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
|
'닌텐도 스위치 온라인':
|
|
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
|
'playstation plus':
|
|
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
|
'플레이스테이션 플러스':
|
|
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
|
'xbox game pass':
|
|
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
|
'엑스박스 게임 패스':
|
|
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
|
};
|
|
|
|
// 모든 서비스 매핑을 합친 맵
|
|
static final Map<String, String> allServices = {
|
|
...ottServices,
|
|
...musicServices,
|
|
...aiServices,
|
|
...programmingServices,
|
|
...officeTools,
|
|
...otherServices,
|
|
};
|
|
|
|
/// 입력된 서비스 이름이나 문자열에서 매칭되는 URL을 찾아 반환
|
|
///
|
|
/// [text] 검색할 텍스트 (서비스명)
|
|
/// [usePartialMatch] 부분 일치도 허용할지 여부 (기본값: true)
|
|
///
|
|
/// 반환값: 매칭된 URL 또는 null (매칭 실패시)
|
|
static String? findMatchingUrl(String text, {bool usePartialMatch = true}) {
|
|
// 입력 텍스트가 비어있거나 null인 경우
|
|
if (text.isEmpty) {
|
|
return null;
|
|
}
|
|
|
|
// 소문자로 변환하여 처리
|
|
final String lowerText = text.toLowerCase().trim();
|
|
|
|
// 정확히 일치하는 경우
|
|
if (allServices.containsKey(lowerText)) {
|
|
return allServices[lowerText];
|
|
}
|
|
|
|
// 부분 일치 검색이 활성화된 경우
|
|
if (usePartialMatch) {
|
|
// 가장 긴 부분 매칭 찾기
|
|
String? bestMatch;
|
|
int maxLength = 0;
|
|
|
|
for (var entry in allServices.entries) {
|
|
final String key = entry.key;
|
|
|
|
// 입력된 텍스트에 서비스 키워드가 포함되어 있거나, 서비스 키워드에 입력된 텍스트가 포함된 경우
|
|
if (lowerText.contains(key) || key.contains(lowerText)) {
|
|
// 더 긴 매칭을 우선시
|
|
if (key.length > maxLength) {
|
|
maxLength = key.length;
|
|
bestMatch = entry.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestMatch;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// 서비스 이름을 기반으로 URL 제안
|
|
static String? suggestUrl(String serviceName) {
|
|
if (serviceName.isEmpty) {
|
|
print('SubscriptionUrlMatcher: 빈 serviceName');
|
|
return null;
|
|
}
|
|
|
|
// 소문자로 변환하여 비교
|
|
final lowerName = serviceName.toLowerCase().trim();
|
|
|
|
try {
|
|
// 정확한 매칭을 먼저 시도
|
|
for (final entry in allServices.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print('SubscriptionUrlMatcher: 정확한 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// OTT 서비스 검사
|
|
for (final entry in ottServices.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print(
|
|
'SubscriptionUrlMatcher: OTT 서비스 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// 음악 서비스 검사
|
|
for (final entry in musicServices.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print(
|
|
'SubscriptionUrlMatcher: 음악 서비스 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// AI 서비스 검사
|
|
for (final entry in aiServices.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print(
|
|
'SubscriptionUrlMatcher: AI 서비스 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// 개발 서비스 검사
|
|
for (final entry in programmingServices.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print(
|
|
'SubscriptionUrlMatcher: 개발 서비스 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// 오피스 툴 검사
|
|
for (final entry in officeTools.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print(
|
|
'SubscriptionUrlMatcher: 오피스 툴 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// 기타 서비스 검사
|
|
for (final entry in otherServices.entries) {
|
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
|
print(
|
|
'SubscriptionUrlMatcher: 기타 서비스 매칭 - $lowerName -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// 유사한 이름 검사 (퍼지 매칭) - 단어 기반으로 검색
|
|
for (final entry in allServices.entries) {
|
|
final serviceWords = lowerName.split(' ');
|
|
final keyWords = entry.key.toLowerCase().split(' ');
|
|
|
|
// 단어 단위로 일치하는지 확인
|
|
for (final word in serviceWords) {
|
|
if (word.length > 2 &&
|
|
keyWords.any((keyWord) => keyWord.contains(word))) {
|
|
print(
|
|
'SubscriptionUrlMatcher: 단어 기반 매칭 - $word (in $lowerName) -> ${entry.key}');
|
|
return entry.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 추출 가능한 도메인이 있는지 확인
|
|
final domainMatch = RegExp(r'(\w+)').firstMatch(lowerName);
|
|
if (domainMatch != null && domainMatch.group(1)!.length > 2) {
|
|
final domain = domainMatch.group(1)!.trim();
|
|
if (domain.length > 2 &&
|
|
!['the', 'and', 'for', 'www'].contains(domain)) {
|
|
final url = 'https://www.$domain.com';
|
|
print('SubscriptionUrlMatcher: 도메인 추출 - $lowerName -> $url');
|
|
return url;
|
|
}
|
|
}
|
|
|
|
print('SubscriptionUrlMatcher: 매칭 실패 - $lowerName');
|
|
return null;
|
|
} catch (e) {
|
|
print('SubscriptionUrlMatcher: URL 매칭 중 오류 발생: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 서비스명 또는 웹사이트 URL을 기반으로 해지 안내 페이지 URL 찾기
|
|
///
|
|
/// [serviceNameOrUrl] 서비스명 또는 웹사이트 URL
|
|
///
|
|
/// 반환값: 해지 안내 페이지 URL 또는 null (해지 안내 페이지가 없는 경우)
|
|
static String? findCancellationUrl(String serviceNameOrUrl) {
|
|
if (serviceNameOrUrl.isEmpty) {
|
|
return null;
|
|
}
|
|
|
|
// 소문자로 변환하여 처리
|
|
final String lowerText = serviceNameOrUrl.toLowerCase().trim();
|
|
|
|
// 직접 서비스명으로 찾기
|
|
if (cancellationUrls.containsKey(lowerText)) {
|
|
return cancellationUrls[lowerText];
|
|
}
|
|
|
|
// 서비스명에 부분 포함으로 찾기
|
|
for (var entry in cancellationUrls.entries) {
|
|
final String key = entry.key.toLowerCase();
|
|
if (lowerText.contains(key) || key.contains(lowerText)) {
|
|
return entry.value;
|
|
}
|
|
}
|
|
|
|
// URL을 통해 서비스명 추출 후 찾기
|
|
if (lowerText.startsWith('http')) {
|
|
// URL 도메인 추출 (https://www.netflix.com 에서 netflix 추출)
|
|
final domainRegex = RegExp(r'https?://(?:www\.)?([a-zA-Z0-9-]+)');
|
|
final match = domainRegex.firstMatch(lowerText);
|
|
|
|
if (match != null && match.groupCount >= 1) {
|
|
final domain = match.group(1)?.toLowerCase() ?? '';
|
|
|
|
// 도메인으로 서비스명 찾기
|
|
for (var entry in cancellationUrls.entries) {
|
|
if (entry.key.toLowerCase().contains(domain)) {
|
|
return entry.value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 해지 안내 페이지를 찾지 못함
|
|
return null;
|
|
}
|
|
|
|
/// 서비스에 공식 해지 안내 페이지가 있는지 확인
|
|
///
|
|
/// [serviceNameOrUrl] 서비스명 또는 웹사이트 URL
|
|
///
|
|
/// 반환값: 해지 안내 페이지 제공 여부
|
|
static bool hasCancellationPage(String serviceNameOrUrl) {
|
|
return findCancellationUrl(serviceNameOrUrl) != null;
|
|
}
|
|
}
|