突破平台壁垒:Compose-Multiplatform支付功能跨端集成指南
你是否还在为多平台应用的支付功能实现而烦恼?既要适配Android的支付宝、微信支付,又要对接iOS的内购系统,不同平台的开发语言和API差异让代码维护成本飙升。本文将带你一步掌握如何在Compose-Multiplatform项目中优雅实现统一的支付逻辑,让跨平台支付集成不再复杂。读完本文你将获得:多平台支付架构设计方案、平台特定代码隔离实践、支付状态管理最佳实践以及完整的集成步骤示例。
多平台支付架构设计
Compose-Multiplatform的核心优势在于使用Kotlin单一代码库开发跨平台应用,但支付功能涉及各平台原生能力,因此需要采用分层抽象+平台实现的架构模式。
架构设计图
graph TD
A[共享UI层] -->|调用| B[支付抽象接口]
B --> C[Android支付实现]
B --> D[iOS支付实现]
C --> E[支付宝SDK]
C --> F[微信支付SDK]
D --> G[Apple内购StoreKit]
B --> H[支付状态管理]
H --> I[共享数据模型]
这种架构通过定义统一接口隔离平台差异,共享UI层只需调用抽象接口,无需关心具体平台实现。支付状态通过ViewModel在各平台间保持同步,确保用户体验一致。
支付抽象接口定义
在共享模块中定义支付相关的抽象接口和数据模型,是实现跨平台支付的基础。以下是核心代码实现:
// commonMain/src/PaymentService.kt
interface PaymentService {
suspend fun createOrder(orderInfo: OrderInfo): Result<OrderResult>
suspend fun queryOrderStatus(orderId: String): Result<OrderStatus>
fun handlePaymentResult(data: PlatformSpecificData): Boolean
}
// 订单数据模型
data class OrderInfo(
val productId: String,
val amount: Double,
val currency: String = "CNY",
val subject: String,
val extra: Map<String, String> = emptyMap()
)
data class OrderResult(
val orderId: String,
val platformOrderId: String,
val payUrl: String? = null,
val rawData: String? = null
)
enum class OrderStatus {
PENDING, PAID, FAILED, REFUNDED
}
// 平台特定数据封装
expect class PlatformSpecificData
// 支付异常定义
sealed class PaymentException(message: String) : Exception(message) {
class NetworkError(message: String) : PaymentException(message)
class InvalidParams(message: String) : PaymentException(message)
class PlatformError(message: String, val code: Int) : PaymentException(message)
}
这段代码定义了支付服务的核心能力:创建订单、查询状态和处理支付结果。使用expect关键字声明平台特定数据类,在各平台模块中提供实际实现。
平台特定实现
Android平台实现
Android平台需要集成支付宝和微信支付SDK,在androidMain中实现支付接口:
// androidMain/src/AndroidPaymentService.kt
actual class PlatformSpecificData(
val intent: Intent? = null,
val uri: Uri? = null,
val data: Bundle? = null
)
class AndroidPaymentService : PaymentService {
private val alipaySDK = AlipaySDK()
private val wechatSDK = WechatPaySDK()
override suspend fun createOrder(orderInfo: OrderInfo): Result<OrderResult> {
return try {
when (orderInfo.paymentMethod) {
PaymentMethod.ALIPAY -> createAlipayOrder(orderInfo)
PaymentMethod.WECHAT -> createWechatOrder(orderInfo)
else -> Result.failure(UnsupportedOperationException("支付方式不支持"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
private suspend fun createAlipayOrder(orderInfo: OrderInfo): Result<OrderResult> {
// 调用支付宝SDK创建订单
val orderString = alipaySDK.createOrder(orderInfo)
return Result.success(OrderResult(
orderId = orderInfo.productId,
platformOrderId = UUID.randomUUID().toString(),
rawData = orderString
))
}
// 微信支付实现...
override fun handlePaymentResult(data: PlatformSpecificData): Boolean {
// 处理支付宝/微信返回的支付结果
data.intent?.let { intent ->
return alipaySDK.handlePaymentResult(intent) || wechatSDK.handleIntent(intent)
}
return false
}
// 其他方法实现...
}
Android平台实现需要处理Activity跳转和结果回调,建议在MainActivity中接收支付结果并转发给支付服务:
// MainActivity.kt
class MainActivity : ComponentActivity() {
private lateinit var paymentService: PaymentService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
paymentService = AndroidPaymentService()
setContent {
AppTheme {
// 提供支付服务给组合树
CompositionLocalProvider(
LocalPaymentService provides paymentService
) {
MainScreen()
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PAYMENT_REQUEST_CODE) {
paymentService.handlePaymentResult(PlatformSpecificData(data))
}
}
companion object {
const val PAYMENT_REQUEST_CODE = 1001
}
}
// 提供CompositionLocal以便UI访问支付服务
val LocalPaymentService = compositionLocalOf<PaymentService> {
error("PaymentService not provided")
}
iOS平台实现
iOS平台主要集成Apple内购(StoreKit),以下是核心实现代码:
// iosMain/src/PaymentService.kt
import StoreKit.SKPaymentQueue
import StoreKit.SKProductsRequest
actual class PlatformSpecificData(
val transaction: SKPaymentTransaction? = null,
val url: NSURL? = null
)
class IOSPaymentService : PaymentService {
private val paymentQueue = SKPaymentQueue.defaultQueue()
private var productsRequest: SKProductsRequest? = null
private val orderCache = mutableMapOf<String, OrderInfo>()
init {
paymentQueue.addTransactionObserver(PaymentTransactionObserver { transaction ->
handleTransaction(transaction)
})
}
override suspend fun createOrder(orderInfo: OrderInfo): Result<OrderResult> {
return suspendCancellableCoroutine { continuation ->
// 查询产品信息
val productIdentifiers = NSSet(arrayOf(orderInfo.productId))
productsRequest = SKProductsRequest(productIdentifiers = productIdentifiers).apply {
delegate = object : SKProductsRequestDelegateProtocol {
override fun productsRequest(request: SKProductsRequest, didReceive response: SKProductsResponse) {
val product = response.products.firstOrNull()
if (product == null) {
continuation.resume(Result.failure(PaymentException.InvalidParams("Product not found")))
return
}
// 创建支付请求
val payment = SKPayment(product: product)
orderCache[orderInfo.productId] = orderInfo
paymentQueue.addPayment(payment)
continuation.resume(Result.success(OrderResult(
orderId = orderInfo.productId,
platformOrderId = product.productIdentifier
)))
}
override fun request(request: SKRequest, didFailWithError error: NSError) {
continuation.resume(Result.failure(
PaymentException.PlatformError(error.localizedDescription, error.code)
))
}
}
start()
}
}
}
private fun handleTransaction(transaction: SKPaymentTransaction) {
when (transaction.transactionState) {
SKPaymentTransactionState.Purchased -> {
// 处理购买成功
val orderInfo = orderCache.remove(transaction.payment.productIdentifier)
// 验证购买凭证并通知服务器
verifyReceipt(transaction)
paymentQueue.finishTransaction(transaction)
}
SKPaymentTransactionState.Failed -> {
// 处理购买失败
paymentQueue.finishTransaction(transaction)
}
// 处理其他状态...
}
}
private fun verifyReceipt(transaction: SKPaymentTransaction) {
// 实现凭证验证逻辑
}
// 其他方法实现...
}
iOS支付实现利用StoreKit框架处理内购流程,需要注意:
- 产品ID必须在App Store Connect中预先配置
- 支付结果通过
SKPaymentTransactionObserver接收 - 购买凭证需要在服务端验证,防止客户端篡改
- 测试需要使用Sandbox测试账号
共享支付UI组件
Compose-Multiplatform允许我们创建跨平台的支付UI组件,以下是一个支付按钮和订单列表的实现:
// commonMain/src/PaymentUI.kt
@Composable
fun PaymentButton(
product: Product,
onClick: () -> Unit,
modifier: Modifier = Modifier,
isLoading: Boolean = false
) {
Button(
onClick = onClick,
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
enabled = !isLoading
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = MaterialTheme.colors.onPrimary,
strokeWidth = 2.dp
)
Spacer(modifier = Modifier.size(8.dp))
}
Text(
text = "支付 ¥${product.price}",
style = MaterialTheme.typography.button
)
}
}
@Composable
fun OrderHistoryList(
orders: List<Order>,
onOrderClick: (Order) -> Unit,
modifier: Modifier = Modifier
) {
LazyColumn(modifier = modifier.fillMaxSize()) {
items(orders) { order ->
OrderItem(
order = order,
onClick = { onOrderClick(order) }
)
Divider()
}
}
}
@Composable
private fun OrderItem(order: Order, onClick: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() }
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = order.subject,
style = MaterialTheme.typography.subtitle1,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.size(4.dp))
Text(
text = "订单号: ${order.orderId}",
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
)
}
Column(horizontalAlignment = Alignment.End) {
Text(
text = "¥${order.amount}",
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold
)
OrderStatusBadge(status = order.status)
}
}
}
@Composable
private fun OrderStatusBadge(status: OrderStatus) {
val (color, text) = when (status) {
OrderStatus.PENDING -> Pair(Color(0xFFFFC107), "待支付")
OrderStatus.PAID -> Pair(Color(0xFF4CAF50), "已支付")
OrderStatus.FAILED -> Pair(Color(0xFFF44336), "支付失败")
OrderStatus.REFUNDED -> Pair(Color(0xFF9E9E9E), "已退款")
}
Box(
modifier = Modifier
.background(color = color, shape = RoundedCornerShape(12.dp))
.padding(horizontal = 8.dp, vertical = 2.dp)
) {
Text(
text = text,
style = MaterialTheme.typography.caption.copy(color = Color.White),
fontSize = 12.sp
)
}
}
这些UI组件在各平台上保持一致的外观和交互,通过MaterialTheme确保符合平台设计规范。支付按钮包含加载状态,订单列表展示历史交易记录和状态。
支付状态管理
使用ViewModel管理支付相关状态,确保状态在配置变更和平台间保持一致:
// commonMain/src/PaymentViewModel.kt
class PaymentViewModel(
private val paymentService: PaymentService,
private val orderRepository: OrderRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<PaymentUiState>(PaymentUiState.Idle)
val uiState: StateFlow<PaymentUiState> = _uiState.asStateFlow()
private val _orders = MutableStateFlow<List<Order>>(emptyList())
val orders: StateFlow<List<Order>> = _orders.asStateFlow()
init {
loadOrderHistory()
}
fun loadOrderHistory() {
viewModelScope.launch {
_uiState.value = PaymentUiState.Loading
try {
val orders = orderRepository.getOrderHistory()
_orders.value = orders
_uiState.value = PaymentUiState.Success
} catch (e: Exception) {
_uiState.value = PaymentUiState.Error(e.message ?: "加载订单失败")
}
}
}
fun createPayment(orderInfo: OrderInfo) {
viewModelScope.launch {
_uiState.value = PaymentUiState.Loading
try {
val result = paymentService.createOrder(orderInfo)
result.onSuccess { orderResult ->
// 保存订单信息
orderRepository.saveOrder(
Order(
orderId = orderResult.orderId,
platformOrderId = orderResult.platformOrderId,
amount = orderInfo.amount,
subject = orderInfo.subject,
status = OrderStatus.PENDING,
createTime = System.currentTimeMillis()
)
)
_uiState.value = PaymentUiState.PaymentCreated(orderResult)
// 启动支付流程
launchPayment(orderResult)
}.onFailure { e ->
_uiState.value = PaymentUiState.Error(e.message ?: "创建订单失败")
}
} catch (e: PaymentException) {
_uiState.value = PaymentUiState.Error(e.message ?: "支付处理异常")
}
}
}
private suspend fun launchPayment(orderResult: OrderResult) {
// 根据平台特性启动支付
}
fun handlePaymentResult(data: PlatformSpecificData) {
viewModelScope.launch {
val success = paymentService.handlePaymentResult(data)
if (success) {
_uiState.value = PaymentUiState.PaymentSuccess
loadOrderHistory() // 刷新订单列表
} else {
_uiState.value = PaymentUiState.PaymentFailed("支付失败,请重试")
}
}
}
}
// UI状态密封类
sealed class PaymentUiState {
object Idle : PaymentUiState()
object Loading : PaymentUiState()
object Success : PaymentUiState()
data class Error(val message: String) : PaymentUiState()
data class PaymentCreated(val orderResult: OrderResult) : PaymentUiState()
object PaymentSuccess : PaymentUiState()
data class PaymentFailed(val message: String) : PaymentUiState()
}
平台特定配置与依赖
Android平台配置
在Android模块的build.gradle.kts中添加支付SDK依赖:
// androidApp/build.gradle.kts
dependencies {
// 支付宝SDK
implementation("com.alipay.sdk:alipaysdk-android:+")
// 微信支付SDK
implementation("com.tencent.mm.opensdk:wechat-sdk-android:+")
// 其他依赖...
}
在AndroidManifest.xml中添加必要权限和配置:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<!-- 支付宝Activity配置 -->
<activity
android:name="com.alipay.sdk.app.H5PayActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:exported="false"
android:screenOrientation="behind"
android:windowSoftInputMode="adjustResize|stateHidden" />
<!-- 微信支付回调Activity -->
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop" />
</application>
iOS平台配置
在iOS模块的Podfile中添加StoreKit依赖:
# iosApp/Podfile
target 'iosApp' do
use_frameworks!
pod 'StoreKit'
end
在Info.plist中添加内购相关配置:
<key>SKProductsUsageDescription</key>
<string>需要访问App Store以处理应用内购买</string>
支付流程测试与调试
支付功能的测试需要特别注意各平台的特殊性:
Android测试要点
- 使用支付宝和微信支付的沙箱环境
- 确保签名配置正确,否则无法调起支付
- 测试不同支付结果的回调处理
- 网络异常场景的错误处理
iOS测试要点
- 使用TestFlight或沙箱测试账号
- 在Xcode中配置正确的签名和App ID
- 产品ID必须与App Store Connect中配置一致
- 测试交易完成后的凭证验证流程
测试工具推荐
- Android Studio的App Inspection工具监控网络请求
- Xcode的Network和Debug导航器调试StoreKit交互
- Charles或Fiddler抓包分析支付请求和响应
完整支付流程示例
以下是在共享UI中集成支付功能的完整示例:
@Composable
fun ProductDetailScreen(
productId: String,
viewModel: ProductViewModel = viewModel(),
paymentViewModel: PaymentViewModel = viewModel(),
navController: NavController
) {
val productState by viewModel.productState.collectAsState()
val paymentState by paymentViewModel.uiState.collectAsState()
val context = LocalContext.current
LaunchedEffect(productId) {
viewModel.loadProduct(productId)
}
LaunchedEffect(paymentState) {
when (paymentState) {
is PaymentUiState.PaymentSuccess -> {
showToast("支付成功!")
navController.popBackStack()
}
is PaymentUiState.Error -> {
showToast((paymentState as PaymentUiState.Error).message)
}
is PaymentUiState.PaymentFailed -> {
showToast((paymentState as PaymentUiState.PaymentFailed).message)
}
else -> {}
}
}
Scaffold(
topBar = { TopAppBar(title = { Text("商品详情") }) }
) { padding ->
when (productState) {
is ProductUiState.Loading -> {
Center(Modifier.fillMaxSize()) {
CircularProgressIndicator()
}
}
is ProductUiState.Error -> {
Center(Modifier.fillMaxSize()) {
Text("加载失败,请重试")
}
}
is ProductUiState.Success -> {
val product = (productState as ProductUiState.Success).product
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
// 商品图片
ProductImage(imageUrl = product.imageUrl)
// 商品信息
ProductInfoSection(product = product)
Spacer(modifier = Modifier.weight(1f))
// 支付按钮
PaymentButton(
product = product,
isLoading = paymentState is PaymentUiState.Loading,
onClick = {
paymentViewModel.createPayment(
OrderInfo(
productId = product.id,
amount = product.price,
subject = product.name,
extra = mapOf("productType" to product.type)
)
)
}
)
Spacer(modifier = Modifier.size(16.dp))
}
}
}
}
}
总结与最佳实践
Compose-Multiplatform实现跨平台支付功能的核心在于抽象接口+平台实现的架构设计,关键最佳实践包括:
- 接口隔离平台差异:通过定义抽象接口,使共享UI层与平台实现解耦
- 状态集中管理:使用ViewModel统一管理支付状态,确保跨平台一致性
- 数据模型共享:核心数据模型在commonMain中定义,避免平台间数据转换
- 平台特性适配:充分利用expect/actual机制处理平台特定逻辑
- 错误统一处理:定义共享的异常体系,简化错误处理流程
- 安全最佳实践:敏感操作(如签名)放在平台模块,避免在共享代码中暴露密钥
通过本文介绍的方法,你可以在Compose-Multiplatform项目中高效实现跨平台支付功能,显著减少重复开发工作,同时保持各平台的原生用户体验。
相关资源
希望本文能帮助你顺利实现Compose-Multiplatform应用的支付功能。如有任何问题或建议,欢迎在项目的GitHub仓库提交issue交流讨论。
提示:支付功能涉及敏感的财务数据,务必在服务端进行订单验证和金额计算,客户端仅负责发起支付和展示结果,以确保交易安全。
Kimi-K2.5Kimi K2.5 是一款开源的原生多模态智能体模型,它在 Kimi-K2-Base 的基础上,通过对约 15 万亿混合视觉和文本 tokens 进行持续预训练构建而成。该模型将视觉与语言理解、高级智能体能力、即时模式与思考模式,以及对话式与智能体范式无缝融合。Python00- QQwen3-Coder-Next2026年2月4日,正式发布的Qwen3-Coder-Next,一款专为编码智能体和本地开发场景设计的开源语言模型。Python00
xw-cli实现国产算力大模型零门槛部署,一键跑通 Qwen、GLM-4.7、Minimax-2.1、DeepSeek-OCR 等模型Go06
PaddleOCR-VL-1.5PaddleOCR-VL-1.5 是 PaddleOCR-VL 的新一代进阶模型,在 OmniDocBench v1.5 上实现了 94.5% 的全新 state-of-the-art 准确率。 为了严格评估模型在真实物理畸变下的鲁棒性——包括扫描伪影、倾斜、扭曲、屏幕拍摄和光照变化——我们提出了 Real5-OmniDocBench 基准测试集。实验结果表明,该增强模型在新构建的基准测试集上达到了 SOTA 性能。此外,我们通过整合印章识别和文本检测识别(text spotting)任务扩展了模型的能力,同时保持 0.9B 的超紧凑 VLM 规模,具备高效率特性。Python00
KuiklyUI基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with unified codebase, ultimate ease of use, and dynamic flexibility. 注意:本仓库为Github仓库镜像,PR或Issue请移步至Github发起,感谢支持!Kotlin08
VLOOKVLOOK™ 是优雅好用的 Typora/Markdown 主题包和增强插件。 VLOOK™ is an elegant and practical THEME PACKAGE × ENHANCEMENT PLUGIN for Typora/Markdown.Less00