tdd 진행
This commit is contained in:
@@ -10,6 +10,8 @@ import 'in_app_purchase_service.dart';
|
||||
class SubscriptionService extends ChangeNotifier {
|
||||
final String _baseUrl = ApiConfig.baseUrl;
|
||||
final String _token;
|
||||
final String _clubId;
|
||||
final String _userId;
|
||||
|
||||
Subscription? _currentSubscription;
|
||||
List<SubscriptionPlan> _availablePlans = [];
|
||||
@@ -17,9 +19,42 @@ class SubscriptionService extends ChangeNotifier {
|
||||
String? _error;
|
||||
|
||||
// 인앱 결제 서비스
|
||||
final InAppPurchaseService _purchaseService = InAppPurchaseService();
|
||||
final InAppPurchaseService _purchaseService;
|
||||
final http.Client _httpClient;
|
||||
|
||||
SubscriptionService(this._token) {
|
||||
// 기본 생성자
|
||||
SubscriptionService(this._token, [this._clubId = '', this._userId = '']) :
|
||||
_purchaseService = InAppPurchaseService(),
|
||||
_httpClient = http.Client() {
|
||||
_loadAvailablePlans();
|
||||
_initializeInAppPurchase();
|
||||
}
|
||||
|
||||
// 테스트용 생성자
|
||||
factory SubscriptionService.forTest({
|
||||
required http.Client client,
|
||||
required InAppPurchaseService purchaseService,
|
||||
required String userId,
|
||||
required String token,
|
||||
required String clubId,
|
||||
}) {
|
||||
return SubscriptionService._internal(
|
||||
token,
|
||||
clubId,
|
||||
userId,
|
||||
client,
|
||||
purchaseService,
|
||||
);
|
||||
}
|
||||
|
||||
// 내부 생성자
|
||||
SubscriptionService._internal(
|
||||
this._token,
|
||||
this._clubId,
|
||||
this._userId,
|
||||
this._httpClient,
|
||||
this._purchaseService,
|
||||
) {
|
||||
_loadAvailablePlans();
|
||||
_initializeInAppPurchase();
|
||||
}
|
||||
@@ -34,7 +69,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
|
||||
// 인앱 결제 관련 게터
|
||||
InAppPurchaseService get purchaseService => _purchaseService;
|
||||
List<ProductDetails> get products => _purchaseService.products;
|
||||
List<dynamic> get products => _purchaseService.products;
|
||||
|
||||
/// 현재 구독 정보 로드
|
||||
Future<void> loadCurrentSubscription() async {
|
||||
@@ -43,7 +78,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
final response = await _httpClient.post(
|
||||
Uri.parse('$_baseUrl${ApiConfig.currentSubscription}'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -66,9 +101,11 @@ class SubscriptionService extends ChangeNotifier {
|
||||
status: SubscriptionStatus.active,
|
||||
startDate: DateTime.now(),
|
||||
endDate: DateTime.now().add(const Duration(days: 36500)), // 100년
|
||||
price: 0,
|
||||
currency: 'KRW',
|
||||
autoRenew: false,
|
||||
lastPaymentDate: null,
|
||||
nextPaymentDate: null,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
} else {
|
||||
_error = '구독 정보를 불러오는데 실패했습니다. (${response.statusCode})';
|
||||
@@ -88,7 +125,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await http.get(
|
||||
final response = await _httpClient.get(
|
||||
Uri.parse('$_baseUrl${ApiConfig.subscriptionPlans}'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -125,20 +162,21 @@ class SubscriptionService extends ChangeNotifier {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
|
||||
try {
|
||||
// 1. 인앱 결제 복원 요청
|
||||
final restoreSuccess = await _purchaseService.restorePurchases();
|
||||
if (!restoreSuccess) {
|
||||
_error = '구독 복원에 실패했습니다.';
|
||||
// 인앱 구매 복원 시도
|
||||
final restored = await _purchaseService.restorePurchases();
|
||||
|
||||
if (restored) {
|
||||
// 서버에서 현재 구독 정보 가져오기
|
||||
await loadCurrentSubscription();
|
||||
return true;
|
||||
} else {
|
||||
_error = '구독을 복원할 수 없습니다.';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 서버에서 최신 구독 정보 조회
|
||||
await fetchCurrentSubscription();
|
||||
return true;
|
||||
} catch (e) {
|
||||
_error = '구독 복원에 실패했습니다: $e';
|
||||
_error = '구독 복원 중 오류가 발생했습니다: $e';
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
@@ -153,8 +191,8 @@ class SubscriptionService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse('$_baseUrl/api/subscriptions/verify'),
|
||||
final response = await _httpClient.get(
|
||||
Uri.parse('$_baseUrl${ApiConfig.currentSubscription}'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $_token',
|
||||
@@ -204,7 +242,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
// 구매 검증이 완료될 때까지 최대 10회 확인
|
||||
while (!verified && attempts < maxAttempts) {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
verified = _purchaseService._purchaseVerified;
|
||||
verified = _purchaseService.isPurchaseVerified;
|
||||
attempts++;
|
||||
|
||||
// 오류가 발생한 경우
|
||||
@@ -224,7 +262,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// 검증된 구매 정보 가져오기
|
||||
final verifiedPurchase = _purchaseService._verifiedPurchase;
|
||||
final verifiedPurchase = _purchaseService.lastVerifiedPurchase;
|
||||
if (verifiedPurchase == null) {
|
||||
_error = '검증된 구매 정보를 찾을 수 없습니다.';
|
||||
_isLoading = false;
|
||||
@@ -233,7 +271,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// 서버에 구독 생성 요청
|
||||
final response = await http.post(
|
||||
final response = await _httpClient.post(
|
||||
Uri.parse('$_baseUrl${ApiConfig.subscribeClub}'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -243,9 +281,9 @@ class SubscriptionService extends ChangeNotifier {
|
||||
'planType': planType.toString().split('.').last,
|
||||
'isYearly': isYearly,
|
||||
'clubId': _clubId,
|
||||
'transactionId': verifiedPurchase['transactionId'],
|
||||
'productId': verifiedPurchase['productId'],
|
||||
'verificationData': verifiedPurchase['verificationData'],
|
||||
'transactionId': verifiedPurchase.purchaseID,
|
||||
'productId': verifiedPurchase.productID,
|
||||
'verificationData': verifiedPurchase.verificationData.serverVerificationData,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -254,8 +292,8 @@ class SubscriptionService extends ChangeNotifier {
|
||||
_currentSubscription = Subscription.fromJson(data);
|
||||
|
||||
// 구매 검증 상태 초기화
|
||||
_purchaseService._purchaseVerified = false;
|
||||
_purchaseService._verifiedPurchase = null;
|
||||
_purchaseService.isPurchaseVerified = false;
|
||||
_purchaseService.lastVerifiedPurchase = null;
|
||||
|
||||
notifyListeners();
|
||||
return true;
|
||||
@@ -285,8 +323,8 @@ class SubscriptionService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('$_baseUrl/api/subscriptions/${_currentSubscription!.id}/cancel'),
|
||||
final response = await _httpClient.post(
|
||||
Uri.parse('$_baseUrl${ApiConfig.subscriptions}/${_currentSubscription!.id}/cancel'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $_token',
|
||||
@@ -324,7 +362,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
final response = await _httpClient.post(
|
||||
Uri.parse('$_baseUrl${ApiConfig.changePlan}'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -369,7 +407,7 @@ class SubscriptionService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
final response = await _httpClient.post(
|
||||
Uri.parse('$_baseUrl${ApiConfig.subscriptions}/autorenew'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
Reference in New Issue
Block a user