Seamless SDK (iOS 支付)
在本页中,您将找到在 iOS 项目中添加、配置和使用 Seamless iOS SDK 进行支付的所有步骤。
推荐的 SDK我们建议使用iOSSeamless SDK,以获得流畅的集成体验。该选项提供了灵活的支付解决方案,预置了用户界面组件和自定义选项。
第 1 步:在项目中加入程序库
您可以使用 CocoaPods 或 Swift 软件包管理器添加该库。
CocoaPods
要在 iOS 项目中添加 Yuno SDK,您需要安装 Yuno SDK。如果您没有 Podfile,请按照CocoaPods 指南创建一个。创建 Podfile 后,在 Podfile 中添加以下一行,即可将 Yuno SDK 与 Cocoapods 整合。
pod "YunoSDK","~> 1.19.0之后,您需要运行安装程序:
吊舱安装Swift 软件包管理器
如果使用Swift 软件包管理器,请将 Yuno SDK 添加为依赖项,如下代码块所示:
依赖项:[
.package(url:"https://github.com/yuno-payments/yuno-sdk-ios.git", .upToNextMajor(from: "1.1.17"))
]步骤 2:使用公钥Initialize SDK
要开始运行 Yuno iOS 无缝结账,您首先需要获取 Yuno 应用程序 ID 和公共 API 密钥。然后,导入并initialize Yuno,如下代码片段所示:
import YunoSDK
Yuno.initialize(
apiKey: "PUBLIC_API_KEY",
config: YunoConfig(),
callback: { (value: Bool) in }
)
UISceneDelegate如果您的应用程序使用
UISceneDelegate,Yuno 的初始化代码被放置在您的SceneDelegate.
无缝签出可让您配置 SDK 的外观。这是一个可选步骤,您可通过类 YunoConfig.要设置配置,请使用以下代码块配置可用元素:
final class YunoConfig {
let cardFormType: CardFormType,
let appearance: Yuno.Appearance,
let saveCardEnabled: Bool,
let keepLoader: Bool
}使用以下选项配置 SDK:
| 参数 | 说明 |
|---|---|
cardFormType | 该字段可用于选择 Payment 和 Enrollment Card 流量。这是一个可选属性。它使用 .oneStep 选项。 |
appearance | 这个可选字段定义了结账的外观。默认情况下,它使用 Yuno 风格。 |
saveCardEnabled | 这个可选字段可让您选择是否在卡片流上显示保存卡片复选框。默认为假。 |
keepLoader | 该可选字段可控制何时隐藏加载器。如果设置为 true,"...... hideLoader() 函数才能隐藏加载器。默认情况下,它被设置为 false。 |
hideCardholderName | 此可选字段允许您在卡片表单中隐藏持卡人姓名字段。当设置为 true持卡人姓名字段未显示。当未指定或设置为 false持卡人姓名字段将显示(默认行为)。隐藏该字段不会影响主卡号、有效期、CVV验证码收集、BIN逻辑或3DS/支付机构验证。当支付机构要求提供持卡人姓名时,商户有责任确保该信息被提交。 |
访问 API 密钥您可以从 Yuno 控制面板的 "开发人员 "部分获取您的 API 密钥。
创建结账会话
在开始付款流程之前,您需要创建一个 checkout_session 使用 创建结账会话 endpoint。该会话将初始化支付流程,并将在下一步中使用。
通过发送控制认证与捕获
payment_method.detail.card.capture在结账环节:false= 仅授权,true= 立即捕获。
关键参数
| 参数 | 需要 | 说明 |
|---|---|---|
amount | 是 | 主交易金额对象包含 currency (ISO 4217 代码)和 value (该货币的数字金额)。 |
workflow | 是 | 将值设为 SDK_SEAMLESS 以便 SDK 能正确完成支付流程。 |
alternative_amount | 没有 | 交易金额的另一种货币表示形式,其结构与 amount (currency 和 value).适用于多币种场景,例如以客户偏好的货币(如美元)显示价格,同时以当地货币(如 COP)处理付款。 |
步骤3:开始结账和付款流程
只需使用一种方法即可启动无缝结账和付款流程 startPaymentSeamlessLite.在 ViewController在显示 Yuno 的地方,调用 Yuno.startPaymentSeamlessLite() 方法。您可以使用 async/await 或回调来使用该方法:
funcstartPaymentSeamlessLite(
with params:SeamlessParams、
paymentSelected:PaymentMethodSelected、
showPaymentStatus:Bool = true
) async -> 结果funcstartPaymentSeamlessLite(
with params:SeamlessParams、
paymentSelected:PaymentMethodSelected、
showPaymentStatus:Bool = true、
callback:@escaping ((Result) -> Void)
)无缝版本需要其他参数。这些参数包括
PaymentMethodSelected:金库token 和/或客户将使用的付款方式。
protocol PaymentMethodSelected {
var vaultedToken: String? { get }
var paymentMethodType: String { get }
}SeamlessParams
class SeamlessParams {
var checkoutSession: String
var country_code: String
var language: String?
var viewController: UIViewController?
}参数
下表列出了以下每个参数的说明 SeamlessParams:
| 参数 | 说明 |
|---|---|
checkoutSession | 指当前付款的结账会话。 |
country_code | 该参数决定了正在配置支付流程的国家。支持国家及其国家代码的完整列表可在 "国家覆盖范围"页面查看。 |
language | 定义付款表单中使用的语言。您可以将其设置为可用语言选项之一:
|
viewController | 该属性表示 UIViewController 用于显示支付流程。尽管为了实现向后兼容性,该属性仍然是可选的,但您必须提供一个可见控制器,以便 SDK 能正确显示其用户界面。 |
Swift 6 并发要求如果使用 Swift 6,则需要执行
YunoPaymentDelegate协议的具体并发考虑因素。Swift 6 引入了更严格的线程安全要求,这将影响您实现委托的方式。请参见 实施YunoPaymentDelegate使用 Swift 6 并发 部分,了解详细的实施方案和最佳做法。
步骤4:处理支付状态(可选)
深度链接和 Mercado Pago Checkout Pro只有在使用依赖深度链接的支付方式或 Mercado Pago Checkout Pro 时,才需要执行此步骤。如果您的付款方式不使用深度链接,您可以跳过这一步。
有些支付方法会让用户离开您的应用程序来完成交易。支付完成后,用户会通过深层链接重定向回到您的应用程序。SDK 会使用此深度链接检查发生了什么,检查支付是否成功、失败或取消,并向用户显示状态屏幕。
为此,您需要更新您的 AppDelegate 将输入的 URL 传回 Yuno SDK。这样,SDK 就能读取结果并显示支付状态。下面的代码片段展示了如何将其添加到您的应用程序中:
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
guard url.scheme == "yunoexample" else { return false }
return Yuno.receiveDeeplink(url, showStatusView: true)
}这段代码会侦听打开应用程序的深度链接。当收到 URL 时,它会检查该方案是否与您在 callback_url 在结账会话设置期间。如果匹配,URL 将通过以下方式传递给 Yuno SDK Yuno.receiveDeeplink(...).然后,SDK 会读取支付结果,如果 showStatusView 设置为 true,向用户显示相应的状态屏幕。
确保 url.scheme 代码中的 callback_url 时提供的 checkout_session.
交易状态
支付完成后,SDK 可以返回不同的交易状态。下表列出了所有可能的状态及其说明:
| 交易状态 | 说明 |
|---|---|
success | 表示交易或payment 流程已成功完成。 |
fail | 该状态表示交易或支付流程失败。这意味着在支付过程中出现了错误或问题,导致无法成功完成支付。 |
processing | 表示交易或付款流程正在处理中。通常在付款处理出现延迟时使用,如等待第三方服务或金融机构的批准。 |
reject | 这种状态表示交易被拒绝。拒绝的原因有多种,如资金不足、欺诈活动或违反特定规则或政策的请求。 |
internalError | 这意味着处理支付流程的系统或基础设施出现了意外错误。这种状态表明服务器或后台出现了问题,而不是用户的输入或请求出现了问题。 |
userCancell | 这种状态表示用户自愿取消或放弃交易或支付流程。它通常用于用户可以选择取消或放弃付款流程的情况。 |
付款状态验证
本节说明当用户取消或退出支付流程时,SDK如何处理支付状态,以及在这些场景中SDK状态与后端支付状态之间的关联关系。
同步支付方式(Apple Pay)
对于Apple Pay等同步支付方式,当用户在收到支付服务提供商(PSP)响应前取消或关闭钱包界面时:
- SDK状态: 返回
userCancell(CANCELLED) - 后端支付状态:遗骸
PENDING直至PSP超时或商户取消 - 重要SDK 将不返回
reject或processing在此情境下
这确保后端支付保持待处理状态,并能由商户系统妥善处理。
异步支付方式(PIX及基于二维码的支付方式)
对于PIX等异步支付方式,当用户在完成支付前关闭二维码窗口(点击X)时:
- SDK状态: 返回
PENDING,可选地带有子状态,例如CLOSED_BY_USER - 后端支付状态:遗骸
PENDING且二维码在到期前始终有效 - 结账会话复用:重新打开相同的结账会话可显示相同的有效二维码
- 不自动取消:当用户关闭二维码窗口时,PIX支付不会自动取消
此功能允许用户在二维码失效前返回支付流程,使用同一二维码完成交易。
已过期的异步支付
如果PIX二维码自然过期:
- 后端状态更新至
EXPIRED - SDK状态SDK 回调和轮询endpoints 点返回
EXPIRED始终如一地
这确保了当支付方式过期时,商户能够收到准确的状态信息。
在使用 startPaymentSeamlessLite 方法:
- 异步/等待:使用异步/等待方法可使流程更加精简。该方法以异步方式返回结果,使代码更易于阅读和管理。
- 回调:您可以通过回调函数处理事务状态,以便在结果可用时立即执行。
这两个选项都提供了灵活性,具体取决于您对异步代码的偏好。
枚举结果 {
case reject, success, fail, processing,internalError,userCancell
}补充功能
Yuno iOS SDK 提供额外的服务和配置,您可以用来改善客户体验。使用SDK 定制功能可更改 SDK 外观,使其与您的品牌相匹配,或配置加载器。
- 加载器通过 SDK 配置选项控制加载器的使用。
- 保存贺卡,以便将来付款:此外,您还可以显示一个复选框,用于使用以下功能保存或注册卡片
cardSaveEnable: true.下面是两种卡片表单复选框的示例。
- 你还可以为卡片形式选择其中一种渲染选项。下面的截图展示了
cardFormTypeONE_STEP和STEP_BY_STEP.
- SDK 定制:更改 SDK 外观,使其与您的品牌相匹配。
实施 YunoPaymentDelegate 使用 Swift 6 并发
YunoPaymentDelegate 使用 Swift 6 并发Swift 6 引入了更严格的并发性要求,这会影响您如何实现 YunoPaymentDelegate 协议。本节将解释这些挑战,并针对不同的实施方案提供解决方案。
了解 Swift 6 中的并发性并发性是指应用程序同时管理多个任务的能力。在 Swift 6 中,并发规则变得更加严格,以增强应用程序的稳定性并防止崩溃。这意味着您的代码结构必须更加严谨,以确保线程安全和适当的任务管理。
问题
在 Swift 6 中,继承自 Sendable 要求其所有实现都是线程安全的。当在标记为 @MainActor.
线程安全意味着你的代码可以被多个线程安全调用,而不会导致崩溃或意外行为。 @MainActor 确保代码在主线程(UI 线程)上运行。
我们的设计决定
我们不会将协议标记为 @MainActor 因为
- 这将迫使所有实现
MainActor兼容的 - 这将降低不使用以下功能的商家的灵活性
MainActor - 每种执行方式都有不同的并发需求
商家的责任
商家有责任根据自己的实施情况处理并发问题。以下是三种不同的方法,您可以根据自己的具体需求加以使用。
方案 1:不可变属性
这种方法使用不可变属性,可自动保证线程安全,是简单配置的理想选择。它最适用于具有固定配置值且在运行时不会改变的简单应用程序。
@MainActor
class MyViewController: UIViewController, YunoPaymentDelegate {
private let _countryCode = "CO"
private let _language = "EN"
nonisolated var countryCode: String { _countryCode }
nonisolated var language: String? { _language }
nonisolated var checkoutSession: String { _checkoutSession }
nonisolated func yunoPaymentResult(_ result: Yuno.Result) {
Task { @MainActor in
}
}
}方案 2:可变属性 MainActor.assumeIsolated
MainActor.assumeIsolated这种方法最适用于配置值可能在运行时发生变化的应用程序(如用户偏好设置),通过使用 MainActor.assumeIsolated.
@MainActor
class MyViewController: UIViewController, YunoPaymentDelegate {
@Published var configLanguage: String = "EN"
@Published var configCountryCode: String = "CO"
nonisolated var language: String? {
MainActor.assumeIsolated { configLanguage }
}
nonisolated var countryCode: String {
MainActor.assumeIsolated { configCountryCode }
}
}方案 3:对于非 MainActor 班级
MainActor 班级这种方法适用于不需要 MainActor 隔离,因此最适合不与用户界面交互的后台服务或实用程序类。
class MyService: YunoPaymentDelegate {
let countryCode: String
let language: String?
let checkoutSession: String
let viewController: UIViewController?
init(countryCode: String, language: String?, checkoutSession: String, viewController: UIViewController?) {
self.countryCode = countryCode
self.language = language
self.checkoutSession = checkoutSession
self.viewController = viewController
}
func yunoPaymentResult(_ result: Yuno.Result) {
}
}⚠️ 重要考虑因素
在委托中实现并发时,请牢记以下要点:
MainActor.assumeIsolated:仅在保证从MainActor.这是一种安全机制,它告诉 Swift "相信我,我知道这是在主线程上运行的"。nonisolated:表示可以从任何线程访问,因此必须是线程安全的。当您的属性或方法不依赖于用户界面状态时,请使用此属性或方法。viewController:仍为@MainActor因为它应始终由主线程访问。用户界面组件必须始终在主线程上运行,以防止崩溃。
26 天前已更新