Electron inAppPurchase 的 Transaction 对象详解:字段语义、状态机与源码级数据流

发布时间:2026/9/7 1:55:22
Electron inAppPurchase 的 Transaction 对象详解:字段语义、状态机与源码级数据流 Electron inAppPurchase 的 Transaction 对象详解字段语义、状态机与源码级数据流【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electronTransaction是 ElectroninAppPurchase模块Mac App Store 应用内购买中描述一笔交易的核心数据结构。本文基于 docs/api/structures/transaction.md 完整解析其全部字段与嵌套对象并结合 shell/browser/mac/in_app_purchase_observer.mm、shell/browser/api/electron_api_in_app_purchase.cc 等源码说明每个字段在 StoreKit 回调中的真实来源、状态取值与 ISO 日期格式细节帮助你在主进程中正确监听并处理transactions-updated事件。Transaction 对象在 API 中的位置inAppPurchase模块运行于主进程其完整 API 见 in-app-purchase 模块文档。Transaction对象主要通过以下路径到达你的业务代码事件驱动监听inAppPurchase模块的transactions-updated事件回调签名为(event, transactions)其中transactions即为Transaction[]数组。文档特别强调应当尽早且务必在调用purchaseProduct之前注册该事件监听否则会丢失早期的交易更新。模块内部发射从源码看InAppPurchase类实现了OnTransactionsUpdated方法直接调用Emit(transactions-updated, transactions)见 electron_api_in_app_purchase.cc#L223-L226。因此Transaction是异步购买流程中你唯一能感知购买成功 / 失败 / 恢复 / 延迟的信使对象。顶层字段逐一解析原文档对Transaction的定义共 6 个顶层字段加 1 个嵌套对象下表逐一说明并补充源码中确认的语义细节字段类型说明源码级来源transactionIdentifierstring唯一标识一笔成功支付交易的字符串SKPaymentTransaction.transactionIdentifier的 UTF-8 转换transactionDatestring该交易被加入 App Store 支付队列的日期ISO 8601 格式由NSDate经dateToISOString:格式化originalTransactionIdentifierstringApp Store 所恢复交易的原始标识符transaction.originalTransaction.transactionIdentifier仅当存在originalTransaction时非空transactionStatestring交易状态取值purchasing/purchased/failed/restored/deferred之一由 StoreKit 状态枚举索引 0–4 映射而来errorCodeInteger交易处理过程中发生错误时的错误码transaction.error.code仅当存在error时写入errorMessagestring交易处理过程中发生错误时的错误信息transaction.error.localizedDescriptionpaymentObject支付详情见下文嵌套对象章节SKPayment对象转换结果几个值得注意的细节transactionDate是 ISO 8601 字符串而非时间戳。源码中 dateToISOString: 使用en_US_POSIXlocale 与yyyy-MM-ddTHH:mm:ssZZZZZ格式生成该字符串。这也解释了 in-app-purchase 模块文档 中finishTransactionByDate(date)为何要求传入ISO formatted date——两者使用的是同一格式约定。originalTransactionIdentifier的可选性从 skPaymentTransactionToStruct: 的实现看只有当 StoreKit 提供了originalTransaction典型场景为恢复购买或订阅续订时才填充该字段普通新购买时该字段为空字符串C 结构体默认值见 in_app_purchase_observer.h#L48-L60。错误字段的填充时机errorCode与errorMessage仅在 StoreKit 报告error非空时写入通常伴随failed状态出现。正常情况下这两个字段为默认值0与空字符串。payment嵌套对象payment描述该笔交易对应的支付请求内容字段如下字段类型说明productIdentifierstring所购买产品的标识符与你在 App Store Connect 配置、并传给purchaseProduct(productID)的 ID 对应quantityInteger购买数量。C 结构体中默认值为1见 Payment 定义且只有当 StoreKit 报告的quantity 1时才会覆盖默认值applicationUsernamestring用于标识用户在你的系统中账户的不透明字符串。它对应inAppPurchase.purchaseProduct(productID, { quantity, username })中username选项的落点——Apple 官方建议用它关联交易与你的服务端用户paymentDiscountPaymentDiscount可选应用于本次支付的折扣优惠详情仅在 StoreKit 提供了paymentDiscount时出现从转换代码 skPaymentToStruct: 可以看到所有字段均做了非 nil 判断后才拷贝到结构体保证 JavaScript 侧拿到的对象形态稳定。paymentDiscount字段PaymentDiscount是一个可选嵌套对象完整定义见 docs/api/structures/payment-discount.md包含 5 个字段identifierstring - 唯一标识该产品折扣优惠的字符串。keyIdentifierstring - 标识用于生成签名的 key 的字符串。noncestring - 由你定义的通用唯一标识UUID值。signaturestring - 代表特定折扣优惠属性、经加密签名的 UTF-8 字符串。timestampnumber - 签名创建时间的日期与时间以毫秒表示的 Unix epoch 时间。源码侧对应 PaymentDiscount 结构体 与 skPaymentDiscountToStruct:其中nonce在 C 侧直接取自UUIDStringtimestamp取intValue即 Unix epoch 秒级数值。transactionState状态机五种取值及其来源文档规定transactionState可取purchasing、purchased、failed、restored或deferred。源码中这一字段并非自由字符串而是对 StoreKit 状态枚举索引的白名单映射if (transaction.transactionState 5) { transactionStruct.transactionState base::SysNSStringToUTF8( [[ purchasing, purchased, failed, restored, deferred ] objectAtIndex:transaction.transactionState]); }见 in_app_purchase_observer.mm#L176-L180即索引 0–4 分别对应上述五个值与 SKPaymentTransactionState 的标准顺序一致。实际业务判断建议purchasing支付流程进行中用户正在与 StoreKit 交互不应做资源发放purchased/restored需要向服务端验证收据并解锁内容restored出现在调用restoreCompletedTransactions()之后且此时originalTransactionIdentifier通常有值failed读取errorCode/errorMessage给用户展示失败原因deferred交易被延迟例如家长控制待批准后续状态更新可能再次触发transactions-updated。数据流全景从 StoreKit 回调到 JavaScript 对象完整的数据链路如下每一环都有对应的源码文件可查证StoreKit 回调InAppTransactionObserver实现了SKPaymentTransactionObserver协议的paymentQueue:updatedTransactions:方法在初始化时通过[[SKPaymentQueue defaultQueue] addTransactionObserver:self]注册到默认支付队列见 in_app_purchase_observer.mm#L36-L60。结构体转换runCallback:将每个SKPaymentTransaction*经skPaymentTransactionToStruct:转为in_app_purchase::TransactionC 结构体并把回调投递到浏览器线程content::GetUIThreadTaskRunner。JS 模块发射electron::api::InAppPurchase::OnTransactionsUpdated收到std::vectorin_app_purchase::Transaction后通过Emit(transactions-updated, transactions)触发事件。V8 字典序列化事件参数经 gin 转换器 Converterin_app_purchase::Transaction::ToV8 逐项写入 JS 对象键名与文档字段名一一对应transactionIdentifier、transactionDate、originalTransactionIdentifier、transactionState、errorCode、errorMessage、paymentpayment内嵌的paymentDiscount仅在有值时才dict.Set从而在 JS 侧呈现为可选字段。实战示例监听并处理交易结合 in-app-purchase 模块文档 的方法签名一个典型的主进程使用模式如下仅示意需在你的 macOS App Store 应用中配合 App Store Connect 产品配置使用const { inAppPurchase } require(electron); // 文档强调务必尽早注册监听且必须在调用 purchaseProduct 之前完成 inAppPurchase.on(transactions-updated, (event, transactions) { for (const transaction of transactions) { switch (transaction.transactionState) { case purchased: case restored: { // transaction.transactionIdentifier服务端收据验证的唯一凭证 // transaction.payment.productIdentifier解锁对应商品 // transaction.payment.applicationUsername关联你的用户体系 console.log( ${transaction.transactionState} ${transaction.payment.productIdentifier} x${transaction.payment.quantity} ${transaction.transactionDate} ); // 生产环境中将 transactionIdentifier 送服务端验证后解锁内容 // 最后调用 inAppPurchase.finishTransactionByDate(transaction.transactionDate) break; } case failed: console.error(购买失败 [${transaction.errorCode}]: ${transaction.errorMessage}); break; case deferred: console.warn(交易延迟等待后续 transactions-updated 更新); break; default: // purchasing break; } } }); // 发起购买Promise 返回 true 表示产品有效并已加入支付队列 inAppPurchase.purchaseProduct(com.example.product, { quantity: 1, username: local-user-42 // 落点到 transaction.payment.applicationUsername }).then((added) console.log(added to queue:, added)); // 恢复购买后可用 ISO 日期精确完成某笔交易与 transactionDate 同格式 // inAppPurchase.restoreCompletedTransactions(); // inAppPurchase.finishTransactionByDate(2025-01-01T12:00:0008:00); // 或一次性完成所有待处理交易 // inAppPurchase.finishAllTransactions();要点回顾finishTransactionByDate的入参格式与transactionDate输出格式一致ISO 8601可直接将事件里的transactionDate回传交易完成finish是 Apple 要求的收尾动作未 finish 的交易在下次启动时会重新进入支付队列并再次触发transactions-updatedpurchaseProduct的 Promise 只表示产品有效且已入队不代表购买成功成功与否必须通过Transaction的transactionState判断。适用前提与平台限制仅 macOS App Store 应用可用从源码结构看electron_api_in_app_purchase.cc中整个实现被#if BUILDFLAG(IS_MAC)包裹见 electron_api_in_app_purchase.cc#L141-L243模块的Initialize也仅在 macOS 构建时向exports注入inAppPurchase。非 macOS 平台上不存在该模块依赖 Mac App Store 分发应用内购买依赖SKPaymentQueue即应用需经 Mac App Store 分发并配置好产品标识符主进程 APIinAppPurchase仅在 Main 进程可用渲染进程若需交互应通过 IPC 与主进程协作可参考 glossary 对进程模型的说明。小结Transaction对象是 Electron 应用内购买流程中连接 StoreKit 与 JavaScript 世界的契约顶层 6 个字段刻画交易的身份、时间与状态payment嵌套对象刻画购买的商品细节paymentDiscount作为可选项承载折扣签名信息。源码确认了日期采用 ISO 8601、状态取值来自固定五值白名单、可选字段在无值时缺省等行为这些细节与文档定义完全一致可作为你设计购买状态处理逻辑与finishTransactionByDate收尾流程的可靠依据。【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考