This commit is contained in:
2025-12-12 14:54:45 +08:00
parent 1eeeef266b
commit 2b08dd44ee
4 changed files with 141 additions and 24 deletions

View File

@@ -58,6 +58,7 @@
/// pay /// pay
#define API_VALIDATE_RECEIPT @"/api/apple/validate-receipt" // 排行榜标签列表 #define API_VALIDATE_RECEIPT @"/api/apple/validate-receipt" // 排行榜标签列表
#define API_INAPP_PRODUCT_LIST @"/products/inApp/list" // 查询 type=in-app-purchase 的商品列表 #define API_INAPP_PRODUCT_LIST @"/products/inApp/list" // 查询 type=in-app-purchase 的商品列表
#define API_SUBSCRIPTION_PRODUCT_LIST @"/products/subscription/list" // 查询订阅商品列表
/// AI /// AI
#define API_AI_TALK @"/chat/talk" // 排行榜标签列表 #define API_AI_TALK @"/chat/talk" // 排行榜标签列表

View File

@@ -9,6 +9,11 @@
#import "KBVipPayHeaderView.h" #import "KBVipPayHeaderView.h"
#import "KBVipSubscribeCell.h" #import "KBVipSubscribeCell.h"
#import "KBVipReviewListCell.h" #import "KBVipReviewListCell.h"
#import "PayVM.h"
#import "KBPayProductModel.h"
#import "FGIAPProductsFilter.h"
#import "FGIAPManager.h"
#import "KBBizCode.h"
static NSString * const kKBVipHeaderId = @"kKBVipHeaderId"; static NSString * const kKBVipHeaderId = @"kKBVipHeaderId";
static NSString * const kKBVipSubscribeCellId = @"kKBVipSubscribeCellId"; static NSString * const kKBVipSubscribeCellId = @"kKBVipSubscribeCellId";
@@ -16,7 +21,7 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
@interface KBVipPay () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> @interface KBVipPay () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) UICollectionView *collectionView; // @property (nonatomic, strong) UICollectionView *collectionView; //
@property (nonatomic, strong) NSArray<NSDictionary *> *plans; // @property (nonatomic, strong) NSArray<KBPayProductModel *> *plans; //
@property (nonatomic, assign) NSInteger selectedIndex; // @property (nonatomic, assign) NSInteger selectedIndex; //
@property (nonatomic, strong) UIButton *closeButton; // @property (nonatomic, strong) UIButton *closeButton; //
@property (nonatomic, strong) UIImageView *bgImageView; // @property (nonatomic, strong) UIImageView *bgImageView; //
@@ -27,6 +32,8 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
@property (nonatomic, strong) UIButton *payButton; // @property (nonatomic, strong) UIButton *payButton; //
@property (nonatomic, strong) UILabel *agreementLabel; // @property (nonatomic, strong) UILabel *agreementLabel; //
@property (nonatomic, strong) UIButton *agreementButton; // Embership Agreement @property (nonatomic, strong) UIButton *agreementButton; // Embership Agreement
@property (nonatomic, strong) PayVM *payVM;
@property (nonatomic, strong) FGIAPProductsFilter *filter;
@end @end
@@ -46,13 +53,10 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
make.left.top.right.equalTo(self.view); make.left.top.right.equalTo(self.view);
make.height.mas_equalTo(224); make.height.mas_equalTo(224);
}]; }];
// self.payVM = [PayVM new];
self.plans = @[ self.filter = [[FGIAPProductsFilter alloc] init];
@{@"title":@"Monthly Subscription", @"price":@"$4.49", @"strike":@"$4.49"}, self.plans = @[];
@{@"title":@"Monthly Subscription", @"price":@"$4.49", @"strike":@"$4.49"}, self.selectedIndex = NSNotFound;
@{@"title":@"Monthly Subscription", @"price":@"$4.49", @"strike":@"$4.49"},
];
self.selectedIndex = 1; //
// //
[self.view addSubview:self.collectionView]; [self.view addSubview:self.collectionView];
@@ -89,18 +93,67 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
// Header // Header
self.headerHeight = [self kb_calcHeaderHeightForWidth:KB_SCREEN_WIDTH]; self.headerHeight = [self kb_calcHeaderHeightForWidth:KB_SCREEN_WIDTH];
[self.collectionView reloadData]; [self.collectionView reloadData];
[self fetchSubscriptionPlans];
} }
- (void)viewDidAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated]; [super viewDidAppear:animated];
// Header [self selectCurrentPlanAnimated:NO];
}
#pragma mark - Data
- (void)fetchSubscriptionPlans {
__weak typeof(self) weakSelf = self;
[self.payVM fetchSubscriptionProductsNeedShow:YES completion:^(NSInteger sta, NSString * _Nullable msg, NSArray<KBPayProductModel *> * _Nullable products) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) self = weakSelf;
if (!self) { return; }
if (sta != KBBizCodeSuccess || ![products isKindOfClass:NSArray.class]) {
self.plans = @[];
self.selectedIndex = NSNotFound;
[self.collectionView reloadData];
NSString *tip = msg.length ? msg : KBLocalized(@"Failed to load products");
if (tip.length) { [KBHUD showInfo:tip]; }
return;
}
self.plans = products ?: @[];
self.selectedIndex = self.plans.count > 0 ? 0 : NSNotFound;
[self.collectionView reloadData];
[self selectCurrentPlanAnimated:NO];
});
}];
}
- (void)selectCurrentPlanAnimated:(BOOL)animated {
if (self.selectedIndex == NSNotFound) { return; }
if (self.selectedIndex < 0 || self.selectedIndex >= self.plans.count) { return; }
NSIndexPath *ip = [NSIndexPath indexPathForItem:self.selectedIndex inSection:1]; NSIndexPath *ip = [NSIndexPath indexPathForItem:self.selectedIndex inSection:1];
if (!ip) { return; } if (!ip) { return; }
// [self.collectionView selectItemAtIndexPath:ip animated:animated scrollPosition:UICollectionViewScrollPositionNone];
[self.collectionView selectItemAtIndexPath:ip animated:NO scrollPosition:UICollectionViewScrollPositionNone];
// cell willDisplay
KBVipSubscribeCell *cell = (KBVipSubscribeCell *)[self.collectionView cellForItemAtIndexPath:ip]; KBVipSubscribeCell *cell = (KBVipSubscribeCell *)[self.collectionView cellForItemAtIndexPath:ip];
if (cell) { [cell applySelected:YES animated:NO]; } if ([cell isKindOfClass:KBVipSubscribeCell.class]) {
[cell applySelected:YES animated:animated];
}
}
- (KBPayProductModel *)currentSelectedPlan {
if (self.selectedIndex == NSNotFound) { return nil; }
if (self.selectedIndex < 0 || self.selectedIndex >= self.plans.count) { return nil; }
id plan = self.plans[self.selectedIndex];
if (![plan isKindOfClass:KBPayProductModel.class]) { return nil; }
return plan;
}
- (NSString *)displayTitleForPlan:(KBPayProductModel *)plan {
if (!plan) { return @""; }
if (plan.productDescription.length) { return plan.productDescription; }
NSString *name = plan.name ?: @"";
NSString *unit = plan.unit ?: @"";
if (name.length && unit.length) { return [NSString stringWithFormat:@"%@%@", name, unit]; }
if (name.length) { return name; }
if (unit.length) { return unit; }
return KBLocalized(@"Subscription");
} }
#pragma mark - Header Height Calc #pragma mark - Header Height Calc
@@ -126,8 +179,41 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
#pragma mark - Bottom Actions #pragma mark - Bottom Actions
- (void)onTapPayButton { - (void)onTapPayButton {
// TODO: UI KBPayProductModel *plan = [self currentSelectedPlan];
[KBHUD showInfo:KBLocalized(@"Pay clicked")]; if (!plan) {
[KBHUD showInfo:KBLocalized(@"Please select a product")];
return;
}
NSString *productId = plan.productId;
if (productId.length == 0) {
[KBHUD showInfo:KBLocalized(@"Product unavailable")];
return;
}
[KBHUD show];
__weak typeof(self) weakSelf = self;
[self.filter requestProductsWith:[NSSet setWithObject:productId] completion:^(NSArray<SKProduct *> * _Nonnull products) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) self = weakSelf;
if (!self) { return; }
SKProduct *match = nil;
for (SKProduct *product in products) {
if ([product.productIdentifier isEqualToString:productId]) {
match = product;
break;
}
}
if (!match) {
[KBHUD dismiss];
[KBHUD showInfo:KBLocalized(@"Unable to load product information")];
return;
}
[[FGIAPManager shared].iap buyProduct:match onCompletion:^(NSString * _Nonnull message, FGIAPManagerPurchaseRusult result) {
dispatch_async(dispatch_get_main_queue(), ^{
[KBHUD dismiss];
});
}];
});
}];
} }
- (void)agreementButtonAction{ - (void)agreementButtonAction{
[KBHUD showInfo:KBLocalized(@"Open agreement")]; [KBHUD showInfo:KBLocalized(@"Open agreement")];
@@ -149,9 +235,16 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) { if (indexPath.section == 1) {
KBVipSubscribeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipSubscribeCellId forIndexPath:indexPath]; KBVipSubscribeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipSubscribeCellId forIndexPath:indexPath];
NSDictionary *plan = self.plans[indexPath.item]; if (indexPath.item < self.plans.count) {
[cell configTitle:plan[@"title"] price:plan[@"price"] strike:plan[@"strike"]]; KBPayProductModel *plan = self.plans[indexPath.item];
NSString *title = [self displayTitleForPlan:plan];
NSString *price = [plan priceDisplayText];
[cell configTitle:title price:price strike:nil];
[cell applySelected:(indexPath.item == self.selectedIndex) animated:NO]; [cell applySelected:(indexPath.item == self.selectedIndex) animated:NO];
} else {
[cell configTitle:@"" price:@"" strike:nil];
[cell applySelected:NO animated:NO];
}
return cell; return cell;
} else { } else {
KBVipReviewListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipReviewListCellId forIndexPath:indexPath]; KBVipReviewListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kKBVipReviewListCellId forIndexPath:indexPath];
@@ -169,7 +262,7 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
#pragma mark - UICollectionView Delegate #pragma mark - UICollectionView Delegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section != 1) { return; } if (indexPath.section != 1 || indexPath.item >= self.plans.count) { return; }
if (self.selectedIndex == indexPath.item) { return; } if (self.selectedIndex == indexPath.item) { return; }
NSInteger old = self.selectedIndex; NSInteger old = self.selectedIndex;
self.selectedIndex = indexPath.item; self.selectedIndex = indexPath.item;
@@ -185,7 +278,7 @@ static NSString * const kKBVipReviewListCellId = @"kKBVipReviewListCellId";
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
// //
if (indexPath.section == 1 && [cell isKindOfClass:KBVipSubscribeCell.class]) { if (indexPath.section == 1 && indexPath.item < self.plans.count && [cell isKindOfClass:KBVipSubscribeCell.class]) {
BOOL sel = (indexPath.item == self.selectedIndex); BOOL sel = (indexPath.item == self.selectedIndex);
KBVipSubscribeCell *c = (KBVipSubscribeCell *)cell; KBVipSubscribeCell *c = (KBVipSubscribeCell *)cell;
if (sel) { if (sel) {

View File

@@ -30,6 +30,10 @@ typedef void(^KBPayProductsCompletion)(NSInteger sta,
- (void)fetchInAppProductsNeedShow:(BOOL)needShow - (void)fetchInAppProductsNeedShow:(BOOL)needShow
completion:(KBPayProductsCompletion)completion; completion:(KBPayProductsCompletion)completion;
/// 查询订阅商品列表
- (void)fetchSubscriptionProductsNeedShow:(BOOL)needShow
completion:(KBPayProductsCompletion)completion;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -32,9 +32,30 @@
- (void)fetchInAppProductsNeedShow:(BOOL)needShow - (void)fetchInAppProductsNeedShow:(BOOL)needShow
completion:(KBPayProductsCompletion)completion { completion:(KBPayProductsCompletion)completion {
if (needShow) { [KBHUD show]; }
NSDictionary *params = @{ @"type": @"in-app-purchase" }; NSDictionary *params = @{ @"type": @"in-app-purchase" };
[[KBNetworkManager shared] GET:API_INAPP_PRODUCT_LIST [self fetchProductListWithPath:API_INAPP_PRODUCT_LIST
params:params
needShow:needShow
completion:completion];
}
- (void)fetchSubscriptionProductsNeedShow:(BOOL)needShow
completion:(KBPayProductsCompletion)completion {
NSDictionary *params = @{ @"type": @"subscription" };
[self fetchProductListWithPath:API_SUBSCRIPTION_PRODUCT_LIST
params:params
needShow:needShow
completion:completion];
}
#pragma mark - Helpers
- (void)fetchProductListWithPath:(NSString *)path
params:(NSDictionary *)params
needShow:(BOOL)needShow
completion:(KBPayProductsCompletion)completion {
if (needShow) { [KBHUD show]; }
[[KBNetworkManager shared] GET:path
parameters:params parameters:params
headers:nil headers:nil
autoShowBusinessError:NO autoShowBusinessError:NO
@@ -64,8 +85,6 @@
}]; }];
} }
#pragma mark - Helpers
//+ (NSInteger)extractStatusFromResponseObject:(id)obj response:(NSURLResponse *)resp { //+ (NSInteger)extractStatusFromResponseObject:(id)obj response:(NSURLResponse *)resp {
// // JSON code/status/success // // JSON code/status/success
// if ([obj isKindOfClass:NSDictionary.class]) { // if ([obj isKindOfClass:NSDictionary.class]) {