用C#验证iOS应用内购买收据

17

我正在使用 C# Web服务验证我的iOS 应用内购买 收据。

在Xcode中,我通过以下方式将收据作为字符串获取:

- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
    NSString* receiptString = [[NSString alloc] initWithString:transaction.payment.productIdentifier];

    NSLog(@"%@",receiptString);

    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];

    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];

    NSString *jsonObjectString = [receipt base64EncodedStringWithOptions:0];

}

我将这个字符串(收据)作为参数发送到我的 C# Web 服务。以下是我的 Web 服务方法:

[WebMethod(Description = "Purchase Item Verify")]
public string PurchaseItem(string receiptData)
 {
    string returnmessage = "";

    try
    {
        var json = "{ 'receipt-data': '" + receiptData + "'}";

        ASCIIEncoding ascii = new ASCIIEncoding();
        byte[] postBytes = Encoding.UTF8.GetBytes(json);

        HttpWebRequest request;
        request = WebRequest.Create("https://sandbox.itunes.apple.com/verifyReceipt") as HttpWebRequest;
        request.Method = "POST";
        request.ContentType = "application/json";
        request.ContentLength = postBytes.Length;

        Stream postStream = request.GetRequestStream();
        postStream.Write(postBytes, 0, postBytes.Length);
        postStream.Close();

        var sendresponse = (HttpWebResponse)request.GetResponse();

        string sendresponsetext = "";
        using (var streamReader = new StreamReader(sendresponse.GetResponseStream()))
        {
            sendresponsetext = streamReader.ReadToEnd();
        }
        returnmessage = sendresponsetext;

    }
    catch (Exception ex)
    {
        ex.Message.ToString();
    }
    return returnmessage;
}

它总是返回{"status":21002}

我已经搜索了两天,但仍然找不到解决方案。有人可以帮帮我吗?我错在哪里了?

**我正在沙盒中进行测试,这就是为什么我使用沙盒URL的原因。我可以在我的应用程序中验证交易收据。


嗨Jerry,你有把jsonObjectString发送给Web服务吗?实际上我也卡在同样的问题上了。 - Yogesh Lolusare
请解决此问题 https://stackoverflow.com/questions/48188231/validating-receipt-with-the-app-store-in-net-is-success-but-when-sent-the-same - Meer Abdul Rahiman
4个回答

19

我得到了解决方案。

最终适用于我的代码是:

 public string PurchaseItem(string receiptData)
{
    string returnmessage = "";
    try
    {
       // var json = "{ 'receipt-data': '" + receiptData + "'}";

        var json = new JObject(new JProperty("receipt-data", receiptData)).ToString();

        ASCIIEncoding ascii = new ASCIIEncoding();
        byte[] postBytes = Encoding.UTF8.GetBytes(json);

      //  HttpWebRequest request;
        var request = System.Net.HttpWebRequest.Create("https://sandbox.itunes.apple.com/verifyReceipt");
        request.Method = "POST";
        request.ContentType = "application/json";
        request.ContentLength = postBytes.Length;

        //Stream postStream = request.GetRequestStream();
        //postStream.Write(postBytes, 0, postBytes.Length);
        //postStream.Close();

        using (var stream = request.GetRequestStream())
        {
            stream.Write(postBytes, 0, postBytes.Length);
            stream.Flush();
        }

      //  var sendresponse = (HttpWebResponse)request.GetResponse();

        var sendresponse = request.GetResponse();

        string sendresponsetext = "";
        using (var streamReader = new StreamReader(sendresponse.GetResponseStream()))
        {
            sendresponsetext = streamReader.ReadToEnd().Trim();
        }
        returnmessage = sendresponsetext;

    }
    catch (Exception ex)
    {
        ex.Message.ToString();
    }
    return returnmessage;

花费两天半的时间只是为了修改一个方法。感谢上帝。


3
如果这能让你稍稍安慰一下的话,你可能刚刚帮我省下了两天半的工作时间。我还会在答案中添加一个小注释,这将有助于管理订阅的人们。另外,我可以确认上面的代码在Xamarin中完全正常,因此如果您使用Xamarin.iOS框架,您可以在手机上进行收据验证。 - Nande
我之前调用了close而不是flush,成功连接后却收到了“无法连接到远程服务器”的错误提示(内部异常只是告诉我主机强制断开了连接)。希望其他遇到这个问题的人能够在这里找到答案。祝好运(也感谢原作者)。 - Hunter-Orionnoir
1
值得注意的是,System.Net.HttpWebRequest 构造函数现在已经过时;MSDN 建议使用 WebRequest.Create() 代替,如果 URI 方案为 https://http://,则返回一个 HttpWebRequest 对象。这样可以避免“通过派生类型访问类型的静态成员”错误。 - Jamie Dexter
我没有使用 JObject,而是改变了格式以使用双引号... var json = $@"{{ ""receipt-data"": ""{receiptData}""}}"; - Ian Warburton
当我从iTunes验证收据时,我得到了状态码0和有效的收据数据,但是当通过Web服务将相同的响应返回给应用程序时,状态码更改为21002,并且我在应用程序中获得的响应是(""{\"status\":21002}"")。 - IOS Dev

4
这里有一个使用HTTPClient的替代异步实现方式:
public static async Task<string> CheckReceiptWithAppStore()
    {
        string responseStr = null;

        string uri = "https://sandbox.itunes.apple.com/verifyReceipt";

        string receiptData = // Get your receipt from wherever you store it

        var json = new JObject(new JProperty("receipt-data", receiptData), 
            new JProperty("password", "paste-your-shared-secret-here")).ToString();

        using (var httpClient = new HttpClient())
        {        

            if (receiptData != null)
            {
                HttpContent content = new StringContent(json);

                try
                {
                    Task<HttpResponseMessage> getResponse = httpClient.PostAsync(uri, content);
                    HttpResponseMessage response = await getResponse;
                    responseStr = await response.Content.ReadAsStringAsync();
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error verifying receipt: " + e.Message);
                }
            }
        }

        return responseStr;
    }

对于非订阅购买,共享密钥并非必需。


3

如果要管理订阅,@Jerry Naing的回答还需要提供您的共享密钥(可以从iTunes Connect检索/生成)。最简单的方法是在定义json变量的行中添加一个额外的属性。

var json = new JObject(new JProperty("receipt-data", receiptData), new JProperty("password", "put_your_shared_secret_here")).ToString();

没有提供共享密钥将导致21004状态响应。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接