iOS内购图文流程(2017)

#什么是内购?

只要在iPhone App上购买的不是实物产品(也就是虚拟产品如qq币.虎牙币.电子书……) 都需要走内购流程,苹果在里面抽走三成。

#使用内购需要走的流程。

1,填写协议,税务和银行业务;
2,填写用户和职能; 创建内购的项目
3,写代码,和后台配合
4,添加项目内购测试账号;检验成果

#具体步骤:

###接下来的步骤特别繁琐,请做好准备。

一.填写协议,税务和银行业务配置 https://itunesconnect.apple.com

##1.请求合同

###填写地址信息

###阅读同意协议

##2.填写联系方式

###添加新的联系人

###选中联系人

##3.填写银行信息

###添加银行卡

###选择银行卡所在国家

###填写银行CNAPS Code货币类型 CN###银行账号如果是对公的账号,需要填写公司的英文名称,如果没有的话拼音

##4.填写税务信息
U.S Tax Forms: 美国税务
Australia Tax Forms:澳大利亚税务
Canada Tax Forms: 加拿大税务

第一个问题如下:询问你是否是美国居民,有没有美国伙伴关系或者美国公司,如果没有直接选择No。

第二个问题如下:询问你有没有在美国的商业性活动,没有也直接选No

###然后填写你的税务信息,包括以下几点:
Individual or Organization Name:个人或者组织名称
Country of incorporation: 所在国家
Type of Beneficial Owner:受益方式,独立开发者选个人
Permanent Residence:居住地址
Mailing address:邮寄地址
Name of Person Making this Declaration:声明人
Title:头衔

###填写完这些信息后就可以提交了


##5.等待审核,时间24小时左右。

#二.创建内购项目

###选中你要添加内购项目的APP然后点击功能,点击+创建内购项目。

###根据自己APP的需求选择类型

###填写项目名称id (ps:应用在首次添加内购买项目时需要与新的应用版本一起提交审核。)

###添加沙盒技术测试员

###用户职能>沙箱技术测试员

##注意事项:

###邮箱随便写但是格式要正确(test@163.com),但是不能用已经是AppleID的邮箱

###App Store 地区不要乱选。虽然随便哪个地区都可以用来测试(还没上线之前app并没有地区之分),但是在沙盒测试的时候,弹出的购买提示框会根据当前AppleID(沙盒账号)的地区显示语言的。

###使用沙盒测试一定要用真机 (越狱的不行)

###沙盒账号不能直接登录AppleStore登录时会提示”不允许创建iTunes账户”

###进行沙盒测试时需要退出AppleStore的账号

#三.代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <Foundation/Foundation.h>
#import "WXApi.h"

typedef NS_ENUM(NSInteger , Payment) {
    IAP6 = 6,
    IAP25 = 25,
    IAP60 = 60
};

@interface PayCenter : NSObject

singleton_interface(PayCenter);

- (void)startInit;

- (void)payWithMoney:(NSInteger)money andType:(PayType)type dataDic:(NSDictionary *)dataDic;

@property (nonatomic, strong) MBProgressHUD * hud;

@property (nonatomic, copy) NSString * rechargeNumber;

@end

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#define kProductID_IAP6 @"org.qqhl.nursning.6"//6

#import "PayCenter.h"
#import "PayHttpRequest.h"
//#import "Order.h"
//#import "DataSigner.h"
//#import <AlipaySDK/AlipaySDK.h>
#import <StoreKit/StoreKit.h>

@interface PayCenter()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
{
    int buyType;
    NSInteger _money;
}
@property (nonatomic, strong) NSMutableDictionary * dataDic;

@end
@implementation PayCenter

singleton_implementation(PayCenter);

- (void)startInit
{
    [WXApi registerApp:kWeChatShareAppKey withDescription:appDefaultTilte];
}

- (void)payWithMoney:(NSInteger)money andType:(PayType)type dataDic:(NSDictionary *)dataDic
{
    _money = money;
    self.dataDic = [NSMutableDictionary dictionaryWithDictionary:dataDic];
    switch (type) {
        case PayTypeWX:
            [self payTypeWXPay];
            break;
        case ApplePay:
//            [self payTypeAliPay];
            [[SKPaymentQueue defaultQueue]addTransactionObserver:self];
           
            if ([SKPaymentQueue canMakePayments]) {
                HULog(@"允许程序内付费购买");

                [self payTypeApplePay];
            }else {
                HULog(@"不允许程序内付费购买");
                UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"提示"
                                                                    message:@"您的手机没有打开程序内付费购买"
                                                                   delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
               
                [alerView show];
               
            }

            break;
        default:
            break;
    }
}

- (void)payTypeApplePay{
    NSLog(@"---------请求对应的产品信息------------");
    NSArray *product = nil;
    switch (_money) {
        case IAP6:
        {
            product=[[NSArray alloc] initWithObjects:kProductID_IAP6,nil];
            NSLog(@"---------IAP6------------");

        }
            break;
        case IAP25:
        {
            NSLog(@"---------IAP25------------");

        }
            break;
        case IAP60:
        {
            NSLog(@"---------IAP60------------");
        }

            break;
    
        default:
            break;
    }
   
    NSSet *nsSet = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:nsSet];
    request.delegate = self;
    [request start];
}
#pragma mark -  SKProductsRequestDelegate代理
//返回的在苹果服务器请求的产品信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    HULog(@"收到产品信息");
    NSArray *myProduct = response.products;
    HULog(@"产品id %@",response.invalidProductIdentifiers);
    NSLog(@"产品付费数量%d",(int)[myProduct count]);
//populate UI
    for (SKProduct *product in myProduct) {
      NSLog(@"product info");
      NSLog(@"SKProduct 描述信息%@", [product description]);
      NSLog(@"产品标题 %@" , product.localizedTitle);
      NSLog(@"产品描述信息: %@" , product.localizedDescription);
      NSLog(@"价格: %@" , product.price);
      NSLog(@"Product id: %@" , product.productIdentifier);
    }
    SKPayment *payment = nil;
    switch (_money) {
        case IAP6:
        {
            payment = [SKPayment paymentWithProductIdentifier:kProductID_IAP6];
        }
            break;
        case IAP25:{
           
        }
        default:
            break;
    }
   
    NSLog(@"发起购买请求");
    [[SKPaymentQueue defaultQueue]addPayment:payment];
   
   
}
//实现监听方法
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    NSLog(@"调用了几次这个方法?");
    SKPaymentTransaction *transaction = transactions.lastObject;
    switch (transaction.transactionState) {
        case SKPaymentTransactionStatePurchased: {
            NSLog(@"购买完成,向自己的服务器验证 ---- %@", transaction.payment.applicationUsername);
            NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]];
            NSString *receipt = [data base64EncodedStringWithOptions:0];
//            [self buySuccessWithReceipt:receipt transaction:transaction];
        }
            break;
        case SKPaymentTransactionStateFailed: {
            NSLog(@"交易失败");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        }
            break;
        case SKPaymentTransactionStateRestored: {
            NSLog(@"已经购买过该商品");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        }
            break;
        case SKPaymentTransactionStatePurchasing: {
            NSLog(@"商品添加进列表");
        }
            break;
        default: {
            NSLog(@"这是什么情况啊?");
        }
            break;
    }
}

@end

####购买成功后需要跟服务器交互验证交易是否成功

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
// 验证凭据,获取到苹果返回的交易凭据  
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
// 传输的是BASE64编码的字符串
/**
BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
BASE64是可以编码和解码的
*/
NSDictionary *requestContents = @{
@"receipt-data": [receipt base64EncodedStringWithOptions:0]
};
NSError *error;
// 转换为 JSON 格式
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
// 不存在
if (!requestData) { /* ... Handle error ... */ }

// 发送网络POST请求,对购买凭据进行验证
NSString *verifyUrlString;
测试环境 verifyUrlString = @"https://sandbox.itunes.apple.com/verifyReceipt";
生产环境 verifyUrlString = @"https://buy.itunes.apple.com/verifyReceipt";
// 国内访问苹果服务器比较慢,timeoutInterval 需要长一点
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:verifyUrlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];

[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];

// 在后台对列中提交验证请求,并获得官方的验证JSON结果
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
NSLog(@"链接失败");
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!jsonResponse) {
NSLog(@"验证失败");
}

// 比对 jsonResponse 中以下信息基本上可以保证数据安全
/*
bundle_id
application_version
product_id
transaction_id
*/

NSLog(@"验证成功");
}
}];

}

苹果官方文档
刚接触内购如有不足之处请指教

-------------本文结束感谢您的阅读-------------

本文标题:iOS内购图文流程(2017)

文章作者:leon

发布时间:2017年06月20日 - 17:06

最后更新:2019年04月25日 - 16:04

原始链接:https://huanghaipo.github.io/2017/06/20/iOS内购图文流程(2017)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!