Files
peko-ios/YuMi/Modules/YMMine/View/Recharge/IAPManager.m
2024-09-24 18:01:08 +08:00

287 lines
10 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// IAPManager.m
// YuMi
//
// Created by P on 2024/9/24.
//
#import "IAPManager.h"
#import "Api+Mine.h"
#import "YuMi-swift.h"
#import "RechargeStorage.h"
@interface IAPManager()
@property (nonatomic, copy) NSString *orderID;
@property (nonatomic, copy) NSString *transactionID;
@property (nonatomic, copy) void(^successPurchase)(void);
@property (nonatomic, copy) void(^failurePurchase)(NSError *error);
@property (nonatomic, copy) void(^contactCustomerService)(NSString *uid);
@end
@implementation IAPManager
+ (instancetype)sharedManager {
static IAPManager *proxy;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
proxy = [[self alloc] init];
});
return proxy;
}
- (void)purchase:(NSString *)productId
success:(void(^)(void))success
failure:(void(^)(NSError *error))failure
contactCS:(void(^)(NSString *uid))contactCS {
self.successPurchase = success;
self.failurePurchase = failure;
self.contactCustomerService = contactCS;
[self handleIAPState];
[self requestAPPOrderData:productId];
}
- (void)handleSuccessPurchase {
if (self.successPurchase) {
@kWeakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@kStrongify(self);
self.successPurchase();
});
}
}
- (void)handleFailurePurchase:(NSString *)errorMsg {
if (self.failurePurchase) {
@kWeakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@kStrongify(self);
if (errorMsg.length == 0) {
self.failurePurchase(nil);
} else {
self.failurePurchase([NSError errorWithDomain:errorMsg code:-1 userInfo:nil]);
}
});
}
}
- (void)handleContactCS {
@kWeakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@kStrongify(self);
TTAlertConfig *config = [[TTAlertConfig alloc]init];
config.title = YMLocalizedString(@"XPIAPRechargeViewController7");
config.message = YMLocalizedString(@"XPIAPRechargeViewController8");
TTAlertButtonConfig *confirmButtonConfig = [[TTAlertButtonConfig alloc]init];
confirmButtonConfig.title = YMLocalizedString(@"XPIAPRechargeViewController9");
UIImage *image = [UIImage gradientColorImageFromColors:@[UIColorFromRGB(0x13E2F5),UIColorFromRGB(0x9DB4FF),UIColorFromRGB(0xCC67FF)] gradientType:GradientTypeLeftToRight imgSize:CGSizeMake(200, 200)];
confirmButtonConfig.backgroundColor = [UIColor colorWithPatternImage:image];
confirmButtonConfig.cornerRadius = 38/2;
config.confirmButtonConfig = confirmButtonConfig;
@kWeakify(self);
[TTPopup alertWithConfig:config confirmHandler:^{
@kStrongify(self);
[self loadCSUid];
} cancelHandler:^{}];
});
}
- (void)handleIAPState {
if (@available(iOS 15.0, *)) {
@kWeakify(self);
[[PIIAPRegulate shared] setConditionBlock:^(enum StoreConditionResult state, NSDictionary<NSString *,id> * _Nullable param) {
@kStrongify(self);
switch (state) {
case StoreConditionResultStart:
NSLog(@"~~~```~~~ Purchase started");
break;
case StoreConditionResultPay:
NSLog(@"~~~```~~~ Processing payment");
break;
case StoreConditionResultVerifiedServer:
NSLog(@"~~~```~~~ Verified by server");
[self handleIAPSuccess:param];
break;
case StoreConditionResultUserCancelled:
NSLog(@"~~~```~~~ User cancelled purchase");
[self handleFailurePurchase:@""];
break;
case StoreConditionResultNoProduct:
NSLog(@"~~~```~~~ No product found");
[self handleFailurePurchase:[NSString stringWithFormat:@"%@ No product found", YMLocalizedString(@"XPIAPRechargeViewController1")]];
break;
case StoreConditionResultFailedVerification:
NSLog(@"~~~```~~~ Verification failed");
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController1")];
break;
case StoreConditionResultUnowned:
NSLog(@"~~~```~~~ Result Unowned");
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController1")];
break;
default:
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController0")];
break;
}
}];
}
}
// 生成后端订单
- (void)requestAPPOrderData:(NSString *)productId {
if (@available(iOS 15.0, *)) {
@kWeakify(self);
[Api requestIAPRecharge:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
@kStrongify(self);
if (code == 200) {
NSString *orderId = (NSString *)data.data[@"recordId"];
NSString *uuid = (NSString *)data.data[@"appAccountToken"];
[self requestIAPOrder:orderId
productID:productId
uuid:uuid];
} else if (code == 50000) {
[self handleContactCS];
[self handleFailurePurchase:@""];
} else {
[self handleFailurePurchase:msg];
}
}
chargeProdId:productId
uid:[AccountInfoStorage instance].getUid
ticket:[AccountInfoStorage instance].getTicket
deviceInfo:[YYUtility deviceID]
clientIp:[YYUtility ipAddress]];
} else {
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController10")];
}
}
// 获取客服 UID
- (void)loadCSUid {
if (self.contactCustomerService) {
[Api requestContactCustomerServiceCompletion:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
NSString *uid = [NSString stringWithFormat:@"%@",data.data];
self.contactCustomerService(uid);
}
}];
}
}
// 开始进行内购,查询线上是否有对应 productID 的物品如果有会自动拉起付费弹窗PIIAPRegulate - purchase
- (void)requestIAPOrder:(NSString *)orderID productID:(NSString *)productID uuid:(NSString *)uuid {
self.orderID = orderID;
if (@available(iOS 15.0, *)) {
@kWeakify(self);
[[PIIAPRegulate shared] demandCommodityThingWithProductId:productID
uuid:uuid
completionHandler:^(NSError * _Nullable error) {
@kStrongify(self);
if (error) {
// 已在 ConditionBlock 中回调
}
}];
}
}
// 处理内购付款成功
- (void)handleIAPSuccess:(NSDictionary *)param {
self.transactionID = [param objectForKey:@"transactionId"];
if (self.transactionID.length == 0) {
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController1")];
return;
}
[self saveTransactionID];
[self checkReceiptWithTransactionID];
}
// 内购付款成功后保存收据 id 到钥匙串
- (void)saveTransactionID {
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
NSString *encodedReceipt = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
// 初始化字典并逐步填充
NSMutableDictionary *receiptInfo = [NSMutableDictionary dictionary];
// 使用通用方法添加字典键值对,减少冗余代码
[self addValueIfNotNil:self.transactionID forKey:@"transactionId" toDictionary:receiptInfo];
[self addValueIfNotNil:encodedReceipt forKey:@"receipt" toDictionary:receiptInfo];
[self addValueIfNotNil:self.orderID forKey:@"orderId" toDictionary:receiptInfo];
// 如果字典为空,直接返回
if (receiptInfo.count == 0) {
return;
}
// 保存数据到 RechargeStorage
[RechargeStorage saveTranscationId:self.transactionID
recipt:[receiptInfo toJSONString]
uid:[AccountInfoStorage instance].getUid];
}
// 通过苹果收据与后端订单进行验单与发货
// MARK: 必须等待结果
- (void)checkReceiptWithTransactionID {
@kWeakify(self);
[Api checkReceipt:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
@kStrongify(self);
if (code == 200) {
[self handleSuccessPurchase];
[self handleCheckReceiptSuccess];
} else {
// TODO: 重试逻辑
[self handleFailurePurchase:msg];
if (code == 1444) {
// 参数异常
}
}
}
chooseEnv:@"true"
chargeRecordId:self.orderID
transcationId:self.transactionID
uid:[AccountInfoStorage instance].getUid
ticket:[AccountInfoStorage instance].getTicket];
}
// 查找在缓存中的 transactionID 并处理
- (void)handleCheckReceiptSuccess {
if (@available(iOS 15.0, *)) {
@kWeakify(self);
[[PIIAPRegulate shared] verifyBusinessAccomplishWithTransactionID:self.transactionID
completionHandler:^(BOOL success, NSError * _Nullable error) {
@kStrongify(self);
if (success) {
// 移除全部缓存
if ([RechargeStorage delegateTranscationId:self.transactionID
uid:[AccountInfoStorage instance].getUid]) {
self.orderID = @"";
self.transactionID = @"";
}
} else {
// TODO: 补单
}
}];
}
}
- (void)addValueIfNotNil:(id)value forKey:(NSString *)key toDictionary:(NSMutableDictionary *)dictionary {
if (value != nil && key != nil) {
dictionary[key] = value;
}
}
#pragma mark - RETRY
@end