# iOSPurchase **Repository Path**: dingxiaowei/iOSPurchase ## Basic Information - **Project Name**: iOSPurchase - **Description**: Unity iOS内购 - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 28 - **Forks**: 12 - **Created**: 2016-10-31 - **Last Updated**: 2023-11-30 ## Categories & Tags **Categories**: ios-modules, payment-dev **Tags**: None ## README 前言:最近项目需要切换到iOS平台做一些提交审核和支付对接相关的工作,上一篇刚分享了最新的iOS10提交审核的一些坑,这篇分享一些内购相关的流程。 # Unity iOS内购 ### 思路: Unity调用iOS内购代码实现 ### 效果图: ![购买弹框](http://img.blog.csdn.net/20161031223902497) ![购买结果](http://img.blog.csdn.net/20161031223923716) ### 流程 这里就不重复写了,直接上截图 ![这里写图片描述](http://img.blog.csdn.net/20161031224839086) ### OC代码: #### IAPInterface(主要是实现Unity跟OC的IAP代码的一个交互作用,等于是一个中间桥梁) ``` #import @interface IAPInterface : NSObject @end ``` ``` #import "IAPInterface.h" #import "IAPManager.h" @implementation IAPInterface void TestMsg(){ NSLog(@"Msg received"); } void TestSendString(void *p){ NSString *list = [NSString stringWithUTF8String:p]; NSArray *listItems = [list componentsSeparatedByString:@"\t"]; for (int i =0; i #import @interface IAPManager : NSObject{ SKProduct *proUpgradeProduct; SKProductsRequest *productsRequest; } -(void)attachObserver; -(BOOL)CanMakePayment; -(void)requestProductData:(NSString *)productIdentifiers; -(void)buyRequest:(NSString *)productIdentifier; @end ``` ``` #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{ NSArray *products = response.products; for (SKProduct *p in products) { UnitySendMessage("Main", "ShowProductList", [[self productInfo:p] UTF8String]); } for(NSString *invalidProductId in response.invalidProductIdentifiers){ NSLog(@"Invalid product id:%@",invalidProductId); } [request autorelease]; } -(void)buyRequest:(NSString *)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("Main", "ProvideContent", [[self transactionInfo:transaction] UTF8String]); } -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: [self completeTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: [self restoreTransaction:transaction]; break; default: 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 ``` #### Unity中调用的C#代码 ``` using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; public class IAPExample : MonoBehaviour { public List productInfo = new List(); [DllImport("__Internal")] private static extern void TestMsg();//测试信息发送 [DllImport("__Internal")] private static extern void TestSendString(string s);//测试发送字符串 [DllImport("__Internal")] private static extern void TestGetString();//测试接收字符串 [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); } bool back = false; //获取商品回执 void ProvideContent(string s){ Debug.Log ("[MsgFrom ios]proivideContent : "+s); back = true; } // Use this for initialization void Start () { InitIAPManager(); } void OnGUI(){ if(Btn ("GetProducts")){ if(!IsProductAvailable()) throw new System.Exception("IAP not enabled"); productInfo = new List(); RequstProductInfo("com.aladdin.fishpocker1\tcom.aladdin.fishpocker2"); } GUILayout.Space(40); if (back) GUI.Label (new Rect (10, 150, 100, 100), "Message back"); for(int i=0; i