iOS,苹果虚拟商品内付详解,从零到完成支付,2019-04-09

首先絮叨下为什么写这篇文章,每个人做每件事都有第一次,第一次做一件事总有一些弯路要走,我开始搞内付的时候 也是在网上翻了许多文章,但是没有找到一篇完整的, 苹果虚拟商品内付前期准备工作又是有许多牵连的,所以想要写这篇文章总结下  好了 下面开始讲如何实现内购,


因为实现的过程中有些截图没有保存,所以会有些图缺失,文章重在讲解流程和遇到的问题以及各个流程的关系

http://www.cnblogs.com/huanghaipo/p/7064383.html参考


文章结构:

1:银行税务,协议

2:证书

3:添加内购虚拟商品信息

4:xcode 配置

5:实现代码

6:内购实现过程中遇到的问题讲解

7:添加测试账号


1:协议,税务和银行业务

如果你的苹果账号是一个新的账号没有添加协议税务和银行业务信息,那么首先需要添加这些信息,上面参考链接里面有,

如果协议,税务和银行业务信息没有添加 那么后面就算实现了代码,app会返回给你的信息是搜索不到商品,

协议,税务和银行业务填写的信息是需要审核的,所以这个有必要先完成,

下图是我下午填写的信息第二天看到的状态都是有效,这样就可以搜索到商品信息了,



2:证书


一般我们用到的上传App Store和调试证书 applepay 是没有亮点的,但是内购支付证书需要这个,

当然 你可以在创建appid的时候 选择applepay 也可以在创建之后 点击下面的edit 更改



点击edit进入下图

如上图选择applepay 点击edit


上图如果点击进来没有merchant ID 那么需要在1出创建

参考 https://blog.csdn.net/shenguanhua/article/details/50772186

完成之后 会有一个这样的证书


安装到项目里面是这样的


证书只是讲解了与普通测试上架证书的不同,没有很详细的步骤讲解,

3:添加内购虚拟商品

这个问题 网上文章最多,参考 https://www.cnblogs.com/fengmin/p/8126079.html


如上图所示,是添加完商品之后的样子,



上面图箭头指向的审核信息一定也要填写,


上图 箭头指向的产品ID,是在程序里面搜索此商品的标识,只要是唯一就可以,程序里面就是通过这个id搜索到这个商品的,



如上图 我的审核信息没有填写,点击保存,会出现元数据丢失


4:xcode配置



如果 打开Apple Pay,如果是新的xocde 没有添加账号 1 和2 的箭头指向 会有错误,此时 点击 3箭头指向的加号或者刷新会有错误提示 ,需要xcode添加账号



如上图 点击xocde->preferences 



点击 箭头指向加号添加账号 ,完成之后 点击 3处的刷新 就好了

1处的 merchant ID是签名创建了才会有,这里如果没有 merchant ID 那么证书是有问题的 ,签名见了如何创建merchant ID


4 :代码

上面一些配置信息比较复杂,下面就是简单的代码了,代码我整个粘贴的.m里面的所有代码 如果网站编码没有问题 复制粘贴就可以运行,

#import "ViewController.h"

#import

@interface ViewController ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>

@property (nonatomic,copy) NSString *currentProId;

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];


    UIButton *button2 = [UIButton buttonWithType:UIButtonTypeCustom];

    button2.frame=CGRectMake(300,100,100,100);

    button2.backgroundColor = [UIColor greenColor];

    [button2setTitle:@"6元" forState:UIControlStateNormal];

    [button2addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];

    [self.viewaddSubview:button2];


}

- (void)btnClick:(UIButton*)button

{

    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];

    _currentProId = @"201904081503";//产品id

    if([SKPaymentQueue canMakePayments]){

        [self requestProductData:_currentProId];

    }else{

        NSLog(@"不允许程序内付费");

    }

}

//去苹果服务器请求商品

- (void)requestProductData:(NSString*)type{

    NSLog(@"-------------请求对应的产品信息----------------");


//    [SVProgressHUD showWithStatus:nil maskType:SVProgressHUDMaskTypeBlack];





    NSArray*product = [[NSArrayalloc]initWithObjects:type,nil];


    NSSet*nsset = [NSSetsetWithArray:product];

    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];

    request.delegate=self;

    [requeststart];


}

//收到产品返回信息

- (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProductsResponse*)response{


    NSLog(@"--------------收到产品反馈消息---------------------");

    NSArray*product = response.products;

    if([productcount] ==0){

//        [SVProgressHUD dismiss];

        NSLog(@"--------------没有商品------------------");

        return;

    }


    NSLog(@"productID:%@", response.invalidProductIdentifiers);

    NSLog(@"产品付费数量:%lu",(unsignedlong)[product count]);


    SKProduct*p =nil;

    for(SKProduct*proinproduct) {

        NSLog(@"%@", [prodescription]);

        NSLog(@"%@", [pro localizedTitle]);

        NSLog(@"%@", [pro localizedDescription]);

        NSLog(@"%@", [proprice]);

        NSLog(@"%@", [pro productIdentifier]);


        if([pro.productIdentifierisEqualToString:_currentProId]){

            p = pro;

        }

    }


    SKPayment*payment = [SKPaymentpaymentWithProduct:p];


    NSLog(@"发送购买请求");

    [[SKPaymentQueue defaultQueue] addPayment:payment];

}

//请求失败

- (void)request:(SKRequest*)request didFailWithError:(NSError*)error{

//    [SVProgressHUD showErrorWithStatus:@"支付失败"];

    NSLog(@"------------------错误-----------------:%@", error);

}

- (void)requestDidFinish:(SKRequest*)request{

//    [SVProgressHUD dismiss];

    NSLog(@"------------反馈信息结束-----------------");

}

//沙盒测试环境验证

#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"

//正式环境验证

#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"

/**

 *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题

 *

 */

-(void)verifyPurchaseWithPaymentTransaction{

    //从沙盒中获取交易凭证并且拼接成请求体数据

    NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];

    NSData*receiptData=[NSDatadataWithContentsOfURL:receiptUrl];


    NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串


    NSString*bodyString = [NSStringstringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接请求数据

    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];



    //创建请求到苹果官方进行购买验证

    NSURL*url=[NSURLURLWithString:SANDBOX];

    NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];

    requestM.HTTPBody=bodyData;

    requestM.HTTPMethod=@"POST";

    //创建连接并发送同步请求

    NSError*error=nil;

    NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];

    if(error) {

        NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);

        return;

    }

    NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];

    NSLog(@"%@",dic);

    if([dic[@"status"]intValue]==0){

        NSLog(@"购买成功!");

        NSDictionary*dicReceipt= dic[@"receipt"];

        NSDictionary*dicInApp=[dicReceipt[@"in_app"]firstObject];

        NSString*productIdentifier= dicInApp[@"product_id"];//读取产品标识

        //如果是消耗品则记录购买数量,非消耗品则记录是否购买过

        NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];

        if([productIdentifierisEqualToString:@"123"]) {

            intpurchasedCount=[defaultsintegerForKey:productIdentifier];//已购买数量

            [[NSUserDefaultsstandardUserDefaults]setInteger:(purchasedCount+1)forKey:productIdentifier];

        }else{

            [defaultssetBool:YESforKey:productIdentifier];

        }

        //在此处对购买记录进行存储,可以存储到开发商的服务器端

    }else{

        NSLog(@"购买失败,未通过验证!");

    }

}

//监听购买结果

- (void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*)transaction{

    for(SKPaymentTransaction*tranintransaction){

        switch(tran.transactionState) {

            case SKPaymentTransactionStatePurchased:{

                NSLog(@"交易完成");

                // 发送到苹果服务器验证凭证

                [self verifyPurchaseWithPaymentTransaction];

                [[SKPaymentQueue defaultQueue] finishTransaction:tran];


            }

                break;

            case SKPaymentTransactionStatePurchasing:

                NSLog(@"商品添加进列表");


                break;

            case SKPaymentTransactionStateRestored:{

                NSLog(@"已经购买过商品");


                [[SKPaymentQueue defaultQueue] finishTransaction:tran];

            }

                break;

            case SKPaymentTransactionStateFailed:{

                NSLog(@"交易失败");

                [[SKPaymentQueue defaultQueue] finishTransaction:tran];

//                [SVProgressHUD showErrorWithStatus:@"购买失败"];

            }

                break;

            default:

                break;

        }

    }

}

//交易结束

- (void)completeTransaction:(SKPaymentTransaction*)transaction{

    NSLog(@"交易结束");


    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

}

- (void)dealloc{

    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

}

@end




6:  实现过程中遇到的问题,

在查找网上资料的时候,没有找到一篇很完整的文章,所以在开发的过程中,会出现许多问题,我在上面讲解的过程中也 有提到 ,

1:银行税务,协议

2:证书

3:添加内购虚拟商品信息

4:xcode 配置

5:实现代码

上面5个人步骤 在实现内购的过程中都是必要的,我在网站找资料的时候 没有遇到一个很完整的文章所以遇到了一些问题,如果上面5个步骤都完成了,应该不会有什么问题



7:添加测试账号


在用户和访问里面 沙箱技术,测试员添加测试账号 ,我的账号是我自己的appleid ,添加测试员不能添加当前账户的邮箱id,,我在购买的过程中,没有使用沙箱技术里面测试员的账户购买了 结果购买失败,于是用朋友的appleid 添加了 测试员 ,再次用测试员的账户购买 成功了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容