注册 登录
主题 : 内购返回21004状态码
级别: 侠客
状态: 连续签到 - [8天]
UID: 577446
精华: 0
发帖: 108
可可豆: 427 CB
威望: 409 点
在线时间: 170(时)
注册时间: 2016-08-11
最后登录: 2017-10-08
0 楼:  发表于: 2017-08-11 23:40    发自: Web Page
来源于 一般提问 分类

内购返回21004状态码   

用沙箱测试账号        完成了一次购买的流程




但是,在二次发送验证凭据的时候




苹果服务器给我返回的是21004的状态码


查阅之后,知道:


[table=746.4375px][tr][td]21004[/td][td]提供的shared secret不匹配你账号中的shared secret[/td][/tr][/table]



这是啥原因呢?        不匹配我账号的  shared secret?



苹果服务器给我返回的是这样的信息:



dict = {
    environment = Sandbox;
    status = 21004;
}




而我从沙盒里面获取购买凭据的代码:



// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSLog(@"receiptURL = %@",receiptURL);
// 从沙盒中获取到购买凭据
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];





我看过帖子里面,在4月份的时候,很多前辈都遇到这种问题      




主要是因为苹果服务器那边炸了,现在弄好了  




那,为啥我的就不行哦~~~~




我试过用旧的办法获取凭证:


    for (SKPaymentTransaction *tran in transactions) {
        switch (tran.transactionState) {

NSData *receiptData = tran.transactionReceipt;


旧的办法不行,直接炸穿了了。




啥原因呢~~~~~大腿们

级别: 侠客
状态: 连续签到 - [8天]
UID: 577446
精华: 0
发帖: 108
可可豆: 427 CB
威望: 409 点
在线时间: 170(时)
注册时间: 2016-08-11
最后登录: 2017-10-08
1 楼:  发表于: 2017-08-11 23:42    发自: Web Page
额     居然不支持   html 中  table的格式    

好吧    老老实实   写字好了:

Status        描述
21004         提供的shared secret不匹配你账号中的shared secret
级别: 骑士
状态: 连续签到 - [36天]
UID: 497385
精华: 0
发帖: 406
可可豆: 596 CB
威望: 509 点
在线时间: 350(时)
注册时间: 2015-08-14
最后登录: 2017-10-19
2 楼:  发表于: 2017-08-12 08:55    发自: Web Page
回 1楼(啦啦F) 的帖子
你可以换白天试试,晚上苹果上班,谁知道他们又在搞啥,有时候抽风很正常。
There is no shortcut to success. Only by knocking more codes can lead you to the way to success...
级别: 侠客
状态: 连续签到 - [8天]
UID: 577446
精华: 0
发帖: 108
可可豆: 427 CB
威望: 409 点
在线时间: 170(时)
注册时间: 2016-08-11
最后登录: 2017-10-08
3 楼:  发表于: 2017-08-12 09:54    发自: Web Page
回 2楼(技术哥丶) 的帖子
现在是白天了  
  
还是不行

依然是21004的状态码

不过,我这几次帖子都好像有你的身影哦
级别: 骑士
状态: 连续签到 - [36天]
UID: 497385
精华: 0
发帖: 406
可可豆: 596 CB
威望: 509 点
在线时间: 350(时)
注册时间: 2015-08-14
最后登录: 2017-10-19
4 楼:  发表于: 2017-08-12 10:09    发自: Web Page
回 3楼(啦啦F) 的帖子
哈哈。最近也在折腾自己的app,内购这块用的多。问题是我这几天测试都没发现你所说的问题呢。。。
There is no shortcut to success. Only by knocking more codes can lead you to the way to success...
级别: 骑士
状态: 连续签到 - [36天]
UID: 497385
精华: 0
发帖: 406
可可豆: 596 CB
威望: 509 点
在线时间: 350(时)
注册时间: 2015-08-14
最后登录: 2017-10-19
5 楼:  发表于: 2017-08-12 10:13    发自: Web Page
回 3楼(啦啦F) 的帖子
还有个问题,你使用的是测试验证地址还是正式的验证地址?

http://www.cocoachina.com/bbs/read.php?tid=454367

这个帖子有说。但是你那个状态码没有具体详细说明。
There is no shortcut to success. Only by knocking more codes can lead you to the way to success...
级别: 侠客
状态: 连续签到 - [141天]
UID: 563339
精华: 0
发帖: 78
可可豆: 823 CB
威望: 520 点
在线时间: 367(时)
注册时间: 2016-05-16
最后登录: 2017-10-19
6 楼:  发表于: 2017-08-12 10:47    发自: Web Page
你用的是沙盒测试员的测试账号吗?
级别: 侠客
状态: 连续签到 - [8天]
UID: 577446
精华: 0
发帖: 108
可可豆: 427 CB
威望: 409 点
在线时间: 170(时)
注册时间: 2016-08-11
最后登录: 2017-10-08
7 楼:  发表于: 2017-08-12 11:38    发自: Web Page
回 5楼(技术哥丶) 的帖子
当然是 沙箱测试员的账号呀
发送的地址自然也是   沙盒测试的地址


我把全部代码粘贴上来吧
级别: 侠客
状态: 连续签到 - [8天]
UID: 577446
精华: 0
发帖: 108
可可豆: 427 CB
威望: 409 点
在线时间: 170(时)
注册时间: 2016-08-11
最后登录: 2017-10-08
8 楼:  发表于: 2017-08-12 11:42    发自: Web Page
全部代码奉上,看看我哪里有错哦





//
//  TextViewController.m
//  MoveStall
//
//  Created by HZhenF on 2017/8/10.
//  Copyright © 2017年 HuangZhenFeng. All rights reserved.
//

#import "TextViewController.h"
#import <StoreKit/StoreKit.h>

#define StallID @"xt1001"

@interface TextViewController ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>

@end

@implementation TextViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    [self setupControls];
    
    //添加一个交易队列观察者
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
}

// 去苹果服务器请求产品信息
- (void)requestProductData:(NSString *)productId {
    
    
//    [SVProgressHUD show];
    NSLog(@"-------------请求对应的产品信息----------------");
    //根据商品ID查找商品信息
    NSArray *productArr = [[NSArray alloc]initWithObjects:productId, nil];
    //创建SKProductsRequest对象,用想要出售的商品的标识来初始化, 然后附加上对应的委托对象。
    //该请求的响应包含了可用商品的本地化信息。
    NSSet *productSet = [NSSet setWithArray:productArr];
    
    SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
    
    request.delegate = self;
    [request start];
    
}



-(void)btnAction
{
    NSLog(@"btnAction");
    //判断是否可进行支付
    if ([SKPaymentQueue canMakePayments]) {
        [self requestProductData:StallID];
    }else{
        NSLog(@"不允许程序内付费");
    }
}


-(void)setupControls
{
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(100, 100, 100, 100);
    [btn setTitle:@"点我支付" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}


// 收到产品返回信息
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    NSLog(@"--------------收到产品反馈消息---------------------");
    NSArray *productArr = response.products;
    
    if ([productArr count] == 0) {
        //        [SVProgressHUD dismiss];
        NSLog(@"没有该商品");
        return;
    }
    
    
    NSLog(@"productId = %@",response.invalidProductIdentifiers);
    NSLog(@"产品付费数量 = %zd",productArr.count);
    
    SKProduct *p = nil;
    
    for (SKProduct *pro in productArr) {
        NSLog(@"description:%@",pro.description);
        NSLog(@"localizedTitle:%@",pro.localizedTitle);
        NSLog(@"localizedDescription:%@",[pro localizedDescription]);
        NSLog(@"price:%@",[pro price]);
        NSLog(@"productIdentifier:%@",[pro productIdentifier]);
        NSLog(@"priceLocale:%@",pro.priceLocale);
        
        if ([pro.productIdentifier isEqualToString:StallID]) {
            p = pro;
        }
    }
    
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    
    //发送内购请求
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}


// 监听购买结果
- (void) paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions {
    NSLog(@"-----paymentQueue--------");
    
    for (SKPaymentTransaction *tran in transactions) {
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased: //交易完成
                NSLog(@"交易完成");
                // 发送到苹果服务器验证凭*****br />                [self verifyPurchaseWithPaymentTrasaction];
                
                NSLog(@"从队列中删除商品");
                // 将交易从交易队列中删除
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStatePurchasing: //商品添加进列表
                NSLog(@"商品添加进列表");
                break;
            case SKPaymentTransactionStateRestored: //购买过
                // 发送到苹果服务器验证凭*****br />                NSLog(@"购买过");
                // 将交易从交易队列中删除
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStateFailed: //交易失败
                NSLog(@"交易失败");
                // 将交易从交易队列中删除
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
//                [SVProgressHUD showErrorWithStatus:@"购买失败"];
                break;
                
            default:
                break;
        }
    }
}



//沙盒测试环境验*****br />#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
//正式环境验*****br />#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
// 验证购买
- (void)verifyPurchaseWithPaymentTrasaction
{
    NSLog(@"verifyPurchaseWithPaymentTrasaction");
    // 验证凭据,获取到苹果返回的交易凭据
    // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSLog(@"receiptURL = %@",receiptURL);
    // 从沙盒中获取到购买凭据
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    
    // 发送网络POST请求,对购买凭据进行验*****br />    //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
    //正式验证地址:https://buy.itunes.apple.com/verifyReceipt
    NSURL *url = [NSURL URLWithString:SANDBOX];
//    NSMutableURLRequest *urlRequest =
//    [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
    urlRequest.HTTPMethod = @"POST";
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    
    urlRequest.HTTPBody = payloadData;
    
    
    NSURLSession *session = [NSURLSession sharedSession];
    [[session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            NSLog(@"error = %@",[error localizedDescription]);
        }
        else
        {
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            NSLog(@"dict = %@",dict);
        }
    }] resume];
    
    // 提交验证请求,并获得官方的验证JSON结果 iOS9后更改了另外的一个方法
    //    NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
    
//    // 官方验证结果为空
//    if (result == nil) {
//        NSLog(@"验证失败");
//        return;
//    }
//    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
//    if (dict != nil) {
//        // 比对字典中以下信息基本上可以保证数据安全
//        // bundle_id , application_version , product_id , transaction_id
//        NSLog(@"验证成功!购买的商品是:%@", @"_productName");
//        NSLog(@"dict = %@",dict);
//    }
    
}


-(void)dealloc
{
    //解除监听
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

@end

级别: 侠客
状态: 连续签到 - [8天]
UID: 577446
精华: 0
发帖: 108
可可豆: 427 CB
威望: 409 点
在线时间: 170(时)
注册时间: 2016-08-11
最后登录: 2017-10-08
9 楼:  发表于: 2017-08-12 11:42    发自: Web Page
本次Log信息打印:

2017-08-12 11:40:05.420479+0800 MoveStall[4914:1047909] -------------请求对应的产品信息----------------
2017-08-12 11:40:08.080362+0800 MoveStall[4914:1047909] --------------收到产品反馈消息---------------------
2017-08-12 11:40:08.080521+0800 MoveStall[4914:1047909] productId = (
)
2017-08-12 11:40:08.080551+0800 MoveStall[4914:1047909] 产品付费数量 = 1
2017-08-12 11:40:08.080608+0800 MoveStall[4914:1047909] description:<SKProduct: 0x1c001ae30>
2017-08-12 11:40:08.080624+0800 MoveStall[4914:1047909] localizedTitle:(null)
2017-08-12 11:40:08.080639+0800 MoveStall[4914:1047909] localizedDescription:(null)
2017-08-12 11:40:08.080676+0800 MoveStall[4914:1047909] price:12
2017-08-12 11:40:08.080706+0800 MoveStall[4914:1047909] productIdentifier:xt1001
2017-08-12 11:40:08.080835+0800 MoveStall[4914:1047909] priceLocale:<__NSCFLocale: 0x1c00da240>
2017-08-12 11:40:08.080915+0800 MoveStall[4914:1047909] -----paymentQueue--------
2017-08-12 11:40:08.080939+0800 MoveStall[4914:1047909] 商品添加进列表
2017-08-12 11:40:10.335100+0800 MoveStall[4914:1047909] -----paymentQueue--------
2017-08-12 11:40:10.335179+0800 MoveStall[4914:1047909] 交易完成
2017-08-12 11:40:10.335196+0800 MoveStall[4914:1047909] verifyPurchaseWithPaymentTrasaction
2017-08-12 11:40:10.390899+0800 MoveStall[4914:1047909] receiptURL = file:///private/var/mobile/Containers/Data/Application/C838E5EA-A869-4B58-9E6A-1D976F6B67B1/StoreKit/sandboxReceipt
2017-08-12 11:40:10.391867+0800 MoveStall[4914:1047909] 从队列中删除商品
2017-08-12 11:40:12.009228+0800 MoveStall[4914:1047930] dict = {
    environment = Sandbox;
    status = 21004;
}

描述
快速回复

关注本帖(如果有新回复会站内信通知您)

发帖、回帖都会得到可观的积分奖励。查看论坛积分规则

按"Ctrl+Enter"直接提交
    顶部