这两天一直在处理在Unity IOS支付,中间遇到很多问题,今天终于完成了,这里记录一下。
1、游戏内购买类型
我们知道IOS内购有两种方式
1、交付内置型产品的流程。
1. 程序通过bundle存储的plist文件得到产品标识符的列表。
2. 程序向App Store发送请求,得到产品的信息。
3. App Store返回产品信息。
4. 程序把返回的产品信息显示给用户(App的store界面)
5. 用户选择某个产品
6. 程序向App Store发送支付请求
7. App Store处理支付请求并返回交易完成信息。
8. App获取信息并提供内容给用户。
2、 服务器类型
1. 程序向服务器发送请求,获得一份产品列表。
2. 服务器返回包含产品标识符的列表。
3. 程序向App Store发送请求,得到产品的信息。
4. App Store返回产品信息。
5. 程序把返回的产品信息显示给用户(App的store界面)
6. 用户选择某个产品
7. 程序向App Store发送支付请求
8. App Store处理支付请求并返回交易完成信息。
9. 程序从信息中获得数据,并发送至服务器。
10. 服务器纪录数据,并进行审(我们的)查。
11. 服务器将数据发给App Store来验证该交易的有效性。
12. App Store对收到的数据进行解析,返回该数据和说明其是否有效的标识。
13. 服务器读取返回的数据,确定用户购买的内容。
14. 服务器将购买的内容传递给程序。
我们项目用的是第二种,用自己的服务器来验证苹果交易是否有效。
后端服务器发送凭证失败的处理
如果出现网络问题,导致无法验证。我们需要持久化保存购买凭证,在用户下次启动APP的时候在后台向服务器再一次发起验证,直到成功然后移除该凭证。
//Unity里Application.persistentDataPath 具有读写权限
public static string receiptURL = Application.persistentDataPath+"/ReceiptFile";
//通过要验证的id来删除本地保存的receipt
//在接收到后端服务器成功返回充值成功消息后调用
public static void DeleteReceipt(string receiptID)
{
//如果Receipt目录不存在,不做任何处理,直接return
if (!Directory.Exists(receiptURL))
{
return;
}
string receiptFile = string.Format("{0}/{1}", receiptURL, receiptID);
//如果要删除的receipt文件不存在,不做任何处理,直接return
if (!File.Exists(receiptFile))
{
return;
}
//删除验证文件
File.Delete(receiptFile);
}
//保存要验证的交易信息
// @ApplePurchaseRequest request 是要发给后端的proto协议格式
public static void SaveReceipt(ApplePurchaseRequest request)
{
//判断要写入的文件夹是否存在,如果不存在,创建目录为receiptURL的文件夹
if (!Directory.Exists(receiptURL))
{
Directory.CreateDirectory(receiptURL);
}
//判断要写入的文件是否存在,如果不存在,创建路径为receiptFile的文件
string receiptFile = string.Format("{0}/{1}", receiptURL, request.receiptId);
StreamWriter sw;
FileInfo t = new FileInfo(receiptFile);
if (!t.Exists)
{
//如果此文件不存在则创建
sw = t.CreateText();
}
else
{
//文件存在,打开文件
sw = t.AppendText();
}
//将要保存的文件写成json格式,转化为string保存到文件里
Debug.Log("Apple Transaction Receipt save address is :" + receiptFile);
JsonData data = new JsonData();
data["count"] = request.count;
data["receiptData"] = request.receiptData;
data["receiptId"] = request.receiptId;
data["cost"] = request.cost;
data["templateId"] = request.templateId;
string dataStr = JsonMapper.ToJson(data);
sw.Write(dataStr);
sw.Close();
sw.Dispose();
}
//发送transactionRectipt给后端服务器
private static void SendVerfication(IOSStoreKitResult result)
{
string msg =string.Format("//11111111111111///\n");
msg += string.Format("IOSStoreKitResult _ProductIdentifier : {0}\n" ,result.ProductIdentifier);
msg += string.Format("IOSStoreKitResult _State : {0}\n", result.State);
msg += string.Format("IOSStoreKitResult Receipt : {0}\n", result.Receipt);
msg += string.Format("IOSStoreKitResult _TransactionIdentifier : {0}\n", result.TransactionIdentifier);
msg += string.Format("IOSStoreKitResult _ApplicationUsername : {0}\n", result.ApplicationUsername);
msg += string.Format("11111111111111///");
ApplePurchaseRequest apr = new ApplePurchaseRequest();
apr.count = 1;
apr.receiptData = result.Receipt;
apr.receiptId = result.TransactionIdentifier;
int templeteID = 0;
float cost = 0;
foreach (var it in ConfigManager.Instance().ShopConfigData)
{
if (it.Value.AppStoreId.ToString() == result.ProductIdentifier)
{
templeteID = it.Value.ID;
cost = it.Value.Price;
break;
}
}
apr.cost = cost.ToString();
apr.templateId = templeteID;
MessagePacket msgPacket = new MessagePacket();
msgPacket.Body = ProtobufUtility.GetByteFromProtoBuf(apr);
msgPacket.Code = Utils.BuildProtoCode((int)Module.SHOP, (int)Shop.APPLE_PURCHASE);
NetMgr.Instance().SendMsg(msgPacket, null);
//在给后端发送验证的时候保存到本地,这和收到服务器返回的消息后删除receipt 成对调用
SaveReceipt(apr);
Debug.Log(msg);
}
如果在给发送到后端服务器的时候没有成功收到返回,在重新启动游戏时,去检查receiptURL目录下所有文件,把所有文件再重新向后端服务器发送
public IEnumerator CheckReceipt()
{
//判断receiptURL目录是否存在,如果不存在则不处理
if (!Directory.Exists(PaymentManager.receiptURL))
{
yield return null;
}
else
{
//得到receiptURL目录下的所有文件,读取文件里有内容,发送给后端服务器再重新验证
string[] files = Directory.GetFiles(PaymentManager.receiptURL);
foreach (string file in files)
{
string receiptFile = string.Format("{0}/{1}", PaymentManager.receiptURL, Path.GetFileName(file));
receiptFile = "file://" + receiptFile;
WWW www = new WWW(receiptFile);
yield return www;
string _result = www.text;
JsonData data = JsonMapper.ToObject(_result);
ApplePurchaseRequest apr = new ApplePurchaseRequest();
apr.cost = data["cost"].ToString();
apr.count = (int)data["count"];
apr.receiptData = data["receiptData"].ToString();
apr.receiptId = data["receiptId"].ToString();
apr.templateId = (int)data["templateId"];
MessagePacket msgPacket = new MessagePacket();
msgPacket.Body = ProtobufUtility.GetByteFromProtoBuf(apr);
msgPacket.Code = Utils.BuildProtoCode((int)Module.SHOP, (int)Shop.APPLE_PURCHASE);
NetMgr.Instance().SendMsg(msgPacket, null);
}
}
}
这样来处理掉单问题。
如果有不正确的地方,希望大家指正。