PAYATHON 2026

如何确定 Microsoft Store 订阅 add-on 的付款日期?

支付老李

结论

app 无法查询 Microsoft Store 订阅每个月的实际扣款日期。

StoreProduct.IsInUserCollection、StoreContext.GetAppLicenseAsync() 等 API 返回的是订阅权益状态,包括权益当前是否有效、何时到期,但不包含扣款记录、支付结算时间或完整的续订历史。用户离线数月后再次上线时,app 无法通过 Microsoft Store API 还原离线期间每笔付款的实际日期。

可以查询哪些信息

通过 Windows.Services.Store API,可以检查订阅 add-on 当前是否有效,并读取许可证的到期时间:

using System;
using System.Threading.Tasks;
using Windows.Services.Store;

public sealed class SubscriptionStatus
{
    public bool IsActive { get; init; }
    public DateTimeOffset ExpirationDate { get; init; }
}

public static async Task<SubscriptionStatus> GetSubscriptionStatusAsync(
    string addOnStoreId)
{
    StoreContext context = StoreContext.GetDefault();
    StoreAppLicense appLicense = await context.GetAppLicenseAsync();

    if (appLicense.AddOnLicenses.TryGetValue(
            addOnStoreId,
            out StoreLicense license))
    {
        return new SubscriptionStatus
        {
            IsActive = license.IsActive,
            ExpirationDate = license.ExpirationDate
        };
    }

    return new SubscriptionStatus
    {
        IsActive = false,
        ExpirationDate = DateTimeOffset.MinValue
    };
}

这里的 ExpirationDate 是当前订阅权益的到期时间,并不是付款时间。续订失败、宽限期、取消订阅、退款以及 Microsoft Store 的账单处理流程都可能影响这个时间。

StoreSubscriptionInfo 中的 BillingPeriod 和 BillingPeriodUnit 只能说明订阅周期,例如每月一次,无法用来查询实际扣款日期。

推荐的实现方式

建议分别记录以下两类数据:

  • Microsoft Store 提供的订阅权益状态;
  • app 自己管理的 credit 发放和使用流水。

首次购买成功时,可以记录:

PurchaseObservedAt
InitialExpirationDate
StoreId
SkuStoreId

此后每次 app 在线,都重新获取许可证并保存一份权益快照:

CheckedAt
IsActive
ExpirationDate

例如:

public sealed class EntitlementSnapshot
{
    public DateTimeOffset CheckedAt { get; init; }
    public bool IsActive { get; init; }
    public DateTimeOffset ExpirationDate { get; init; }
}

public static async Task<EntitlementSnapshot> CaptureSnapshotAsync(
    string addOnStoreId)
{
    SubscriptionStatus status =
        await GetSubscriptionStatusAsync(addOnStoreId);

    return new EntitlementSnapshot
    {
        CheckedAt = DateTimeOffset.UtcNow,
        IsActive = status.IsActive,
        ExpirationDate = status.ExpirationDate
    };
}

如果新的 ExpirationDate 晚于上次保存的值,可以确认订阅权益已经延长。但这只能说明权益期限发生了变化,无法证明扣款发生在某个确切时刻,也无法可靠地拆分出用户离线期间每个月的付款日期。

credit 应如何处理

如果 credit 需要按订阅周期发放,建议使用自己的账期标识,避免重复同步造成重复发放:

UserId
SubscriptionId
PeriodKey
CreditGranted
GrantedAt
SourceExpirationDate

PeriodKey 可以表示业务定义的订阅周期,例如:

2026-08

每次发放前,先检查同一组 SubscriptionId + PeriodKey 是否已经存在。这样的幂等设计可以避免用户重复上线、设备时间变化或同步重试导致 credit 被多次发放。

如果用户连续离线多个周期,而业务规则要求”只有实际付款后才逐月发放”,那么客户端许可证状态不足以准确补发这些周期的 credit。此时需要调整业务规则,例如:

  • 只按照当前已确认的有效权益发放 credit;
  • 限制离线期间可累计的周期数;
  • 要求用户定期联网验证;
  • 通过自己的服务端保存每次观察到的权益变化。

即使使用服务端,也只能保存和核验 Microsoft Store 对外提供的权益信息,无法获得客户端 API 没有公开的逐笔扣款时间。

账单中的表述

app 可以展示自己记录的 credit 流水,例如”credit 发放时间”、“credit 使用时间”和”对应权益周期”,但不应把推算日期标记为”Microsoft 实际付款时间”。

如果必须向用户展示真实的扣款日期和金额,应引导用户查看 Microsoft 账户中的订单历史或账单记录。app 内可以明确区分:

  • 订阅权益确认时间
  • 订阅权益到期时间
  • credit 发放时间
  • credit 使用时间

这些时间可以由 app 准确记录。Microsoft Store 的实际扣款时间无法通过当前的 app 内 Store API 获取。

备注:内容仅供参考。