客户端IAP二次验证

 

1、首先苹果IAP把每次购买抽象成了一个事务(SKPaymentTransaction),

 

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response;
每次在上述方法中收到反馈信息之后添加的[[SKPaymentQueue defaultQueue] addPayment:payment];
就相当于在事务管理中添加了一个事务,只有事务被正常结束(finishTransaction:)该次支付行为才算完成。
即使一次支付中途被中断,这次事务也并没有丢失。假设支付没有完成App就退出了(比如突然崩溃了),那么当下次App重启之后(调用了addTransactionObserver:),之前被中断的事务会接着进行。
这个的缺点就是中间等待重启的时间可能会很长,影响用户体验。

 

2、在购买成功后的客户端二次验证。

每次购买成功之后都会把encodeStr存到本地,验证成功再进行删除。以后每次打开应用或打开充值会先查看是否有未完成的验证

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//交易结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
    NSLog(@"5.交易结果");
    for (SKPaymentTransaction *transaction in transactions){
        switch (transaction.transactionState){
            case SKPaymentTransactionStatePurchased:{//交易完成
                NSLog(@"交易完成");
                [self completeTransaction:transaction];
            } break;
            case SKPaymentTransactionStateFailed://交易失败
            { [self failedTransaction:transaction];
                NSLog(@"-----交易失败 --------");
                [SVProgressHUD dismiss];
                UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"提示"
                                                                     message:@"购买失败,请重新尝试购买"
                                                                    delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
                [alerView2 show];
                 
            }break;
            case SKPaymentTransactionStateRestored://已经购买过该商品
                [self restoreTransaction:transaction];
                NSLog(@"-----已经购买过该商品 --------");
            case SKPaymentTransactionStatePurchasing:      //商品添加进列表
                NSLog(@"-----商品添加进列表 --------");
                break;
            default:
                break;
        }
    }
}
- (void)completeTransaction: (SKPaymentTransaction *)transaction{
    NSString * productIdentifier = transaction.payment.productIdentifier;
    if ([productIdentifier length] > 0) {
        [SVProgressHUD dismiss];
        //添加IAPModel到本地
        //获取验证凭证
        NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
        //从沙盒中获取购买凭证
        NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
        //进行Base64加密
        NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        WekeIAPModel *model = [[WekeIAPModel alloc]initWithReceiptStr:encodeStr user_id:[AppSingle sharedAppSingle].user_id];
        [[AppSingle sharedAppSingle].IAPArray addObject:model];
        [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].IAPArray fileName:WekeIAP];
        [self verifyPruchaseWithEncodeStr:encodeStr];
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    }else{
        [SVProgressHUD dismiss];
        UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"提示"
                                                             message:@"购买失败,未能连接到苹果服务器"
                                                            delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
        [alerView2 show];
    }
}
//验证购买凭证
- (void)verifyPruchaseWithEncodeStr :(NSString *)encodeStr{
    //验证URL POST请求
    NSURL *url = [NSURL URLWithString:Buy_Verify_Receipt_Url];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f];
    request.HTTPMethod = @"POST";
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    request.HTTPBody = payloadData;
    // 提交验证请求,并获得官方的验证JSON结果
    NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    // 官方验证结果为空
    if (result == nil) {
        NSLog(@"验证失败");
    }else{
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
        NSLog(@"线上环境验证结果%@",dict);
        if ([[dict objectForKey:@"status"] integerValue] == 21007) {
            //如果是沙盒环境
            [self verifyPruchaseInSANDBOXWithEncodeStr:encodeStr];
        }else if ([[dict objectForKey:@"status"] integerValue] == 0){
            NSLog(@"线上环境验证成功,进行增加积分等操作");
            //验证成功 删除IAP数组中的该Model
            for (WekeIAPModel *model in self.IAPArray) {
                if ([model.encodeStr isEqualToString:encodeStr]) {
                    [[AppSingle sharedAppSingle].IAPArray removeObject:model];
                    [self.IAPArray removeObject:model];
                    [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].IAPArray fileName:WekeIAP];
                }
            }
            [self postGrede];
             
        }else{
            NSLog(@"线上环境验证失败");
            UIAlertController *alertCT = [UIAlertController alertControllerWithTitle:@"提示" message:@"充值验证失败,请重试" preferredStyle:(UIAlertControllerStyleAlert)];
            UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            }];
            UIAlertAction *care = [UIAlertAction actionWithTitle:@"重试"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [self verifyPruchaseWithEncodeStr:encodeStr];
            }];
            [alertCT addAction:cancel];
            [alertCT addAction:care];
            [self presentViewController:alertCT animated:YES completion:nil];
        }
    }
}
 
//在沙盒中验证
- (void)verifyPruchaseInSANDBOXWithEncodeStr:(NSString *)encodeStr{
 
    //验证URL POST请求
    NSURL *url = [NSURL URLWithString:SANDBOX_VERIFY_RECEIPT_URL];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f];
    request.HTTPMethod = @"POST";
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    request.HTTPBody = payloadData;
    // 提交验证请求,并获得官方的验证JSON结果
    NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    if (result== nil) {
        NSLog(@"沙盒验证失败");
        UIAlertController *alertCT = [UIAlertController alertControllerWithTitle:@"提示" message:@"充值验证失败,请重试" preferredStyle:(UIAlertControllerStyleAlert)];
        UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        }];
        UIAlertAction *care = [UIAlertAction actionWithTitle:@"重试"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            [self verifyPruchaseInSANDBOXWithEncodeStr:encodeStr];
        }];
        [alertCT addAction:cancel];
        [alertCT addAction:care];
        [self presentViewController:alertCT animated:YES completion:nil];
    }else{
        NSLog(@"沙盒验证成功");
        [self postGrede];
        //验证成功 删除IAP数组中的该Model
        for (WekeIAPModel *model in self.IAPArray) {
            if ([model.encodeStr isEqualToString:encodeStr]) {
                [[AppSingle sharedAppSingle].IAPArray removeObject:model];
                [self.IAPArray removeObject:model];
                [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].IAPArray fileName:WekeIAP];
            }
        }
         
    }
}

 

复制代码
posted @   小师傅啊小师傅  阅读(475)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
点击右上角即可分享
微信分享提示