Apple Pay

📘

Legend Base SDK 提供 Apple Pay 集成:

  • 重定向流程:跳转到 Legend 托管页面完成支付(兼容性高,无需任何 Apple Pay 商户配置)

集成路线图

如果你是第一次接入 Apple Pay,按以下顺序走:

步骤说明
1️⃣ 完成 SDK 初始化new LegendBase() 实例化与登录授权,参考 开始使用
2️⃣ 检测设备能力canSetupApplePay() 确认当前浏览器是否支持 Apple Pay
3️⃣ 拉起支付startApplePay(),获取重定向 URL 后打开 Legend 托管页
4️⃣ 处理结果redirect 回跳参数 + getIntentStatus() 查询最终状态
%%{init: {"flowchart": {"htmlLabels": true, "curve": "basis"}, "themeVariables": {"fontSize": "20px"}}}%%
flowchart LR
    A["1. SDK 初始化<br/>new LegendBase()<br/>+ 登录授权"] --> B["2. canSetupApplePay()<br/>检测设备能力"]
    B --> C["3. startApplePay()<br/>获取跳转 URL"]
    C --> D["重定向到 Legend 托管页<br/>完成 Apple Pay 授权"]
    D --> E["4. getIntentStatus()<br/>查询订单结果"]

UI 触发时机

集成路线图讲的是「SDK 方法是什么」;这一节讲的是「在交易所自己的 UI 上,用户的哪个动作触发哪个 SDK 调用」。

%%{init: {"sequence": {"actorMargin": 60, "messageMargin": 35}, "themeVariables": {"fontSize": "16px"}}}%%
sequenceDiagram
    autonumber
    participant U as 用户
    participant E as 交易所页面
    participant SDK as Legend Base SDK
    participant L as Legend 托管页

    U->>E: 进入购买页
    E->>SDK: canSetupApplePay()
    SDK-->>E: true / false
    E-->>U: 显示 / 隐藏 Apple Pay 选项

    U->>E: 选 Apple Pay + 填金额
    U->>E: 点「确认支付」按钮
    E->>SDK: startApplePay({...})
    SDK-->>E: { url, payment_intent_id }
    E-->>U: 重定向到 url

    U->>L: 在 Legend 托管页完成 Apple Pay 授权
    L-->>U: 重定向回 redirect URL(带 status/order_id/...)

    U->>E: 落到「支付结果页」
    E->>SDK: getIntentStatus(payment_intent_id)
    SDK-->>E: 订单完整信息
    E-->>U: 显示 success / pending / failed UI

UI 时机速查表

用户的 UI 动作调用方法用途
购买页加载(如 onMount / mountedcanSetupApplePay()决定是否在支付方式列表显示「Apple Pay」选项
用户点击「确认支付」按钮(onClick 回调)startApplePay({...})拿到 url 后立即重定向用户;如需指定托管页语言,追加 &lang= 查询参数
支付结果页加载(回跳之后)读 URL 的 status 参数 + getIntentStatus({...})先用 status 区分 success / cancelled / failed / error 决定展示哪种 UI,再用 payment_intent_id 查询最终订单状态

在开始集成前我们建议阅读下面的文档说明:

重定向的支付流程

🟢

优点

运行环境兼容性高,不用另外配置 Apple Pay(商户 id、证书、域名验证等)。

🟠

缺点

交互流程长,数据消息传输路径长。

  • 调用canSetupApplePay()方法,获取当前运行环境是否支持 Apple Pay。
const resp = await legend.canSetupApplePay()
console.log("canSetupApplePay: \n", resp);
  • 调用startApplePay()方法,获取跳转支付的地址。
const { url, payment_intent_id } = await legend.startApplePay({
  app_state, // 合作伙伴自定义的订单关联字符串,会原样回传给重定向参数和回调
  address: "", // 加密货币目标钱包地址(可选)
  network: "ETH",
  pair: "USDCUSD",
  side: "buy",
  size: "50",
  external_order_id: `otcsb-applepay-${Date.now().toString()}`,
  layout: 'worldpay', // 'worldpay' | 'legend' | 'mini', 推荐 'worldpay'
  // 用于接收支付结果的重定向地址;重定向方式由合作伙伴自选,
  // 例如 location.href = url 当前页跳转,或 window.open(url, '_blank') 新窗口打开
  redirect: "https://www.legendtrading.com"
});
console.log("url: %o", url);
console.log("payment_intent_id: %o", payment_intent_id);

startApplePay() 字段说明

字段必填类型说明
app_statestring合作伙伴自定义的订单关联字符串,会原样回传给重定向参数和回调。⚠️ 该值会出现在回跳 URL 里,不要传入敏感信息
addressstring加密货币目标钱包地址;留空表示入合作伙伴账户
networkstring区块链网络代码(示例值 "ETH"
pairstring交易对,格式 {crypto}{fiat},例如 "USDCUSD" 表示用 USD 买 USDC
sidestring交易方向,例如 "buy"
sizestring金额(请以字符串形式传入,不要使用 number)
external_order_idstring合作伙伴侧订单号,便于双方对账,建议保持唯一
layoutstring重定向页面 UI 风格:"worldpay"(推荐)/ "legend" / "mini"
redirectstring接收支付结果的回跳 URL;重定向方式由合作伙伴自选(如 location.href = url 当前页跳转,或 window.open(url, "_blank") 新窗口打开)
  • 将用户重定向到上述方法返回的 url。

托管页的语言与配色

用户被重定向到的是 Legend 托管页,这段体验的语言和配色由以下规则决定。

语言

在返回的 url 后面追加 lang 查询参数即可指定托管页语言:

const { url, payment_intent_id } = await legend.startApplePay({ /* ... */ });

// 指定简体中文
location.href = `${url}&lang=zh-CN`;
⚠️

lang拼接在返回 URL 上的查询参数,不是 startApplePay() 的入参。写成 startApplePay({ lang: "zh-CN" }) 不会生效。

注意用 & 连接 —— 返回的 url 本身已经带了 ?intent=...

支持的取值:

lang语言
en英文
zh-CN简体中文
zh-HK繁体中文(香港)
zh-TW繁体中文(台湾)

取值规则:

  • zh 会被归一化为 zh-CN(简体)。面向港台用户请显式传 zh-HKzh-TW,否则会得到简体界面
  • 传入不支持的值不会报错,会静默降级为按用户浏览器的 Accept-Language 协商
  • 完全不传 lang 时,同样按浏览器语言协商;协商不到则回落到 en

配色

托管页与 Apple Pay 按钮自动跟随用户系统的深浅色模式,无需配置。用户在支付过程中切换系统主题,页面与按钮会实时同步。

重定向的目标网页UI和数据消息可以定制,需要定制的需求请和我们具体沟通。

  • 用户完成支付后会被重定向回到调用startApplePay()方法时传入的 redirect 地址。重定向的请求参数会包含额外的 statusorder_idtrade_idpayment_intent_idtrade_id 仅在订单成功创建后返回,其余状态下为空。

回跳参数 status 取值

status含义trade_id建议的结果页文案
success支付成功,订单已创建有值支付成功
pending支付处理中,需后续查询处理中,请稍候
failed明确的业务失败支付失败,请重试
error技术异常(网络、接口报错等)支付失败,请重试
cancelled用户主动取消支付支付已取消
# 成功示例:trade_id 有值
https://www.legendtrading.com/zh_Hans?status=success&order_id=otcsb-applepay-1750725740082&trade_id=45856e7d-ff06-43a7-88cb-22eee5e09ac4&payment_intent_id=

# 失败示例:trade_id 为空(订单未成功创建)
https://www.legendtrading.com/?status=error&order_id=otcsb-applepay-1750724991438&trade_id=&payment_intent_id=200aeaa9-6b7e-49ee-a766-491253a99f3d

# 用户取消示例
https://www.legendtrading.com/?status=cancelled&order_id=otcsb-applepay-1750724991438&trade_id=&payment_intent_id=200aeaa9-6b7e-49ee-a766-491253a99f3d

区分「用户取消」与「支付失败」

用户在 Apple Pay 支付面板里主动关闭面板时,回跳 statuscancelled。这与真正的支付失败语义完全不同 —— 取消不是错误,结果页不应该展示错误提示或让用户联系客服。

🚨

行为变更:在旧版本中,用户主动取消与技术异常都回跳 status=error,合作伙伴无法区分。从本版本起,用户取消单独回跳 status=cancelled

cancelled新增取值,如果你的结果页 switch 没有对应分支,会落到 default,不会崩溃,但用户会看到错误文案 —— 建议按下方示例补上分支。

// 支付结果页
const params = new URLSearchParams(location.search);
const status = params.get('status');
const paymentIntentId = params.get('payment_intent_id');

switch (status) {
  case 'success':
    showSuccessPage(params.get('trade_id'));
    break;

  case 'pending':
    // 用 payment_intent_id 轮询 getIntentStatus() 直至终态
    showPendingPage(paymentIntentId);
    break;

  case 'cancelled':
    // 用户主动取消 —— 不是错误,引导用户重新发起即可
    showToast('支付已取消');
    showRetryButton();
    break;

  case 'failed':
  case 'error':
    showErrorPage('支付失败,请重试');
    showRetryButton();
    break;

  default:
    showErrorPage('未知的支付状态');
}
📘

关于 onPay 中间态回调前端集成 中描述的 clicked / authorizing / processing / cancelledonPay 状态,在重定向流程下不会触发。因为调用 startApplePay() 之后页面已经跳转到 Legend 托管页,你的 SDK 实例连同 onPay / onError 回调都随原页面一起卸载了。整个 Apple Pay 交互期间的 SDK 事件都发生在托管页的独立实例里,你能拿到的唯一信息就是回跳 URL 的 query 参数。

  • 使用getIntentStatus()方法查询支付订单的状态。常见使用场景:在 redirect 回跳页面调用一次,基于 URL 参数中的 payment_intent_id 拿到订单的完整字段;或对 pending 状态轮询直至终态。具体调用时机和频率由合作伙伴根据业务场景决定。
const resp = await legend.getIntentStatus({
  payment_intent_id: "92a8527c-01e3-47c6-bf07-e9ac3c8d8cda"
});
console.log("intent status: \n%o", resp);