You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Corda支付场景下更新Invoice State和Payment State的Flow最佳实践咨询

Corda 支付场景状态更新Flow最佳实践解答

结论

最佳实践为方案2:仅创建单个updatePaymentAndInvoiceFlow,在同一Flow内同时更新Payment State和所有关联Invoice State


方案选型理由

为什么不推荐方案1

  • 存在数据一致性风险:方案1拆分为多个独立Flow执行,一旦中间环节出现异常(如网络中断、某节点拒绝签名),会出现Payment State已标记为PAID但部分关联Invoice State仍为未支付的状态不一致问题,严重违反业务逻辑。
  • 额外性能损耗:多轮Flow会触发多次签名协商、公证校验流程,节点IO和通信开销远高于单次交易。
  • 语义不匹配:「支付完成后批量更新所有关联发票状态」是一个不可拆分的原子业务操作,拆分为多个Flow会提升代码维护成本。

方案2的优势

  • 原子性保障:Corda交易天生具备ACID特性,单次交易内的所有状态更新要么全部生效要么全部回滚,从底层避免数据不一致问题。
  • 执行效率更高:仅需一次签名收集、一次公证校验,流程耗时更短。
  • 可维护性更强:单个Flow对应完整业务逻辑,代码语义清晰,后续迭代调整成本更低。

核心实现示例

// 定义交易命令,标记本次为支付+发票批量更新操作
class UpdatePaymentAndInvoiceCommand : TypeOnlyCommandData()

/**
 * 发起方Flow:入参为付款批次ID
 */
@InitiatingFlow
@StartableByRPC
class UpdatePaymentAndInvoiceFlow(private val paymentBatchId: String) : FlowLogic<SignedTransaction>() {
    override fun call(): SignedTransaction {
        val notary = serviceHub.networkMapCache.notaryIdentities.first()
        
        // 1. 查找对应付款批次的Payment State输入
        val paymentStateAndRef = serviceHub.vaultService.queryBy<PaymentState>()
            .states.single { it.state.data.batchId == paymentBatchId }
        val inputPayment = paymentStateAndRef.state.data
        check(inputPayment.status != "PAID") { "该付款批次已完成支付" }
        
        // 2. 查找该批次关联的所有Invoice State输入
        val invoiceStateAndRefs = serviceHub.vaultService.queryBy<InvoiceState>()
            .states.filter { it.state.data.relatedPaymentBatchId == paymentBatchId }
        check(invoiceStateAndRefs.isNotEmpty()) { "该付款批次未关联任何发票" }
        
        // 3. 构建更新后的输出状态
        val paidPayment = inputPayment.copy(status = "PAID")
        val paidInvoices = invoiceStateAndRefs.map { it.state.data.copy(status = "PAID") }
        
        // 4. 组装交易
        val txBuilder = TransactionBuilder(notary)
            .addInputState(paymentStateAndRef)
            .apply { invoiceStateAndRefs.forEach { addInputState(it) } }
            .addOutputState(paidPayment, PaymentContract.ID)
            .apply { paidInvoices.forEach { addOutputState(it, InvoiceContract.ID) } }
            .addCommand(
                UpdatePaymentAndInvoiceCommand(),
                // 收集所有需要签名的参与方公钥,按需调整
                (paidPayment.participants + paidInvoices.flatMap { it.participants }).distinct().map { it.owningKey }
            )
        
        // 5. 标准交易流程:验证->自签->收集对方签名->公证->最终落地
        txBuilder.verify(serviceHub)
        val selfSignedTx = serviceHub.signInitialTransaction(txBuilder)
        val otherSingers = (paidPayment.participants + paidInvoices.flatMap { it.participants }).distinct() - ourIdentity
        val sessions = otherSingers.map { initiateFlow(it) }
        val fullySignedTx = subFlow(CollectSignaturesFlow(selfSignedTx, sessions))
        return subFlow(FinalityFlow(fullySignedTx, sessions))
    }
}

/**
 * 响应方Flow
 */
@InitiatedBy(UpdatePaymentAndInvoiceFlow::class)
class UpdatePaymentAndInvoiceResponderFlow(private val counterpartySession: FlowSession) : FlowLogic<SignedTransaction>() {
    override fun call(): SignedTransaction {
        val signedTx = subFlow(object : SignTransactionFlow(counterpartySession) {
            override fun checkTransaction(stx: SignedTransaction) = requireThat {
                // 自定义交易校验规则,按需扩展
                val outputPayment = stx.coreTransaction.outputsOfType<PaymentState>().single()
                val outputInvoices = stx.coreTransaction.outputsOfType<InvoiceState>()
                "Payment状态必须更新为PAID" using (outputPayment.status == "PAID")
                "所有关联发票状态必须更新为PAID" using (outputInvoices.all { it.status == "PAID" })
                "发票关联付款批次匹配" using (outputInvoices.all { it.relatedPaymentBatchId == outputPayment.batchId })
            }
        })
        return subFlow(ReceiveFinalityFlow(counterpartySession, expectedTxId = signedTx.id))
    }
}

内容的提问来源于stack exchange,提问作者Ariya Kluankloi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 12:33:03