.NET服务器端App Store内购验证方案咨询(原API已废弃)
.NET环境下App Store内购验证的可行方案
针对.NET服务器无法使用官方App Store Server Library的问题,以下是几种实用的解决方案:
1. 直接调用App Store Server API
官方库本质是对App Store Server REST API的封装,.NET可以自行构造请求完成验证,核心是处理JWT签名授权。
实现步骤:
- 在App Store Connect生成并下载用于API调用的私钥,记录私钥ID(kid)和团队ID(iss)。
- 使用
System.IdentityModel.Tokens.JwtNuGet包生成签名JWT,作为请求的Authorization头。 - 调用App Store Server API的
/v2/receipts/verify端点验证收据。
代码示例:
using System.IdentityModel.Tokens.Jwt; using System.Security.Cryptography; using System.Text.Json; using Microsoft.IdentityModel.Tokens; // 加载苹果私钥 var privateKeyContent = File.ReadAllText("AuthKey_XXXXXXXXXX.p8"); var ecdsa = ECDsa.CreateFromPem(privateKeyContent); var signingCredentials = new SigningCredentials( new ECDsaSecurityKey(ecdsa), SecurityAlgorithms.EcdsaSha256 ); // 生成JWT令牌 var tokenHandler = new JwtSecurityTokenHandler(); var jwtToken = tokenHandler.CreateJwtSecurityToken( issuer: "你的团队ID", // 如:1234567890 audience: "appstoreconnect-v1", expires: DateTime.UtcNow.AddMinutes(5), signingCredentials: signingCredentials, claims: new[] { new Claim("kid", "你的私钥ID") } // 如:ABCDE12345 ); var authToken = tokenHandler.WriteToken(jwtToken); // 构造验证请求 using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authToken); var requestBody = new { receipt_data = "用户提交的Base64格式收据数据" }; var response = await httpClient.PostAsJsonAsync( "https://api.storekit.itunes.apple.com/v2/receipts/verify", requestBody ); // 处理响应 if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); var verificationResult = JsonSerializer.Deserialize<dynamic>(responseContent); // 根据result中的status、transactionInfo等字段判断验证结果 } else { var errorContent = await response.Content.ReadAsStringAsync(); // 处理错误,如无效收据、签名错误等 }
注意事项:
- 确保私钥文件安全存储,避免硬编码或暴露在版本控制中。
- 处理API的不同响应状态码,比如401(JWT无效)、404(收据不存在)等。
2. 使用第三方.NET封装库
社区已有成熟的.NET库封装了App Store Server API,无需手动处理JWT和请求构造,节省开发时间。
示例(以AppStoreServerApi NuGet包为例):
- 安装NuGet包:
Install-Package AppStoreServerApi - 初始化客户端并调用验证接口:
using AppStoreServerApi; using AppStoreServerApi.Models; var config = new AppStoreServerApiConfiguration( issuerId: "你的团队ID", privateKey: File.ReadAllText("AuthKey_XXXXXXXXXX.p8"), privateKeyId: "你的私钥ID", bundleId: "你的App Bundle ID" // 如:com.yourcompany.yourapp ); var client = new AppStoreServerApiClient(config); var verifyRequest = new VerifyReceiptRequest { ReceiptData = "用户提交的Base64收据数据" }; try { var verifyResponse = await client.VerifyReceiptAsync(verifyRequest); if (verifyResponse.Status == 0) { // 验证成功,处理交易信息 var transactions = verifyResponse.Receipt?.Transactions; } else { // 验证失败,根据status码排查原因 } } catch (ApiException ex) { // 处理API调用异常,如网络错误、权限问题 }
注意事项:
- 选择维护活跃的库,查看NuGet包的更新记录和GitHub仓库的issues,避免使用已废弃的库。
3. 轻量Node.js中转服务(低成本替代)
如果不想编写.NET底层代码,可以搭建一个极简的Node.js服务,利用官方Node库处理验证,再由.NET服务器调用该服务。这种方案资源占用极低,成本远低于完整微服务。
Node服务示例:
const express = require('express'); const fs = require('fs'); const { AppStoreServerAPIClient } = require('@apple/app-store-server-library'); const app = express(); app.use(express.json()); // 初始化官方客户端 const client = new AppStoreServerAPIClient({ issuerId: '你的团队ID', privateKey: fs.readFileSync('AuthKey_XXXXXXXXXX.p8', 'utf8'), privateKeyId: '你的私钥ID', bundleId: '你的App Bundle ID' }); // 暴露验证接口 app.post('/verify-receipt', async (req, res) => { try { const { receiptData } = req.body; const result = await client.verifyReceipt({ receiptData }); res.json(result); } catch (error) { res.status(500).json({ error: error.message }); } }); // 启动服务 const PORT = 3000; app.listen(PORT, () => { console.log(`App Store验证服务运行在端口 ${PORT}`); });
.NET调用示例:
using System.Text.Json; using var httpClient = new HttpClient(); var requestBody = new { receiptData = "用户提交的Base64收据数据" }; var response = await httpClient.PostAsJsonAsync( "http://localhost:3000/verify-receipt", requestBody ); if (response.IsSuccessStatusCode) { var result = await response.Content.ReadFromJsonAsync<dynamic>(); // 处理验证结果 }
注意事项:
- 可以将Node服务和.NET服务器部署在同一台机器或容器中,减少网络延迟。
- 给Node服务添加基础的身份验证(如API Key),避免被非法调用。
内容的提问来源于stack exchange,提问作者Duc Thang
相关产品推荐
相关产品推荐

