rn ios内购集成

react-native ios内购简单源码

在头文件里面

//

//  RNInAppPurchaseModule.h

//

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

#import <React/RCTBridgeModule.h>

#import <StoreKit/StoreKit.h>

@interface RNInAppPurchaseModule : NSObject <RCTBridgeModule, SKPaymentTransactionObserver, SKProductsRequestDelegate>

@end


//

//  RNInAppPurchaseModule.m

//

////沙盒测试环境验证

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

////正式环境验证

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

/*

 内购验证凭据返回结果状态码说明

 21000 App Store无法读取你提供的JSON数据

 21002收据数据不符合格式

 21003收据无法被验证

 21004你提供的共享密钥和账户的共享密钥不一致

 21005收据服务器当前不可用

 21006收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中

 21007收据信息是测试用(sandbox),但却被发送到产品环境中验证

 21008收据信息是产品环境中使用,但却被发送到测试环境中验证

 */

#import "RNInAppPurchaseModule.h"

#import <React/RCTLog.h>

// 未验证订单持久化参数

#define kIapUnverifyOrders  @"iap_unverify_orders"

@interface SKProduct (StringPrice)  //格式化价格字符串

@property (nonatomic, readonly) NSString *priceString;

@end

@implementation SKProduct (StringPrice)

- (NSString *)priceString {

  NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];

  formatter.formatterBehavior = NSNumberFormatterBehavior10_4;

  formatter.numberStyle = NSNumberFormatterCurrencyStyle;

  formatter.locale = self.priceLocale;

  return [formatter stringFromNumber:self.price];

}

@end

@interface RNInAppPurchaseModule() <RCTBridgeModule, SKPaymentTransactionObserver, SKProductsRequestDelegate>

@end

@implementation RNInAppPurchaseModule

{

  NSArray *products;  //所有可卖商品

  NSMutableDictionary *_callbacks;  //回调,key是商品id

  RCTResponseSenderBlock _lostCallBack; //丢单数据的重新监听回调

  NSMutableDictionary *_myProductIds;  //业务服务器商品ID,key是商品id

}

- (instancetype)init

{

  if ((self = [super init])) {

    _callbacks = [[NSMutableDictionary alloc] init];

    _myProductIds = [[NSMutableDictionary alloc] init];

  }

  return self;

}

- (dispatch_queue_t)methodQueue

{

  return dispatch_get_main_queue();

}

RCT_EXPORT_MODULE()

/**

 *  添加商品购买状态监听

 *  @params:

 *        callback针对购买过程中,App意外退出的丢单数据的回调

 */

RCT_EXPORT_METHOD(addTransactionObserverWithCallback:(RCTResponseSenderBlock)callback) {

  //监听商品购买状态变化

  [[SKPaymentQueue defaultQueue] addTransactionObserver:self];

  _lostCallBack = callback;

}

/**

 *  服务器验证成功,删除缓存的凭证

 */

RCT_EXPORT_METHOD(removePurchase:(NSDictionary *)purchase) {

  NSMutableArray *iapUnverifyOrdersArray = [NSMutableArray array];

  if ([[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders] != nil) {

    [iapUnverifyOrdersArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders]];

  }

  for (NSDictionary *unverifyPurchase in iapUnverifyOrdersArray) {

    if ([unverifyPurchase[@"transactionIdentifier"] isEqualToString:purchase[@"transactionIdentifier"]]) {

      [iapUnverifyOrdersArray removeObject:unverifyPurchase];

    }

  }

  [[NSUserDefaults standardUserDefaults] setObject:[iapUnverifyOrdersArray copy] forKey:kIapUnverifyOrders];

  [[NSUserDefaults standardUserDefaults] synchronize];

}

- (NSDictionary *)constantsToExport

{

  //获取当前缓存的所有凭证

  NSMutableArray *iapUnverifyOrdersArray = [NSMutableArray array];

  if ([[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders] != nil) {

    [iapUnverifyOrdersArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders]];

  }

  return @{ @"iapUnverifyOrdersArray": iapUnverifyOrdersArray };

}

/**

 *  购买某个商品

 *  @params:

 *        productIdentifier:商品id

 *        callback:回调,返回

 */

RCT_EXPORT_METHOD(purchaseProduct:(NSString *)productIdentifier

                  myProductId:(NSString *)myProductId

                  callback:(RCTResponseSenderBlock)callback)

{

  SKProduct *product;

  for(SKProduct *p in products)

  {

    if([productIdentifier isEqualToString:p.productIdentifier]) {

      product = p;

      break;

    }

  }

  if(product) {

    SKPayment *payment = [SKPayment paymentWithProduct:product];

    [[SKPaymentQueue defaultQueue] addPayment:payment];

    _callbacks[RCTKeyForInstance(payment.productIdentifier)] = callback;

    _myProductIds[RCTKeyForInstance(payment.productIdentifier)] = myProductId;

  } else {

    callback(@[@"无效商品"]);

  }

}

/**

 *  恢复购买

 */

RCT_EXPORT_METHOD(restorePurchases:(RCTResponseSenderBlock)callback)

{

  NSString *restoreRequest = @"restoreRequest";

  _callbacks[RCTKeyForInstance(restoreRequest)] = callback;

  [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

}

/**

 *  加载所有可卖的商品

 */

RCT_EXPORT_METHOD(loadProducts:(NSArray *)productIdentifiers

                  callback:(RCTResponseSenderBlock)callback)

{

  if([SKPaymentQueue canMakePayments]){

    SKProductsRequest *productsRequest = [[SKProductsRequest alloc]

                                          initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];

    productsRequest.delegate = self;

    _callbacks[RCTKeyForInstance(productsRequest)] = callback;

    [productsRequest start];

  } else {

    callback(@[@"not_available"]);

  }

}

- (void)paymentQueue:(SKPaymentQueue *)queue

 updatedTransactions:(NSArray *)transactions

{

  for (SKPaymentTransaction *transaction in transactions) {

    switch (transaction.transactionState) {

        //购买失败

      case SKPaymentTransactionStateFailed: {

        NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);

        RCTResponseSenderBlock callback = _callbacks[key];

        if (callback) {

          if(transaction.error.code != SKErrorPaymentCancelled){

            NSLog(@"购买失败");

            callback(@[@"购买失败"]);

          } else {

            NSLog(@"购买取消");

            callback(@[@"购买取消"]);

          }

          [_callbacks removeObjectForKey:key];

        } else {

          RCTLogWarn(@"No callback registered for transaction with state failed.");

        }

        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

        break;

      }

        //购买成功

      case SKPaymentTransactionStatePurchased: {

        NSLog(@"购买成功");

        NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);

        RCTResponseSenderBlock callback = _callbacks[key];

        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

        if (callback) {

          //购买成功,获取凭证

          [self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction callback:callback];

        } else if (_lostCallBack) {

          //购买过程中出现意外App推出,下次启动App时的处理

          //购买成功,获取凭证

          [self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction callback:_lostCallBack];

        } else {

          RCTLogWarn(@"No callback registered for transaction with state purcahsed.");

        }

        break;

      }

        //恢复购买

      case SKPaymentTransactionStateRestored:{

        NSLog(@"恢复购买成功");

        NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);

        RCTResponseSenderBlock callback = _callbacks[key];

        if (callback) {

          callback(@[@"恢复购买成功"]);

          [_callbacks removeObjectForKey:key];

        } else {

          RCTLogWarn(@"No callback registered for transaction with state failed.");

        }

        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

        break;

      }

        //正在购买

      case SKPaymentTransactionStatePurchasing:

        NSLog(@"正在购买");

        break;

        //交易还在队列里面,但最终状态还没有决定

      case SKPaymentTransactionStateDeferred:

        NSLog(@"推迟");

        break;

      default:

        break;

    }

  }

}

// 苹果内购支付成功,获取凭证

- (void)buyAppleStoreProductSucceedWithPaymentTransactionp:(SKPaymentTransaction *)transaction callback:(RCTResponseSenderBlock)callback {

  NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);

  NSString *transactionReceiptString= nil;

  //验证凭据,获取到苹果返回的交易凭据

  // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址

  NSURLRequest * appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];

  NSError *error = nil;

  NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];

  if (!receiptData) {

    callback(@[@"获取交易凭证失败"]);

  } else {

    transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

    NSString *key = RCTKeyForInstance(transaction.payment.productIdentifier);

    NSString *myProductId = _myProductIds[key];

    NSDictionary *purchase = @{

                               @"transactionIdentifier": transaction.transactionIdentifier,

                               @"productIdentifier": transaction.payment.productIdentifier,

                               @"receiptData": transactionReceiptString,

                               @"myProductId": myProductId

                               };

    //将凭证缓存,后台验证结束后再删除

    NSMutableArray *iapUnverifyOrdersArray = [NSMutableArray array];

    if ([[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders] != nil) {

      [iapUnverifyOrdersArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:kIapUnverifyOrders]];

    }

    [iapUnverifyOrdersArray addObject:purchase];

    [[NSUserDefaults standardUserDefaults] setObject:[iapUnverifyOrdersArray copy] forKey:kIapUnverifyOrders];

    [[NSUserDefaults standardUserDefaults] synchronize];

    callback(@[[NSNull null], purchase]);

    [_callbacks removeObjectForKey:key];

  }

}

- (void)paymentQueue:(SKPaymentQueue *)queue

restoreCompletedTransactionsFailedWithError:(NSError *)error

{

  NSString *key = RCTKeyForInstance(@"restoreRequest");

  RCTResponseSenderBlock callback = _callbacks[key];

  if (callback) {

    callback(@[@"恢复购买失败"]);

    [_callbacks removeObjectForKey:key];

  } else {

    RCTLogWarn(@"No callback registered for restore product request.");

  }

}

- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue

{

  NSString *key = RCTKeyForInstance(@"restoreRequest");

  RCTResponseSenderBlock callback = _callbacks[key];

  if (callback) {

    NSMutableArray *productsArrayForJS = [NSMutableArray array];

    for(SKPaymentTransaction *transaction in queue.transactions){

      if(transaction.transactionState == SKPaymentTransactionStateRestored) {

        [productsArrayForJS addObject:transaction.payment.productIdentifier];

        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

      }

    }

    callback(@[[NSNull null], productsArrayForJS]);

    [_callbacks removeObjectForKey:key];

  } else {

    RCTLogWarn(@"No callback registered for restore product request.");

  }

}

// 所有可卖商品回调

- (void)productsRequest:(SKProductsRequest *)request

     didReceiveResponse:(SKProductsResponse *)response

{

  NSString *key = RCTKeyForInstance(request);

  RCTResponseSenderBlock callback = _callbacks[key];

  if (callback) {

    products = [NSMutableArray arrayWithArray:response.products];

    NSMutableArray *productsArrayForJS = [NSMutableArray array];

    for(SKProduct *item in response.products) {

      NSDictionary *product = @{

                                @"identifier": item.productIdentifier,

                                @"priceString": item.priceString,

                                @"downloadable": item.downloadable ? @"true" : @"false" ,

                                @"description": item.localizedDescription ? item.localizedDescription : @"商品描述",

                                @"title": item.localizedTitle ? item.localizedTitle : @"商品名称",

                                };

      [productsArrayForJS addObject:product];

    }

    callback(@[[NSNull null], productsArrayForJS]);

    [_callbacks removeObjectForKey:key];

  } else {

    RCTLogWarn(@"No callback registered for load product request.");

  }

}

- (void)dealloc

{

  [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

}

#pragma mark Private

static NSString *RCTKeyForInstance(id instance)

{

  return [NSString stringWithFormat:@"%p", instance];

}

@end

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

推荐阅读更多精彩内容

  • 之前做过游戏SDK,所以当时写了一个内购的类,觉得还可以吧。直接看代码 #import#import#import...
    master_huang阅读 885评论 0 0
  • 自己开发的视频直播项目,牵涉到充值金币,用到了苹果公司的内购,趴坑了两天,这里总结下实现苹果内购。 一. 创建测试...
    Leo丶Dicaprio阅读 3,378评论 8 7
  • 内购完整详细的流程,网站如下: http://blog.sina.com.cn/s/blog_6f5fd9ba01...
    小小东阅读 880评论 0 1
  • - 消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买 - 非消耗型项目 就像你在App S...
    tiger2016阅读 654评论 0 1
  • 内购是啥 App 内购买项目允许顾客通过访问 App Store 购买您 App 中的内容、功能或服务,并安全处理...
    智人一千阅读 9,124评论 0 1