PAYATHON 2026

WP 8.1 中 LaunchUriAsync 是否支持 Callback

支付阿杰

明确结论

Windows.System.Launcher.LaunchUriAsync 不提供交易完成 Callback。它只能请求系统打开指定的 URI,返回值表示系统是否成功发起了启动操作。它无法判断目标应用何时结束,也不能直接获取支付结果。

要接收支付结果,支付应用需要按照双方约定的 URI Scheme 主动回调你的应用。WP 8.1 Windows Runtime 应用不使用 UriMapperBase,而是在清单中注册协议,再通过 Application.OnActivated 处理 ProtocolActivatedEventArgs。

如果支付应用没有提供回调协议、SDK 或服务器端查询接口,仅靠 LaunchUriAsync 无法可靠判断支付是否成功。

为什么 LaunchUriAsync 的返回值不是支付结果

通常会这样调用:

bool launched = await Windows.System.Launcher.LaunchUriAsync(paymentUri);

这里的 launched 只表示系统是否接受了启动请求:

  • true:系统找到了能够处理该 URI 的应用,并尝试启动它。
  • false:系统没有找到合适的应用,或者启动请求未能执行。

即使返回 true,用户仍可能取消支付,交易也可能失败或尚未完成。因此,这个返回值不能作为交易结果。

WP 8.1 切换到支付应用时,当前应用还可能被挂起甚至终止。应用重新进入前台同样不能证明支付成功。

推荐实现方式:使用自定义 URI 回调

支付应用需要支持接收回调地址,并在交易结束后打开该地址。例如,应用发起支付时传入:

paymentapp://pay?orderId=12345&callback=myshop%3A%2F%2Fpayment-result

支付结束后,支付应用调用:

myshop://payment-result?orderId=12345&status=success

系统会通过协议激活重新打开你的应用。

回调参数的名称和格式必须以支付应用的接口文档为准。自行约定参数,并不能让第三方支付应用识别它们。

在应用清单中注册协议

在 Package.appxmanifest 中声明自定义 Scheme,例如 myshop。这表示应用能够处理:

myshop://...

如果使用清单设计器,可以在 Declarations 中添加 Protocol,并将名称设置为:

myshop

协议名称中不要包含 ://。

发起支付

下面的代码会生成订单标识、构造回调 URI,并启动支付应用:

using System;
using Windows.System;

public async Task<bool> StartPaymentAsync(string orderId)
{
    var callbackUri = new Uri("myshop://payment-result");

    var paymentUri = new Uri(
        "paymentapp://pay" +
        "?orderId=" + Uri.EscapeDataString(orderId) +
        "&callback=" + Uri.EscapeDataString(callbackUri.AbsoluteUri));

    return await Launcher.LaunchUriAsync(paymentUri);
}

返回 true 只代表支付应用已被成功调起。订单仍应保持“等待支付”状态,直到应用收到可信的支付结果。

在 WP 8.1 中接收协议激活

Windows Runtime 项目可以在 App.xaml.cs 中重写 OnActivated:

using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;

sealed partial class App : Application
{
    protected override void OnActivated(IActivatedEventArgs args)
    {
        if (args.Kind == ActivationKind.Protocol)
        {
            var protocolArgs = (ProtocolActivatedEventArgs)args;
            HandlePaymentCallback(protocolArgs.Uri);
        }

        base.OnActivated(args);
    }

    private void HandlePaymentCallback(Uri uri)
    {
        if (!string.Equals(
                uri.Scheme,
                "myshop",
                StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        if (!string.Equals(
                uri.Host,
                "payment-result",
                StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        // 解析 orderId、status 等查询参数。
        // 收到回调后,建议再向自己的服务器查询订单状态。
    }
}

协议激活可能发生在应用正常运行时,也可能发生在应用被挂起或终止后。因此,回调处理不能依赖原页面中的内存变量。发起支付前,需要将 orderId 等必要状态保存到本地或服务器。

不要直接信任 URI 中的支付状态

其他应用也可能构造并打开自定义 URI。例如,攻击者可以尝试打开:

myshop://payment-result?orderId=12345&status=success

因此,不能只看到 status=success 就向用户交付商品。更可靠的处理流程是:

  1. 发起支付前,由服务器创建订单并保存订单号。
  2. 调起支付应用。
  3. 收到 URI 回调后,提取订单号或交易号。
  4. 由服务器调用支付平台接口,或检查支付平台发送的服务器通知。
  5. 服务器确认支付成功后,再更新订单并交付内容。

如果支付平台提供签名参数,也必须按照其文档验证签名。不过,客户端校验仍不能替代服务器端确认。

UriMapperBase 为什么不适用

UriMapperBase 属于早期的 Windows Phone Silverlight 导航体系,主要用于将传入的 URI 映射到应用内部页面。WP 8.1 Windows Runtime 应用采用协议激活模型,不需要使用它。

相关 API 的分工如下:

  • 启动外部应用:Launcher.LaunchUriAsync
  • 声明应用自己的回调协议:Package.appxmanifest
  • 接收回调:Application.OnActivated
  • 获取回调 URI:ProtocolActivatedEventArgs.Uri

如果项目使用的是 WP 8.1 Silverlight,而非 WP 8.1 Windows Runtime,其应用生命周期和 URI 处理方式会有所不同。需要根据项目类型选择对应的 API,不能混用两套示例。

支付应用不支持回调时怎么办

如果第三方支付应用没有提供回调 URI,就无法通过 LaunchUriAsync 补出 Callback。此时只能使用支付方已经提供的其他机制,例如:

  • 官方 SDK;
  • Web API 查询订单状态;
  • 支付平台向服务器发送异步通知;
  • 用户返回应用后,由服务器轮询订单状态。

应用恢复到前台只能说明用户已经返回,无法证明交易已经完成,更不能证明支付成功。

备注:内容仅供参考。