高级功能 Web SDK

Yuno Web SDK的高级配置选项和自定义集成。

替代安装方案

基本流程使用 mountCheckout() 用于自动显示支付方式。如需更多控制选项,请使用以下替代方案:

自定义支付方式选择(mountCheckoutLite())

控制要显示的支付方式:

// 1. Fetch available methods from backend
const methods = await fetch('/api/payment-methods').then(r => r.json());

// 2. Display methods in your custom UI
// 3. Mount selected payment method

yuno.mountCheckoutLite({
  paymentMethodType: selectedMethod, // 'CARD', 'PIX', etc.
  vaultedToken: null // or saved token
});

// 4. Still need startPayment()
document.querySelector('#pay-button').addEventListener('click', () => {
  yuno.startPayment();
});

Google Pay 与 Apple Pay 支持 Lite 版本:

await yuno.mountExternalButtons([
  {
    paymentMethodType: 'GOOGLE_PAY',
    elementSelector: '#google-pay-button'
  },
  {
    paymentMethodType: 'APPLE_PAY',
    elementSelector: '#apple-pay-button'
  }
]);

简化流程 (mountSeamlessCheckout())

类似于 mountCheckoutLite() 但通过自动创建付款:

// Use startSeamlessCheckout instead of startCheckout
yuno.startSeamlessCheckout({
  // Same configuration
});

// Mount
yuno.mountSeamlessCheckout({
  paymentMethodType: 'CARD'
});

// Still need startPayment()
document.querySelector('#pay-button').addEventListener('click', () => {
  yuno.startPayment();
});

注册(保存卡片)

付款时节省

yuno.startCheckout({
  checkoutSession: session.id,
  elementSelector: '#payment-container',
  countryCode: 'US',
  card: {
    cardSaveEnable: true // Shows "Save card" checkbox
  },
  async yunoCreatePayment(token) {
    await fetch('/api/payment/create', {
      method: 'POST',
      body: JSON.stringify({
        token,
        vault_on_success: true // Save card after successful payment
      })
    });
    yuno.continuePayment();
  }
});

单独注册

// Create customer session on backend
const customerSession = await fetch('/api/customer/session', {
  method: 'POST',
  body: JSON.stringify({ customer_id: 'cus_123' })
}).then(r => r.json());

// Initialize enrollment
yuno.startEnrollment({
  customerSession: customerSession.id,
  countryCode: 'US',
  async yunoEnrolled(vaultedToken) {
    console.log('Card saved:', vaultedToken);
  }
});

// Mount enrollment form
yuno.mountEnrollment();

拱顶Token

// Use saved card
yuno.mountCheckout({
  vaultedToken: 'vtok_saved_card_123'
});

// Still need startPayment()
document.querySelector('#pay-button').addEventListener('click', () => {
  yuno.startPayment();
});

自定义用户界面(无头集成)

当您需要完全掌控每个用户界面元素、构建高度定制的结账体验,或拥有开发定制界面的资源时,可创建完全自定义的支付表单并实现全面的用户界面控制。Yuno仅处理令牌化功能。

实施

1.Initialize

const yuno = await Yuno.initialize('your-public-key');

const apiClientPayment = yuno.apiClientPayment({
  country_code: "US",
  checkout_session: "checkout_session_id"
});

2. 创建您的自定义表单

<form id="custom-payment-form">
  <input id="card-number" placeholder="Card Number" />
  <input id="expiry" placeholder="MM/YY" />
  <input id="cvv" placeholder="CVV" />
  <input id="cardholder" placeholder="Cardholder Name" />
  <button type="submit">Pay</button>
</form>

3. 生成Token

document.getElementById('custom-payment-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  try {
    const result = await apiClientPayment.generateToken({
      checkout_session: "checkout_session_id",
      payment_method: {
        type: "CARD",
        vaulted_token: null,
        card: {
          save: false,
          detail: {
            number: document.getElementById('card-number').value,
            expiration_month: 12,
            expiration_year: 25,
            security_code: document.getElementById('cvv').value,
            holder_name: document.getElementById('cardholder').value,
            type: "CREDIT"
          }
        }
      }
    });
    
    // Create payment with token
    await createPayment(result.token);
  } catch (error) {
    console.error('Token generation failed:', error);
  }
});

4. 处理3DS及重定向

const continueResult = await yuno.continuePayment({ showPaymentStatus: false });

if (continueResult?.action === 'REDIRECT_URL') {
  window.location.href = continueResult.redirect.init_url;
}

使用拱顶Token

const result = await apiClientPayment.generateToken({
  checkout_session: "checkout_session_id",
  payment_method: {
    type: "CARD",
    vaulted_token: "saved_token_id",
    card: {
      detail: {
        security_code: "123"
      }
    }
  }
});

安全字段(自定义卡片表单)

使用安全的iframe字段构建自定义卡片表单,同时保持PCI合规性。当您需要自定义卡片表单设计、特定字段布局或基于iframe的卡片安全保护时,此方案最为理想。

实施

1. 安装与Initialize

const yuno = await Yuno.initialize('你的公钥');

2. 创建安全字段

<div id="card-number-field"></div>
<div id="cvv-field"></div>
<div id="expiry-field"></div>
yuno.secureFields({
  checkoutSession: 'session_id',
  countryCode: 'US',
  fields: {
    cardNumber: {
      elementSelector: '#card-number-field',
      placeholder: '1234 5678 9012 3456'
    },
    cvv: {
      elementSelector: '#cvv-field',
      placeholder: 'CVV'
    },
    expiry: {
      elementSelector: '#expiry-field',
      placeholder: 'MM/YY'
    }
  },
  onFieldChange: (field, isValid) => {
    console.log(`${field} valid:`, isValid);
  },
  async onSubmit(token) {
    await createPayment(token);
  }
});

3. 自定义样式

fields: {
  cardNumber: {
    elementSelector: '#card-number-field',
    style: {
      base: {
        color: '#333',
        fontSize: '16px',
        fontFamily: 'Arial, sans-serif'
      },
      invalid: {
        color: '#ff0000'
      }
    }
  }
}

多种货币

处理多币种支付,支持自动转换。

// Create session with alternative amount
const session = await fetch('/api/checkout/session', {
  method: 'POST',
  body: JSON.stringify({
    amount: { currency: 'USD', value: 1000 },
    alternative_amount: { currency: 'BRL', value: 5000 }, // Display price
    country: 'BR'
  })
}).then(r => r.json());

// SDK automatically displays both currencies
yuno.startCheckout({
  checkoutSession: session.id,
  countryCode: 'BR',
  // ...
});

样式与主题

自定义CSS

yuno.startCheckout({
  // ... other config
  cssCustomization: {
    primaryColor: '#007bff',
    errorColor: '#dc3545',
    fontFamily: 'Inter, sans-serif'
  }
});

自定义按钮文字

yuno.startCheckout({
  // ... other config
  texts: {
    pay: 'Complete Purchase',
    processing: 'Processing...',
    error: 'Payment Failed'
  }
});

渲染模式

模态显示

yuno.startCheckout({
  renderMode: 'modal',
  elementSelector: '#payment-container'
});

内联显示

yuno.startCheckout({
  renderMode: 'element',
  elementSelector: '#payment-container'
});

预防欺诈

设备指纹识别

由SDK自动收集,用于已配置的欺诈检测服务商(如ClearSale等)。

定制欺诈数据

yuno.startCheckout({
  // ... other config
  async yunoCreatePayment(token, tokenWithInformation) {
    // tokenWithInformation includes fraud data
    await fetch('/api/payment/create', {
      method: 'POST',
      body: JSON.stringify({
        token,
        device_fingerprint: tokenWithInformation.device_fingerprint,
        customer_browser_info: tokenWithInformation.customer.browser_info
      })
    });
  }
});

分期付款

启用分期付款

yuno.startCheckout({
  // ... other config
  card: {
    installments: {
      enabled: true,
      defaultValue: 1
    }
  }
});

定制分期付款计划

在Yuno仪表盘中按支付方式进行配置。

装载机控制

隐藏由乃加载器

yuno.startCheckout({
  showLoading: false, // Use your own loader
  onLoading: (isLoading) => {
    document.getElementById('custom-loader').style.display = 
      isLoading ? 'block' : 'none';
  }
});

自定义加载器

yuno.startCheckout({
  // ... other config
  onLoading: (isLoading) => {
    if (isLoading) {
      showCustomSpinner();
    } else {
      hideCustomSpinner();
    }
  }
});

卡片表格类型

扩展表单(独立字段)

yuno.startCheckout({
  card: {
    type: 'extends' // Shows separate fields for all card details
  }
});

紧凑表单(单字段)

yuno.startCheckout({
  card: {
    type: 'only' // Shows single combined field
  }
});

发行人选择

启用/禁用发行者表单

yuno.startCheckout({
  issuersFormEnable: true // Show issuer selection for bank transfers
});

付款状态页面

自定义状态处理

yuno.startCheckout({
  showPaymentStatus: false, // Handle status yourself
  yunoPaymentResult: (data) => {
    // Redirect to custom status page
    window.location.href = `/payment-status?id=${data.payment_id}&status=${data.status}`;
  }
});

事件回调

所有可用回调

yuno.startCheckout({
  // ... other config
  
  // Payment method selected
  yunoPaymentMethodSelected: (data) => {
    console.log('Selected:', data.type);
    analytics.track('payment_method_selected', { type: data.type });
  },
  
  // Payment created (before processing)
  async yunoCreatePayment(token, tokenInfo) {
    console.log('Creating payment with token:', token);
    await processPayment(token);
    yuno.continuePayment();
  },
  
  // Payment completed
  yunoPaymentResult: (data) => {
    console.log('Payment result:', data.status);
    if (data.status === 'SUCCEEDED') {
      gtag('event', 'purchase', { value: data.amount });
    }
  },
  
  // Error occurred
  yunoError: (error, data) => {
    console.error('Error:', error, data);
    Sentry.captureException(error);
  },
  
  // Loading state changed
  onLoading: (isLoading) => {
    console.log('Loading:', isLoading);
  },
  
  // Card form changed
  card: {
    onChange: ({ error, data }) => {
      if (error) {
        console.log('Card validation error:', error);
      } else {
        console.log('Card data:', data);
      }
    }
  }
});

浏览器兼容性

SDK支持:

  • Chrome 90+
  • Firefox 88及以上版本
  • Safari 14及以上版本
  • 边缘 90+

旧版浏览器的兼容性补丁

<script src="https://polyfill.io/v3/polyfill.min.js"></script>
<script src="https://sdk-web.y.uno/v1.5/main.js"></script>

性能优化

懒加载 SDK

// Load SDK only when needed
async function loadYunoSDK() {
  if (typeof Yuno !== 'undefined') return;
  
  return new Promise((resolve) => {
    const script = document.createElement('script');
    script.src = 'https://sdk-web.y.uno/v1.5/main.js';
    script.onload = resolve;
    document.head.appendChild(script);
  });
}

// Use when payment page loads
document.getElementById('checkout-btn').addEventListener('click', async () => {
  await loadYunoSDK();
  initPayment();
});

预连接至Yuno服务器

<link rel="preconnect" href="https://api.y.uno">
<link rel="preconnect" href="https://sdk-web.y.uno">

沙盒测试

测试模式配置

// Use test keys (pk_test_*)
const yuno = await Yuno.initialize('pk_test_your_key_here');

模拟支付场景

// Backend: Create session with test data
{
  amount: { currency: 'USD', value: 1000 },
  metadata: {
    test_scenario: 'success' // 'success', 'decline', '3ds_required'
  }
}

错误处理

常见错误代码

yunoError: (error, data) => {
  switch(error.code) {
    case 'SESSION_EXPIRED':
      // Recreate session
      refreshSession();
      break;
    case 'INVALID_CARD':
      showError('Please check your card details');
      break;
    case 'INSUFFICIENT_FUNDS':
      showError('Insufficient funds');
      break;
    case 'NETWORK_ERROR':
      showError('Connection error. Please try again.');
      break;
    default:
      showError('An error occurred. Please try again.');
  }
}

Webhooks集成

通过webhooks在后端验证支付状态:

// Backend webhook handler
app.post('/webhooks/yuno', (req, res) => {
  const event = req.body;
  
  switch(event.type) {
    case 'payment.succeeded':
      fulfillOrder(event.data.payment_id);
      break;
    case 'payment.failed':
      cancelOrder(event.data.payment_id);
      break;
  }
  
  res.sendStatus(200);
});

环境配置

发展

const yuno = await Yuno.initialize('pk_test_dev_key', {
  environment: 'sandbox',
  debug: true // Enable console logs
});

生产

const yuno = await Yuno.initialize('pk_live_prod_key', {
  environment: 'production',
  debug: false
});