A PayPal Provider for the Vapor Server-Side Swift Framework

What's New



Padded Currency Values

Format money decimal values with trailing 0 characters, so there are as many fraction digits as the currency exponent number:

85.8 USD => 85.80
10 USD => 10.00
10 XXX => 10

This is because some endpoint returned validation errors if this padding wasn't there.


A Vapor integration for PayPal's REST API. Mostly incomplete, but good for basic payments


Add the package to your manifest's dependencies and to any targets you want access to the package in:

.package(url: "", from: "0.1.0")

Run vapor update or swift package update and regenerate your Xcode project if you are developing with Xcode.


The PayPal package uses two environment variables to authenticate your app with PayPal, PAYAPL_CLIENT_ID and PAYPAL_CLIENT_SECRET. You can access these keys by going to your developer dashboard and selecting the app you want to use the package for:

API keys in PayPal developer dashboard

You can then set these keys in Xcode's environment variables or in the command-line with export <KEY-NAME>=<KEY-VALUE>.

Note: If you set the keys in Xcode, they will need to be re-added every time you regenerate the project. If you set the keys in the command-line, you will need to close Xcode and regenerate your project.

You can log request/response pairs that result in an API error if you are in debug mode and set the env var PAYPAL_LOG_API_ERROR to TRUE.

In your configure.swift file, you will need to import the PayPal module and register the PayPalProvider with your app's services:

try services.register(PayPalProvider())

Using the Package


To create and execute a payment, start by creating a Payment object to send to 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: "", merchant: nil, metadata: nil),
    description: nil,
    payeeNote: "Thanks for paying for the order!",
    custom: nil,
    invoice: nil,
    softDescriptor: nil,
    payment: .unrestricted,
    itemList: items,
    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: "", cancel: "")

Then get the registered Payments service from the container you have access to. This will be a Request object if you are in a route handler:

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

You can then create the payment with PayPal:

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

This method returns a future with the payment that was created. In the payment is an array of URIs that are related to the payment you just created. One of them is for letting the buyer authorize the payment, called approval_url. You send this as a redirect response back to the client:

return { 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)

When this response is sent to the client, they will be redirected to the payment approval page on PayPal. Depending on what they do then, they will be redirected to one of the links that was registered with the payment ( or in our case). These should be URIs for routes in your app.

If the payment is approved by the buyer, they will be redirected back to your 'approved' route. Here you can get the payment ID and the payer ID from the URI's query-strings and execute the payment:

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)

Make sure the amount in the executor object is the proper currency and equals the amount of the payment you created.


  • Swift Tools 4.2.0
View More Packages from this Author


Last updated: Sat Jan 13 2024 05:23:32 GMT-1000 (Hawaii-Aleutian Standard Time)