Online Payments Swift SDK
Online Payments Swift SDK 有助于您在 iOS 应用中接受支付,支持 iOS 11.0 及以上版本,通过 Online Payments 平台。
SDK 的主要功能是在您的 iOS 应用和我们的服务器之间建立一个安全通道。该通道处理安全凭据,以确保支付过程中客户数据的传输安全。
Online Payments SDK 有助于您:
- 处理支付上下文的加密
- API 响应的便捷 Swift 包装
- 支付数据的用户友好格式化,例如卡号和有效期
- 验证用户输入
- 确定与卡关联的支付提供程序
目录
安装
Online Payments Swift SDK 通过以下包管理器可用:CocoaPods、Carthage 或 Swift Package Manager。
CocoaPods
您可以将 Swift SDK 作为 pod 添加到项目中,方法是在您的 Podfile
中添加以下内容
$ pod 'OnlinePaymentsKit'
之后,运行以下命令
$ pod install
Carthage
您可以用 Carthage 将 Swift SDK 添加到您的项目,方法是在您的 Cartfile
中添加以下内容
$ github "wl-online-payments-direct/sdk-client-swift"
之后,运行以下命令
$ carthage update --use-xcframeworks
导航到 Carthage/Build
目录,该目录与 .xcodeproj
或 .xcworkspace
文件在同一目录中。在此目录中存储着 .xcframework
包。将 .xcframework
拖放到目标中“框架、库和嵌入式内容”部分。确保将“嵌入并签名”设置为。
Swift Package Manager
您可以使用 Swift Package Manager 将 Swift SDK 添加到您的项目,方法如下配置
- 转到您项目的设置,并点击“包依赖”标签
- 点击“+”添加新的 Swift 包依赖项
- 在搜索栏中输入 Github URL:
https://github.com/wl-online-payments-direct/sdk-client-swift
- 此外,您还可以设置要包含的包版本。默认选项是选择主分支中的最新版本。
- 点击“添加包”。
当包成功添加后,它将被自动添加为您的目标依赖项。
Objective-C 兼容性
您还可以使用 CocoaPods 或 Carthage 在 Objective-C 项目中使用 Online Payments Swift SDK。一旦您将 Online Payments Kit 添加为依赖项,您可以通过添加以下导入语句轻松使用它: @import OnlinePaymentsKit;
示例应用
为了方便您使用,我们还提供了一个示例应用,该应用可以作为您的自定义实现的依据。如果您对示例应用的界面和感受满意,您无需做出任何更改。示例应用可以在这里找到。
入门
要使用 SDK 接受您的第一笔支付,请完成以下步骤。有关这些步骤的更多详细信息,请参阅支付步骤部分。
- 请求您的服务器使用我们提供的可用 Server SDK 之一创建客户端会话。将会话详情返回到您的应用。
- 使用会话详情初始化 SDK。
let session = Session(
clientSessionId: "47e9dc332ca24273818be2a46072e006",
customerId: "9991-0d93d6a0e18443bd871c89ec6d38a873",
baseURL: "https://clientapi.com",
assetBaseURL: "https://assets.com",
appIdentifier: "Swift Example Application/v2.0.4"
)
- 配置您的支付上下文。
let amountOfMoney = PaymentAmountOfMoney(
totalAmount: 1298, // in cents
currencyCodeString: "EUR" // three letter currency code as defined in ISO 4217
)
let paymentContext = PaymentContext(
amountOfMoney: amountOfMoney,
isRecurring: false, // true, if it is a recurring payment
countryCodeString: "NL" // two letter country code as defined in ISO 3166-1 alpha-2
)
- 检索可用的支付产品。显示
BasicPaymentItem
和AccountOnFile
列表,并要求您的客户选择一项。
session.paymentItems(
for: paymentContext,
groupPaymentProducts: false,
success: {(_ paymentItems: PaymentItems) ->
// Display the contents of paymentItems & accountsOnFile to your customer
)},
failure: { _ in
// Inform the customer that something went wrong while retrieving the available Payment Products
}
)
- 一旦客户选择了所需的支付产品,检索详细的
PaymentProduct
(包括客户需要提供什么信息以授权支付)。向客户显示所需的信息字段。
session.paymentProduct(
withId: "1",
context: paymentContext,
success: {(_ paymentProduct: PaymentProduct) ->
// Display the fields to your customer
)},
failure: { _ in
// Handle failure of retrieving a Payment Product by id
}
)
- 在
PaymentRequest
中保存客户对所需信息字段的输入。
let paymentRequest = PaymentRequest()
paymentRequest.setValue(forField: "cardNumber", value: "1245 1254 4575 45")
paymentRequest.setValue(forField: "cvv", value: "123")
paymentRequest.setValue(forField: "expiryDate", value: "12/25")
- 验证并加密支付请求。然后应将加密的客户数据发送到您的服务器。
session.prepare(
paymentRequest,
success: {(_ preparedPaymentRequest: PreparedPaymentRequest) ->
// Forward the encryptedFields to your server
)},
failure: { _ in
// Handle failure of encrypting Payment Request
}
)
- 使用 Server API 的“创建支付”调用,请求您的服务器创建支付请求。在
encryptedCustomerInput
字段中提供加密数据。
类型定义
会话
与SDK的所有交互都需要一个Session
实例。以下代码片段展示了如何初始化Session
。会话详情是通过使用服务器API执行创建客户端会话调用获得的。
let session = Session(
clientSessionId: "47e9dc332ca24273818be2a46072e006",
customerId: "9991-0d93d6a0e18443bd871c89ec6d38a873",
baseURL: "https://clientapi.com",
assetBaseURL: "https://assets.com",
appIdentifier: "Swift Example Application/v2.0.4",
loggingEnabled: true
)
Session
提供的大部分方法都是围绕客户端API的简单封装。它们执行请求并将响应转换为包含便利函数的Swift对象。
请求和响应的日志记录
您可以记录发送到服务器的请求和从服务器收到的响应。默认情况下,日志记录是禁用的,在生产环境中始终禁用日志记录非常重要。您可以通过两种方式启用日志记录。要么在创建会话时设置其值,如上代码片段所示,要么在会话已创建后设置其值。
session.loggingEnabled = true
PaymentContext
PaymentContext
是一个包含即将进行的支付上下文/设置的实体。它是传递给Session
实例的一些方法参数所必需的。此对象可能包含以下详情
public class PaymentContext {
var countryCodeString: String // ISO 3166-1 alpha-2 country code
var locale: String // IETF Language Tag + ISO 15897, example: 'nl_NL'
var amountOfMoney: PaymentAmountOfMoney // contains the total amount and the ISO 4217 currency code
var isRecurring: Bool // Set `true` when payment is recurring. Default false.
}
支付项
该对象包含了当前支付可用的支付项。使用session.paymentItems
函数来请求数据。
您将接收的对象是PaymentItems
,它包含三个列表。一个包含所有可用的BasicPaymentItem
,一个包含所有分组的BasicPaymentItem
,还有一个包含所有AccountOnFile
。
下方的代码片段展示了如何获取PaymentItems
实例。
session.paymentItems(
for: paymentContext,
groupPaymentProducts: false,
success: {(_ paymentItems: PaymentItems) ->
// Display the contents of paymentItems & accountsOnFile to your customer
)},
failure: { _ in
// Inform the customer that something went wrong while retrieving the available Payment Products
}
)
基本支付产品
SDK提供了两种数据类型表示支付产品信息:BasicPaymentProduct
和PaymentProduct
。实际上,BasicPaymentProduct
的实例仅包含用于显示支付产品列表并供客户选择的必要信息。
PaymentProduct
类型包含额外信息,例如客户需要填充的具体表单字段。这种类型通常用于创建询问客户相关信息的表单。有关更多信息,请参阅PaymentProduct部分。
以下是如何获取Visa产品的显示名称和资产示例。
let basicPaymentProduct = paymentItems.paymentItem(withIdentifier: "1")
let id = basicPaymentProduct.identifier // 1
let label = basicPaymentProduct.displayHintsList.first?.label // VISA
let logoPath = basicPaymentProduct.displayHintsList.first?.logoPath // https://assets.com/path/to/visa/logo.gif
账户信息
AccountOnFile
的实例代表了当前客户存储的卡产品信息。在创建客户端会话时,必须提供当前支付可用的AccountOnFile
ID作为服务API请求体中的参数。如果客户希望使用现有AccountOnFile
进行支付,则应将选定的AccountOnFile
添加到PaymentRequest
中。下方的代码片段展示了如何检索账户信息的显示数据。此标签可以与相应支付产品的标志一起显示给客户。
// All available accounts on file for the payment product
let allAccountsOnFile = basicPaymentProduct.accountsOnFile.accountsOnFile
// Get specific account on file for the payment product
let accountOnFile = basicPaymentProduct.accountOnFile(withIdentifier: "123")
// Shows a mask based formatted value for the obfuscated cardNumber.
// The mask that is used is defined in the displayHints of this accountOnFile
// If the mask is {{9999}} {{9999}} {{9999}} {{9999}} {{999}}, then the result would be **** **** **** 7412
let maskedValue = accountOnFile.maskedValue(forField: "cardNumber")
支付产品
BasicPaymentProduct
仅包含客户区分一个支付产品与其他支付产品所需的信息。然而,一旦选择了支付产品或文件中的账户,客户必须提供额外的信息,例如银行账户号码、信用卡号码或到期日,才能处理支付。每个支付产品可能需要填写几个字段以进行支付。BasicPaymentProduct
的实例不包含关于这些字段的信息。
支付产品字段的信息由 PaymentProductField
实例表示,这些实例包含在 PaymentProduct
实例中。下文将详细描述 PaymentProductField
类。可以像以下代码片段中所示使用 Session
实例检索 PaymentProduct
实例。
session.paymentProduct(
withId: "1",
context: paymentContext,
success: {(_ paymentProduct: PaymentProduct) ->
// Display the fields to your customer
)},
failure: { _ in
// Handle failure of retrieving a Payment Product by id
}
)
PaymentProductField
支付产品字段由 PaymentProductField
实例表示。每个字段都有一个标识符、一个类型、应用字段值的价值的限制的定义,以及关于字段应如何图示展示给客户的信息。此外,可以用字段实例来确定给定值是否适合该字段。
在下面的代码片段中,从支付产品中检索到具有标识符 "cvv"
的字段。检查字段的数据限制以确定该字段是必填项还是可选项。此外,检查字段的显示提示以确定客户提供的服务值是否应在用户界面中隐藏。
let cvvField = paymentProduct.paymentProductField(withId: "cvv")
let isRequired = cvvField.dataRestrictions.isRequired // state if value is required for this field
let shouldObfuscate = cvvField.displayHints.obfuscate // state if field value should be obfuscated
支付请求
一旦选择了一个支付产品并检索到了《PaymentProduct》实例后,就可以构建一个支付请求。此类必须作为客户提供的所有值的容器使用。
let paymentRequest = PaymentRequest(paymentProduct: paymentProduct)
对支付请求进行标记化
《PaymentProduct》有一个属性《tokenize》,用于指示一个支付请求是否应该被存储为文件上的账户。下面的代码片段展示了当请求不应存储为文件上的账户时如何构建支付请求。默认情况下,《tokenize》设置为“false”。
let paymentRequest = PaymentRequest(paymentProduct: paymentProduct) // when you do not pass the tokenize value, it will be false
// you can supply tokenize via the constructor
let paymentRequest = PaymentRequest(paymentProduct: paymentProduct, tokenize: true)
// or by accessing the request's tokenize property
paymentRequest.tokenize = true
如果客户选择了一个文件上的账户,构建支付请求时必须同时提供文件上的账户和相应的支付产品,如下面的代码片段所示。可以从《BasicPaymentProduct》和《PaymentProduct》的实例中检索《AccountOnFile》实例。
// you can supply accountOnFile via the constructor
let paymentRequest = PaymentRequest(
paymentProduct: paymentProduct,
accountOnFile: accountOnFile // when you do not pass the accountOnFile value, it will be nil
)
// or by accessing the request's accountOnFile property
paymentRequest.accountOnFile = accountOnFile
为支付请求设置字段值
一旦配置了支付请求,就可以像下面那样提供支付产品字段的值。如示例中“cardNumber”和“cvv”这样的标识符用于通过支付请求设置字段值。
paymentRequest.setValue(forField: "cardNumber", value: "1245 1254 4575 45")
paymentRequest.setValue(forField: "cvv", value: "123")
paymentRequest.setValue(forField: "expiryDate", value: "12/25")
验证支付请求
一旦所有值都已提供,就可以验证支付请求。幕后验证使用了为添加到 PaymentRequest
的每个字段创建的 DataRestrictions
类。验证后,将有一个错误列表可用,这表明验证过程中发生的任何问题。如果没有错误,则可以将支付请求加密并通过您的服务器发送到我们的平台。如果存在验证错误,则应向客户反馈这些错误。
// validate all fields in the payment request
paymentRequest.validate()
// check if the payment request is valid
if paymentRequest.errors.count == 0 {
// payment request is valid
} else {
// payment request has errors
}
验证是由与 PaymentProductField
链接的 Validator
实例,并作为值返回,例如
paymentRequest.errors.forEach { error in
// do something with the error, like displaying it to the user
}
加密支付请求
一旦设置 PaymentProduct
,提供并验证了 PaymentProductField
的值,并且可能设置了所选的 AccountOnFile
或 tokenize
属性,PaymentRequest
就准备好加密了。使用 session.prepare
执行 PaymentRequest
加密。这将返回一个 PreparedPaymentRequest
,它包含加密的支付请求字段和编码的客户元信息。
session.prepare(
paymentRequest,
success: {(_ preparedPaymentRequest: PreparedPaymentRequest) ->
// Pass the encrypted payment request to your server which should then forward it to the Server API
)},
failure: { _ in
// Handle failure of encrypting a Payment Request
}
)
尽管可以使用您自己的加密算法加密支付请求,但建议您使用 SDK 提供的加密功能。
IINDetails
支付卡号的最初六位数字称为 发卡行识别号码 (IIN)。一旦捕获到卡的最初 6 位数字,您就可以使用 session.iinDetails
调用来检索与提供的 IIN 相关的支付产品和网络。然后您可以验证卡类型并检查是否可以接受此卡。
可以通过使用 session.iinDetails
函数来检查与 IIN 关联的支付产品。此检查的结果是一个 IINDetailsResponse
实例。此类具有表示检查结果的 status
属性和一个表示与 IIN 关联的支付产品 paymentProductId
的属性。返回的 paymentProductId
可用于通过显示适当的支付产品水印向客户提供视觉反馈。
IINDetailsResponse
具有一个通过 IINStatus
枚举表示的 status
属性。以下是一些 IINStatus
枚举值:
supported
表示 IIN 与我们的平台支持的支付产品关联。unknown
表示未识别 IIN。notEnoughDigits
表示提供的数字少于六个,无法执行 IIN 检查。existingButNotAllowed
表示提供的IIN已被识别,但对应的商品当前支付不被允许。
session.iinDetails(
forPartialCreditCardNumber: "123456",
context: paymentContext,
success: {(_ iinDetailsResponse: IINDetailsResponse) ->
// check the status of the associated payment product
let iinStatus = iinDetailsResponse.status
)},
failure: { _ in
// Handle failure of retrieving IIN details
}
)
一些卡是双品牌卡,可以作为本地卡(带有本地品牌)或国际卡(带有国际品牌)处理。如果您尚未设置处理这些本地卡,此API调用将不会在其响应中返回该卡类型。
StringFormatter
为了帮助根据掩码格式化字段值,SDK提供了StringFormatter
类。它允许您对字段值进行格式化,并在字符串上应用和取消应用掩码。
let formatter = StringFormatter()
let mask = "{{9999}} {{9999}} {{9999}} {{9999}} {{999}}"
let value = "1234567890123456"
// apply masked value
let maskedValue = formatter.formatString(string: value, mask: mask) // "1234 5678 9012 3456"
// remove masked value
let unmaskedValue = formatter.unformatString(string: value, mask: mask) // "1234567890123456"
支付步骤
使用Swift SDK设置和完成支付包括以下步骤
1. 为此支付初始化Swift SDK
这需要使用诸如会话和客户标识符、连接URL和支付上下文信息(如货币和总额)之类的信息。
let session = Session(
clientSessionId: "47e9dc332ca24273818be2a46072e006",
customerId: "9991-0d93d6a0e18443bd871c89ec6d38a873",
baseURL: "https://clientapi.com",
assetBaseURL: "https://assets.com",
appIdentifier: "Swift Example Application/v2.0.4"
)
let amountOfMoney = PaymentAmountOfMoney(
totalAmount: 1298, // in cents
currencyCodeString: "EUR" // ISO 4217 currency code
)
let paymentContext = PaymentContext(
amountOfMoney: amountOfMoney,
isRecurring: false, // true, if it is a recurring payment
countryCodeString: "NL" // ISO 3166-1 alpha-2 country code
)
创建会话的响应可以成功用作Session构造函数的输入。
clientSessionId
/customerId
属性用于认证目的。这些可以通过您的服务器使用我们提供的任一Server SDK获取。baseURL
和assetBaseURL
是SDK应该连接到的URL。SDK与其执行任务的两个类型的服务器进行通信。一种类型的服务器提供上面讨论的客户端API。另一种类型的服务器存储SDK使用的静态资源,例如支付产品的徽标。- 支付信息(
paymentContext
)在构造会话时不是必需的,但在请求任何支付产品信息时,您需要提供它。客户可以从中选择支付产品取决于提供的支付信息,因此客户端SDK需要这些信息才能执行其工作。所需支付的必须是- 支付总额,定义为属性
amountOfMoney.totalAmount
- 应使用的货币,定义为属性
amountOfMoney.currencyCodeString
- 执行支付的人所属的国家,定义为属性
countryCodeString
- 支付是一次性支付还是周期性支付
- 支付总额,定义为属性
2. 检索支付项目
检索可用于此支付的所有支付产品和账户信息。您的应用程序可以使用这些数据来创建支付产品选择屏幕。
session.paymentItems(
for: paymentContext,
groupPaymentProducts: false,
success: {(_ paymentItems: PaymentItems) ->
// Display the contents of paymentItems & accountsOnFile to your customer
)},
failure: { _ in
// Inform the customer that something went wrong while retrieving the available Payment Products
}
)
对于某些支付产品,客户可以指示他们希望在线支付平台存储他们在使用此类支付产品时输入的部分数据。例如,可以存储大多数信用卡支付产品的持卡人姓名和卡号。存储的数据称为 AccountOnFile
或令牌。AccountOnFile
ID 必须在服务器API的创建客户端会话调用的请求体中提供,以便用于当前支付。当客户想使用相同的支付产品进行另一次支付时,可以选择此支付中存储的账户之一。在这种情况下,客户不需要输入已在 AccountOnFile
中存储的信息。从客户端API接收的SDK可用的可用支付产品列表也包含每个支付产品的文件账户。您的应用程序可以将该支付产品列表和文件账户列表展示给客户。
如果客户希望使用现有的 AccountOnFile
进行支付,应将选定的 AccountOnFile
添加到 PaymentRequest
。
3. 检索支付产品详情
检索客户根据所选支付产品或文件账户所需提供的所有支付产品详细信息,包括其字段。您的应用程序可以此信息创建支付产品详情屏幕。
session.paymentProduct(
withId: "1",
context: paymentContext,
success: {(_ paymentProduct: PaymentProduct) ->
// Assign the paymentProduct to the paymentRequest
let paymentRequest = PaymentRequest(paymentProduct: paymentProduct)
// Display the fields to your customer
)},
failure: { _ in
// Handle failure of retrieving a Payment Product by id
}
)
session.prepare(
paymentRequest,
success: {(_ preparedPaymentRequest: PreparedPaymentRequest) ->
// Pass the encrypted payment request to your server which should then forward it to the Server API
)},
failure: { _ in
// Handle failure of encrypting a Payment Request
}
)