• Stars
    star
    125
  • Rank 286,335 (Top 6 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Swift on Server SDK for the Stripe API

StripeKit

Test

StripeKit is a Swift package used to communicate with the Stripe API for Server Side Swift Apps.

Version support

Stripe API version 2022-11-15 -> StripeKit: 22.0.0

Installation

To start using StripeKit, in your Package.swift, add the following

.package(url: "https://github.com/vapor-community/stripe-kit.git", from: "22.0.0")

Using the API

Initialize the StripeClient

let httpClient = HTTPClient(..)
let stripe = StripeClient(httpClient: httpClient, 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.

do {
    let charge = try await stripe.charges.create(amount: 2500,
                                                 currency: .usd,
                                                 description: "A server written in swift.",
                                                 source: "tok_visa")
    if charge.status == .succeeded {
        print("New swift servers are on the way πŸš€")
    } else {
        print("Sorry you have to use Node.js 🀒")
    }
} catch {
    // Handle error
}

Expandable objects

StripeKit supports expandable objects via 3 property wrappers:

@Expandable, @DynamicExpandable and @ExpandableCollection

All API routes that can return expanded objects have an extra parameter expand: [String]? that allows specifying which objects to expand.

Usage with @Expandable:

  1. Expanding a single field.
// Expanding a customer from creating a `PaymentIntent`.
     let paymentIntent = try await stripeclient.paymentIntents.create(amount: 2500, currency: .usd, expand: ["customer"])
     // Accessing the expanded `Customer` object
     paymentIntent.$customer.email
  1. Expanding multiple fields.
// Expanding a customer and payment method from creating a `PaymentIntent`.
let paymentIntent = try await stripeclient.paymentIntents.create(amount: 2500, currency: .usd, expand: ["customer", "paymentMethod"])
// Accessing the expanded `StripeCustomer` object   
 paymentIntent.$customer?.email // "[email protected]"
// Accessing the expanded `StripePaymentMethod` object
 paymentIntent.$paymentMethod?.card?.last4 // "1234"
  1. Expanding nested fields.
// Expanding a payment method and its nested customer from creating a `PaymentIntent`.
let paymentIntent = try await stripeclient.paymentIntents.create(amount: 2500, currency: .usd, expand: ["paymentMethod.customer"])
// Accessing the expanded `PaymentMethod` object
 paymentIntent.$paymentMethod?.card?.last4 // "1234"
// Accessing the nested expanded `Customer` object   
 paymentIntent.$paymentMethod?.$customer?.email // "[email protected]"
  1. Usage with list all.

Note: For list operations expanded fields must start with data

// Expanding a customer from listing all `PaymentIntent`s.
let list = try await stripeclient.paymentIntents.listAll(filter: ["expand": ["data.customer"...]])
// Accessing the first `StripePaymentIntent`'s expanded `Customer` property
list.data?.first?.$customer?.email // "[email protected]"

Usage with @DynamicExpandable:

Some objects in stripe can be expanded into different objects. For example:

An ApplicationFee 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:

let applicationfee = try await stripeclient.applicationFees.retrieve(fee: "fee_1234", expand: ["originatingTransaction"])
// Access the originatingTransaction as a Charge
applicationfee.$originatingTransaction(as: Charge.self)?.amount // 2500
...
// Access the originatingTransaction as a Transfer
applicationfee.$originatingTransaction(as: Transfer.self)?.destination // acc_1234

Usage with @ExpandableCollection:

  1. Expanding an array of ids
let invoice = try await stripeClient.retrieve(invoice: "in_12345", expand: ["discounts"])

// Access the discounts array as `String`s
invoice.discounts.map { print($0) } // "","","",..

// Access the array of `Discount`s
invoice.$discounts.compactMap(\.id).map { print($0) } // "di_1","di_2","di_3",...  

Nuances with parameters and type safety

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 hashs 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"]

let connectAccount = try await 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)
print("New Stripe Connect account ID: \(connectAccount.id)")

Authentication via the Stripe-Account header

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 using a builder style API:

   stripe.refunds
    .addHeaders(["Stripe-Account": "acc_12345",
             "Authorization": "Bearer different_api_key",
             "Stripe-Version": "older-api-version"])
    .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.

Idempotent Requests

Similar to the account header, you can use the same builder style API to attach Idempotency Keys to your requests.

    let key = UUID().uuidString
    stripe.refunds
    .addHeaders(["Idempotency-Key": key])
    .create(charge: "ch_12345", reason: .requestedByCustomer)

Webhooks

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) async throws -> 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 HTTPResponse(status: .ok)
        
    default: return HTTPResponse(status: .ok)
    }
}

Using with Vapor

StripeKit is pretty easy to use but to better integrate with Vapor these are some helpful extensions

import Vapor
import StripeKit

extension Application {
    public var stripe: StripeClient {
        guard let stripeKey = Environment.get("STRIPE_API_KEY") else {
            fatalError("STRIPE_API_KEY env var required")
        }
        return .init(httpClient: self.http.client.shared, apiKey: stripeKey)
    }
}

extension Request {
    private struct StripeKey: StorageKey {
        typealias Value = StripeClient
    }
    
    public var stripe: StripeClient {
        if let existing = application.storage[StripeKey.self] {
            return existing
        } else {
            guard let stripeKey = Environment.get("STRIPE_API_KEY") else {
                fatalError("STRIPE_API_KEY env var required")
            }
            let new = StripeClient(httpClient: self.application.http.client.shared, apiKey: stripeKey)
            self.application.storage[StripeKey.self] = new
            return new
        }
    }
}

extension StripeClient {
    /// Verifies a Stripe signature for a given `Request`. This automatically looks for the header in the headers of the request and the body.
    /// - Parameters:
    ///     - req: The `Request` object to check header and body for
    ///     - secret: The webhook secret used to verify the signature
    ///     - tolerance: In seconds the time difference tolerance to prevent replay attacks: Default 300 seconds
    /// - Throws: `StripeSignatureError`
    public static func verifySignature(for req: Request, secret: String, tolerance: Double = 300) throws {
        guard let header = req.headers.first(name: "Stripe-Signature") else {
            throw StripeSignatureError.unableToParseHeader
        }
        
        guard let data = req.body.data else {
            throw StripeSignatureError.noMatchingSignatureFound
        }
        
        try StripeClient.verifySignature(payload: Data(data.readableBytesView), header: header, secret: secret, tolerance: tolerance)
    }
}

extension StripeSignatureError: AbortError {
    public var reason: String {
        switch self {
        case .noMatchingSignatureFound:
            return "No matching signature was found"
        case .timestampNotTolerated:
            return "Timestamp was not tolerated"
        case .unableToParseHeader:
            return "Unable to parse Stripe-Signature header"
        }
    }
    
    public var status: HTTPResponseStatus {
        .badRequest
    }
}

Whats Implemented

Core Resources

  • Balance
  • Balance Transactions
  • Charges
  • Customers
  • Disputes
  • Events
  • Files
  • File Links
  • Mandates
  • PaymentIntents
  • SetupIntents
  • SetupAttempts
  • Payouts
  • Refunds
  • Tokens
  • EphemeralKeys

Payment Methods

  • Payment Methods
  • Bank Accounts
  • Cash Balance
  • Cards
  • Sources

Products

  • Products
  • Prices
  • Coupons
  • Promotion Codes
  • Discounts
  • Tax Codes
  • Tax Rates
  • Shipping Rates

Checkout

  • Sessions

Payment Links

  • Payment Links

Billing

  • Credit Notes
  • Customer Balance Transactions
  • Customer Portal
  • Customer Tax IDs
  • Invoices
  • Invoice Items
  • Plans
  • Quotes
  • Quote Line Items
  • Subscriptions
  • Subscription items
  • Subscription Schedule
  • Test Clocks
  • Usage Records

Connect

  • Account
  • Account Links
  • Account Sessions
  • Application Fees
  • Application Fee Refunds
  • Capabilities
  • Country Specs
  • External Accounts
  • Persons
  • Top-ups
  • Transfers
  • Transfer Reversals
  • Secret Management

Fraud

  • Early Fraud Warnings
  • Reviews
  • Value Lists
  • Value List Items

Issuing

  • Authorizations
  • Cardholders
  • Cards
  • Disputes
  • Funding Instructions
  • Transactions

Terminal

  • Connection Tokens
  • Locations
  • Readers
  • Hardware Orders
  • Hardware Products
  • Hardware SKUs
  • Hardware Shipping Methods
  • Configurations

Sigma

  • Scheduled Queries

Reporting

  • Report Runs
  • Report Types

Identity

  • VerificationSessions
  • VerificationReports

Webhooks

  • Webhook Endpoints
  • Signature Verification

Idempotent Requests

License

StripeKit is available under the MIT license. See the LICENSE file for more info.

More Repositories

1

awesome-vapor

A curated list of Vapor-related awesome projects.
Ruby
1,174
star
2

sockets

πŸ”Œ Non-blocking TCP socket layer, with event-driven server and client.
Swift
575
star
3

HTMLKit

Create and render HTML templates with HTMLKit
Swift
403
star
4

stripe

Stripe library for Vapor
Swift
179
star
5

example

Starting point for using the Vapor framework.
Swift
164
star
6

Imperial

Federated Authentication with OAuth providers
Swift
153
star
7

postgresql

Robust PostgreSQL interface for Swift
Swift
131
star
8

mailgun

πŸ“§ Service to assist with sending emails from Vapor apps
Swift
119
star
9

vapor-aws-lambda-runtime

Run your Vapor api server on AWS Lambda using the official Swift Server runtime.
Swift
105
star
10

slack-bot

An example Slack Bot application built with Vapor in Swift.
Swift
76
star
11

chat-example

Realtime chat example built with Vapor.
CSS
73
star
12

ferno

Vapor Firebase Realtime database provider
Swift
71
star
13

sendgrid

SendGrid-powered mail backend for Vapor
Swift
71
star
14

postgresql-provider

PostgreSQL Provider for the Vapor web framework.
Swift
70
star
15

chat-ios-example

A basic realtime chat project using Vapor on the server -- See vapor-chat.herokuapp.com
Swift
68
star
16

pagination

Simple Vapor 3 Pagination
Swift
64
star
17

apns

Vapor APNS for iOS
Swift
60
star
18

docker

Docker images for Vapor.
Dockerfile
60
star
19

leaf-markdown

Markdown renderer for Vapor
Swift
60
star
20

wkhtmltopdf

Generate and return PDFs from Vapor views
Swift
58
star
21

PassKit

🎟️ πŸ“¦ A package for creating passes and orders for Apple Wallet with Vapor.
Swift
58
star
22

forms

Brings simple, dynamic and re-usable web form handling to Vapor.
Swift
56
star
23

Lingo-Vapor

Vapor provider for Lingo - the Swift localization library
Swift
55
star
24

markdown

Swift cmark wrapper for SwiftPM
Swift
50
star
25

google-cloud-kit

Swift
46
star
26

VaporMonitoring

Monitoring for Vapor
Swift
46
star
27

todo-example

An example implementation for http://todobackend.com
Swift
46
star
28

google-cloud

Access GoogleCloud APIs using Vapor
Swift
45
star
29

CSRF

A package to add protection to Vapor against CSRF attacks.
Swift
41
star
30

mongo-provider

MongoDB Provider for Vapor
Swift
40
star
31

vapor-ext

βš™οΈ A collection of Swift extensions for wide range of Vapor data types and classes
Swift
36
star
32

async

⏱ Promises and reactive-streams in Swift built for high-performance and scalability.
Swift
35
star
33

bcrypt

Swift implementation of the BCrypt password hashing function
Swift
34
star
34

swiftybeaver-provider

SwiftyBeaver Logging Provider for Vapor, the server-side Swift web framework https://swiftybeaver.com
Swift
33
star
35

json

Convenience wrapper for Foundation JSON.
Swift
30
star
36

mysql-provider

MySQL provider for the Vapor web framework.
Swift
30
star
37

tls

πŸ”’ Non-blocking, event-driven TLS built on OpenSSL & macOS security.
Swift
29
star
38

styleguide

An opinionated, unofficial style guide for developing production-level Vapor applications
28
star
39

mongo-driver

MongoDB driver for Fluent
Swift
28
star
40

open-api

OpenAPI integration for Vapor.
Swift
27
star
41

node

A formatted data encapsulation meant to facilitate the transformation from one object to another
Swift
25
star
42

penny

Penny Coin is a Slack Bot in Swift written with Vapor. It is used to track coins in the Vapor community.
Swift
23
star
43

postgresql-driver

PostgreSQL driver for Fluent
Swift
23
star
44

gzip-provider

gzip support for Vapor
Swift
21
star
45

VaporTwilioService

Twilio API provider for all your Vapor needs
Swift
19
star
46

redis-provider

Adds Redis Cache to Vapor
Swift
17
star
47

auth-provider

Middleware and conveniences for using Auth in Vapor.
Swift
17
star
48

bits

A bite sized library for dealing with bytes.
Swift
16
star
49

fluent-example

Starting point for using the Fluent framework.
Swift
16
star
50

auth-example

Authentication example for Vapor 1.0.
Swift
16
star
51

mustache-provider

Render Mustache templates in Vapor
Swift
16
star
52

soto-cognito-authentication

Authenticating with AWS Cognito for Vapor
Swift
15
star
53

JWT3PA

Handles routes, registration and login for Sign in with Apple and Google
Swift
14
star
54

fluent-provider

A provider for including Fluent in Vapor applications
Swift
13
star
55

queues-database-hooks

A package for tracking queue job statuses in your database via Queue Hooks
Swift
13
star
56

onesignal

Swift
12
star
57

openai

A Swift package for interacting with the OpenAI API using Vapor
Swift
12
star
58

moat

A line of defense for your Vapor application including attack filtering + extras.
Swift
12
star
59

telesign-provider

A Telesign provider for Vapor.
Swift
10
star
60

debugging

A library to aid Vapor users with better debugging around the framework
Swift
10
star
61

sendgrid-kit

πŸ“§ A Swift on Server SDK for the SendGrid API
Swift
10
star
62

sqlite-provider

SQLite3 provider for Vapor
Swift
10
star
63

sqlite-driver

SQLite driver for Fluent
Swift
10
star
64

pokedex-example

PokΓ©dex example created with Vapor
Swift
9
star
65

dashboard-example

A dashboard site showing some
CSS
9
star
66

jwt-provider

(Deprecated) Adds conveniences for using JWTs in Vapor 2.
Swift
9
star
67

board-example

A demonstration of a board written in Vapor demonstrating database relationships.
Swift
9
star
68

random

Module for generating random bytes and numbers.
Swift
8
star
69

csqlite

C module map for SQLite
Swift
8
star
70

repository-template

πŸ’§ A starting point for Vapor APIs... According the Style Guide
Swift
8
star
71

cpostgresql

PostgreSQL C module map
Swift
7
star
72

vapor-sitemap

A dynamic sitemap generator for Vapor.
Swift
7
star
73

mars-rovers

View photos from the Mars rovers.
Swift
7
star
74

kitura-provider

Use IBM's Kitura HTTP server in your Vapor application.
Swift
7
star
75

sublime-leaf

Leaf syntax highlighting for Sublime Text
7
star
76

Vii

Vii attempts to reverse engineer an existing database into Vapor 4 compatible models
Swift
7
star
77

documentation-zh

Documentation markdown for all Vapor packages in Chinese.
HTML
7
star
78

leaf-provider

Add leaf templating into your vapor app
Swift
7
star
79

htmlkit-vapor-provider

Swift
6
star
80

PackageCatalogAPI

A replacement for the IBM Swift Package Catalog.
Swift
6
star
81

ctls

LibreSSL / OpenSSL module map for Swift
Swift
6
star
82

migrator

A package for updating from Vapor 1 to Vapor 2
Swift
6
star
83

queues-mongo-driver

A MongoDB implementation for https://github.com/vapor/queues
Swift
6
star
84

libc

Wraps Linux and OS X libc
Swift
6
star
85

yeoman

Yeoman generators for Vapor.
Swift
6
star
86

polymorphic

Syntax for easily accessing values from generic data.
Swift
5
star
87

clibressl

LibreSSL wrapped in a Swift package.
C
5
star
88

light-template

An extremely lightweight, no frills template for Vapor web apps.
Swift
5
star
89

vapor-extensions

A collection of Swift extensions, with handy methods, syntactic sugar, and performance improvements for wide range of Vapor data types and classes
Swift
5
star
90

MongoKitten-Provider

Integrate MongoKitten with Vapor
Swift
5
star
91

cmysql

MySQL C module map
Swift
5
star
92

postman-provider

Postman Provider for Vapor
Swift
5
star
93

MultiLogging

Logging utility package for Vapor 3
Swift
5
star
94

checkpoint

Verify's Amazon Alexa requests
Swift
4
star
95

GatewayAPIKit

A Swift package for sending SMS using GatewayAPI
Swift
4
star
96

swift

Helps manage and install Swift 3 versions.
Shell
4
star
97

vapor-elementary

A modern and efficient HTML rendering library - inspired by SwiftUI, built for the web.
Swift
4
star
98

jobs-postgresql-driver

A PostgreSQL Persistance Layer for the Vapor Jobs framework
Swift
4
star
99

HTMLKit-Components

This package contains common UI-components wich are build with HTMLKit.
Swift
4
star
100

swift-dependency-submission

Calculates dependencies for a Swift build-target and submits the list to the Dependency Submission API
Swift
4
star