PayPal

一个用于 PayPal REST API 的 Vapor 集成。大部分功能尚未完成,但适用于基本支付

安装

将此软件包添加到您的 manifest 文件的 dependencies 中,并添加到您希望访问此软件包的任何 targets 中

.package(url: "https://github.com/skelpo/PayPal.git", from: "0.1.0")

运行 vapor updateswift package update,如果您正在使用 Xcode 进行开发,请重新生成您的 Xcode 项目。

配置

PayPal 软件包使用两个环境变量来验证您的应用与 PayPal 的身份,分别是 PAYAPL_CLIENT_IDPAYPAL_CLIENT_SECRET。您可以通过访问您的开发者仪表板并选择您想要使用此软件包的应用来获取这些密钥

API keys in PayPal developer dashboard

然后,您可以在 Xcode 的环境变量中或在命令行中使用 export <KEY-NAME>=<KEY-VALUE> 来设置这些密钥。

注意: 如果您在 Xcode 中设置密钥,则每次重新生成项目时都需要重新添加它们。如果您在命令行中设置密钥,则需要关闭 Xcode 并重新生成您的项目。

如果您处于调试模式并将环境变量 PAYPAL_LOG_API_ERROR 设置为 TRUE,则可以记录导致 API 错误的请求/响应对。

在您的 configure.swift 文件中,您需要导入 PayPal 模块并将 PayPalProvider 注册到您应用的 services 中

try services.register(PayPalProvider())

使用软件包

支付

要创建和执行支付,首先创建一个 Payment 对象以发送给 PayPal

let address = try Address(
    recipientName: "Ira Harding",
    defaultAddress: true,
    line1: "578 Wild Wood",
    line2: nil,
    city: "New Haven",
    state: "CN",
    countryCode: "US",
    postalCode: "79812",
    phone: nil,
    type: nil
)
let item = try Payment.Item(
    quantity: "3",
    price: "39.00",
    currency: .usd,
    sku: "8EFAFEF3-72D2-4E5C-85EC-C14BA2F9D997",
    name: "Plum Pudding",
    description: "With sugar an inch thick",
    tax: "8.00"
)
let details = try DetailedAmount.Detail(
    subtotal: "117.00",
    shipping: "15.00",
    tax: "8.00",
    handlingFee: "10.00",
    shippingDiscount: nil,
    insurance: nil,
    giftWrap: nil
)
let amount = try DetailedAmount(currency: .usd, total: "150.00", details: details)
let items = try Payment.ItemList(
    items: [item],
    address: nil,
    phoneNumber: nil
)
let transaction = try Payment.Transaction(
    amount: amount,
    payee: Payee(email: "payee@example.com", merchant: nil, metadata: nil),
    description: nil,
    payeeNote: "Thanks for paying for the order!",
    custom: nil,
    invoice: nil,
    softDescriptor: nil,
    payment: .unrestricted,
    itemList: items,
    notify: "https://example.com/notify"
)
let payment = try Payment(
    intent: .sale,
    payer: PaymentPayer(method: .paypal, funding: nil, info: nil),
    context: nil,
    transactions: [transaction],
    experience: nil,
    payerNote: "Thanks for ordering!",
    redirects: Redirects(return: "https://example.com/approved", cancel: "https://example.com/canceled")
)

然后从您可以访问的容器中获取已注册的 Payments 服务。如果您在路由处理程序中,这将是一个 Request 对象

let payments = try request.make(Payments.self)

然后您可以使用 PayPal 创建支付

let payment = try payments.create(payment: payment)

此方法返回一个 future,其中包含已创建的支付。在支付中是一个 URI 数组,这些 URI 与您刚创建的支付相关。其中一个用于让买家授权支付,称为 approval_url。您将其作为重定向响应发送回客户端

return payment.map { payment -> Response in
    guard let uri = result.links?.filter({ $0.rel == "approval_url" }).first?.href else {
        throw Abort(.failedDependency, reason: "Cannot get payment approval URL")
    }
    return req.redirect(to: uri)
}

当此响应发送到客户端时,他们将被重定向到 PayPal 上的支付批准页面。根据他们接下来的操作,他们将被重定向到与支付注册的链接之一(在我们的例子中是 https://example.com/approvedhttps://example.com/canceled)。这些应该是您应用中路由的 URI。

如果支付获得买家批准,他们将被重定向回您的 “approved” 路由。在这里,您可以从 URI 的查询字符串中获取支付 ID 和付款人 ID,并执行支付

func approved(_ request: Request)throws -> Future<SOME-TYPE> {
    let paymentID = try request.query.get(String.self, at: "paymentId")
    let payerID = try request.query.get(String.self, at: "PayerID")
    let payments = try request.make(Payments.self)
    let executor = try Payment.Executor(payer: payerID, amounts: [ DetailedAmount(currency: .usd, total: "150.00", details: nil) ])
    
    payments.execute(payment: paymentID, with: executor)
}

确保 executor 对象中的金额是正确的货币,并且等于您创建的支付金额。