手把手教你成为Microsoft Store 个人开发者8:MSIX实现应用内购买(实现按月订阅)

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。

源码指引:github源码指引_初级代码游戏的博客-CSDN博客


        应用内购买。对开发者而言,订阅比一次性买断更好。

        有点奇怪,应用本身没有订阅模式。但是内购有订阅模式。

        更奇怪的是微软采用的术语“加载项”,英文"AddOn",感觉怪怪的,不如直接叫“内购”啊。

        “加载项”就是内购,可以是一次性买断的,也可以是计数的,计数的还分应用自己计数和商店负责计数,也可以是订阅制的,订阅可以按月、按季、按年。

        最大的困难不在技术上,在术语和翻译的混乱中。

目录

一、创建内购(加载项、AddOn)

二、在代码中检查并引导用户购买

2.1 总代码

2.2 关键数据类型和初始化

2.2.1 StoreContext

2.2.2 StoreAppLicense

2.2.3 StoreProductQueryResult

2.3 初始化InitLicense

2.4 检查指定内购项目是否购买CheckIfUserHasSubscription

2.5 订购subscriptionPluginAsync


一、创建内购(加载项、AddOn)

        在合作伙伴中心的应用和游戏列表里面看:

        加载项就是内购,点击一个应用名称,查看应用:

 

        这个地方叫作“管理附加设备”,右边有个按钮叫做“创建加载项”,点击这个就可以创建内购:

         产品类型其实不复杂,点击“了解更多”看详细介绍。其实就是钻石、永久和订阅,买十个钻石,游戏里面买东西,永久解锁一个技能,或者按月为技能付钱。

        产品ID就是个名字,而且只有你自己能看到,程序里面用的是商店为你生成的“StoreID”(是12个字母的随机字符串)。

        后面的操作就没什么困难了,选择定价方式、周期、有没有试用,再给每种语言编写个说明给用户看。

        提交之后很快就能在商店看到应用变成了“应用内购买”。

二、在代码中检查并引导用户购买

2.1 总代码

        前面已经提交了内购,现在应用名称下面已经多了“应用内购买”:

        但是应用内购买只能在应用里面触发购买,所以一定要写代码。

        代码一般的功能就是检查用户是否已经购买了指定的内购项目,如果没有就调用商店的接口引导用户购买。

        下面的例子改编自官方示例,但是更具有可操作性:

	internal class ProcessLicense
	{
		private StoreContext context = StoreContext.GetDefault();
		public StringBuilder sbState = new StringBuilder();

		public bool bInited = false;//指示初始化完成,如果bInitError=false则应用商店信息和用户订购信息均已获得
		public bool bInitError = false;//指示初始化出错,部分数据可能并没有获得,需要重试
		public DateTime LastTryInit;//最后一次尝试的时间

		public StoreAppLicense appLicense;
		private StoreProductQueryResult storeProductQueryResult;

		public async void InitLicense()
		{
			bInited = false;
			bInitError = false;
			LastTryInit = DateTime.Now;

			//获取插件信息
			storeProductQueryResult = await context.GetAssociatedStoreProductsAsync(new string[] { "Durable" });

			if (storeProductQueryResult.ExtendedError != null)
			{
				sbState.AppendLine("Something went wrong while getting the add-ons. " +
					"ExtendedError:" + storeProductQueryResult.ExtendedError);
				bInitError = true;
			}

			//获取用户的许可证
			appLicense = await context.GetAppLicenseAsync();
			
			bInited = true;
		}
		public async Task GetSubscriptionsInfo()
		{
			// Subscription add-ons are Durable products.
			string[] productKinds = { "Durable" };
			List<String> filterList = new List<string>(productKinds);

			StoreProductQueryResult queryResult =
				await context.GetAssociatedStoreProductsAsync(productKinds);

			if (queryResult.ExtendedError != null)
			{
				// The user may be offline or there might be some other server failure.
				sbState.Append($"ExtendedError: {queryResult.ExtendedError.Message}");
				return;
			}

			foreach (KeyValuePair<string, StoreProduct> item in queryResult.Products)
			{
				// Access the Store product info for the add-on.
				StoreProduct product = item.Value;

				// For each add-on, the subscription info is available in the SKU objects in the add-on. 
				foreach (StoreSku sku in product.Skus)
				{
					sbState.AppendLine("StoreId " + sku.StoreId);
					sbState.AppendLine("    IsInUserCollection " + sku.IsInUserCollection.ToString());
					sbState.AppendLine("    IsSubscription " + sku.IsSubscription.ToString());
					sbState.AppendLine("    IsTrial " + sku.IsTrial.ToString());
					sbState.AppendLine("    Language " + sku.Language);
					sbState.AppendLine("    Price " + sku.Price.FormattedPrice);
					if (sku.IsSubscription)
					{
						StoreDurationUnit billingPeriodUnit = sku.SubscriptionInfo.BillingPeriodUnit;
						uint billingPeriod = sku.SubscriptionInfo.BillingPeriod;
						sbState.AppendLine("        BillingPeriodUnit " + sku.SubscriptionInfo.BillingPeriodUnit.ToString());
						sbState.AppendLine("        BillingPeriod " + sku.SubscriptionInfo.BillingPeriod.ToString());
						sbState.AppendLine("        HasTrialPeriod " + sku.SubscriptionInfo.HasTrialPeriod.ToString());
						if (sku.SubscriptionInfo.HasTrialPeriod)
						{
							sbState.AppendLine("        TrialPeriodUnit " + sku.SubscriptionInfo.TrialPeriodUnit.ToString());
							sbState.AppendLine("        TrialPeriod " + sku.SubscriptionInfo.TrialPeriod.ToString());
						}
					}
				}
			}
		}

		//用户购买
		public async Task subscriptionPluginAsync(string subscriptionStoreId)
		{
			// Get the StoreProduct that represents the subscription add-on.
			StoreProduct subscriptionStoreProduct = GetSubscriptionProduct(subscriptionStoreId);
			if (subscriptionStoreProduct == null)
			{
				return;
			}

			// Check if the first SKU is a trial and notify the customer that a trial is available.
			// If a trial is available, the Skus array will always have 2 purchasable SKUs and the
			// first one is the trial. Otherwise, this array will only have one SKU.
			StoreSku sku = subscriptionStoreProduct.Skus[0];
			if (sku.SubscriptionInfo.HasTrialPeriod)
			{
				// You can display the subscription trial info to the customer here. You can use 
				// sku.SubscriptionInfo.TrialPeriod and sku.SubscriptionInfo.TrialPeriodUnit 
				// to get the trial details.
			}
			else
			{
				// You can display the subscription purchase info to the customer here. You can use 
				// sku.SubscriptionInfo.BillingPeriod and sku.SubscriptionInfo.BillingPeriodUnit
				// to provide the renewal details.
			}

			// Prompt the customer to purchase the subscription.
			await PromptUserToPurchaseAsync(subscriptionStoreProduct);
		}
		//检查用户是否已经订购指定的插件
		public bool CheckIfUserHasSubscription(string subscriptionStoreId)
		{
			if (!bInited) return false;

			// Check if the customer has the rights to the subscription.
			foreach (var addOnLicense in appLicense.AddOnLicenses)
			{
				StoreLicense license = addOnLicense.Value;
				if (license.SkuStoreId.StartsWith(subscriptionStoreId))
				{
					if (license.IsActive)
					{
						// The expiration date is available in the license.ExpirationDate property.
						return true;
					}
				}
			}

			// The customer does not have a license to the subscription.
			return false;
		}

		private StoreProduct GetSubscriptionProduct(string subscriptionStoreId)
		{
			// Look for the product that represents the subscription.
			foreach (var item in storeProductQueryResult.Products)
			{
				StoreProduct product = item.Value;
				if (product.StoreId == subscriptionStoreId)
				{
					return product;
				}
			}

			sbState.AppendLine("The subscription "+ subscriptionStoreId + " was not found.");
			return null;
		}

		private async Task PromptUserToPurchaseAsync(StoreProduct subscriptionStoreProduct)
		{
			// Request a purchase of the subscription product. If a trial is available it will be offered 
			// to the customer. Otherwise, the non-trial SKU will be offered.
			StorePurchaseResult result = await subscriptionStoreProduct.RequestPurchaseAsync();

			// Capture the error message for the operation, if any.
			string extendedError = string.Empty;
			if (result.ExtendedError != null)
			{
				extendedError = result.ExtendedError.Message;
			}

			switch (result.Status)
			{
				case StorePurchaseStatus.Succeeded:
					// Show a UI to acknowledge that the customer has purchased your subscription 
					// and unlock the features of the subscription. 
					break;

				case StorePurchaseStatus.NotPurchased:
					sbState.AppendLine("The purchase did not complete. " +
						"The customer may have cancelled the purchase. ExtendedError: " + extendedError);
					break;

				case StorePurchaseStatus.ServerError:
				case StorePurchaseStatus.NetworkError:
					sbState.AppendLine("The purchase was unsuccessful due to a server or network error. " +
						"ExtendedError: " + extendedError);
					break;

				case StorePurchaseStatus.AlreadyPurchased:
					sbState.AppendLine("The customer already owns this subscription." +
							"ExtendedError: " + extendedError);
					break;
			}
		}
	}

2.2 关键数据类型和初始化

2.2.1 StoreContext

        这个我们已经用过了,上下文对象,总入口。

        初始化:StoreContext.GetDefault()

2.2.2 StoreAppLicense

        用户的许可证,如果没有联网,本地有缓存数据。

        初始化:appLicense = await context.GetAppLicenseAsync()。

        对于winforms程序,本地调试不会失败,我的电脑上返回的值是有效订购。

2.2.3 StoreProductQueryResult

        应用数据查询结果,要先获得这个信息,然后根据这个信息调用商店接口。

        初始化:

			//获取插件信息
			storeProductQueryResult = await context.GetAssociatedStoreProductsAsync(new string[] { "Durable" });

			if (storeProductQueryResult.ExtendedError != null)
			{
				sbState.AppendLine("Something went wrong while getting the add-ons. " +
					"ExtendedError:" + storeProductQueryResult.ExtendedError);
				bInitError = true;
			}

        参数"Durable"表示订阅类型(为啥不给定义个枚举类型啊?)。

        这个代码不和应用商店关联是无法获得结果的,对于UWP项目,可以直接和商店关联,本地调试代码就可以获得安装的应用的信息(必须先上架然后从商店安装到本地),而对于winforms项目,没法在项目上关联(是通过独立的MSIX安装项目关联的),所以本地调试是得不到结果的。

2.3 初始化InitLicense

        初始化所有对象,获取用户订购信息和应用信息。

2.4 检查指定内购项目是否购买CheckIfUserHasSubscription

        参数为内购项目的StoreID,在这里:

2.5 订购subscriptionPluginAsync

        参数和上面的是一样的,这个函数最终会打开商店让用户购买,买不买用户自己决定。结果可以定时查询或注册许可证变化事件来获取。


(这里是文档结束)        

分数阶傅里叶变换(Fractional Fourier Transform, FRFT)是对传统傅里叶变换的拓展,它通过非整数阶的变换方式,能够更有效地处理非线性信号以及涉及时频局部化的问题。在信号处理领域,FRFT尤其适用于分析非平稳信号,例如在雷达、声纳和通信系统中,对线性调频(Linear Frequency Modulation, LFM)信号的分析具有显著优势。LFM信号是一种频率随时间线性变化的信号,因其具有宽频带和良好的时频分辨率,被广泛应用于雷达和通信系统。FRFT能够更精准地捕捉LFM信号的时间和频率信息,相比普通傅里叶变换,其性能更为出色。 MATLAB是一种强大的数值计算和科学计算工具,拥有丰富的函数库和用户友好的界面。在MATLAB中实现FRFT,通常需要编写自定义函数或利用信号处理工具箱中的相关函数。例如,一个名为“frft”的文件可能是用于执行分数阶傅里叶变换的MATLAB脚本或函数,并展示其在信号处理中的应用。FRFT的正确性验证通常通过对比变换前后信号的特性来完成,比如评估信号的重构质量、信噪比等。具体而言,可以通过计算原始信号与经过FRFT处理后的信号之间的相似度,或者对比LFM信号的关键参数(如初始频率、扫频率和持续时间)是否在变换后得到准确恢复。 在MATLAB代码实现中,通常包含以下步骤:首先,生成LFM信号模型,设定其初始频率、扫频率、持续时间和采样率等参数;其次,利用自定义的frft函数对LFM信号进行分数阶傅里叶变换;接着,使用MATLAB的可视化工具(如plot或imagesc)展示原始信号的时域和频域表示,以及FRFT后的结果,以便直观对比;最后,通过计算均方误差、峰值信噪比等指标来评估FRFT的性能。深入理解FRFT的数学原理并结合MATLAB编程技巧,可以实现对LFM信号的有效分析和处理。这个代码示例不仅展示了理论知识在
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初级代码游戏

知识究竟是有价还是无价

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值