iOS苹果内购(详细步骤)

注意 apple Pay 不是苹果内购

1.第一步,先到 App Store Connect 中 的 协议部分,去填写信息,(这部分一般法务去解决)

(1)查看付费同意条款

(2)设置付费协议,添加账户,

(3)选择报税表等

(4)填写联系人

2.添加内购项目 (这个需要开发参与)

(1)添加内购项目

此处的产品ID 为唯一ID:  一般的规则:  bundleID.xxx    ;

(2)设置内购项目类型

消耗型和非消耗型的区别是:

消耗型:可以重复多次购买

非消耗型:只能购买一次,不会过期,例如:永久会员等;

(3).设置名称,可以自定义;

(4)按照要求设置销售范围,价格,税务类别

(5)添加App Store 本地化版本;(只要是设置语言)

(6)App Store 推广

此处推广图也要是唯一的,不能和其余的商品推广图一致;(不能包含alpha通道)

如果设计给的图不合规,可以尝试把图片用预览打开后 导出 ,取消alpha ,导出即可使用;

(7)审核信息就是,商品在App 中的页面位置截图,以及描述

(8)存储即可

此时我们已经创建了这个商品,接下来就是在项目中获取商品信息,并支付

3.在项目中的代码操作:

逻辑:  先通过后端的接口获取到商品的产品ID,然后在页面中发起购买请求,这个请求是向App Store发出的,整个过程不用与后端交互,等结果返回后,将支付凭证发给后端即可.

具体的代码,可以参考:

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>


typedef enum : NSUInteger {
    ApplePayManagerTypeSuccess,         // 购买成功
    ApplePayManagerTypeFailed,          // 购买失败
    ApplePayManagerTypeCancle,          // 取消购买
    ApplePayManagerTypeVerFailed,       // 订单校验失败
    ApplePayManagerTypeVerSuccess,      // 订单校验成功
    ApplePayManagerTypeNotArrow,        // 不允许内购
} ApplePayManagerType;

typedef void (^IAPCompletionHandle)( ApplePayManagerType type,NSData * _Nullable data);

NS_ASSUME_NONNULL_BEGIN

@interface ApplePayManager : NSObject

@property (nonatomic, strong)UIView * targetView;

+ (instancetype)shareAPManager;

//开始内购
- (void)startPurchWithDict:(NSDictionary *)purch completeHandle:(IAPCompletionHandle)handle;

#import "ApplePayManager.h"

#import "MineMemberInAppVerifyReceiptRequest.h"

@interface ApplePayManager ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>

@property (nonatomic,copy) NSString * purchID;

@property (nonatomic,copy) IAPCompletionHandle handle;

@property (nonatomic,strong) NSDictionary * parmas;


@end

@implementation ApplePayManager

+(instancetype)shareAPManager{
    static ApplePayManager *IAPManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        IAPManager = [[ApplePayManager alloc] init];
    });
    return IAPManager;
}

- (instancetype)init{
    self = [super init];
    if (self) {
        // 购买监听写在程序入口,程序挂起时移除监听,这样如果有未完成的订单将会自动执行并回调 paymentQueue:updatedTransactions:方法
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    }
    return self;
}

- (void)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

#pragma mark - public  去苹果服务器请求产品信息
-(void)startPurchWithDict:(NSDictionary *)purch completeHandle:(IAPCompletionHandle)handle{
    if (purch.allKeys.count > 0) {
        self.parmas = purch;
        if ([purch objectForKey:@"productId"]) {
            if ([SKPaymentQueue canMakePayments]) {
                // 开始购买服务
                self.purchID = [purch objectForKey:@"productId"];
                self.handle = handle;
                NSSet *nsset = [NSSet setWithArray:@[self.purchID]];
                SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
                request.delegate = self;
                [request start];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.targetView makeToastActivity:CSToastPositionCenter];
                });
            }else{
                [self handleActionWithType:ApplePayManagerTypeNotArrow data:nil];
            }
        }
    }
}

#pragma mark --收到产品返回信息 SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    NSArray *product = response.products;
    if([product count] <= 0){
        NSLog(@"--------------没有商品------------------");
        return;
    }
    
    SKProduct *p = nil;
    for(SKProduct *pro in product){
        if([pro.productIdentifier isEqualToString:self.purchID]){
            p = pro;
            break;
        }
    }
    
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
    NSLog(@"%@",[p description]);
    NSLog(@"%@",[p localizedTitle]);
    NSLog(@"%@",[p localizedDescription]);
    NSLog(@"%@",[p price]);
    NSLog(@"%@",[p productIdentifier]);
    NSLog(@"发送购买请求");

    //发送内购请求
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}


//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
//    [self.targetView hideToastActivity];
    NSLog(@"------------------错误-----------------:%@", error);
}

- (void)requestDidFinish:(SKRequest *)request{
//    [self.targetView hideToastActivity];
    NSLog(@"------------反馈信息结束-----------------");
}

#pragma mark - SKPaymentTransactionObserver // 监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    [self.targetView hideToastActivity];
    for (SKPaymentTransaction *tran in transactions) {
        NSLog(@"%ld====",(long)tran.transactionState);
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased: //交易完成
                //订阅特殊处理
                if(tran.originalTransaction){
                    //如果是自动续费的订单originalTransaction会有内容
                    NSLog(@"自动续费的订单,originalTransaction = %@",tran.originalTransaction.transactionIdentifier);
                    //tran.originalTransaction.transactionIdentifier
                    //SKPaymentTransaction
                    [self completeTransaction:tran isAutomatically:YES];
                    //[self verifyPurchaseWithPaymentTransaction:tran isTestServer:NO];
                }else{
                    //普通购买,以及 第一次购买 自动订阅
                   // NSLog(@"%@-------",tran.transactionIdentifier);
                    [self completeTransaction:tran isAutomatically:NO];
                }
                
                break;
            case SKPaymentTransactionStatePurchasing://商品添加进列表
                //NSLog(@"%ld====",tran.error.code);
                //NSLog(@"%@====",[[NSString alloc]initWithData:tran.payment.requestData encoding:NSUTF8StringEncoding]);
                //[TDGAVirtualCurrency onChargeRequst:@"" iapId:@"" currencyAmount:0 currencyType:@"" virtualCurrencyAmount:0 paymentType:@""];
                break;
            case SKPaymentTransactionStateRestored://购买过
                NSLog(@"已经购买过商品");
                // 消耗型不支持恢复购买
                //[[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStateFailed://交易失败
                  NSLog(@"%@====",tran.error);
                //SKErrorUnknown
                [self failedTransaction:tran];
                
                break;
            default:
                break;
        }
    }
}


/// AppStore 从促销点击购买处理
- (BOOL)paymentQueue:(SKPaymentQueue *)queue shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product {
    // 直接返回 NO,不交给苹果处理
    NSString *productID = product.productIdentifier;
    NSLog(@"AppStore 促销点击购买: productIdentifier - %@", productID);
    
    return YES;
}

#pragma mark - delegate
// 交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction isAutomatically:(BOOL)isAutomatically{
    
    NSLog(@"%@", transaction.description);
    // Your application should implement these two methods.
    //    票据的校验是保证内购安全完成的非常关键的一步,一般有三种方式:
    //    1、服务器验证,获取票据信息后上传至信任的服务器,由服务器完成与App Store的验证(提倡使用此方法,比较安全)
    //    2、本地票据校验
    //    3、本地App Store请求验证
   

    NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
    // 向自己的服务器验证购买凭证
    //NSError *error;
    //转化为base64字符串
    NSString *receiptString=[receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    NSLog(@"receipt ==== \n%@", receiptString);

    //最终将这个receipt3的发送给服务器去验证就没问题啦!
    //自动订阅(自动续费月卡)需要多加一个参数
    
    NSString * product_id = transaction.payment.productIdentifier;
    NSString * transaction_id = transaction.transactionIdentifier;
    
    
    NSMutableDictionary * requestContents = [[NSMutableDictionary alloc]init];
    //订阅特殊处理
    if (isAutomatically) {
         //如果是自动续费的订单originalTransaction会有内容
        NSString * transaction_id2 = transaction.originalTransaction.transactionIdentifier;
        NSString * transaction_id = transaction.transactionIdentifier;
//        [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id,@"originalTransaction":transaction_id2}];
    }else{
        if (self.parmas.allKeys.count > 0) {
//            [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"uid":self.parmas[@"uid"],@"amount":self.parmas[@"amount"],@"actorid":self.parmas[@"userRoleId"],@"server":self.parmas[@"serverId"],@"order_no":self.parmas[@"cpOrderNo"],@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id}];
        }
    }
    [self.targetView makeToastActivity:CSToastPositionCenter];
    NSDictionary * dic = @{@"productId": product_id, @"transactionId": transaction_id, @"receipt": receiptString, @"isSandbox": @0};
    [MineMemberInAppVerifyReceiptRequest requestWithDic:dic requestFinish:^(MineMemberInAppVerifyReceiptRequest * request) {
        [self.targetView hideToastActivity];
        int code = [[request.responseJson objectForKey:@"code"] intValue];
        NSString * stateString = [request.responseJson objectForKey:@"msg"];
        if (code == 200) {
            [self handleActionWithType:ApplePayManagerTypeVerSuccess data:nil];
        }else {
            [self.targetView makeToast:@"校验失败" duration:2 position:CSToastPositionCenter];
        }
    } requestFailed:^(MineMemberInAppVerifyReceiptRequest *request) {
        [self.targetView hideToastActivity];
        [self.targetView makeToast:@"请求失败" duration:2 position:CSToastPositionCenter];
    }];
    

    //本地像苹果app store验证,上面是像自己的服务器验证
    [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
    // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    //[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}


// 交易失败
- (void)failedTransaction:(SKPaymentTransaction *)transaction{
    if (transaction.error.code != SKErrorPaymentCancelled) {
        [self handleActionWithType:ApplePayManagerTypeFailed data:nil];
    }else{
        [self handleActionWithType:ApplePayManagerTypeCancle data:nil];
    }
    
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

- (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag{
    //交易验证
    NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
    
    if(!receipt){
        // 交易凭证为空验证失败
        [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
        return;
    }
    // 购买成功将交易凭证发送给服务端进行再次校验
    // [self handleActionWithType:ApplePayManagerTypeSuccess data:receipt];
    
    NSError *error;
    NSDictionary *requestContents = @{
                                      @"receipt-data": [receipt base64EncodedStringWithOptions:0]
                                      };
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                          options:0
                                                            error:&error];
    
    if (!requestData) { // 交易凭证为空验证失败
        [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
        return;
    }
    
    //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
    //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
    
    NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
    if (flag) {
        serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
    }
    NSURL *storeURL = [NSURL URLWithString:serverString];
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];
    
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            // 无法连接服务器,购买校验失败
            [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
        } else {
            NSError *error;
            NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
            if (!jsonResponse) {
                // 苹果服务器校验数据返回为空校验失败
                [self handleActionWithType:ApplePayManagerTypeVerFailed data:nil];
            }
            // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
            NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
            if (status && [status isEqualToString:@"21007"]) {
//                [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
            }else if(status && [status isEqualToString:@"0"]){
//                [self handleActionWithType:ApplePayManagerTypeVerSuccess data:nil];
            }
        }
    }];
    [dataTask resume];
    
    // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


#pragma mark - private
- (void)handleActionWithType:(ApplePayManagerType)type data:(NSData *)data{
    switch (type) {
        case ApplePayManagerTypeSuccess:
            NSLog(@"购买成功");
            break;
        case ApplePayManagerTypeFailed:
            NSLog(@"购买失败");
            break;
        case ApplePayManagerTypeCancle:
            NSLog(@"用户取消购买");
            [self.targetView makeToast:@"用户取消购买" duration:2 position:CSToastPositionCenter];
            break;
        case ApplePayManagerTypeVerFailed:
            [self.targetView makeToast:@"订单校验失败" duration:2 position:CSToastPositionCenter];
            NSLog(@"订单校验失败");
            break;
        case ApplePayManagerTypeVerSuccess:
            NSLog(@"订单校验成功");
            break;
        case ApplePayManagerTypeNotArrow:
            NSLog(@"不允许程序内付费");
            break;
        default:
            break;
    }
    if(_handle){
        _handle(type,data);
    }
}

上报完成即可

在App 提交审核的时候,要附带着添加指定的内购产品,

注意: 在未审核通过之前,这些商品,均是沙盒测试环境下的,支付并不会涉及金额交易,而且后端可以查询到支付信息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
uniapp是一种跨平台的应用开发框架,可以同时开发iOS和Android应用。在uniapp中实现iOS内购需要按照以下步骤进行操作: 1. 首先,在iTunes Connect(https://appstoreconnect.apple.com/)应用后台登录并创建App内购项目,在此过程中需要设置税务和银行卡等配置信息。 2. 然后,在HbuilderX中勾选Apple应用内支付选项。这样可以在uniapp中使用内购功能。 3. 接下来,根据uniapp的支付文档(https://uniapp.dcloud.net.cn/api/plugins/payment.html#requestpayment),使用相应的API进行支付操作。uniapp提供了一些插件,可以方便地实现支付功能。 需要注意的是,iOS内购与Apple Pay是不同的概念。Apple Pay是一种支付方式,类似于支付宝和微信支付,而iOS内购是指在iPhone应用中购买虚拟物品(例如QQ币、鱼翅、电子书等)。 总结来说,要在uniapp中实现iOS内购,需要在iTunes Connect创建内购项目,勾选HbuilderX中的Apple应用内支付选项,并参考uniapp的支付文档进行相应的开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [uniapp苹果内购总结](https://blog.csdn.net/weixin_37391237/article/details/126409915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [iOS内购与apple pay](https://download.csdn.net/download/u011189314/14110233)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值