博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unity调用iOS原生内购
阅读量:4692 次
发布时间:2019-06-09

本文共 13464 字,大约阅读时间需要 44 分钟。

我项目中主要是对消耗品进行内购,非消耗品没进行测试,对iOS商店后台的构建,我这边不说了,下面主要是对Unity怎么实现iOS原生内购功能进行讲解。

一 在Xcode中编写原生内购代码给Unity调用

 1 IAPManager.h

#import 
#import
@interface IAPManager : NSObject
{ SKProduct *proUpgradeProduct; SKProductsRequest *productsRequest; NSString *productIndentify;}-(void)attachObserver;-(BOOL)CanMakePayment;-(void)requestProductData:(NSString *)productIdentifiers;-(void)buyRequest:(NSString *)productIdentifier;//保存Unity传递的商品ID@end

2 IAPManager.m

#import "IAPManager.h"@implementation IAPManager-(void) attachObserver{    NSLog(@"AttachObserver");    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];}-(BOOL) CanMakePayment{    return [SKPaymentQueue canMakePayments];}-(void) requestProductData:(NSString *)productIdentifiers{    NSArray *idArray = [productIdentifiers componentsSeparatedByString:@"\t"];    NSSet *idSet = [NSSet setWithArray:idArray];    [self sendRequest:idSet];}-(void)sendRequest:(NSSet *)idSet{    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:idSet];    request.delegate = self;    [request start];}-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{        NSLog(@"-----------收到产品反馈信息--------------");    NSArray *products = response.products;    NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);    NSLog(@"产品付费数量: %d", (int)[products count]);    // populate UI        for (SKProduct *p in products) {        NSLog(@"product info");        NSLog(@"SKProduct 描述信息%@", [products description]);        NSLog(@"产品标题 %@" , p.localizedTitle);        NSLog(@"产品描述信息: %@" , p.localizedDescription);        NSLog(@"价格: %@" , p.price);        NSLog(@"Product id: %@" , p.productIdentifier);                UnitySendMessage("IOSIAPMgr", "ShowProductList", [[self productInfo:p] UTF8String]);    }        for(NSString *invalidProductId in response.invalidProductIdentifiers){        NSLog(@"Invalid product id:%@",invalidProductId);    }        // [request autorelease];}-(void)buyRequest:(NSString *)productIdentifier{    //  NSArray* transactions=[SKPaymentQueue defaultQueue].transactions;    //  if(transactions.count>0)    // {    // for(SKPaymentTransaction *tran in transactions)    // {    //   NSLog(@"**************************************************************%@",tran.transactionState);    //检查是否有完成的交易    //   SKPaymentTransaction* transaction =[transactions firstObject];    // if(tran.transactionState==SKPaymentTransactionStatePurchasing)    //   {    //   NSLog(@"----------------------%@",tran.transactionState);    //   [[SKPaymentQueue defaultQueue] finishTransaction:tran];    // return;    //  }    //}        // }    productIndentify=productIdentifier;    SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];    [[SKPaymentQueue defaultQueue] addPayment:payment];}-(NSString *)productInfo:(SKProduct *)product{    NSArray *info = [NSArray arrayWithObjects:product.localizedTitle,product.localizedDescription,product.price,product.productIdentifier, nil];        return [info componentsJoinedByString:@"\t"];}-(NSString *)transactionInfo:(SKPaymentTransaction *)transaction{        return [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];        //return [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSASCIIStringEncoding];}-(NSString *)encode:(const uint8_t *)input length:(NSInteger) length{    static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";        NSMutableData *data = [NSMutableData dataWithLength:((length+2)/3)*4];    uint8_t *output = (uint8_t *)data.mutableBytes;        for(NSInteger i=0; i
>18) & 0x3f]; output[index + 1] = table[(value>>12) & 0x3f]; output[index + 2] = (i+1)
>6) & 0x3f] : '='; output[index + 3] = (i+2)
>0) & 0x3f] : '='; } return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];}-(void) provideContent:(SKPaymentTransaction *)transaction{ UnitySendMessage("IOSIAPMgr", "ProvideContent", [[self transactionInfo:transaction] UTF8String]);}//沙盒测试环境验证#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=[NSData dataWithContentsOfURL:receiptUrl]; NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串 NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接请求数据 NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; //测试的时候填写沙盒路径,上APPStore的时候填写正式环境路径 NSURL *url=[NSURL URLWithString: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"]; NSLog(@"--------------%@",dicReceipt); //NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject]; for(NSDictionary *tmp in dicReceipt[@"in_app"]) { // NSLog(@"+++++++++++%@",dicInApp); NSString *productIdentifier= tmp[@"product_id"];//读取产品标识 NSLog(@"+++++++++++++++++++++++++++++++++++++%@",productIdentifier); NSLog(@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%@",productIndentify); //如果是消耗品则记录购买数量,非消耗品则记录是否购买过 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; if ([productIdentifier isEqualToString:productIndentify]) { NSInteger purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量 [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier]; UnitySendMessage("IOSIAPMgr", "BuyProcuctSucessCallBack",productIdentifier.UTF8String); break; }else{ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier]; UnitySendMessage("IOSIAPMgr", "BuyProcuctSucessCallBack",productIdentifier.UTF8String); break; } } }else{ NSLog(@"购买失败,未通过验证!"); }}//监听购买结果- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{ for(SKPaymentTransaction *tran in transaction){ 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(@"交易失败"); UIAlertView *mBoxView = [[UIAlertView alloc] initWithTitle:@"交易提示" message:@"交易失败" delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; [mBoxView show]; [[SKPaymentQueue defaultQueue] finishTransaction:tran]; } break; default: { [[SKPaymentQueue defaultQueue] finishTransaction:tran]; } break; } }}-(void) completeTransaction:(SKPaymentTransaction *)transaction{ NSLog(@"Comblete transaction : %@",transaction.transactionIdentifier); [self provideContent:transaction]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];}-(void) failedTransaction:(SKPaymentTransaction *)transaction{ NSLog(@"Failed transaction : %@",transaction.transactionIdentifier); if (transaction.error.code != SKErrorPaymentCancelled) { NSLog(@"!Cancelled"); } [[SKPaymentQueue defaultQueue] finishTransaction:transaction];}-(void) restoreTransaction:(SKPaymentTransaction *)transaction{ NSLog(@"Restore transaction : %@",transaction.transactionIdentifier); [[SKPaymentQueue defaultQueue] finishTransaction:transaction];}@end

3  IAPInterface.h

#import 
@interface IAPInterface : NSObject@end

4 IAPInterface.mm  Untiy主要是调用这个接口去调用IAPMananger

 

#import "IAPInterface.h"#import "IAPManager.h"@implementation IAPInterface#if defined (__cplusplus)extern "C"{#endif    IAPManager *iapManager = nil;    //初始化商品信息    void InitIAPManager(){        iapManager = [[IAPManager alloc] init];        [iapManager attachObserver];            }    //判断是否可以购买    bool IsProductAvailable(){        return [iapManager CanMakePayment];    }    //获取商品信息    void RequstProductInfo(char *p){        NSString *list = [NSString stringWithUTF8String:p];        NSLog(@"商品列表:%@",list);        [iapManager requestProductData:list];    }    //购买商品    void BuyProduct(char *p){        [iapManager buyRequest:[NSString stringWithUTF8String:p]];    }#if defined (__cplusplus)}#endif@end

二 Unity调用Xcode主要是通过 [DllImport("__Internal")]去调用,代码如下:

using UnityEngine;using System.Collections;using System.Collections.Generic;using System.Runtime.InteropServices;public class IOSIAPMgr : MonoBehaviour{                 public List
productInfo = new List
(); private static IOSIAPMgr _instance; public static IOSIAPMgr Instance{ get{ // if(_instance==null) // { // GameObject go=new GameObject("IOSIAPMgr"); // DontDestroyOnLoad (go); // _instance=go.AddComponent
(); // } return _instance; } } private string _goodsID = ""; void Awake() { _instance = this; Init (); StartCoroutine (InitProductInfo ()); DontDestroyOnLoad (this.gameObject); } private IEnumerator InitProductInfo() { yield return new WaitForSeconds(5f); IOSIAPMgr.Instance.RequstALLProductInfo(); } [DllImport("__Internal")] private static extern void InitIAPManager();//初始化 [DllImport("__Internal")] private static extern bool IsProductAvailable();//判断是否可以购买 [DllImport("__Internal")] private static extern void RequstProductInfo(string s);//获取商品信息 [DllImport("__Internal")] private static extern void BuyProduct(string s);//购买商品 //测试从xcode接收到的字符串 void IOSToU(string s) { Debug.Log("[MsgFrom ios]"+s); } //获取product列表 void ShowProductList(string s){ productInfo.Add(s); } //获取商品回执 void ProvideContent(string s) { Debug.Log("[MsgFrom ios]proivideContent : "+s); } public void Init () { Debug.Log ("初始化InitIAPManager"); InitIAPManager(); } public bool IsProductVailable() { return IsProductAvailable(); } public void RequstALLProductInfo() { if(IsProductAvailable()) { RequstProductInfo("com.smallMBag"); RequstProductInfo("com.MidMBag"); RequstProductInfo("com.BigMBag"); RequstProductInfo("com.SpecialM"); RequstProductInfo("com.SmallFBag"); RequstProductInfo("com.Weapon"); //RequstProductInfo("com.SingleFuHuoSHi"); RequstProductInfo("com.BigFBag"); } } ///
/// 购买商品成功的回调 /// ///
String. public void BuyProcuctSucessCallBack(string str) { } ///
/// 购买商品失败调回调 /// ///
String. public void BuyProcuctFailedCallBack(string str) { } }

三 注意事项

  1 iOS 11的系统对沙盒测试有bug,第一次会让你输入沙盒账号和密码,第二次也会再让你输入一次,最后购买的时候,直接购买失败(仅对沙盒测试,正式上线的时候没有这个问题)。

  2 iOS内购的时候非消耗品的交易一直会存在账单里面而且不会被清除。

  3 使用沙盒账号测试的时候要退出登录苹果手机的账号,不然也对导致购买失败。

  4 在内购商品之前也调用请求商品信息。

 

转载于:https://www.cnblogs.com/weiqiangwaideshijie/p/9103407.html

你可能感兴趣的文章
Binary Agents
查看>>
入门Webpack,看这篇就够了
查看>>
短信拦截马”黑色产业链与溯源取证研究
查看>>
Mac Xdebug安装时遇到了Zend Engine API 不一致的问题
查看>>
最小公倍数
查看>>
asp.net如何定时执行任务
查看>>
在github上实现页面托管预览功能
查看>>
css选择器
查看>>
prim
查看>>
给陌生人写一封信
查看>>
noip2013花匠
查看>>
[CF]Equalize Them All
查看>>
React Ant design table表单与pagination分页配置
查看>>
重大发现: windows下C++ UI库 UI神器-SOUI(转载)
查看>>
linux 压缩文件的命令总结
查看>>
linux tail 命令详解
查看>>
BZOJ-3207 花神的嘲讽计划Ⅰ
查看>>
BZOJ-1069 [SCOI2007]最大土地面积
查看>>
进程与线程的一个简单解释【摘】
查看>>
COJ976 WZJ的数据结构(负二十四)
查看>>