iOS开发内购全套图文教程
iOS应用程序内购/内付费(一)
Unity调用iOS内购代码实现
测试一定要用沙盒账号,否则无效!
这里就不重复写了,直接上截图
#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.count; i++) {
NSLog(@"msg %d : %@",i,listItems[i]);
}
}
void TestGetString(){
NSArray *test = [NSArray arrayWithObjects:@"t1",@"t2",@"t3", nil];
NSString *join = [test componentsJoinedByString:@"\n"];
UnitySendMessage("Main", "IOSToU", [join UTF8String]);
}
IAPManager *iapManager = nil;
void InitIAPManager(){
iapManager = [[IAPManager alloc] init];
[iapManager attachObserver];
}
bool IsProductAvailable(){
return [iapManager CanMakePayment];
}
void RequstProductInfo(void *p){
NSString *list = [NSString stringWithUTF8String:p];
NSLog(@"productKey:%@",list);
[iapManager requestProductData:list];
}
void BuyProduct(void *p){
[iapManager buyRequest:[NSString stringWithUTF8String:p]];
}
@end
#import
#import
@interface IAPManager : NSObject<SKProductsRequestDelegate, SKPaymentTransactionObserver>{
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; i3){
NSInteger value = 0;
for (NSInteger j= i; j<(i+3); j++) {
value<<=8;
if(j0xff & input[j]);
}
}
NSInteger index = (i/3)*4;
output[index + 0] = table[(value>>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
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public class IAPExample : MonoBehaviour {
public List<string> productInfo = new List<string>();
[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<string>();
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; iif(GUILayout.Button (productInfo[i],GUILayout.Height (100), GUILayout.MinWidth (200))){
string[] cell = productInfo[i].Split('\t');
Debug.Log ("[Buy]"+cell[cell.Length-1]);
BuyProduct(cell[cell.Length-1]);
GUI.Label(new Rect (10, 10, 100, 200), string.Format("[Buy]{0}" ,cell[cell.Length-1]));
}
}
}
bool Btn(string msg){
GUILayout.Space (100);
return GUILayout.Button (msg,GUILayout.Width (200),GUILayout.Height(100));
}
}
在这里需要注意几点,
代码中的_currentProId所填写的是你的购买项目的的ID,这个和第二步创建的内购的productID要一致;本例中是 123。
在监听购买结果后,一定要调用[[SKPaymentQueue defaultQueue] finishTransaction:tran];来允许你从支付队列中移除交易。
沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。
请务必使用真机来测试,一切以真机为准。
项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。
真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号
二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的时候需要判断返回Status Code决定是否去沙盒进行二次验证,为了线上用户的使用,验证的顺序肯定是先验证正式环境,此时若返回值为21007,就需要去沙盒二次验证,因为此购买的是在沙盒进行的。
附:苹果支付错误目录
如果我们不做任何处理的话,越狱机是可以直接绕过支付验证直接获得结果的,这样对于我们辛辛苦苦的开发者来说简直噩耗,所以我们有必要了解一下内购验证相关的知识,以及知道如何去预防这样的事情。
http://www.cnblogs.com/zhaoqingqing/p/4597794.html
优点:
缺点:
链接:
优点:
缺点:
链接:
先本地验证一次,后服务器再验证一次(感觉没必要)