Windows 应用开发:试用功能、内购设置与错误处理
在 Windows 应用开发中,设计和实现试用功能、内购功能以及处理错误是至关重要的环节。下面将详细介绍这些方面的内容。
1. 模拟应用行为与试用功能
在本地环境中测试应用的许可状态和内购功能时,可以使用
CurrentAppSimulator
类。当
Simulation
元素的
SimulationMode
属性设置为
Automatic
时,
MethodName
属性指定的方法会自动返回
HResult
属性中指示的错误代码。例如:
<CurrentApp>
<ListingInformation>
<App>
<Name>Basic Timed Trial</Name>
<Description>Basic Timed trial sample</Description>
<Price>0.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</App>
</ListingInformation>
<LicenseInformation>
<App>
<IsActive>true</IsActive>
<IsTrial>true</IsTrial>
<ExpirationDate>2014-06-19T09:00:00.00Z</ExpirationDate>
</App>
</LicenseInformation>
<Simulation SimulationMode="Automatic">
<DefaultResponse MethodName="RequestAppPurchaseAsync_GetResult" HResult="E_FAIL"/>
</Simulation>
</CurrentApp>
不过,这种策略有一定的局限性,因为
MethodName
属性仅支持
RequestAppPurchaseAsync_GetResult
、
RequestProductPurchaseAsync_GetResult
和
LoadListingInformationAsync_GetResult
这三个方法。但在运行自动化测试用例时,可利用
ReloadSimulatorAsync
方法为应用提供不同的元数据定义,例如准备多个不同元数据的 XML 文件并提供给
CurrentAppSimulator
对象,以模拟常见场景。
2. 设置内购功能
要在应用中提供内购功能,需按以下步骤操作:
1.
设计代码模块
:将应用中的功能和产品作为独立模块处理,以便轻松融入许可模型。
2.
在仪表板中配置
:在 Windows 应用商店仪表板的内购部分,指定要提供的功能或产品。
3.
添加自定义许可定义
:为
CurrentAppSimulator
添加包含可购买功能和产品信息的自定义许可定义,以在本地环境中测试应用行为。示例如下:
<?xml version="1.0" encoding="utf-16"?>
<CurrentApp>
<ListingInformation>
<App>
<AppId>01234567-1234-1234-1234-0123456789AB</AppId>
<LinkUri>
http://apps.windows.microsoft.com/app/2B14D306-D8F8-4066-A45B-0FB3464C67F2
</LinkUri>
<CurrentMarket>en-US</CurrentMarket>
<AgeRating>5</AgeRating>
<MarketData xml:lang="en-us">
<Name>In-app Purchase Sample</Name>
<Description>In-app Sample</Description>
<Price>0.99</Price>
<CurrencySymbol>$</CurrencySymbol>
</MarketData>
</App>
<Product ProductId="feature1">
<MarketData xml:lang="en-us">
<Name>Advanced Feature</Name>
<Price>0.99</Price>
<CurrencySymbol>$</CurrencySymbol>
<CurrencyCode>USD</CurrencyCode>
</MarketData>
</Product>
</ListingInformation>
<LicenseInformation>
<App>
<IsActive>true</IsActive>
<IsTrial>false</IsTrial>
</App>
<Product ProductId="feature1">
<IsActive>false</IsActive>
</Product>
</LicenseInformation>
</CurrentApp>
这里
IsTrial
元素设置为
false
,因为内购功能与试用模式不兼容,且提供的产品
IsActive
属性设置为
false
,表示用户尚未购买该功能。
提供自定义许可定义后,可使用
LoadListingInformationAsync
方法检索
ListingInformation
部分中
Product
元素的信息,如功能名称和价格。示例代码如下:
private async void DisplayProductListingInfo()
{
try
{
ListingInformation listingInfo = await
CurrentAppSimulator.LoadListingInformationAsync();
var productListing = listingInfo.ProductListings["feature1"];
InAppPurchaseFeatureName.Text = String.Format(
"Feature name: {0}", productListing.Name);
InAppPurchaseFeaturePrice.Text = String.Format(
"You can buy this feature for {0}", productListing.FormattedPrice);
}
catch (Exception ex)
{
InAppPurchaseErrorMessage.Text = String.Format(
"Unable to show feature info. Exception: {0}", ex.Message);
}
}
还可以显示提供功能的许可信息,示例代码如下:
private void DisplayProductLicenseInfo()
{
try
{
var productLicenses = CurrentAppSimulator.LicenseInformation.ProductLicenses;
var productLicense = productLicenses["feature1"];
if (!productLicense.IsActive)
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
InAppPurchaseFeatureLicenseStatus.Text = String.Format("Advanced feature license status: inactive. You cannot use this feature");
});
}
else
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
InAppPurchaseFeatureLicenseStatus.Text = String.Format("Advanced feature license status: active. You can now use this feature");
});
}
}
catch (Exception ex)
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
InAppPurchaseErrorMessage.Text = String.Format("Unable to show product information. Exception: {0}", ex.Message);
});
}
}
同时,需要修改其他方法以显示新信息:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
await this.LoadCustomSimulator();
this.DisplayListingInformation();
this.DisplayLicenseInfo();
this.DisplayProductListingInfo();
this.DisplayProductLicenseInfo();
CurrentAppSimulator.LicenseInformation.LicenseChanged +=
LicenseInformation_LicenseChanged;
}
private async Task LoadCustomSimulator()
{
StorageFolder proxyDataFolder = await
Package.Current.InstalledLocation.GetFolderAsync("trial-configs");
StorageFile proxyFile = await proxyDataFolder.GetFileAsync("in-app-purchase.xml");
await CurrentAppSimulator.ReloadSimulatorAsync(proxyFile);
}
private void LicenseInformation_LicenseChanged()
{
this.DisplayLicenseInfo();
this.DisplayProductLicenseInfo();
}
实现内购功能时,使用
RequestProductPurchaseAsync
方法,示例代码如下:
private async void InAppPurchaseButton_Click(object sender, RoutedEventArgs e)
{
try
{
await CurrentAppSimulator.RequestProductPurchaseAsync("feature1", false);
}
catch (Exception ex)
{
InAppPurchaseErrorMessage.Text = String.Format("Unable to purchase the feature. Exception: {0}", ex.Message);
}
}
修改 XAML 代码以显示新信息:
<StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="AppId" FontSize="20" Margin="20, 5" Width="Auto" />
<TextBlock x:Name="StoreLink" FontSize="20" Margin="20, 5" Width="Auto" />
<TextBlock x:Name="AppName" FontSize="20" Margin="20, 5" Width="Auto" />
<TextBlock x:Name="LicenseState" FontSize="20" Margin="20, 5" Width="Auto" />
<TextBlock x:Name="LicenseRemainingDays" FontSize="20" Margin="20, 5"
Width="Auto" />
<Button x:Name="BuyButton" Click="BuyButton_Click" Content="Buy app" Width="300"
Height="50" Margin="20, 5" />
<TextBlock x:Name="PurchaseErrorMessage" FontSize="20" Margin="20, 5" Width="Auto"
Visibility="Collapsed" />
<TextBlock x:Name="InAppPurchaseFeatureName" FontSize="20" Margin="20, 5"
Width="Auto" />
<TextBlock x:Name="InAppPurchaseFeatureLicenseStatus" FontSize="20" Margin="20, 5"
Width="Auto" />
<TextBlock x:Name="InAppPurchaseFeaturePrice" FontSize="20" Margin="20, 5"
Width="Auto" />
<Button x:Name="InAppPurchaseButton" Click="InAppPurchaseButton_Click"
Content="Buy advanced feature" Width="300" Height="50" Margin="20, 5" />
<TextBlock x:Name="InAppPurchaseErrorMessage" FontSize="20" Margin="20, 5"
Width="Auto" Visibility="Collapsed" />
</StackPanel>
运行应用后,点击“Buy Advanced Feature”按钮,在模拟器中选择
S_OK
代码可模拟交易成功。
3. 检索和验证购买收据
有时需要检查交易收据,以确定用户是否购买了应用的完整版或特定功能。Windows.ApplicationModel.Store 命名空间支持两种获取收据的方式:
-
购买时获取
:在调用
RequestProductPurchaseAsync
方法(作为唯一参数)或
RequestAppPurchaseAsync
方法(作为第二个参数)时传入
true
。
-
随时获取
:调用
GetAppReceiptAsync
和
GetProductReceiptAsync
方法。
收据是一个 XML 片段,包含交易的所有信息。示例如下:
<?xml version="1.0" encoding="utf-8" ?>
<Receipt Version="1.0" ReceiptDate="2013-04-09T08:34:52Z" CertificateId=""
ReceiptDeviceId="db2e1812-c704-4ce9-82b0-dc0910476139">
<AppReceipt Id="843f7847-faf4-49bc-be5c-ae818216ad57"
AppId="b1cd9500-216a-496a-b377-d8343aa36f32_16p6bmm8bp4fr"
PurchaseDate="2013-04-09T08:34:45Z" LicenseType="Full" />
<ProductReceipt Id="673f1d2a-2518-41e4-883f-b0cb4d2d8f09"
AppId="b1cd9500-216a-496a-b377-d8343aa36f32_16p6bmm8bp4fr" ProductId="feature1"
PurchaseDate="2013-04-09T08:34:47Z" ProductType="Durable" />
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>cdiU06eD8X/w1aGCHeaGCG9w/kWZ8I099rw4mmPpvdU=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
SjRIxS/2r2P6ZdgaR9bwUSa6ZItYYFpKLJZrnAa3zkMylbiWjh9oZGGng2p6… (omitted)
</SignatureValue>
</Signature>
</Receipt>
收据片段的元素和属性如下表所示:
| 元素 | 属性 | 说明 |
| ---- | ---- | ---- |
| Receipt | CertificateId | 用于签署收据的证书指纹 |
| | ReceiptDate | 收据签署和下载的时间 |
| | ReceiptDeviceId | 标识请求收据的设备 |
| AppReceipt | Id | 购买的唯一标识符 |
| | AppId | 操作系统分配的唯一包名称 |
| | LicenseType | 用户购买的许可证类型 |
| | PurchaseDate | 应用购买时间 |
| ProductReceipt | AppId | 应用 ID |
| | ProductId | 产品 ID |
| | PurchaseDate | 产品购买时间 |
| | ProductType | 产品许可证的持续时间,Windows 应用商店应用仅支持 Durable |
| Signature | - | 用于验证收据真实性的数字签名 |
4. 错误处理
在 .NET 应用(包括 Windows 应用商店应用)中,必须能够处理错误和异常,避免其直接呈现给用户。CLR 提供了统一的错误通知模型,任何 .NET 框架操作通常通过抛出异常来指示失败。以下是设计 Windows 应用商店应用时应遵循的基本准则:
-
设置 try/catch 块
:在应用可能无法正常运行的地方设置
try/catch
块,如访问可能不可用或缺失的资源,或依赖第三方库、远程服务等。
-
使用 finally 块
:使用
finally
块释放任何非托管资源并尽快执行必要的清理操作。
-
使用多个 catch 块
:当代码可能抛出不同类型的异常时,使用多个
catch
块,将异常按从最具体到最通用的顺序排列,确保最后一个
catch
块是最通用的。示例代码如下:
try
{
Geoposition pos = await this._geo.GetGeopositionAsync();
LatitudeTextBlock.Text = "Latitude: " + pos.Coordinate.Latitude.ToString();
LongitudeTextBlock.Text = "Longitude: " + pos.Coordinate.Longitude.ToString();
}
catch (UnauthorizedAccessException ex)
{
ErrorMessageText.Text =
"This app needs your permission to access to your location";
}
catch (Exception ex)
{
ErrorMessageText.Text = "Unable to initialize your location";
}
-
创建自定义异常类
:创建自定义异常类时,从
Exception基类或其派生类派生,并实现基类的所有构造函数。示例代码如下:
public class MyCustomException: Exception
{
public MyCustomException()
{
}
}
综上所述,在 Windows 应用开发中,通过合理设计试用功能、内购功能以及有效的错误处理机制,可以提高应用的质量和用户体验。
Windows 应用开发:试用功能、内购设置与错误处理(续)
5. 常见问题解答
为了帮助开发者更好地理解和应用上述知识,下面对一些常见问题进行解答。
问题 1:在本地环境测试应用行为时,应该使用哪个类?
答案是使用
CurrentAppSimulator
类来在本地测试应用。在本地环境中,
CurrentAppSimulator
类允许我们模拟应用的许可状态和内购功能。但在将应用发布到 Windows 应用商店之前,需要将所有对
CurrentAppSimulator
类的引用替换为
CurrentApp
类。这是因为
CurrentApp
类用于实际的生产环境,与 Windows 应用商店进行真实的交互。
问题 2:在基于功能的试用模式中,
LicensingInformation
类的
IsActive
属性返回
false
意味着什么?
当
IsActive
属性返回
false
时,有以下几种可能情况:
- 如果是应用的许可信息,可能表示应用的许可已被撤销或缺失。
- 如果是某个功能的许可信息,可能表示该功能的试用期限已过,或者用户尚未购买该功能。
问题 3:在购买试用应用时,
RequestAppPurchaseAsync
方法的布尔参数有什么含义?
这个布尔参数表示是否希望该方法返回应用购买的收据。如果传入
true
,则在购买成功后会返回收据;如果传入
false
,则不会返回收据。
6. 流程图展示
下面是一个简单的 mermaid 流程图,展示了内购功能的基本流程:
graph LR
A[启动应用] --> B[加载自定义许可定义]
B --> C[显示应用和功能信息]
C --> D{用户点击购买按钮}
D -- 是 --> E[调用 RequestProductPurchaseAsync 方法]
E --> F{交易是否成功}
F -- 是 --> G[更新功能许可状态]
F -- 否 --> H[显示错误信息]
D -- 否 --> C
7. 总结与建议
在 Windows 应用开发过程中,设计和实现试用功能、内购功能以及处理错误是非常重要的环节。以下是一些总结和建议:
-
测试方面
:使用
CurrentAppSimulator
类在本地环境中充分测试应用的行为,确保应用在不同许可状态和内购场景下都能正常工作。在发布应用之前,务必将
CurrentAppSimulator
替换为
CurrentApp
。
-
许可检查
:通过
CurrentApp
类的
LicenseInformation
属性检查应用的许可状态,根据
IsActive
和
IsTrial
属性做出相应的处理。
-
内购功能
:合理设计应用的代码,将功能和产品作为独立模块处理,方便实现内购功能。使用
RequestAppPurchaseAsync
和
RequestProductPurchaseAsync
方法让用户购买应用和其他功能。
-
错误处理
:遵循错误处理的基本准则,设置
try/catch
块、使用
finally
块进行资源清理、合理使用多个
catch
块处理不同类型的异常。同时,可以创建自定义异常类来处理特定的异常情况。
希望以上内容能帮助开发者在 Windows 应用开发中更好地实现试用功能、内购功能和错误处理,提高应用的质量和用户体验。
超级会员免费看
1万+

被折叠的 条评论
为什么被折叠?



