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: "8.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.
@Expandable
:// 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 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 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]"
...
}
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]"
}
@DynamicExpandable
: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.
## Error Handling
None of the API calls throw errors. Instead each route returns a successful `EventLoopFuture` or a failed `EventLoopFuture`.
~~~swift
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.
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! ๐
link |
Stars: 0 |
Last commit: 3 years ago |
Modified AccountLink to include "account_onboarding"
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics