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 / mounted) | canSetupApplePay() | 决定是否在支付方式列表显示「Apple Pay」选项 |
用户点击「确认支付」按钮(onClick 回调) | startApplePay({...}) | 拿到 url 后立即重定向用户;如需指定托管页语言,追加 &lang= 查询参数 |
| 支付结果页加载(回跳之后) | 读 URL 的 status 参数 + getIntentStatus({...}) | 先用 status 区分 success / cancelled / failed / error 决定展示哪种 UI,再用 payment_intent_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() 字段说明
startApplePay() 字段说明| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
app_state | 否 | string | 合作伙伴自定义的订单关联字符串,会原样回传给重定向参数和回调。⚠️ 该值会出现在回跳 URL 里,不要传入敏感信息 |
address | 否 | string | 加密货币目标钱包地址;留空表示入合作伙伴账户 |
network | 否 | string | 区块链网络代码(示例值 "ETH") |
pair | 是 | string | 交易对,格式 {crypto}{fiat},例如 "USDCUSD" 表示用 USD 买 USDC |
side | 是 | string | 交易方向,例如 "buy" |
size | 是 | string | 金额(请以字符串形式传入,不要使用 number) |
external_order_id | 否 | string | 合作伙伴侧订单号,便于双方对账,建议保持唯一 |
layout | 否 | string | 重定向页面 UI 风格:"worldpay"(推荐)/ "legend" / "mini" |
redirect | 是 | string | 接收支付结果的回跳 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-HK或zh-TW,否则会得到简体界面 - 传入不支持的值不会报错,会静默降级为按用户浏览器的
Accept-Language协商 - 完全不传
lang时,同样按浏览器语言协商;协商不到则回落到en
配色
托管页与 Apple Pay 按钮自动跟随用户系统的深浅色模式,无需配置。用户在支付过程中切换系统主题,页面与按钮会实时同步。
重定向的目标网页UI和数据消息可以定制,需要定制的需求请和我们具体沟通。

- 用户完成支付后会被重定向回到调用
startApplePay()方法时传入的 redirect 地址。重定向的请求参数会包含额外的status、order_id、trade_id和payment_intent_id。trade_id仅在订单成功创建后返回,其余状态下为空。
回跳参数 status 取值
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 支付面板里主动关闭面板时,回跳 status 为 cancelled。这与真正的支付失败语义完全不同 —— 取消不是错误,结果页不应该展示错误提示或让用户联系客服。
行为变更:在旧版本中,用户主动取消与技术异常都回跳
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/cancelled等onPay状态,在重定向流程下不会触发。因为调用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);