• Stars
    star
    112
  • Rank 302,456 (Top 7 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 5 years ago
  • Updated 2 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,125
star
2

sockets

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

HTMLKit

Create and render HTML templates with HTMLKit
Swift
392
star
4

stripe

Stripe library for Vapor
Swift
174
star
5

example

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

Imperial

Federated Authentication with OAuth providers
Swift
146
star
7

postgresql

Robust PostgreSQL interface for Swift
Swift
132
star
8

mailgun

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

vapor-aws-lambda-runtime

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

slack-bot

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

chat-example

Realtime chat example built with Vapor.
CSS
72
star
12

postgresql-provider

PostgreSQL Provider for the Vapor web framework.
Swift
71
star
13

sendgrid

SendGrid-powered mail backend for Vapor
Swift
71
star
14

ferno

Vapor Firebase Realtime database provider
Swift
68
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
62
star
18

leaf-markdown

Markdown renderer for Vapor
Swift
60
star
19

docker

Docker images for Vapor.
Dockerfile
60
star
20

wkhtmltopdf

Generate and return PDFs from Vapor views
Swift
57
star
21

forms

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

Lingo-Vapor

Vapor provider for Lingo - the Swift localization library
Swift
54
star
23

markdown

Swift cmark wrapper for SwiftPM
Swift
50
star
24

VaporMonitoring

Monitoring for Vapor
Swift
47
star
25

todo-example

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

google-cloud

Access GoogleCloud APIs using Vapor
Swift
44
star
27

google-cloud-kit

Swift
42
star
28

CSRF

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

PassKit

Vapor implementation for Apple's PassKit server requirements.
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

swiftybeaver-provider

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

bcrypt

Swift implementation of the BCrypt password hashing function
Swift
32
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

styleguide

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

mongo-driver

MongoDB driver for Fluent
Swift
28
star
39

tls

πŸ”’ Non-blocking, event-driven TLS built on OpenSSL & macOS security.
Swift
27
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
14
star
53

JWT3PA

Handles routes, registration and login for Sign in with Apple and Google
Swift
13
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

moat

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

openai

A Swift package for interacting with the OpenAI API using Vapor
Swift
11
star
59

sqlite-provider

SQLite3 provider for Vapor
Swift
11
star
60

telesign-provider

A Telesign provider for Vapor.
Swift
10
star
61

debugging

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

sqlite-driver

SQLite driver for Fluent
Swift
9
star
63

pokedex-example

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

dashboard-example

A dashboard site showing some
CSS
9
star
65

jwt-provider

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

board-example

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

random

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

csqlite

C module map for SQLite
Swift
8
star
69

repository-template

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

Vii

Vii attempts to reverse engineer an existing database into Vapor 4 compatible models
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

sendgrid-kit

Swift
7
star
74

mars-rovers

View photos from the Mars rovers.
Swift
7
star
75

kitura-provider

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

sublime-leaf

Leaf syntax highlighting for Sublime Text
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

yeoman

Yeoman generators for Vapor.
Swift
6
star
84

queues-mongo-driver

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

libc

Wraps Linux and OS X libc
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

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
89

light-template

An extremely lightweight, no frills template for Vapor web apps.
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

jobs-postgresql-driver

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

HTMLKit-Components

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

swift-dependency-submission

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

path-indexable

A protocol to allow multiple structured format types to all share the same subscript access code
Swift
3
star