前端集成
1. 初始化 SDK
const legend = new LegendBase({
mode: "sandbox",
/**
* 错误处理回调
* @param {Object} error - 错误对象
* @param {string} error.code - 错误代码
* @param {string} error.message - 错误信息
*/
onError: (error) => {
alert(error.message)
},
/**
* 支付流程回调
* @param {string} status - 支付状态(success|pending|failed)
*/
onPay: (status, paymentData) => {
// 在这里处理支付逻辑
console.log('支付状态:', status);
console.log(‘相关支付数据’,paymentData)
},
})
// 使用 SDK
try {
await legend.someMethod();
} catch (error) {
// 错误会通过 onError 回调处理,这里可以不用重复处理
}mode 参数为 SDK 运行模式,可选值:sandbox(沙箱环境)和 production(生产环境)
2. 授权
authorize 方法是整个 SDK 的核心授权接口,用于建立与 KYC 服务的安全连接。该方法通过验证服务端返回的签名数据,完成应用的身份认证和用户会话初始化。
const resp = await legend.authorize(authConfig));参数说明
authConfig 是一个包含认证信息的配置对象,所有字段均来自服务端加签返回的数据:
字段名 | 类型 | 必填 | 描述 | 来源 |
|---|---|---|---|---|
timestamp | string | ✅ | 时间戳,用于防止重放攻击 | 服务端生成 |
signature | string | ✅ | 服务端生成的数字签名,用于验证请求的完整性 | 服务端生成 |
appId | string | ✅ | 应用唯一标识符 | 服务端生成 APP-ID |
appKey | string | ✅ | 应用密钥,用于身份验证 | 服务端生成 APP-KEY |
appUid | string | ✅ | 用户在应用系统中的唯一标识 | 服务端生成 APP-UID |
appUrl | string | ✅ | 应用回调地址 | 服务端生成APP-URL |
appEmail | string | ✅ | 用户邮箱地址 | 服务端生成 APP-EMAIL |
appPhone | string | ✅ | 用户手机号码 | 服务端生成 **APP-PHONE ** |
⚠️ 如何获取 authConfig ,参考 获取签名
返回值
Promise<Object>- 授权结果对象,包含会话状态和连接信息
使用示例
// 使用服务端返回的加签数据调用授权方法
const resp = await legend.authorize({
timestamp: data.timestamp, // 来自服务端的时间戳
signature: data.signature, // 来自服务端的数字签名
appId: data["APP-ID"], // 应用ID
appKey: data["APP-KEY"], // 应用密钥
appUid: data["APP-UID"], // 用户ID
appUrl: data["APP-URL"], // 回调URL
appEmail: data["APP-EMAIL"], // 用户邮箱
appPhone: data["APP-PHONE"], // 用户手机号
});
console.log("授权响应:", resp);
console.log("KYC 状态", resp.kyc_status);返回值
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjdjNjk0NzBlYzM0NzA4MTM2YmZiMjEwYjZmYWYwOTk4YTYzMThmMDkwNDdhNTg0MDU3NzNlYmRlMDUwM2M4ZGViZDJhMTBmNDliMjczN2NlIn0...", // 完整的JWT令牌
"user_preference": {
"default_fiat": "EUR",
"skip_bank_disclaimer": true,
"skip_wire_disclaimer": true,
"banner": []
},
"kyc_status": "Approved", // KYC认证状态
"account_type": "Individual", // 账户类型
"account_email": "[email protected]", // 账户邮箱
"account_status": "active", // 账户状态
"expires_at": "2023-03-21 09:09:51", // 令牌过期时间
"next_kyc_step": "questionnaire", // 下一步KYC步骤
"exchange_config": {
"auto_withdrawal": true,
"trade_confirmation_popup": "The purchased coins will arrive in your exchange wallet in 30mins - 1hour.",
"minimum_trading_size": "40",
"exchange_name": "PHEMEX",
"logo_url": "https://legendtrading.com/img/logo-partners/phemex.png",
"allowed_payment_methods": ["wire", "creditcard"],
"user_max_ach_deposit": "1000",
"allowed_fiat_deposit_methods": ["wire", "ach"],
"allowed_fiat_withdraw_methods": ["wire"],
"disable_modify_wallet": false,
"no_sell": false,
"app_type": "CEX",
"allow_usd_wire_transfer": "off"
},
"user_info": {
"edd": {}, // 增强尽职调查信息
"rfi": {} // 信息请求信息
}
}3. 获取应用状态 (getState)
getState 方法用于获取当前用户的完整应用状态信息,包括 KYC 状态、账户信息、余额、交易所配置等。该方法返回一个包含丰富状态信息的对象。
方法签名
const state = legend.getState();返回值
返回 StateObject 类型,包含以下分类信息:
1. KYC 认证状态
字段名 | 类型 | 描述 |
|---|---|---|
kycStatus | string | KYC 认证状态 "Pending": 审核中 "Approved": 已通过 "Rejected": 已拒绝 |
nextKycStep | string | 下一步 KYC 步骤示例:"enter_bank" - 需要完善银行信息 |
originalNextKycStep | string | 原始下一步 KYC 步骤用于回退或重置流程 |
2. 账户基本信息
| 字段名 | 类型 | 描述 |
|---|---|---|
| accountEmail | string | 用户注册邮箱地址 |
| accountEmailHash | string | 邮箱地址的哈希值(用于隐私保护) |
| accountType | string | 账户类型 - "individual": 个人账户 - "institutional": 机构账户 |
3. 会话与安全信息
| 字段名 | 类型 | 描述 |
|---|---|---|
| token | string | 用户访问令牌(JWT格式) |
| tokenType | string | 令牌类型,通常为 "Bearer" |
| expiresIn | number | 令牌剩余有效时间(秒) |
| expiresAt | string | 令牌过期时间(格式:"YYYY-MM-DD HH:mm:ss") |
| isHighRiskIp | boolean | 当前 IP 是否被识别为高风险 |
| ipAddressInfo | object | IP 地址相关信息 |
4. 交易对与资产信息
| 字段名 | 类型 | 描述 |
|---|---|---|
| pairs | Array | 可交易的货币对列表 |
| asset | string | 主计价货币,如 "USD" |
| currencies | Array | 支持的货币列表 |
| currencyUnitList | object | 货币单位配置信息 |
5. 余额信息 (balance)
用户在各类型钱包中的资产余额:
balance: {
crypto_exchange: { // 交易所加密货币余额
[asset: string]: string
},
crypto: { // 主钱包加密货币余额
[asset: string]: string
},
fiat: { // 法币余额
[asset: string]: string
}
}6. 交易所配置 (exchangeConfig)
当前连接的交易所配置信息:
| 字段名 | 类型 | 描述 |
|---|---|---|
| exchange_name | string | 交易所名称 |
| logo_url | string | 交易所 Logo URL |
| app_type | string | 应用类型,如 "CEX"(中心化交易所) |
| auto_withdrawal | boolean | 是否启用自动提现 |
| minimum_trading_size | string | 最小交易金额 |
| trade_confirmation_popup | string | 交易确认弹窗提示文案 |
| allowed_payment_methods | string[] | 允许的支付方式 - "wire": 电汇 - "ach": ACH 转账 - "creditcard": 信用卡 |
| allowed_fiat_deposit_methods | string[] | 允许的法币充值方式 |
| allowed_fiat_withdraw_methods | string[] | 允许的法币提现方式 |
| user_max_ach_deposit | string | 用户 ACH 最大存款限额 |
| disable_modify_wallet | boolean | 是否禁用钱包修改 |
| no_sell | boolean | 是否禁止卖出操作 |
| allow_usd_wire_transfer | string | 是否允许美元电汇转账 |
7. 用户偏好设置 (user_preference)
| 字段名 | 类型 | 描述 |
|---|---|---|
| default_fiat | string | 默认法币货币 |
| skip_bank_disclaimer | boolean | 是否跳过银行免责声明 |
| skip_wire_disclaimer | boolean | 是否跳过电汇免责声明 |
| banner | Array | 用户界面横幅配置 |
8. 其他信息
| 字段名 | 类型 | 描述 |
|---|---|---|
| operationType | string | 当前操作类型,如 "credit_card" |
| minimumTradingSize | number | 最小交易规模(数字格式) |
| exchangeName | string | 交易所名称(简化版) |
| regionListType | string | 区域列表类型 |
使用示例
基本调用
// 获取完整状态对象
const state = legend.getState();
// 检查 KYC 状态
console.log("KYC 状态:", state.kycStatus); // "Approved"
// 获取用户邮箱
console.log("用户邮箱:", state.accountEmail); // "[email protected]"
// 检查令牌过期时间
console.log("令牌过期:", state.expiresAt); // "2023-12-06 03:27:43"检查余额
const state = legend.getState();
// 检查法币余额
console.log("法币余额:", state.balance.fiat);
// 输出: { USD: "1000.00", EUR: "500.00" }
// 检查加密货币余额
console.log("加密货币余额:", state.balance.crypto);
// 输出: { BTC: "0.5", ETH: "2.3" }验证交易权限
const state = legend.getState();
// 检查是否支持信用卡支付
const canUseCreditCard = state.exchangeConfig.allowed_payment_methods.includes('creditcard');
console.log("支持信用卡:", canUseCreditCard); // true
// 检查最小交易金额
const minTradeSize = parseFloat(state.exchangeConfig.minimum_trading_size);
console.log("最小交易金额:", minTradeSize); // 40处理 KYC 流程
const state = legend.getState();
switch (state.kycStatus) {
case "Approved":
console.log("KYC 已通过,可以正常交易");
break;
case "Pending":
console.log("KYC 审核中,下一步:", state.nextKycStep);
break;
case "Rejected":
console.log("KYC 被拒绝,请联系客服");
break;
default:
console.log("未知 KYC 状态");
}4. 读取和设置账单地址
提供账单地址的读取和设置功能,用于在交易过程中处理用户的账单信息。
4.1 获取账单地址 (getBillingAddress)
获取用户在 KYC 认证过程中填写的默认账单地址信息。该地址通常用于表单预填充,提升用户体验。
方法签名
const profile = await legend.getBillingAddress();返回值
返回 Promise<BillingAddress>,包含完整的账单地址信息:
| 字段名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| name | string | ✅ | 用户全名 |
| date_of_birth | string | ✅ | 出生日期 (格式: YYYY-MM-DD) |
| address_line_1 | string | ✅ | 地址行 1(街道地址) |
| address_line_2 | string | ❌ | 地址行 2(公寓号、单元号等) |
| city | string | ✅ | 城市 |
| state | string | ✅ | 州/省份 |
| country | string | ✅ | 国家名称 |
| country_iso2_code | string | ✅ | 国家 ISO 2 位代码 |
| zipcode | string | ✅ | 邮政编码 |
| registration_path | string | ✅ | 注册途径,如 "deposit" |
使用示例
// 获取 KYC 中的主要地址信息
const profile = await legend.getBillingAddress();
console.log("从 KYC 获取的主要地址: \n%o", profile);
// 输出示例:
// {
// "name": "Yao 1 Ji",
// "date_of_birth": "1988-08-08",
// "address_line_1": "1329 Wiley Oak Dr",
// "address_line_2": null,
// "city": "Jarrettsville",
// "country": "United States",
// "country_iso2_code": "US",
// "state": "California",
// "zipcode": "21084-1953",
// "registration_path": "deposit"
// }4.2 设置账单地址 (setBillingAddress)
为当前交易设置一个新的账单地址。重要:此操作会生成一个新的地址记录,不会修改用户原有的 KYC 地址信息。
方法签名
const resp = await legend.setBillingAddress(addressData);参数说明
⚠️ addressData 需要包含与 getBillingAddress 返回对象相同结构的地址信息。
返回值
| 字段名 | 类型 | 描述 |
|---|---|---|
| uuid | string | 新地址的唯一标识符,例如:“581bd72d-5091-4447-ad63-40f388d5794d” |
| result | string | 操作是否成功,例如:"success" |
| message | string | 操作结果信息,例如:"New billing address added." |
使用示例
// 使用从 KYC 获取的地址设置新账单地址
const profile = await legend.getBillingAddress();
const resp = await legend.setBillingAddress(profile);
console.log("设置新账单地址并获取 UUID: \n%o", resp.uuid);
// 输出示例: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
// 或者创建自定义地址
const customAddress = {
name: "Zhang San",
date_of_birth: "1990-01-01",
address_line_1: "123 Main Street",
address_line_2: "Apt 4B",
city: "New York",
state: "New York",
country: "United States",
country_iso2_code: "US",
zipcode: "10001",
registration_path: "deposit"
};
const customResp = await legend.setBillingAddress(customAddress);
console.log("自定义地址设置结果:", customResp);
// 输出示例:
// {
// "result": "success",
// "message": "New billing address added.",
// "uuid": "581bd72d-5091-4447-ad63-40f388d5794d"
// }5. 支付方式管理
提供支付方式的查询、添加、删除和事件监听功能,支持信用卡等多种支付方式。
5.1 获取支付方式
5.1.1 获取所有支付方式
获取用户当前可用的所有支付方式列表。
// 获取全部支付方式
const paymentMethods = await legend.getAllPaymentMethods();
console.log("所有支付方式: \n%o", paymentMethods);5.1.2 根据交易对获取支付方式
根据特定交易对和交易方向获取适用的支付方式。
// 根据交易对获取支付方式
const tradePaymentMethods = await legend.getPaymentMethodsByTrade({
pair: "USDTUSD", // 交易对
side: "buy", // 交易方向: "buy" 或 "sell"
});
console.log("交易相关支付方式: \n%o", tradePaymentMethods);参数说明:
| 字段名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| pair | string | ✅ | 交易货币对,如 "USDTUSD" |
| side | string | ✅ | 交易方向:"buy"(买入)或 "sell"(卖出) |
5.2 添加支付方式(信用卡)
通过 iframe 方式安全地添加信用卡支付方式,支持 3DS 认证。
5.2.1 HTML 结构要求
<!-- 信用卡号输入框 -->
<div class="frame-another-input"></div>
<!-- 支付表单容器 -->
<section class="frame-wrapper">
<div class="frame-card-number"></div> <!-- 卡号 -->
<div class="frame-expiry-date"></div> <!-- 有效期 -->
<div class="frame-cvv"></div> <!-- CVV -->
<button class="btn-submit">提交</button> <!-- 提交按钮 -->
</section>
<!-- 3DS 认证 iframe -->
<iframe class="frame-3ds" title="3D Secure 认证"></iframe>5.2.2 JavaScript 实现
// 首先设置账单地址(必需)
const addressResult = await legend.setBillingAddress(profile);
const billingUuid = addressResult.uuid;
// 事件处理函数
const handleFrameEvents = (type) => (...args) => {
console.log(`事件类型: ${type}`, ...args);
// 处理表单验证状态
if (type === "FRAME_VALIDATION_CHANGED") {
const $frame = document.querySelector(`.frame-${args[0].element}`);
if (args[0].isValid || args[0].isEmpty) {
$frame.classList.remove("error");
} else {
$frame.classList.add("error");
}
}
// 处理特定事件
switch (type) {
case "CARD_TOKENIZED":
console.log("卡片令牌化成功");
break;
case "CARD_TOKENIZATION_FAILED":
console.error("卡片令牌化失败");
break;
case "READY":
console.log("支付框架准备就绪");
break;
}
};
try {
const result = await legend.addPaymentMethod({
// 必需参数
uuidAddress: billingUuid, // 使用 setBillingAddress() 返回的 UUID
btnSubmitSelector: ".btn-submit",
frame3DsSelector: ".frame-3ds",
// 3DS 认证回调
showFrame3Ds: () => {
console.log("显示 3DS 认证窗口");
document.querySelector(".frame-3ds").style.display = "block";
},
hideFrame3Ds: () => {
console.log("隐藏 3DS 认证窗口");
document.querySelector(".frame-3ds").style.display = "none";
},
// 卡片字段配置
cardNumber: {
frameSelector: ".frame-another-input",
},
expiryDate: {
// 使用默认配置
},
cvv: {
// 使用默认配置
},
// 样式定制
style: {
placeholder: {
base: {
color: "#ff2052",
fontSize: "15px"
}
}
},
// 事件监听
events: {
CARD_BIN_CHANGED: handleFrameEvents("CARD_BIN_CHANGED"),
CARD_SUBMITTED: handleFrameEvents("CARD_SUBMITTED"),
CARD_TOKENIZED: handleFrameEvents("CARD_TOKENIZED"),
CARD_TOKENIZATION_FAILED: handleFrameEvents("CARD_TOKENIZATION_FAILED"),
CARD_VALIDATION_CHANGED: handleFrameEvents("CARD_VALIDATION_CHANGED"),
FRAME_ACTIVATED: handleFrameEvents("FRAME_ACTIVATED"),
FRAME_FOCUS: handleFrameEvents("FRAME_FOCUS"),
FRAME_BLUR: handleFrameEvents("FRAME_BLUR"),
FRAME_VALIDATION_CHANGED: handleFrameEvents("FRAME_VALIDATION_CHANGED"),
PAYMENT_METHOD_CHANGED: handleFrameEvents("PAYMENT_METHOD_CHANGED"),
READY: handleFrameEvents("READY"),
},
});
console.log("添加支付方式成功: \n%o", result);
} catch (error) {
console.error("添加支付方式失败: ", error);
}5.2.3 配置参数说明
必需参数:
| 字段名 | 类型 | 描述 |
|---|---|---|
| uuidAddress | string | setBillingAddress() 返回的 UUID |
| btnSubmitSelector | string | 提交按钮的 CSS 选择器 |
| frame3DsSelector | string | 3DS iframe 的 CSS 选择器 |
卡片字段配置:
| 字段名 | 类型 | 描述 | |
|---|---|---|---|
| cardNumber | object | 卡号输入框配置 | |
| expiryDate | object | 有效期输入框配置 | |
| cvv | object | CVV | 输入框配置 |
3DS 回调函数:
| 函数名 | 描述 |
|---|---|
| showFrame3Ds() | 显示 3DS 认证窗口时调用 |
| hideFrame3Ds() | 隐藏 3DS 认证窗口时调用 |
5.3 删除支付方式
删除指定的支付方式。
// 删除支付方式
const result = await legend.removePaymentMethod({
payment_method_id: "pm_123456789" // 支付方式ID
});
console.log("删除支付方式结果: \n%o", result);参数说明:
| 字段名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| payment_method_id | string | ✅ | 支付方式的唯一标识符 |
5.4 支付事件监听
在最新版本中,我们强烈推荐使用构造函数回调配置的方式来处理 SDK 的各种状态和交互。相关文档查看 8. onPay 支付回调
const legend = new LegendBase({
/**
* 支付流程回调
* @param {string} status - 支付状态(success|pending|failed)
*/
onPay: (status, paymentData) => {
// 在这里处理支付逻辑
console.log('支付状态:', status);
console.log(‘相关支付数据’,paymentData)
},
});⚠️所以对于原先事件,我们依然支持 但不推荐。
5.4.1 支付成功(不推荐)
legend.on("legend:payment-success", (event) => {
console.log("支付成功: \n%o", event.data);
// 处理成功逻辑,如更新UI、跳转页面等
});5.4.2 支付失败(不推荐)
legend.on("legend:payment-failed", (event) => {
console.error("支付失败:", event);
// 示例事件数据:
// {
// subject: "Unable to verify your card due to insufficient funds or credits.",
// message: "Please try a different card."
// }
alert(`支付失败: ${event.message}`);
});5.4.3 支付处理中(不推荐)
legend.on("legend:payment-pending", (event) => {
console.log("支付处理中: \n%o", event);
// 显示处理中状态
});5.4.4 支付错误(不推荐)
legend.on("legend:payment-error", (event) => {
console.error("支付错误: \n%o", event);
// 处理系统错误
});完整示例:添加信用卡支付
async function addCreditCard() {
try {
// 1. 设置账单地址
const address = await legend.getBillingAddress();
const addressResult = await legend.setBillingAddress(address);
// 2. 添加支付方式
const paymentResult = await legend.addPaymentMethod({
uuidAddress: addressResult.uuid,
btnSubmitSelector: ".btn-submit",
frame3DsSelector: ".frame-3ds",
showFrame3Ds: () => document.querySelector(".frame-3ds").style.display = "block",
hideFrame3Ds: () => document.querySelector(".frame-3ds").style.display = "none",
cardNumber: { frameSelector: ".frame-card-number" },
expiryDate: {},
cvv: {},
events: {
CARD_TOKENIZED: () => console.log("卡片添加成功"),
CARD_TOKENIZATION_FAILED: (error) => console.error("卡片添加失败", error)
}
});
console.log("支付方式添加成功:", paymentResult);
return paymentResult;
} catch (error) {
console.error("添加支付方式失败:", error);
throw error;
}
}6. 交易询价
提供基于法币金额的交易询价功能,用于在交易前获取实时的价格、手续费等交易详情。
方法说明
quoteByFiat 方法根据指定的法币金额,计算在特定交易对和支付方式下可获得的加密货币数量及相关费用。
方法签名
const quote = await legend.quoteByFiat(quoteParams);参数说明
quoteParams 对象包含以下字段:
| 字段名 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| pair | string | ✅ | 交易货币对 "USDTUSD", "BTCUSD" | |
| payment_method_id | string | ✅ | 支付方式 ID "pm_123456789" | |
| side | string | ✅ | 交易方向 "buy"(买入), "sell"(卖出) | |
| size | number | ✅ | 法币金额 50(表示 50 USD) |
返回值
返回 QuoteResponse 对象,包含完整的交易报价信息:
| 字段名 | 类型 | 描述 |
|---|---|---|
| created | string | 报价创建时间(ISO 8601 格式) "2025-10-09T08:43:36.785355Z" |
| pair | string | 交易货币对 "USDTUSD" |
| price | string | 交易价格(1个加密货币对应的法币价格) "1.00530" |
| quantity | number | 可获得的加密货币数量 47.4983 |
| processing_fee | string | 处理手续费 "0.75" |
| network_fee | number | 网络手续费 1.5 |
| side | string | 交易方向 "buy" |
| size | number | 输入的法币金额 50 |
示例代码
// 基于法币金额询价(买入 USDT)
const quote = await legend.quoteByFiat({
pair: "USDTUSD",
payment_method_id: "pm_123456789",
side: "buy",
size: 50, // 50 USD
});
console.log("法币询价结果: \n%o", quote);
// 输出示例:
// {
// created: "2025-10-09T08:43:36.785355Z",
// pair: "USDTUSD",
// price: "1.00530",
// quantity: 47.4983,
// processing_fee: "0.75",
// network_fee: 1.5,
// side: "buy",
// size: 50
// }7. 交易执行
提供基于法币金额的交易执行功能,支持信用卡支付和 3DS 安全认证。
方法说明
orderByFiat 方法用于执行加密货币交易,根据指定的法币金额和支付方式完成买入或卖出操作。
方法签名
const orderResult = await legend.orderByFiat(orderParams);参数说明
orderParams 对象包含以下字段:
| 字段名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| frame3DsSelector | string | ✅ | 3DS 验证容器的 CSS 选择器 ".frame-3ds"| |
| showFrame3Ds | function | ✅ | 显示 3DS 验证窗口的回调函数 () => console.log("显示3DS")| |
| hideFrame3Ds | function | ✅ | 隐藏 3DS 验证窗口的回调函数 () => console.log("隐藏3DS")| |
| app_state | string | ✅ | 应用状态标识,用于关联订单(会在 webhook 的 reference 字段中传递) "user_123_session"| |
| network | string | ✅ | 数字货币使用的区块链网络 "ETH", "BTC", "TRX"| |
| pair | string | ✅ | 交易货币对(最后三位为法币,之前为数字货币) "USDCUSD", "USDTUSD"| |
| payment_method_id | string | ✅ | 支付方式 ID "pm_123456789"| |
| side | string | ✅ | 交易方向 "buy"(买入), "sell"(卖出)」 |
| size | number | ✅ | 法币交易金额 50| |
| cvv | string | ✅ | 信用卡 CVV 安全码(需要从用户输入获取) "123"| |
返回值
返回 OrderResponse 对象,包含交易执行结果:
| 字段名 | 类型 | 描述 |
|---|---|---|
| trade_id | string | 交易唯一标识符 "e792990b-9b79-423e-824e-bf125bd90e2a" |
| quantity | number | 实际获得的加密货币数量 47.498 |
| price | number | 成交价格 1.00531 |
| pair | string | 交易货币对 "USDTUSD" |
| side | string | 交易方向 "buy" |
| created | string | 交易创建时间(ISO 8601 格式) "2025-10-09T09:05:47.020480Z" |
| network_fee | number | 网络手续费 1.5 |
| processing_fee | string | 处理手续费 "0.75" |
| size | number | 输入的法币金额 50 |
使用示例
7.1 基本用法
// 执行法币交易(买入 USDT)
const resp = await legend.orderByFiat({
frame3DsSelector: ".frame-3ds",
showFrame3Ds: () => {
console.log("显示 3DS 验证窗口");
document.querySelector(".frame-3ds").style.display = "block";
},
hideFrame3Ds: () => {
console.log("隐藏 3DS 验证窗口");
document.querySelector(".frame-3ds").style.display = "none";
},
app_state: "user_123_session_001", // 用于关联订单的任意字符串
network: "ETH",
pair: "USDTUSD",
payment_method_id: "pm_123456789",
side: "buy",
size: 50,
cvv: "123", // 从用户输入获取
});
console.log("交易执行结果: \n%o", resp);
// 输出示例:
// {
// trade_id: "e792990b-9b79-423e-824e-bf125bd90e2a",
// quantity: 47.498,
// price: 1.00531,
// pair: "USDTUSD",
// side: "buy",
// created: "2025-10-09T09:05:47.020480Z",
// network_fee: 1.5,
// processing_fee: "0.75",
// size: 50
// }7.2 HTML 结构要求
<!-- 3DS 认证容器 -->
<div class="frame-3ds" style="display: none;">
<!-- 3DS 认证 iframe 将在此渲染 -->
</div>
<!-- CVV 输入框(如果需要用户输入) -->
<div class="cvv-input-container">
<label for="cvv">信用卡安全码 (CVV)</label>
<input type="password" id="cvv" maxlength="4" placeholder="123">
</div>7.3 完整交易流程
async function executeTrade(tradeConfig) {
try {
const {
amount,
paymentMethodId,
cryptoAsset = "USDT",
network = "ETH",
cvv
} = tradeConfig;
// 1. 先进行询价
const quote = await legend.quoteByFiat({
pair: `${cryptoAsset}USD`,
payment_method_id: paymentMethodId,
side: "buy",
size: amount
});
console.log("询价结果:", quote);
// 2. 执行交易
const orderResult = await legend.orderByFiat({
frame3DsSelector: ".frame-3ds",
showFrame3Ds: () => {
console.log("显示 3DS 认证");
document.querySelector(".frame-3ds").classList.add("active");
},
hideFrame3Ds: () => {
console.log("隐藏 3DS 认证");
document.querySelector(".frame-3ds").classList.remove("active");
},
app_state: `trade_${Date.now()}_${cryptoAsset}`,
network: network,
pair: `${cryptoAsset}USD`,
payment_method_id: paymentMethodId,
side: "buy",
size: amount,
cvv: cvv
});
console.log("交易成功:", orderResult);
return orderResult;
} catch (error) {
console.error("交易执行失败:", error);
throw error;
}
}
// 使用示例
const tradeResult = await executeTrade({
amount: 100,
paymentMethodId: "pm_123456789",
cryptoAsset: "USDT",
network: "ETH",
cvv: "123"
});7.4 交易状态监听
const legend = new LegendBase({
/**
* 支付流程回调
* @param {string} status - 支付状态(success|pending|failed)
*/
onPay: (status, paymentData) => {
// 在这里处理支付逻辑
console.log('支付状态:', status);
console.log(‘相关支付数据’,paymentData)
},
});8.onPay 支付回调
支付流程的状态回调函数,用于处理支付过程中的各种状态变化。
回调函数签名
/**
* 支付流程回调
* @param {string} status - 支付状态 ('success' | 'pending' | 'failed')
* @param {Object} paymentData - 支付数据,根据状态不同数据结构有所差异
*/
onPay: (status, paymentData) => {
// 处理支付状态变化
}状态详情
1、status = 'success' - 支付成功
支付已成功完成,返回完整的交易信息。
paymentData 结构:
{
"target": "checkout-redirect",
"status": "success",
"message": "Card added successfully.",
"data": {
"trade_id": "0ad4c41d-c76c-4a25-8343-7a0ae96786b3", // 交易ID
"quantity": 47.4961, // 成交数量
"price": 1.00535, // 成交价格
"pair": "USDTUSD", // 交易对
"side": "buy", // 买卖方向 (buy/sell)
"created": "2025-07-13T03:19:38.549313Z", // 创建时间
"network_fee": 1.5, // 网络费用
"processing_fee": "0.75", // 处理费用
"size": 50 // 订单大小
}
}使用示例:
onPay: (status, paymentData) => {
if (status === 'success') {
console.log('支付成功!');
console.log('交易ID:', paymentData.data.trade_id);
console.log('成交数量:', paymentData.data.quantity);
console.log('总费用:', paymentData.data.network_fee + parseFloat(paymentData.data.processing_fee));
// 显示成功页面,更新订单状态等
showSuccessPage(paymentData.data);
}
}2、status = 'pending' - 支付处理中
支付正在处理中,需要等待进一步的结果。
paymentData 结构:
{
"target": "checkout-redirect",
"status": "pending",
"data": {
"target": "checkout-redirect",
"status": "pending"
}
}使用示例:
onPay: (status, paymentData) => {
if (status === 'pending') {
console.log('支付处理中...');
// 显示加载状态,防止用户重复提交
showLoadingState('支付处理中,请稍候...');
}
}3、status = 'failed' - 支付失败
支付失败,返回具体的错误信息。
paymentData 结构:
{
"target": "checkout-redirect",
"status": "failed",
"subject": "Unable to verify your card.", // 错误主题
"message": "There's an issue with your card. Please try another one.", // 错误详情
"data": {
"target": "checkout-redirect",
"status": "failed",
"subject": "Unable to verify your card.",
"message": "There's an issue with your card. Please try another one."
}
}// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card.",
// "message": "There’s an issue with your card. Please try another one.",
// "data": {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card.",
// "message": "There’s an issue with your card. Please try another one.",
// }
// }// {
// "target": "checkout-redirect",
// "status": "failed",
// "error_code": "failed_to_trade",
// "subject": "Payment failed",
// "message": "We’re sorry but card payment failed. Please try again.",
// "data": {
// "target": "checkout-redirect",
// "status": "failed",
// "error_code": "failed_to_trade",
// "subject": "Payment failed",
// "message": "We’re sorry but card payment failed. Please try again."
// }
// }// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card.",
// "message": "Please call the customer support of your issuing bank to make sure the transaction wasn’t incorrectly flagged as fraudulent, or try a different card.",
// "data": {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card.",
// "message": "Please call the customer support of your issuing bank to make sure the transaction wasn’t incorrectly flagged as fraudulent, or try a different card."
// }
// }// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card due to the failure of 3D-Secure authentication.",
// "message": "Please double check the card number and details entered.",
// "data": {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card due to the failure of 3D-Secure authentication.",
// "message": "Please double check the card number and details entered."
// }
// }// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card as it was declined by your issuing bank.",
// "message": "Please try a different card. Debit cards have higher success rates than credit cards."
// "data": {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card as it was declined by your issuing bank.",
// "message": "Please try a different card. Debit cards have higher success rates than credit cards."
// }
// }// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card due to insufficient funds or credits.",
// "message": "Please try a different card.",
// "data": {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card due to insufficient funds or credits.",
// "message": "Please try a different card."
// }
// }使用示例:
onPay: (status, paymentData) => {
if (status === 'failed') {
console.log('支付失败:', paymentData.subject);
console.log('失败原因:', paymentData.message);
// 显示错误提示给用户
showErrorModal({
title: paymentData.subject,
message: paymentData.message
});
}
}完整使用示例
const legend = new LegendBase({
onPay: (status, paymentData) => {
switch (status) {
case 'success':
// 支付成功处理
console.log('🎉 支付成功! 交易ID:', paymentData.data.trade_id);
updateOrderStatus('completed', paymentData.data);
redirectToSuccessPage();
break;
case 'pending':
// 支付处理中
console.log('⏳ 支付处理中...');
showProcessingOverlay();
break;
case 'failed':
// 支付失败
console.log('❌ 支付失败:', paymentData.message);
showErrorMessage(paymentData.subject, paymentData.message);
enableRetryButton();
break;
default:
console.warn('未知的支付状态:', status);
}
}
});9.onError 错误处理回调
统一错误处理回调函数,用于捕获和处理 SDK 运行过程中发生的所有错误。
回调函数签名
/**
* 错误处理回调
* @param {Object} error - 错误对象
* @param {string|number} error.code - 错误代码
* @param {string} error.message - 错误信息
* @param {Object} [error.errors] - 错误详情对象
* @param {Object} [error.metadata] - 错误元数据
* @param {string} [error.success] - 请求成功状态
*/
onError: (error) => {
// 处理错误逻辑
}
错误格式示例
422
{
"code":422,
"message": "Unauthenticated",
"errors": {
"signature": "Unauthenticated"
}
}{
"code":422,
"message": "Timestamp is not valid.",
"errors": {
"timestamp": "Timestamp is not valid."
}
}{
"code": 422,
"message": "The parent id field is required.",
"errors": {
"parent_id": ["The parent id field is required."]
}
}
{
"code": 422,
"message": "The selected residential is invalid.",
"errors": {
"residential": ["The selected residential is invalid."]
}
}{
"code": 422,
"message": "The city is not allowed.",
"errors": {
"city": ["The city is not allowed."]
}
}
{
"code": 422,
"message": "The country field is required when country iso2 code is not present.",
"errors": {
"country": [
"The country field is required when country iso2 code is not present."
]
}
}{
"code": 422,
"message": "The country iso2 code must be 2 characters.",
"errors": {
"country_iso2_code": ["The country iso2 code must be 2 characters."]
}
}{
"code": 422,
"message": "The address line 1 field is required.",
"errors": {
"address_line_1": ["The address line 1 field is required."]
}
}
{
"code": 422,
"message": "The state field is required.",
"errors": {
"state": ["The state field is required."]
}
}{
"code": 422,
"message": "The data field is required.",
"errors": {
"data": ["The data field is required."]
}
}{
"code": 422,
"message": "The data.token field is required.",
"errors": {
"data.token": ["The data.token field is required."]
}
}{
"code": 422,
"message": "The currency field is required.",
"errors": {
"currency": ["The currency field is required."]
}
}{
"code": 422,
"message": "The billing address field is required.",
"errors": {
"billing_address": ["You are not the owner of this model."]
}
}{
"code": 422,
"message": "The selected billing address is invalid.",
"errors": {
"billing_address": ["The selected billing address is invalid."]
}
}
403
{
"code": 403,
"message": "To complete the verification process for your account, please check your email. We have sent instructions on how to finalize the verification of your latest deposits/withdrawals.",
"error_code": "RFI_REQUIRED",
"errors": {
"rfi": ["RFI required"]
},
"data": {
"url": "",
"email": "",
"grace_period_end_timestamp": null
}
}
{
"code": 403,
"message": "To complete the verification process for your account, please check your email. We have sent instructions on how to finalize the verification of your latest deposits/withdrawals.",
"error_code": "EDD_REQUIRED",
"errors": {
"edd": ["EDD required"]
},
"data": {
"email": ""
}
}50X
{
"code":500,
"message": "Card rejected.",
"error_code": "card_rejected",
"errors": {
"trade": ["Card rejected."]
}
}
{
"code":503,
"message": "Account suspended - Please contact [email protected]",
"error": "Account suspended - Please contact [email protected]"
}101100
{
"success": "false",
"code": "101100",
"message": "Due to compliance reasons, we are unable to offer services to users from this region.",
"metadata": {
"ip": "1.1.1.1",
"country": "AF"
}
}完整使用示例
const legend = new LegendBase({
onError: (error) => {
console.error('SDK 错误:', error);
// 根据错误代码进行分类处理
switch (error.code) {
case '101100':
// 区域限制错误
showBlockingError({
title: '服务不可用',
message: error.message,
type: 'regional_restriction'
});
break;
case 401:
case 422:
if (error.message.includes('Unauthenticated')) {
// 认证错误
handleAuthenticationError();
} else if (error.errors) {
// 参数验证错误
handleValidationError(error.errors);
} else {
// 其他 4xx 错误
showWarningToast(error.message);
}
break;
case 500:
case 502:
case 503:
// 服务器错误
showServerError(error.message);
break;
default:
// 未知错误
showGenericError(error.message);
}
}
});
// 处理参数验证错误的辅助函数
function handleValidationErrors(errors) {
const errorMessages = [];
for (const [field, messages] of Object.entries(errors)) {
if (Array.isArray(messages)) {
errorMessages.push(...messages);
} else {
errorMessages.push(messages);
}
}
if (errorMessages.length > 0) {
showValidationErrorModal(errorMessages.join('\n'));
}
}
10. 事件(不推荐)
🎯 推荐使用回调配置
在最新版本中,我们强烈推荐使用构造函数回调配置的方式来处理 SDK 的各种状态和交互。
const legend = new LegendBase({
onError: (error) => {
console.error('SDK 错误:', error);
},
onPay: (paymentData) => {
console.log('支付数据:', paymentData);
},
onAuthChange: (status) => {
console.log('KYC认证状态:', status);
}
});⚠️ 事件模型:依然支持但不推荐
该事件罗列了 SDK 中所有包含事件,用于参考
// bind events
legend.on("*", (type, e) => {
console.log("custom event dispatched: \n[%o], %o", type, e);
});
legend.on("legend:payment-success", (e) => {
// 在 addPaymentMethod() 方法或者 orderByFiat() 方法运行时触发。
// 表示支付成功,添加卡片时的支付验证成功。
console.log("legend:payment-success: \n%o", e);
// {
// data: {
// "trade_id": "0ad4c41d-c76c-4a25-8343-7a0ae96786b3",
// "quantity": 47.4961,
// "price": 1.00535,
// "pair": "USDTUSD",
// "side": "buy",
// "created": "2025-07-13T03:19:38.549313Z",
// "network_fee": 1.5,
// "processing_fee": "0.75",
// "size": 50
// }
// }
// {
// "target": "checkout-redirect",
// "status": "success",
// "message": "Card added successfully.",
// "data": {}
// }
});
legend.on("legend:payment-failed", (e) => {
// 在 addPaymentMethod() 方法或者 orderByFiat() 方法运行时触发。
// 表示支付失败,添加卡片时的支付验证失败。
console.log("legend:payment-failed: \n%o", e);
// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card.",
// "message": "There’s an issue with your card. Please try another one."
// }
});
legend.on("legend:payment-pending", (e) => {
// 在 addPaymentMethod() 方法或者 orderByFiat() 方法运行时触发。
// 表示支付或者添加卡片时的支付验证没有完成,等待外部服务的响应
console.log("legend:payment-pending: \n%o", e);
});
legend.on("legend:payment-error", (e) => {
// 在 addPaymentMethod() 方法或者 orderByFiat() 方法运行时触发。
// 表示支付或者添加卡片时的支付验证发生错误。
console.log("legend:payment-error: \n%o", e);
// {
// "target": "checkout-redirect",
// "status": "failed",
// "subject": "Unable to verify your card.",
// "message": "There’s an issue with your card. Please try another one."
// }
// 也有另一种字符串格式的错误消息,需要检查数据类型
// "Card form invalid"
});
legend.on("legend:add-payment-method", (e) => {
// 在 addPaymentMethod() 方法执行完成,添加成功之后触发。
console.log("legend:add-payment-method: \n%o", e);
// {
// "type": "CREDIT",
// "entry": "",
// }
});
legend.on("legend:frame-3ds", (e) => {
// 在 addPaymentMethod() 方法或者 orderByFiat() 方法运行中遇到 3DS 验证时触发。
// 表示需要显示或隐藏 3DS 验证的弹窗元素。
// 也可以在调用方法时传入 showFrame3Ds 和 hideFrame3Ds 参数完成。
console.log("legend:frame-3ds: \n%o", e);
// { visible: true }
});
legend.on("legend:kyc-connect", (e) => {
// 在实例调用 authorize() 方法之后触发,可以用来确认当前用户的 KYC 状态。
// 也可以手动调用实例中的 getState() 方法,读取 kycStatus 确认 KYC 状态。
console.log("legend:kyc-connect: \n%o", e);
// { status: "Approved" }
});
legend.on("cancel-payment-method", (e) => {
// 通常不需要处理这个取消事件,SDK 内部用于集成时兼容不同交互流程。
// 表示取消了上一次的 addPaymentMethod() 方法的调用。
console.log("cancel-payment-method: \n%o", e);
// { status: "cancel" }
});Updated 7 months ago
