// // KeyboardViewController+Subscription.m // CustomKeyboard // // Created by Codex on 2026/02/22. // #import "KeyboardViewController+Private.h" #import "KBAuthManager.h" #import "KBFullAccessManager.h" #import "../Utils/KBExtensionAppLauncher.h" #import "KBKeyboardSubscriptionProduct.h" #import "KBKeyboardSubscriptionView.h" @implementation KeyboardViewController (Subscription) - (void)showSubscriptionPanel { // 1) 先判断权限:未开启“完全访问”则走引导逻辑 if (![[KBFullAccessManager shared] hasFullAccess]) { // 未开启完全访问:保持原有引导路径 // [KBHUD showInfo:KBLocalized(@"Processing...")]; [[KBFullAccessManager shared] ensureFullAccessOrGuideInView:self.view]; return; } // 点击充值要先判断是否登录 // 2) 权限没问题,再判断是否登录:未登录 -> 直接拉起主 App,由主 App 负责完成登录 if (!KBAuthManager.shared.isLoggedIn) { NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; __weak typeof(self) weakSelf = self; [KBExtensionAppLauncher openPrimaryURL:ul fallbackURL:scheme usingInputController:self source:(self.view ?: (UIResponder *)weakSelf) completion:nil]; return; } [self kb_setPanelMode:KBKeyboardPanelModeSubscription animated:YES]; } - (void)hideSubscriptionPanel { if (self.kb_panelMode != KBKeyboardPanelModeSubscription) { return; } [self kb_setPanelMode:KBKeyboardPanelModeMain animated:YES]; } #pragma mark - KBKeyboardSubscriptionViewDelegate - (void)subscriptionViewDidTapClose:(KBKeyboardSubscriptionView *)view { [[KBMaiPointReporter sharedReporter] reportClickWithEventName:@"click_keyboard_subscription_close_btn" pageId:@"keyboard_subscription_panel" elementId:@"close_btn" extra:nil completion:nil]; [self hideSubscriptionPanel]; } - (void)subscriptionView:(KBKeyboardSubscriptionView *)view didTapPurchaseForProduct:(KBKeyboardSubscriptionProduct *)product { NSMutableDictionary *extra = [NSMutableDictionary dictionary]; if ([product.productId isKindOfClass:NSString.class] && product.productId.length > 0) { extra[@"product_id"] = product.productId; } [[KBMaiPointReporter sharedReporter] reportClickWithEventName:@"click_keyboard_subscription_product_btn" pageId:@"keyboard_subscription_panel" elementId:@"product_btn" extra:extra.copy completion:nil]; [self hideSubscriptionPanel]; [self kb_openRechargeForProduct:product]; } #pragma mark - Actions - (void)kb_openRechargeForProduct:(KBKeyboardSubscriptionProduct *)product { if (![product isKindOfClass:KBKeyboardSubscriptionProduct.class] || product.productId.length == 0) { [KBHUD showInfo:KBLocalized(@"Product unavailable")]; return; } NSString *encodedId = [self.class kb_urlEncodedString:product.productId]; NSString *title = [product displayTitle]; NSString *encodedTitle = [self.class kb_urlEncodedString:title]; NSMutableArray *params = [NSMutableArray arrayWithObjects:@"autoPay=1", @"prefill=1", nil]; if (encodedId.length) { [params addObject:[NSString stringWithFormat:@"productId=%@", encodedId]]; } if (encodedTitle.length) { [params addObject:[NSString stringWithFormat:@"productTitle=%@", encodedTitle]]; } NSString *query = [params componentsJoinedByString:@"&"]; NSString *urlString = [NSString stringWithFormat:@"%@://recharge?src=keyboard&%@", KB_APP_SCHEME, query]; NSURL *scheme = [NSURL URLWithString:urlString]; NSString *ulString = [NSString stringWithFormat:@"%@?src=keyboard&%@", KB_UL_RECHARGE, query]; NSURL *ul = [NSURL URLWithString:ulString]; __weak typeof(self) weakSelf = self; [KBExtensionAppLauncher openPrimaryURL:ul fallbackURL:scheme usingInputController:self source:(self.view ?: (UIResponder *)weakSelf) completion:^(BOOL success) { if (success) { return; } dispatch_async(dispatch_get_main_queue(), ^{ [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; }); }]; } + (NSString *)kb_urlEncodedString:(NSString *)value { if (value.length == 0) { return @""; } NSString *reserved = @"!*'();:@&=+$,/?%#[]"; NSMutableCharacterSet *allowed = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; [allowed removeCharactersInString:reserved]; return [value stringByAddingPercentEncodingWithAllowedCharacters:allowed] ?: @""; } @end