前端集成

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. 账户基本信息

字段名类型描述
accountEmailstring用户注册邮箱地址
accountEmailHashstring邮箱地址的哈希值(用于隐私保护)
accountTypestring账户类型 
- "individual": 个人账户 
- "institutional": 机构账户

3. 会话与安全信息

字段名类型描述
tokenstring用户访问令牌(JWT格式)
tokenTypestring令牌类型,通常为 "Bearer"
expiresInnumber令牌剩余有效时间(秒)
expiresAtstring令牌过期时间(格式:"YYYY-MM-DD HH:mm:ss")
isHighRiskIpboolean当前 IP 是否被识别为高风险
ipAddressInfoobjectIP 地址相关信息

4. 交易对与资产信息

字段名类型描述
pairsArray可交易的货币对列表
assetstring主计价货币,如 "USD"
currenciesArray支持的货币列表
currencyUnitListobject货币单位配置信息

5. 余额信息 (balance)

用户在各类型钱包中的资产余额:

balance: {
  crypto_exchange: {    // 交易所加密货币余额
    [asset: string]: string
  },
  crypto: {            // 主钱包加密货币余额  
    [asset: string]: string
  },
  fiat: {              // 法币余额
    [asset: string]: string
  }
}

6. 交易所配置 (exchangeConfig)

当前连接的交易所配置信息:

字段名类型描述
exchange_namestring交易所名称
logo_urlstring交易所 Logo URL
app_typestring应用类型,如 "CEX"(中心化交易所)
auto_withdrawalboolean是否启用自动提现
minimum_trading_sizestring最小交易金额
trade_confirmation_popupstring交易确认弹窗提示文案
allowed_payment_methodsstring[]允许的支付方式
- "wire": 电汇
- "ach": ACH 转账
- "creditcard": 信用卡
allowed_fiat_deposit_methodsstring[]允许的法币充值方式
allowed_fiat_withdraw_methodsstring[]允许的法币提现方式
user_max_ach_depositstring用户 ACH 最大存款限额
disable_modify_walletboolean是否禁用钱包修改
no_sellboolean是否禁止卖出操作
allow_usd_wire_transferstring是否允许美元电汇转账

7. 用户偏好设置 (user_preference)

字段名类型描述
default_fiatstring默认法币货币
skip_bank_disclaimerboolean是否跳过银行免责声明
skip_wire_disclaimerboolean是否跳过电汇免责声明
bannerArray用户界面横幅配置

8. 其他信息

字段名类型描述
operationTypestring当前操作类型,如 "credit_card"
minimumTradingSizenumber最小交易规模(数字格式)
exchangeNamestring交易所名称(简化版)
regionListTypestring区域列表类型

使用示例

基本调用

// 获取完整状态对象
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>,包含完整的账单地址信息:

字段名类型必填描述
namestring用户全名
date_of_birthstring出生日期 (格式: YYYY-MM-DD)
address_line_1string地址行 1(街道地址)
address_line_2string地址行 2(公寓号、单元号等)
citystring城市
statestring州/省份
countrystring国家名称
country_iso2_codestring国家 ISO 2 位代码
zipcodestring邮政编码
registration_pathstring注册途径,如 "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 返回对象相同结构的地址信息。

返回值

字段名类型描述
uuidstring新地址的唯一标识符,例如:“581bd72d-5091-4447-ad63-40f388d5794d”
resultstring操作是否成功,例如:"success"
messagestring操作结果信息,例如:"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);

参数说明:

字段名类型必填描述
pairstring交易货币对,如 "USDTUSD"
sidestring交易方向:"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 配置参数说明

必需参数:

字段名类型描述
uuidAddressstringsetBillingAddress() 返回的 UUID
btnSubmitSelectorstring提交按钮的 CSS 选择器
frame3DsSelectorstring3DS iframe 的 CSS 选择器

卡片字段配置:

字段名类型描述
cardNumberobject卡号输入框配置
expiryDateobject有效期输入框配置
cvvobjectCVV输入框配置

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_idstring支付方式的唯一标识符

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 对象包含以下字段:

字段名类型必填描述示例
pairstring交易货币对 "USDTUSD", "BTCUSD"
payment_method_idstring支付方式 ID "pm_123456789"
sidestring交易方向 "buy"(买入), "sell"(卖出)
sizenumber法币金额 50(表示 50 USD)

返回值

返回 QuoteResponse 对象,包含完整的交易报价信息:

字段名类型描述
createdstring报价创建时间(ISO 8601 格式) "2025-10-09T08:43:36.785355Z"
pairstring交易货币对 "USDTUSD"
pricestring交易价格(1个加密货币对应的法币价格) "1.00530"
quantitynumber可获得的加密货币数量 47.4983
processing_feestring处理手续费 "0.75"
network_feenumber网络手续费 1.5
sidestring交易方向 "buy"
sizenumber输入的法币金额 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 对象包含以下字段:

字段名类型必填描述
frame3DsSelectorstring3DS 验证容器的 CSS 选择器 ".frame-3ds"|
showFrame3Dsfunction显示 3DS 验证窗口的回调函数 () => console.log("显示3DS")|
hideFrame3Dsfunction隐藏 3DS 验证窗口的回调函数 () => console.log("隐藏3DS")|
app_statestring应用状态标识,用于关联订单(会在 webhook 的 reference 字段中传递) "user_123_session"|
networkstring数字货币使用的区块链网络 "ETH", "BTC", "TRX"|
pairstring交易货币对(最后三位为法币,之前为数字货币) "USDCUSD", "USDTUSD"|
payment_method_idstring支付方式 ID "pm_123456789"|
sidestring交易方向 "buy"(买入), "sell"(卖出)」
sizenumber法币交易金额 50|
cvvstring信用卡 CVV 安全码(需要从用户输入获取) "123"|

返回值

返回 OrderResponse 对象,包含交易执行结果:

字段名类型描述
trade_idstring交易唯一标识符 "e792990b-9b79-423e-824e-bf125bd90e2a"
quantitynumber实际获得的加密货币数量 47.498
pricenumber成交价格 1.00531
pairstring交易货币对 "USDTUSD"
sidestring交易方向 "buy"
createdstring交易创建时间(ISO 8601 格式) "2025-10-09T09:05:47.020480Z"
network_feenumber网络手续费 1.5
processing_feestring处理手续费 "0.75"
sizenumber输入的法币金额 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" }
});