StripeKit is a Swift package used to communicate with the Stripe API for Server Side Swift Apps.
You can check the CHANGELOG to see which version of StripeKit meets your needs.
To start using StripeKit, in your Package.swift
, add the following
.package(url: "https://github.com/vapor-community/stripe-kit.git", from: "14.0.0")
Initialize the StripeClient
let httpClient = HTTPClient(..)
let stripe = StripeClient(httpClient: httpClient, eventLoop: eventLoop, apiKey: "sk_12345")
And now you have acess to the APIs via stripe
.
The APIs you have available correspond to what's implemented.
For example to use the charges
API, the stripeclient has a property to access that API via routes.
stripe.charges.create(amount: 2500,
currency: .usd,
description: "A server written in swift.",
source: "tok_visa").flatMap { (charge) -> EventLoopFuture<Void> in
if charge.status == .succeeded {
print("New servers are on the way ๐")
} else {
print("Sorry you have to use Node.js ๐คข")
}
}
StripeKit supports expandable objects via 2 property wrappers:
@Expandable
and @DynamicExpandable
All API routes that can return expanded objects have an extra parameter expand: [String]?
that allows specifying which objects to expand.
- Expanding a single field.
// Expanding a customer from creating a `PaymentIntent`.
stripeclient.paymentIntents.create(amount: 2500, currency: .usd, expand: ["customer"])
.flatMap { paymentIntent in
// Accessing the expanded `StripeCustomer` object
paymentIntent.$customer.email
...
}
- Expanding multiple fields.
// Expanding a customer and payment method from creating a `PaymentIntent`.
stripeclient.paymentIntents.create(amount: 2500, currency: .usd, expand: ["customer", "paymentMethod"])
.flatMap { paymentIntent in
// Accessing the expanded `StripeCustomer` object
paymentIntent.$customer?.email // "[email protected]"
// Accessing the expanded `StripePaymentMethod` object
paymentIntent.$paymentMethod?.card?.last4 // "1234"
...
}
- Expanding nested fields.
// Expanding a payment method and its nested customer from creating a `PaymentIntent`.
stripeclient.paymentIntents.create(amount: 2500, currency: .usd, expand: ["paymentMethod.customer"])
.flatMap { paymentIntent in
// Accessing the expanded `StripePaymentMethod` object
paymentIntent.$paymentMethod?.card?.last4 // "1234"
// Accessing the nested expanded `StripeCustomer` object
paymentIntent.$paymentMethod?.$customer?.email // "[email protected]"
...
}
- Usage with list all.
Note: For list operations expanded fields must start with
data
// Expanding a customer from listing all `PaymentIntent`s.
stripeclient.paymentIntents.listAll(filter: ["expand": ["data.customer"...]])
.flatMap { list in
// Accessing the first `StripePaymentIntent`'s expanded `StripeCustomer` property
list.data?.first?.$customer?.email // "[email protected]"
}
Some objects in stripe can be expanded into different objects. For example:
A StripeApplicationFee
has an originatingTransaction
property that can be expanded into either a charge or a transfer.
When expanding it you can specify which object you expect by doing the following:
stripeclient.applicationFees.retrieve(fee: "fee_1234", expand: ["originatingTransaction"])
.flatMap { applicationfee in
// Access the originatingTransaction as a StripeCharge
applicationfee.$originatingTransaction(as: StripeCharge.self)?.amount // 2500
...
// Access the originatingTransaction as a StripeTransfer
applicationfee.$originatingTransaction(as: StripeTransfer.self)?.destination // acc_1234
}
Stripe has a habit of changing APIs and having dynamic parameters for a lot of their APIs.
To accomadate for these changes, certain routes that take arguments that are hash
s or Dictionaries
, are represented by a Swift dictionary [String: Any]
.
For example consider the Connect account API.
// We define a custom dictionary to represent the paramaters stripe requires.
// This allows us to avoid having to add updates to the library when a paramater or structure changes.
let individual: [String: Any] = ["address": ["city": "New York",
"country": "US",
"line1": "1551 Broadway",
"postal_code": "10036",
"state": "NY"],
"first_name": "Taylor",
"last_name": "Swift",
"ssn_last_4": "0000",
"dob": ["day": "13",
"month": "12",
"year": "1989"]]
let businessSettings: [String: Any] = ["payouts": ["statement_descriptor": "SWIFTFORALL"]]
let tosDictionary: [String: Any] = ["date": Int(Date().timeIntervalSince1970), "ip": "127.0.0.1"]
stripe.connectAccounts.create(type: .custom,
country: "US",
email: "[email protected]",
businessType: .individual,
defaultCurrency: .usd,
externalAccount: "bank_token",
individual: individual,
requestedCapabilities: ["platform_payments"],
settings: businessSettings,
tosAcceptance: tosDictionary).flatMap { connectAccount in
print("New Stripe Connect account ID: \(connectAccount.id)")
}
The first, preferred, authentication option is to use your (the platform accountโs) secret key and pass a Stripe-Account
header identifying the connected account for which the request is being made. The example request performs a refund of a charge on behalf of a connected account:
stripe.refunds.headers.add(name: "Stripe-Account", value: "acc_12345")
stripe.refunds.create(charge: "ch_12345", reason: .requestedByCustomer)
NOTE: The modified headers will remain on the route instance (refunds in this case) of the StripeClient
if a reference to it is held. If you're accessing the StripeClient in the scope of a function, the headers will not be retained.
None of the API calls throw errors. Instead each route returns a successful EventLoopFuture
or a failed EventLoopFuture
.
stripe.charges.create(amount: 2500,
currency: .usd,
description: "A server written in swift.",
source: "tok_visa")
.flatMap { (charge) -> EventLoopFuture<Void> in
if charge.status == .succeeded {
print("New servers are on the way ๐")
} else {
print("Sorry you have to use Node.js ๐คข")
}
}
.flatMapError { error in
print("Stripe error \(error.message)")
}
The webhooks API is available to use in a typesafe way to pull out entities. Here's an example of listening for the payment intent webhook.
func handleStripeWebhooks(req: Request) throws -> EventLoopFuture<HTTPResponse> {
let signature = req.headers["Stripe-Signature"]
try StripeClient.verifySignature(payload: req.body, header: signature, secret: "whsec_1234")
// Stripe dates come back from the Stripe API as epoch and the StripeModels convert these into swift `Date` types.
// Use a date and key decoding strategy to successfully parse out the `created` property and snake case strpe properties.
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
decoder.keyDecodingStrategy = .convertFromSnakeCase
let event = try decoder.decode(StripeEvent.self, from: req.bodyData)
switch (event.type, event.data?.object) {
case (.paymentIntentSucceeded, .paymentIntent(let paymentIntent)):
print("Payment capture method: \(paymentIntent.captureMethod?.rawValue)")
return eventLoop.makeSucceededFuture(HTTPResponse(status: .ok))
default: return eventLoop.makeSucceededFuture(HTTPResponse(status: .ok))
}
}
See the Vapor helper library to use StripeKit with Vapor.
- Balance
- Balance Transactions
- Charges
- Customers
- Disputes
- Events
- Files
- File Links
- Mandates
- PaymentIntents
- SetupIntents
- SetupAttempts
- Payouts
- Refunds
- Tokens
- Payment Methods
- Bank Accounts
- Cards
- Sources
- Products
- Prices
- Coupons
- Promotion Codes
- Discounts
- Tax Rates
- Sessions
- Credit Notes
- Customer Balance Transactions
- Customer Portal
- Customer Tax IDs
- Invoices
- Invoice Items
- Plans
- Quotes
- Quote Line Items
- Subscriptions
- Subscription items
- Subscription Schedule
- Usage Records
- Account
- Account Links
- Application Fees
- Application Fee Refunds
- Capabilities
- Country Specs
- External Accounts
- Persons
- Top-ups
- Transfers
- Transfer Reversals
- Early Fraud Warnings
- Reviews
- Value Lists
- Value List Items
- Authorizations
- Cardholders
- Cards
- Disputes
- Transactions
- Connection Tokens
- Locations
- Readers
- Orders
- Order Items
- Returns
- SKUs
- Ephemeral Keys
- Scheduled Queries
- Report Runs
- Report Types
- VerificationSessions
- VerificationReports
- Webhook Endpoints
- Signature Verification
StripeKit is available under the MIT license. See the LICENSE file for more info.
Feel free to submit a pull request whether it's a clean up, a new approach to handling things, adding a new part of the API, or even if it's just a typo. All help is welcomed! ๐