Giter VIP home page Giter VIP logo

stripe-kit's Introduction

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.

stripe-kit's People

Contributors

0xtim avatar andreaferrando avatar andrew804 avatar andrewangeta avatar anthonycastelli avatar baarde avatar balestrapatrick avatar buratti avatar calebkleveter avatar cball avatar danielinoa avatar dbossotto avatar frizlab avatar gwynne avatar jaapwijnen avatar leogdion avatar madsodgaard avatar maxim-inv avatar mladen-k avatar mrdepth avatar nsmutablestring avatar patrick-kladek avatar ratranqu avatar robipresotto avatar sorix avatar stalkermv avatar tanner0101 avatar varungakhar avatar vzsg avatar winsmith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stripe-kit's Issues

Expandable Objects

👋

I just push a very basic draft about how we could implement expandable objects.
It is a braking change so before going on I'd like to have a feedback.
You can find the diff here.

The result would be

let customer = try stripe.customers.retrieve(customer: "cus_123", expand: ["default_source"]).wait()

print(customer.defaultSource?.last4) // 4242
print(customer.$defaultSource.id) //card_1234

and

let customer = try stripe.customers.retrieve(customer: "cus_123").wait()

print(customer.defaultSource?.last4) // nil
print(customer.$defaultSource.id) //card_1234

Cheers 🍻

PaymentIntents Precondition failed -> NIO crash

Using latest Vapor 4.27.1 and NIO 2.20.2 there is a Crash with this message:
Precondition failed: file /{PATH_TO_PROJECT}/.build/checkouts/swift-nio/Sources/NIO/ChannelPipeline.swift, line 1416

`
func createPaymentIntent(req: Request) throws -> EventLoopFuture {

    guard let paymentIntentRequest: PaymentIntentRequest = try? req.content.decode(PaymentIntentRequest.self) else {
        throw Abort(.notFound)
    }
    
    return req.stripe.paymentIntents.create(amount: self.calculateOrderAmount(paymentIntentRequest: paymentIntentRequest),
                                                currency: .eur,
                                                applicationFeeAmount: nil,
                                                captureMethod: nil,
                                                confirm: nil,
                                                confirmationMethod: nil,
                                                customer: nil,
                                                description: nil,
                                                errorOnRequiresAction: nil,
                                                mandate: nil,
                                                mandateData: nil,
                                                metadata: nil,
                                                offSession: nil,
                                                onBehalfOf: nil,
                                                paymentMethod: nil,
                                                paymentMethodData: nil,
                                                paymentMethodOptions: nil,
                                                paymentMethodTypes: nil,
                                                receiptEmail: nil,
                                                returnUrl: nil,
                                                savePaymentMethod: nil,
                                                setupFutureUsage: nil,
                                                shipping: nil,
                                                statementDescriptor: nil,
                                                statementDescriptorSuffix: nil,
                                                transferData: nil,
                                                transferGroup: nil,
                                                useStripeSDK: nil,
                                                expand: nil)
        .map { paymentIntent in
                      
            debugPrint(paymentIntent.id)
            return PaymentIntentResponse(clientSecret: paymentIntent.clientSecret)
    }
}

private func calculateOrderAmount(paymentIntentRequest: PaymentIntentRequest) -> Int {
    
    // TODO: calculate price
    return 100
}

`

JSON decoding error in StripeSource

let future = req.stripe.sources.attach(source: requestmodel.card_token!, customer: customer_id)
future.whenFailure { (error)
req.logger.info("(error.debugDescription)")
}

[ INFO ] Swift.DecodingError.valueNotFound(Swift.Double, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "super", intValue: nil)], debugDescription: "Expected Double but found null value instead.", underlyingError: nil)) [request-id: EF3EEC1F-1E19-4EE3-855C-34FE32B6A49B]

Using above code to attach card to stripe customer. I am able to see card attached on stripe dashboard but getting this error.

Thank You.

Webhook Wrappers

The payloads that come through from Webhooks are slightly different than the models. For example, here's the payload from the session creation webook:

{
  "id": "ID here",
  "object": "event",
  "api_version": "2020-03-02",
  "created": 1589153882,
  "data": {
    "object": {
      "id": "ID here",
      "object": "checkout.session",
      "billing_address_collection": null,
      "cancel_url": "URL Here",
      "client_reference_id": "Client Ref Here",
      "customer": "Customer ID Here",
      "customer_email": null,
      "display_items": [],
      "livemode": false,
      "locale": null,
      "metadata": {
      },
      "mode": "payment",
      "payment_intent": "Payment Intent",
      "payment_method_types": [
        "card"
      ],
      "setup_intent": null,
      "shipping": null,
      "shipping_address_collection": null,
      "submit_type": null,
      "subscription": null,
      "success_url": "Success URL here"
    }
  },
  "livemode": false,
  "pending_webhooks": 2,
  "request": {
    "id": null,
    "idempotency_key": null
  },
  "type": "checkout.session.completed"
}

This almost matches the required structure for StripeSession, but has the extra wrapper around it (i.e. root.data.object matches StripeSession).

For the time being I've been decoding this:

struct StripeWebhookObject: Content {
    let data: DataObject

    struct DataObject: Content {
        let object: Object
    }

    struct Object: Content {
        let client_reference_id: String?
        let customer: String?
    }
}

Which works just fine, so not urgent or anything, but it might be nice to provide webhook decodable wrappers.

Assertion Failed

I receive the following error in the console:

Assertion failed: file AsyncHTTPClient/HTTPClient.swift, line 117 2021-05-06 16:19:12.537202-0400 Run[9051:882865] Assertion failed: file AsyncHTTPClient/HTTPClient.swift, line 117

when running the following code:

`
func createCustomerStripe(_ req: Request) throws -> EventLoopFuture {

    print("createCustomerStripe called.")
    
    let customerFromClient = try req.content.decode(CustomersDTO.self)

    let httpClient = HTTPClient(eventLoopGroupProvider: .shared(req.eventLoop))
    let stripe = StripeClient(httpClient: httpClient, eventLoop: req.eventLoop, apiKey: "test_key")
    
    let newCustomer = stripe.customers.create(email: customerFromClient.email,
                                              name: customerFromClient.name,
                                              phone: customerFromClient.phone)
    
    return newCustomer
}

`

IMPORTANT: Stripe receives each new customer when this func is called. A new customer is created with the data submitted (email, name, phone). Stripe Logs show no errors. However, Vapor crashes. Please help!

Error decoding connected accounts

It seems like there is a problem decoding the objects returned by stripe.connectAccounts.retrieve.

The error I am getting is:

valueNotFound(Swift.Double, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "super", intValue: nil)], debugDescription: "Expected Double but found null value instead.", underlyingError: nil)

But if I try curl https://api.stripe.com/v1/accounts/ the returned object is exactly as described in the Stripe documentation.

I tried to investigate the error but I was not able to find any single reference to super in the whole stripe-kit project.

Updates needed.

  • Remove savePaymentMethod from PaymentIntent APIs.
  • Remove unifiedProration from InvoiceItem APIs.
  • Remove plan and quantity from Subscription.

5.0.0 Release

Branch collecting breaking changes to release in the next major version

(⚠️ = breaking changes)

  • ⚠️ Pin to API version 2019-12-03
  • Add support for subscription schedules #18
  • ⚠️ created can't be optional anymore #19
  • Add support for Webhooks endpoints #20
  • Add support for Events endpoints #22

Webhook events for Stripe Identity

Are there plans to add the new webhook events for Stripe Identity?

  • identity.verification_session.canceled
  • identity.verification_session.created
  • identity.verification_session.processing
  • identity.verification_session.redacted
  • identity.verification_session.requires_input
  • identity.verification_session.verified

If there is no timeline, I can attempt to implement this and create a PR.

Not returning JSON - Creating a Stripe Connect Account

Hi everyone. I'm having trouble with my route that handles the onboarding of a user. With a POST call, I create a new Stripe Connect Account with confirmation on Stripe's logs. However, I do not receive any JSON in response to this POST. Stripe's logs show that the response is JSON with all relevant parameters attached to this new Stripe Connect Account. I have triple-checked the model that decodes the JSON. The same HTTP request made in Postman yields a response that contains email in the POST request (not JSON).

I have determined that the problem must exist on my server because 1) my client reads an error that the data received was not valid JSON, and 2) Stripe's logs show that the response to the POST is JSON.

I have attached my Vapor code below. I know that I am not implementing the proper return because whatever is being returned is not JSON. This is strange as the AccountRoutes.swift found in Stripe Kit show that the return to a create function is a StripeConnectAccount. Thank you for the help in advance!

`func createStripeConnectAccount(_ req: Request) throws -> EventLoopFuture {

    print("createStripeAccount called.")
    
    let stripeAccountInput = try req.content.decode(AccountDTO.self)
    
    let newStripeAccount = req.stripe.connectAccounts.create(
                                                        type: .express,
                                                        country: "US",
                                                        email: stripeAccountInput.email,
                                                        businessType: .individual,
                                                        defaultCurrency: .usd,
                                                        metadata: [
                                                            "licensePlate": stripeAccountInput.licensePlate,
                                                            "username": stripeAccountInput.username,
                                                        ],
                                                        capabilities: [:]
                                                        )
    
    return newStripeAccount
}`

Best,
Jake

Support `automatic_tax` parameter when creating Session

The Stripe API supports a parameter called automatic_tax that enables automatic tax collection when the Taxing Module is enabled.

Somehow this isn't something you can enable on the dashboard – you must add the parameter to Session creation to enable the feature. And I need it :D~

I'd like to create a PR where I addd a new parameter automaticTax, similar to the same parameter in QuoteRoutes.swift.

Error on loading charges for a specific payment_intent

Hi, I am trying to get the charge for a specific payment_intent id.

On my Vapor setup I am using:

req.stripe.charges.listAll(filter: ["payment_intent": "pi_XXXXXXXXXXXXXXXX"])

but I always get an StripeKit.StripeError and not a NIO.EventLoopFuture<StripeKit.StripeChargesList>

With curl it works like this:

curl https://api.stripe.com/v1/charges \
  -u sk_test_XXXXXXXXXXXXXXXXXXXXXXXXXX: \
  -d payment_intent="pi_XXXXXXXXXXXXXXXX" \
  -G

Is there any documentation or help?
thx Stefan

`@Expandable` used with array of Ids.

Usually the @Expandable property wrapper is used to represent an id of a stripe resource as a String or the Model specified if used with the property wrapper.

Gotta figure out a way to use the @Expandable<Model> property wrapper for an array of Ids

@Expandable<[StripeDiscount]> public var discounts: [String]?
// Property type '[String]?' does not match that of the 'wrappedValue'
// property of its wrapper type 'Expandable<[StripeDiscount]>'

Too busy to properly address this. Will come back to it later.
API-doc

Wrong parameter type

This issue leads to critial bug and an app is crashed when I tried to update a subscription. Please accept PR #68.

Initializers for models?

It would be helpful if you could create a Stripe model (StripeCustomer for example) with an initializer. Although all of the models right now are structs, one easy way in Xcode to generate initializers is to make them classes temporarily and generate member wise initializer.

Screen Shot 2020-02-13 at 6 59 53 PM

Will result in the following initializer.
Note we don't automatically get default nil values in the initializers.

Screen Shot 2020-02-13 at 7 00 18 PM

This is easy but takes time is all.

Decoding error with Payment Intent

Stripe is returning 'fail' instead of 'failed' for StripeCardValidationCheck.

StripeKit is expecting 'failed':
Screen Shot 2021-04-08 at 9 13 08 PM

But Stripe sends 'fail'. Example (/v1/payment_intents):
Screen Shot 2021-04-08 at 9 12 33 PM
Documentation:
Screen Shot 2021-04-08 at 9 17 14 PM

Exception:
dataCorrupted(Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "charges", intValue: nil), CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "paymentMethodDetails", intValue: nil), CodingKeys(stringValue: "card", intValue: nil), CodingKeys(stringValue: "checks", intValue: nil), CodingKeys(stringValue: "addressLine1Check", intValue: nil)], debugDescription: "Cannot initialize StripeCardValidationCheck from invalid String value fail", underlyingError: nil))

Thanks

Error when adding to Package.swift

Any idea why when I add this to my Package.swift file and run vapor xcode -y, I get the following error:

error: the package stripekit[https://github.com/vapor-community/stripekit.git] @ 1.0.0 contains incompatible dependencies:
swift-nio-http-client[https://github.com/swift-server/swift-nio-http-client.git] @ master

Error: Could not generate Xcode project: error: the package stripekit[https://github.com/vapor-community/stripekit.git] @ 1.0.0 contains incompatible dependencies:
swift-nio-http-client[https://github.com/swift-server/swift-nio-http-client.git] @ master

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.