高级功能 iOS SDK

iOS的高级配置和自定义集成。

替代安装方案

基本流程使用 Yuno.startPayment() 该方案可自动处理完整的支付流程。若需更多控制权,请使用以下替代方案:

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

选择要显示的支付方式。您的委托人必须实现 YunoPaymentFullDelegate 具有所需属性:

class PaymentViewController: UIViewController, YunoPaymentFullDelegate {
    var checkoutSession: String { return _checkoutSession }
    var countryCode: String { "US" }
    var language: String? { "en" }
    var viewController: UIViewController? { self }
    
    private var _checkoutSession: String = ""
    
    func setupPayment() async {
        // 1. Create session
        let session = await createCheckoutSession()
        _checkoutSession = session.checkoutSession
        
        // 2. Fetch available methods
        let methods = await fetchPaymentMethods(sessionId: checkoutSession)
        
        // 3. Display in your UI, then start payment with selected method
        Yuno.startPaymentLite(
            paymentMethodType: selectedMethod, // "CARD", "PIX", etc.
            vaultedToken: nil, // or saved token
            delegate: self
        )
    }
}

简化流程 (startPaymentSeamlessLite)

与精简版类似,但具备自动创建付款功能:

Yuno.startPaymentSeamlessLite(
 paymentMethodType: "CARD",
 vaultedToken: nil,
    delegate: self
)

注册(保存卡片)

付款时节省

您的委托人通过属性提供会话信息:

class PaymentViewController: UIViewController, YunoPaymentFullDelegate {
    var checkoutSession: String { _checkoutSession }
    var countryCode: String { "US" }
    var language: String? { "en" }
    var viewController: UIViewController? { self }
    
    private var _checkoutSession: String = ""
    
    func setupPayment() async {
        let session = await createCheckoutSession()
        _checkoutSession = session.checkoutSession
        
        // Start payment - SDK will show save card checkbox automatically
        Yuno.startPayment()
    }
    
    // In delegate:
    func yunoCreatePayment(with token: String, information: [String: Any]) {
        Task {
            await createPayment(
                token: token,
                vaultOnSuccess: true // Save after successful payment
            )
            Yuno.continuePayment(showPaymentStatus: true)
        }
    }
}

单独注册

class EnrollmentViewController: UIViewController, YunoEnrollmentDelegate {
    var customerSession: String { _customerSession }
    var countryCode: String { "US" }
    var language: String? { "en" }
    var viewController: UIViewController? { self }
    
    private var _customerSession: String = ""
    
    func setupEnrollment() async {
        // Create customer session on backend
        let session = await createCustomerSession(customerId: "cus_123")
        _customerSession = session.id
        
        // Start enrollment - SDK reads session from delegate
        Yuno.enrollPayment(delegate: self)
    }
    
    func yunoEnrollmentStatus(status: Yuno.EnrollmentStatus, vaultedToken: String?) {
        if status == .successful, let token = vaultedToken {
            print("Card saved:", token)
        }
    }
    
    func yunoDidSelect(enrollmentMethod: EnrollmentMethodSelected) {
        print("Selected enrollment method:", enrollmentMethod)
    }
}

拱顶Token

通过提供保险库token 使用已保存的卡片 startPaymentLite:

class PaymentViewController: UIViewController, YunoPaymentFullDelegate {
    var checkoutSession: String { _checkoutSession }
    var countryCode: String { "US" }
    var language: String? { "en" }
    var viewController: UIViewController? { self }
    
    private var _checkoutSession: String = ""
    
    func payWithSavedCard(vaultedToken: String) {
        Yuno.startPaymentLite(
            paymentMethodType: "CARD",
            vaultedToken: vaultedToken,
            delegate: self
        )
    }
}

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

当您需要完全掌控每个用户界面元素、打造高度定制的结账体验,或拥有开发定制界面的资源时,可构建具备完整用户界面控制权的完全定制化支付表单。

import YunoSDK

class CustomPaymentVC: UIViewController {
    
    func processWithCustomUI() async {
        // 1. Initialize headless client
        let apiClient = Yuno.apiClientPayment(
            countryCode: "US",
            checkoutSession: "session_id"
        )
        
        // 2. Collect card data in your custom UI
        let cardData = CardData(
            number: "4111111111111111",
            expirationMonth: 12,
            expirationYear: 25,
            securityCode: "123",
            holderName: "John Doe",
            type: .credit
        )
        
        // 3. Generate token
        do {
            let result = try await apiClient.generateToken(data: TokenCollectedData(
                checkoutSession: "session_id",
                paymentMethod: CollectedData(
                    type: "CARD",
                    card: cardData
                )
            ))
            
            // 4. Create payment with token
            await createPayment(token: result.token)
            
            // 5. Handle continuation if needed
            if apiClient.shouldContinue {
                try await apiClient.continuePayment()
            }
        } catch {
            print("Error: \(error)")
        }
    }
}

使用拱顶Token

let result = try await apiClient.generateToken(data: TokenCollectedData(
 checkoutSession: "session_id",
    paymentMethod: CollectedData(
        type: "CARD",
 vaultedToken: "saved_token_id",
        card: CardData(securityCode: "123")
    )
))

渲染模式集成

在您的自定义视图中显示付款表单。

class PaymentViewController: UIViewController, YunoPaymentRenderFlowProtocol {
    
    func startRenderMode() async {
        let session = try await createCheckoutSession()
        
        let config = YunoConfig(
            checkoutSession: session.id,
            countryCode: "US"
        )
        
        Yuno.startPaymentRenderFlow(with: config, delegate: self)
    }
    
    // SDK provides view to embed
    func formView() -> UIView? {
        let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 350, height: 500))
        containerView.backgroundColor = .systemBackground
        return containerView
    }
    
    // Handle form submission
    func submitForm() async {
        // Customer submitted payment form
    }
    
    // Handle result
    func yunoPaymentResult(_ result: PaymentResult) {
        if result.status == .succeeded {
            navigateToSuccess()
        }
    }
}

SwiftUI:

struct RenderModeView: View, YunoPaymentRenderFlowProtocol {
    @State private var paymentView: UIView?
    
    var body: some View {
        if let view = paymentView {
            PaymentViewWrapper(view: view)
                .frame(height: 500)
        }
    }
    
    func startPayment() async {
        let config = YunoConfig(checkoutSession: "session_id", countryCode: "US")
        await Yuno.startPaymentRenderFlow(with: config, delegate: self)
    }
    
    func formView() -> UIView? {
        let view = UIView()
        DispatchQueue.main.async {
            paymentView = view
        }
        return view
    }
}

struct PaymentViewWrapper: UIViewRepresentable {
    let view: UIView
    
    func makeUIView(context: Context) -> UIView { view }
    func updateUIView(_ uiView: UIView, context: Context) {}
}

造型与外观

自定义 SDK 外观 Yuno.Appearance:

import YunoSDK

func configureAppearance() {
    var appearance =Yuno.Appearance()
    
    // 颜色
    appearance.primaryColor = UIColor.systemBlue
    appearance.backgroundColor = UIColor.systemBackground
    appearance.textColor = UIColor.label
    appearance.errorColor = UIColor.systemRed
    
    // 字体样式
    appearance.fontFamily = "SF Pro Display"
    appearance.fontSize = 16
    
    // 圆角半径
    appearance.cornerRadius = 12
    
    // 应用样式
    Yuno.setAppearance(appearance)
}

Swift 6 并发

使用正确的注解处理并发警告:

@MainActor
class PaymentViewController: UIViewController, YunoPaymentFullDelegate {
    var checkoutSession: String { _checkoutSession }
    var countryCode: String { "US" }
    var language: String? { "en" }
    var viewController: UIViewController? { self }
    
    private var _checkoutSession: String = ""
    
    // Safe to call from any thread
    nonisolated func startPayment() {
        Task { @MainActor in
            Yuno.startPayment()
        }
    }
    
    // UI updates on main thread
    @MainActor
    func yunoPaymentResult(_ result: PaymentResult) {
        updateUI(result)
    }
}

非隔离委托人:

extension PaymentViewController {
    nonisolated func yunoPaymentResult(_ result: PaymentResult) {
        MainActor.assumeIsolated {
            // UI updates here
            self.showResult(result)
        }
    }
}

ClearSale 集成

启用欺诈预防:

安装 ClearSale SDK:

pod 'ClearSaleSDK'

Initialize:

import ClearSale

func application(_ application: UIApplication, 
                didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    // Initialize ClearSale
    ClearSale.setup(apiKey: "your-clearsale-key")
    
    // Initialize Yuno
    Yuno.initialize(apiKey: "your-public-key")
    
    return true
}

ClearSale数据会随支付交易自动收集并发送。

自定义配置

卡片流类型

在Yuno初始化期间配置卡片输入流程:

// In AppDelegate or App struct
Yuno.initialize(
    apiKey: "your-public-key",
    cardFlow: .oneStep // or .stepByStep
)

隐藏持卡人姓名

配置持卡人姓名可见性:

// Set globally
Yuno.hideCardholderName = true

显示/隐藏状态屏幕

控制付款状态屏幕 continuePayment():

func yunoCreatePayment(with token: String, information: [String: Any]) {
    Task {
        await createPayment(token: token)
        Yuno.continuePayment(showPaymentStatus: false) // Handle result yourself
    }
}

错误处理

func yunoPaymentResult(_ result: PaymentResult) {
    switch result.status {
    case .succeeded:
        handleSuccess(result)
    case .failed:
        handleFailure(result.error)
    case .pending:
        handlePending(result)
    case .rejected:
        handleRejection(result)
    }
}

func handleFailure(_ error: YunoError?) {
    guard let error = error else { return }
    
    switch error.code {
    case "SESSION_EXPIRED":
        // Recreate session
        Task { await createNewSession() }
    case "INVALID_CARD":
        showAlert("Please check your card details")
    case "INSUFFICIENT_FUNDS":
        showAlert("Insufficient funds")
    case "NETWORK_ERROR":
        showAlert("Connection error. Please try again.")
    default:
        showAlert("Payment failed: \(error.message)")
    }
}

网络钩子

在后端验证支付状态:

// Backend receives webhook
POST /webhooks/yuno
{
  "type": "payment.succeeded",
  "data": {
    "payment_id": "pay_123",
    "status": "SUCCEEDED",
    "amount": 2500
  }
}

测试

测试模式

// Use test key
Yuno.initialize(apiKey: "pk_test_your_key")

调试日志记录

// Enable logs in development
#if DEBUG
Yuno.setLogLevel(.verbose)
#endif

性能

预加载 SDK

// Preload in background
Task(priority: .background) {
    _ = Yuno.self
}

延迟加载

// Load only when needed
lazy var yuno: Yuno = {
    Yuno.initialize(apiKey: "pk_test_key")
    return Yuno.shared
}()