Giter Club home page Giter Club logo

stripe's Introduction

Stripe

Swift Vapor

Stripe is a Vapor helper to use StripeKit

Usage guide

In your Package.swift file, add the following

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

To use Stripe in your Vapor application, set the environment variable for you Stripe API key

export STRIPE_API_KEY="sk_123456"      

Now you can access a StripeClient via Request.

struct ChargeToken: Content {
    var token: String
}

func chargeCustomer(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
    return try req.content.decode(ChargeToken.self).flatMap { charge in
        return req.stripe.charge.create(amount: 2500, currency: .usd, source: charge.stripeToken).map { stripeCharge in
            if stripeCharge.status == .success {
                return .ok
            } else {
                print("Stripe charge status: \(stripeCharge.status.rawValue)")
                return .badRequest
            }
        }
    }
}

License

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

Want to help?

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! 😀

stripe's People

Contributors

andreaferrando avatar andrewangeta avatar anthonycastelli avatar baarde avatar balestrapatrick avatar calebkleveter avatar jonny7 avatar kschins avatar ksmandersen avatar madsodgaard avatar nsmutablestring avatar robipresotto avatar sorix avatar tanner0101 avatar vzsg 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's Issues

Unable to prefill customer email when creating a checkout session

If I set the customerEmail property when creating a checkout session, I get the following error when triggering my route:

{"error":true,"reason":"Value of type 'UnkeyedDecodingContainer' required for key 'customerDetails.super'."}

Why does this happen? Based on the following information from the Stripe API docs, this should just prefill the email, no?

customer_email
optional
If provided, this value will be used when the Customer object is created. If not provided, customers will be asked to enter their email address. Use this parameter to prefill customer data if you already have an email on file. To access information about the customer once a session is complete, use the customer field.

Nullable Source "creation" Date

I ran into this decoding error when trying to make a charge: Value required for key 'source.created'.

The code that got me there was the following:

let stripe = try req.make(StripeClient.self)

return try stripe.charge.create(amount: 1234, currency: .usd, description: "TESTING", customer: "cus_DSt0Pqu1iiiQDt").map() { charge in
    ...
}

I can fix it by changing the created var in Source.swift to an optional and its decoding line to decodeIfPresent. I'd open a PR for it but I don't have write access. Let me know how you'd like to progress. Thanks!

Content-Type error from Stripe API

Hey guys,

Sorry to bother, another issue. I am getting an error response from the stripe API declaring a content-type error. Not sure what the issue is, will try and do some digging.

"Invalid request: unsupported Content-Type text/plain; charset=utf-8. If error persists and you need assistance, please contact [email protected]."

Source.type missing crash

Quite possibly I've misunderstood something as I'm very new to Stripe. I've followed the docs on Checkout and then the docs on Creating Charges and successfully retrieved my 'Strip token. I then make the following call to stripe-provider:

 return try stripeClient.charge.create(
                amount: Int(amount * 100.0),
                currency: .usd,
                source: stripeToken,
                statementDescriptor: "EFL 1 Year"
            )

According to Stripe's website the charge is successful. However, StripeSource.init crashes in the following code as type is nil.

        type = try container.decodeIfPresent(SourceType.self, forKey: .type)
        
        switch type! {
        case .card:

I've locally made the following changes and all works as expected:

        type = try container.decodeIfPresent(SourceType.self, forKey: .type)
        
        switch type {
        case .some(.card):
            card = try container.decodeIfPresent(StripeCard.self, forKey: .card)
...
            
        default: break
        }

Am I missing something in my call to cause type to be set in the response?

Collecting platform fees with Connect destination charges

As already discussed with @anthonycastelli and @Andrewangeta, the platform fee collection when creating destination charges is currently broken.

The documentation says that this should be achieved by simply specifying the total amount, together with destination[account] and destination[amount] where latter should be calculated by subtracting the platform’s fee from the total charge amount. No explicit "application fee" parameter is needed.

Please find my PR that fixes this issue referenced below. ✌️

[ ERROR ] DecodingError.valueNotFound - Token issue

after the payment looks successful, i am redirected to a page where i get following error.

{"error":true,"reason":"Value of type 'String' required for key 'token'."}

in the console log, i also see:

[ ERROR ] DecodingError.valueNotFound: Value of type 'String' required for key 'token'. (ErrorMiddleware.swift:26)

i launch the stripe popup via the following simple integration they explain at:

https://stripe.com/docs/checkout#integration-simple

Any idea what step I am missing at this moment?

URL query injection

When submitting a request, the query parameters are concatenated without any kind of percent encoding.

  • Values that contain + (e.g. +1 (505) 503-4455) contain spaces instead.
  • Values that contain & (e.g. Hamlin, Hamlin & McGill) are truncated.
  • An attacker may be able to add or change parameters in any request that accepts an arbitrary value submitted by the user (e.g. a valid email address).

Few bugs

try drop.addProvider(Stripe.Provider.self) - No longer works,

try StripeClient(apiKey: key).account.create(type: .standard, email: user.person.email, country: user.person.address.country, metadata: node).serializedResponse() - returns a Thread 2: Fatal error: Unexpectedly found nil while unwrapping an Optional value

DecodingError.keyNotFound: Value required for key 'error'.

func createCustomerId(_ req: Request) throws -> Future {

    return try req.content.decode(StripeCustomer.self).flatMap(to: StripeCustomer.self, { user in
        let response = try req.make(StripeClient.self).customer.create(email: user.email)
        return response
    })
}

}

Works perfectly, but the response is an error? Not sure where it's catching or missing a value.

Make Models Parameterizable

The models don't extend Parameterizable which causes the user to have to extract each parameter individually in the request and then set them to model creation. This is very verbose.

Only have optional properties on models where they are truly optional.

Currently for all the stripe models all the properties are optional. This doesn't need to be the case anymore since the library is in a solid state right now. Slowly but surely we can start removing optional types from properties that are guaranteed by the API like id and object for example.

Provide default values for all route initializers.

Currently when initializing a route service optional values don't have a default of nil and the initializer can sometimes looks like this:

try droplet.stripe?.subscriptions.create(forCustomer: "someid",
                                                     plan: plan,
                                                     applicationFeePercent: nil,
                                                     couponId: nil,
                                                     items: nil,
                                                     quantity: nil,
                                                     source: nil,
                                                     taxPercent: nil,
                                                     trialEnd: nil,
                                                     trialPeriodDays: nil,
                                                     metadata: Node(["hello":"world"]))

ideally when you only have to pass in parameters that you care about, it should look cleaner and easier to read.

try droplet.stripe?.subscriptions.create(forCustomer: "someid",
                                                     plan: plan,
                                                     metadata: Node(["hello":"world"]))

Documentation

There is a lack of documentation or started application that shows how to properly use this package.

Unable to retrieve list of customers

Unable to retrieve a list of customers.

Error:

"error": true,
"reason": "Value required for key 'total_count'."
}```

My code is 
func index(_ req: Request) throws -> Future<StripeCustomersList> {
    let client = try req.make(StripeClient.self)
    print(client)
    return try client.customer.listAll()
}

Charges list request fails due to trailing /

When trying to retrieve a list of all charges for a customer, the network request with Stripe fails due to a trailing / in the URL. v1/charges/ is invalid and should be v1/charges.

According to the Stripe API logs,

{ "error": { "message": "Unrecognized request URL (GET: /v1/charges/). If you are trying to list objects, remove the trailing slash. If you are trying to retrieve an object, make sure you passed a valid (non-empty) identifier in your code. Please see https://stripe.com/docs or we can help at https://support.stripe.com/.", "type": "invalid_request_error" } }

Concrete Routes Don't Allow Optional Values

I ran into the issue of trying to create a customer with only an email and description. Normally most parameters are optional. This is true in the Model and also in the protocol for CustomerRoutes.swift. The issue lies in the fact that the implementation StripeCustomerRoutes doesn't set the optional values to = nil like the extension of the protocol. This causes the parameter to be required or else you will receive an error to the one bellow.

error: missing argument for parameter 'accountBalance' in call
            let stripeCustomer = try client.customer.create(email: customer.email, description: customer.description)
                                                            ^
                                                            accountBalance: <#Int?#>,
Stripe.CustomerRoutes:2:17: note: 'create(accountBalance:businessVatId:coupon:defaultSource:description:email:metadata:shipping:source:)' declared here

Add metadata protocol to encode custom structs as part of requests.

Quoting @anthonycastelli

"One thought I had regarding metadata for Stripe is why don't we create a basic protocol, i.e.

protocol StripeMetadata: Content { }

then we can be more explicit with metadata. Right now I have a bunch of integers and optionals that I have to convert for my metadata into a string dictionary, it'd be nice to just create a struct, then populate that.

struct ChargeMetadata: StripeMetadata {
    var userName: String
    var userId: UUID
}
return charge.create(..., metadata: ChargeMetadata(userName: user.name, userId: user.id"))

At this point we could then serialize the data as part of the body data, right?"

Conform StripeError to AbortError

I have error middleware in place so that my API gets properly formatted errors. When trying to conform the StripeError to AbortError, the responses are still in the format of StripeErrors. Any insight on this?

I tried the following:

extension StripeError: AbortError {
    public var status: HTTPResponseStatus {
        return .badRequest
    }
    
    public var reason: String {
        return "because it failed..."
    }   
}

For some reason, I'm also getting status code 200 for errors..

[ ERROR ] DecodingError.keyNotFound: Value required for key 'sources.data.Index 0.client_secret'

Getting the above error from the Stripe API when creating a new Customer. To the creation call, I'm passing in an email and a token source. Here's the code for the endpoint that's being hit:

func create(_ req: Request) throws -> Future<HTTPStatus> {
        return try req.content.decode(UserRequest.self).flatMap { userRequest in
            let stripeClient = try req.make(StripeClient.self)
            return try stripeClient.customer.create(email: userRequest.email, source: userRequest.stripeToken)
                .flatMap(to: HTTPStatus.self) { stripeCustomer in
                    let newUser = User(email: userRequest.email,
                                       password: userRequest.password,
                                       plate: userRequest.plate,
                                       stripeCustomerID: stripeCustomer.id)
                    return newUser.save(on: req).map { user in
                        return HTTPStatus.created
                    }
            }
        }
    }

LICENSE

Hey! I am interested in including this library into my project. Since there is no license specified, may I know what's your thought on it?
I think MIT license would make sense in this case. If you want I can open a PR by adding it 👍

Additional owners: European issue

In Europe, Stripe always requires that you provide information about additional owners of the company and this is documented [here|https://stripe.com/docs/connect/updating-accounts#additional-owners]. You can have up to 4 additional owners as they need anyone owning 25% or more but you don't always have 4.

In the event of 0 additional owners the field still needs to be populated similar to the PHP library:
"additional_owners" => ""

In doing so Stripe doesn't complain about the additional owners fields being populated.

Thanks

Multiple Stripe Accounts

I have a strange request, but it's a thing:

So I have a service that will charge different stripe accounts. How should the config be configured for that? We need some kind of a way to either replace the given stripe config - or to use multiple configs at the same time.

As of right now it does not look like either the Vapor-Services nor the structure of this package allow for that.

Any suggestions or ideas?

Upcoming Invoice has no id

When retrieving the upcoming invoice for a customer, the decoding fails as the response does not contain an id for the upcoming invoice (the invoice does not exist yet).

I see several ways to solve that issue but they're all bad:

  • Make Invoice.id optional. (but it's inconsistent and breaks source compatibility)
  • Add a new UpcomingInvoice type. (but it's inelegant)
  • Implement init(from:) and generate a fake id if it's missing. (but We're Not Hackers!)

Does anybody have a better idea? What solution is the least bad?

Replace Node in route initializers and specify a stronger type to then convert to Node

Routes only take In Nodes for collection types like JSON, Arrays of JSON, Dictionaries, Arrays etc. It's worth adding the specific type we're looking for in the parameter list and then convert it to node inside the api call. This would help make the API more easier to use and leaves out the guessing of what type the API is looking for. For example

This

create(name: String, id: String?, active: Bool?, attributes: Node?, caption: String?, deactivateOn: Node?, description: String?, images: Node?, packageDimensions: Node?, shippable: Bool?, url: String?, metadata: Node? = nil)

would become this

create(name: String, id: String?, active: Bool?, attributes: [String]?, caption: String?, deactivateOn: [String]?, description: String?, images: [String]?, packageDimensions: Node?, shippable: Bool?, url: String?, metadata: [String: Node]? = nil)

Add webhooks support

I'm looking to add support for Webhooks. Has there been discussion on how we want to implement this?

It seems like the events use roughly the same models that we already have, just needs some parent data. I have a rough implementation already. I think we need some sort of StripeAPIResponse object to handle decoding (mainly the unix timestamps).

Should we start a new branch for this?

Better Setup Walkthrough

The README would benefit from a more in-depth setup explanation.

For example, I added the Package line, ran vapor update, regen'd the xcodeproj, and then tried the setup steps, but I don't know what to import. Tried Stripe, StripeProvider, and StripeClient to no avail. Also, I think the "Register the config and the provider to your Application" code example could be explained better. Does all of that go in app.swift? configure.swift?

Would really appreciate the additional explanation because following the current steps does not work for me. Thanks!

Typo in Customer.bussinessVATId

Update (2018-09-26)

The problem is actually due to a typo in Customer.bussinessVATId.

Please disregard the end of this comment.


The VAT information isn't encoded and decoded anymore.

Changes since API version 2018-05-21:

The business_vat_id field was changed from String to Hash called tax_info, consisting of tax_id and type, in both requests and responses.

Source

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.