Git Product home page Git Product logo

dwolla-v2-ruby's Introduction

Dwolla SDK for Ruby

This repository contains the source code for Dwolla's Ruby-based SDK, which allows developers to interact with Dwolla's server-side API via a Ruby API, with automatic OAuth token management included. Any action that can be performed via an HTTP request can be made using this SDK when executed within a server-side environment.

Table of Contents

Getting Started

Installation

To begin using this SDK, you will first need to download it to your machine. We use RubyGems to distribute this package.

Add this line to your application's Gemfile:

gem 'dwolla_v2', '~> 3.1'

And then execute:

$ bundle

Or install it yourself as:

$ gem install dwolla_v2

Initialization

Before any API requests can be made, you must first determine which environment you will be using, as well as fetch the application key and secret. To fetch your application key and secret, please visit one of the following links:

Finally, you can create an instance of Client with key and secret replaced with the application key and secret that you fetched from one of the aforementioned links, respectively.

# config/initializers/dwolla.rb
$dwolla = DwollaV2::Client.new(
  key: ENV["DWOLLA_APP_KEY"],
  secret: ENV["DWOLLA_APP_SECRET"],
  environment: :sandbox # defaults to :production
)

Configure Faraday (Optional)

Dwolla for Ruby uses Faraday to make HTTP requests. You can configure your own Faraday middleware and adapter when configuring your client. Remember to always include an adapter last, even if you want to use the default adapter.

# config/initializers/dwolla.rb
$dwolla = DwollaV2::Client.new(
  key: ENV["DWOLLA_APP_KEY"],
  secret: ENV["DWOLLA_APP_SECRET"]
) do |config|

  config.faraday do |faraday|
    faraday.response :logger
    faraday.adapter Faraday.default_adapter
  end
end

Making Requests

Once you've created a Client, currently, you can make low-level HTTP requests.

Low-level Requests

To make low-level HTTP requests, you can use the get(), post(), and delete() methods.

GET

# GET api.dwolla.com/resource?foo=bar
$dwolla.get "resource", foo: "bar"

POST

# POST api.dwolla.com/resource {"foo":"bar"}
$dwolla.post "resource", foo: "bar"

# POST api.dwolla.com/resource multipart/form-data foo=...
$dwolla.post "resource", foo: Faraday::UploadIO.new("/path/to/bar.png", "image/png")

DELETE

# DELETE api.dwolla.com/resource
$dwolla.delete "resource"
Setting Headers

To set additional headers on a request, you can pass a Hash of headers as the 3rd argument.

For example:

$dwolla.post "customers", { firstName: "John", lastName: "Doe", email: "[email protected]" },
                          { 'Idempotency-Key': 'a52fcf63-0730-41c3-96e8-7147b5d1fb01' }

Responses

The following snippets demonstrate successful and errored responses from the Dwolla API.

An errored response is returned when Dwolla's servers respond with a status code that is greater than or equal to 400, whereas a successful response is when Dwolla's servers respond with a 200-level status code.

Success

Successful requests return a DwollaV2::Response.

res = $dwolla.get "/"
# => #<DwollaV2::Response response_status=200 response_headers={"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:30:23 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; charset=UTF-8", "content-length"=>"150", "connection"=>"close", "set-cookie"=>"__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly", "x-request-id"=>"69a4e612-5dae-4c52-a6a0-2f921e34a88a", "cf-ray"=>"28ac1f81875941e3-MSP"} {"_links"=>{"events"=>{"href"=>"https://api-sandbox.dwolla.com/events"}, "webhook-subscriptions"=>{"href"=>"https://api-sandbox.dwolla.com/webhook-subscriptions"}}}>

res.response_status
# => 200

res.response_headers
# => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:30:23 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; charset=UTF-8", "content-length"=>"150", "connection"=>"close", "set-cookie"=>"__cfduid=d9dcd0f586c166d36cbd45b992bdaa11b1459179023; expires=Tue, 28-Mar-17 15:30:23 GMT; path=/; domain=.dwolla.com; HttpOnly", "x-request-id"=>"69a4e612-5dae-4c52-a6a0-2f921e34a88a", "cf-ray"=>"28ac1f81875941e3-MSP"}

res._links.events.href
# => "https://api-sandbox.dwolla.com/events"
Error

If the server returns an error, a DwollaV2::Error (or one of its subclasses) will be raised. DwollaV2::Errors are similar to DwollaV2::Responses.

begin
  $dwolla.get "/not-found"
rescue DwollaV2::NotFoundError => e
  e
  # => #<DwollaV2::NotFoundError response_status=404 response_headers={"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"} {"code"=>"NotFound", "message"=>"The requested resource was not found."}>

  e.response_status
  # => 404

  e.response_headers
  # => {"server"=>"cloudflare-nginx", "date"=>"Mon, 28 Mar 2016 15:35:32 GMT", "content-type"=>"application/vnd.dwolla.v1.hal+json; profile=\"http://nocarrier.co.uk/profiles/vnd.error/\"; charset=UTF-8", "content-length"=>"69", "connection"=>"close", "set-cookie"=>"__cfduid=da1478bfdf3e56275cd8a6a741866ccce1459179332; expires=Tue, 28-Mar-17 15:35:32 GMT; path=/; domain=.dwolla.com; HttpOnly", "access-control-allow-origin"=>"*", "x-request-id"=>"667fca74-b53d-43db-bddd-50426a011881", "cf-ray"=>"28ac270abca64207-MSP"}

  e.code
  # => "NotFound"
rescue DwollaV2::Error => e
  # ...
end
DwollaV2::Error subclasses:

See https://developers.dwolla.com/api-reference#errors for more info.

  • DwollaV2::AccessDeniedError
  • DwollaV2::InvalidCredentialsError
  • DwollaV2::NotFoundError
  • DwollaV2::BadRequestError
  • DwollaV2::InvalidGrantError
  • DwollaV2::RequestTimeoutError
  • DwollaV2::ExpiredAccessTokenError
  • DwollaV2::InvalidRequestError
  • DwollaV2::ServerError
  • DwollaV2::ForbiddenError
  • DwollaV2::InvalidResourceStateError
  • DwollaV2::TemporarilyUnavailableError
  • DwollaV2::InvalidAccessTokenError
  • DwollaV2::InvalidScopeError
  • DwollaV2::UnauthorizedClientError
  • DwollaV2::InvalidAccountStatusError
  • DwollaV2::InvalidScopesError
  • DwollaV2::UnsupportedGrantTypeError
  • DwollaV2::InvalidApplicationStatusError
  • DwollaV2::InvalidVersionError
  • DwollaV2::UnsupportedResponseTypeError
  • DwollaV2::InvalidClientError
  • DwollaV2::MethodNotAllowedError
  • DwollaV2::ValidationError
  • DwollaV2::TooManyRequestsError
  • DwollaV2::ConflictError

Changelog

  • 4.0.0 - Major version update to include support for Faraday 2.0, discontinue support for Ruby versions prior to v2.6, and adopt faraday-multipart in place of Faraday middleware.
  • 3.2.0 - Minor version update to support Ruby 3.2+. The change involves adding '**' to the codebase to make opts in ./lib/dwollav2/client.rb compliant with Ruby 3.2+. (Thanks @peterb-onramp!). #68
  • 3.1.1 - Fix deprecation warning on Faraday::Connection#authorization (Thanks @javierjulio!). #60
  • 3.1.0 - Added DwollaV2::MaxNumberOfResourcesError (Thanks @paulyeo21!). #54
  • 3.0.1 - Update dependencies (Thanks @sealabcore!). #48
  • 3.0.0 - Add integrations auth functions
  • 3.0.0.beta1 - Add token management functionality to DwollaV2::Client
  • 2.2.1 - Update dependencies
  • 2.2.0 - Change token url from www.dwolla.com/oauth/v2/token to accounts.dwolla.com/token
  • 2.1.0 - Ensure Time.iso8601 is defined so timestamps get parsed. #38 (Thanks @javierjulio!)
  • 2.0.3 - Add DuplicateResourceError #34 (Thanks @javierjulio!)
  • 2.0.2 - Fix bug in #30 (Thanks again @sobrinho!
  • 2.0.1 - Fix bugs in #27 + #28 (Thanks @sobrinho!)
  • 2.0.0
  • Rename DwollaV2::Response #status => #response_status, #headers => #response_headers to prevent conflicts with response body properties.
  • Remove support for Ruby versions < 2 (Bump public_suffix dependency version).
  • 1.2.3 - Implement #empty? on DwollaV2::Token to allow it to be passed to ActiveRecord constructor.
  • 1.2.2 - Strip domain from URLs provided to token.* methods.
  • 1.2.1 - Update sandbox URLs from uat => sandbox.
  • 1.2.0 - Refer to Client :id as :key in docs/public APIs for consistency.
  • 1.1.2 - Add support for verified_account and dwolla_landing auth flags.
  • 1.1.1 - Add TooManyRequestsError and ConflictError classes.
  • 1.1.0 - Support setting headers on a per-request basis.
  • 1.0.1 - Set user agent header.
  • 1.0.0 - Refactor Error class to be more like response, add ability to access keys using methods.
  • 0.4.0 - Refactor and document how DwollaV2::Response works
  • 0.3.1 - better DwollaV2::Error error messages
  • 0.3.0 - ISO8601 values in response body are converted to Time objects
  • 0.2.0 - Works with attr_encrypted
  • 0.1.1 - Handle 500 error with HTML response body when requesting a token

Community

  • If you have any feedback, please reach out to us on our forums or by creating a GitHub issue.
  • If you would like to contribute to this library, bug reports and pull requests are always appreciated!
    • After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
    • To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Docker

If you prefer to use Docker to run dwolla-v2-python locally, a Dockerfile is included at the root directory. Follow these instructions from Docker's website to create a Docker image from the Dockerfile, and run it.

Additional Resources

To learn more about Dwolla and how to integrate our product with your application, please consider visiting the following resources and becoming a member of our community!

dwolla-v2-ruby's People

Contributors

elbowdonkey avatar javierjulio avatar paulyeo21 avatar peterb-onramp avatar sausman avatar sealabcore avatar shreyathapa avatar sobrinho avatar spencerhunter avatar tarricsookdeo avatar

Stargazers

 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dwolla-v2-ruby's Issues

Deprecation warning with Faraday v1

Since we use both Dwolla and Plaid together, Plaid recently has made major updates and with updating to Plaid v14, it requires Faraday v1 at a minimum. The Dwolla library seems to play well with Faraday v1 but can cause many reported deprecation warnings due to this line:

f.authorization :Bearer, access_token if access_token

WARNING: Faraday::Connection#authorization is deprecated; it will be removed in version 2.0.
While initializing your connection, use #request(:authorization, ...) instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.

I believe the affected line should be changed to the following and that should do it:

        f.request :authorization, 'Bearer', access_token if access_token

It seems this should be supported as far back as Faraday v0.15 so it should be safe for a minor or patch release.

testing with rspec

Are there any examples of rspec testing with responses and errors?(using webmock of course)

Ruby 3.2+ Keywords Error In Lib/Client.rb

Our specs fail when updating to ruby 3.2. We traced the issue back to

def initialize opts
opts[:id] ||= opts[:key]
raise ArgumentError.new ":key is required" unless opts[:id].is_a? String
not being keywords compliant.

https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released/

Methods taking a rest parameter (like *args) and wishing to delegate keyword arguments through foo(*args) must now be marked with ruby2_keywords (if not already the case). In other words, all methods wishing to delegate keyword arguments through *args must now be marked with ruby2_keywords, with no exception. This will make it easier to transition to other ways of delegation once a library can require Ruby 3+. Previously, the ruby2_keywords flag was kept if the receiving method took *args, but this was a bug and an inconsistency. A good technique to find potentially missing ruby2_keywords is to run the test suite, find the last method which must receive keyword arguments for each place where the test suite fails, and use puts nil, caller, nil there. Then check that each method/block on the call chain which must delegate keywords is correctly marked with ruby2_keywords

To fix we add the '**' before the opts parameter and this resolved the issue.

Converting camel/kebab case to snake case and vice versa

I've been working with the library since beginning of last week to learn how to implement our system at work with Dwolla to cover all our cases. Everything has gone great but one of the issues we have is the inconsistency in casing which would leads us to write code that utilizes the Dwolla library with mixed case, both camel (response properties) and kebab (for _links values). As is standard for Ruby projects we write code in snake case. Would the team be interested in accepting changes so requests can be provided in snake case and converted to camel case and responses the other way around? I've dealt with this before successfully with another service. Has this topic ever come up internally? Would their be any desire to have specific response objects?

I spent time on this today and came up with changes that would work with hashie, just something quick to see if it was even doable. I looked into using Dash and their property transformation extension but it requires that you define all available properties so that was a no go. The other issue is because of the mixed case and how method_missing works, I couldn't get around by just changing the SuperHash class but also had to update the Response class too. I wanted to see if I could get this to work so at least responses could use snake_case but still support the original casing too. This is what I came up with:

For the Response class:

def method_missing method, *args, &block
  if method != :_links && method != :_embedded
    method = method.to_s.gsub(/(?:_+)([a-z])/) { $1.upcase }.to_sym
  end
  # ...
end

For the SuperHash class:

def method_missing method
  dashed_method = method.to_s.gsub(/_/, "-").to_sym

  if key? dashed_method
    self[dashed_method]
  elsif key? method
    self[method]
  else
    super method
  end
end

With these changes I can for example do the following:

customer = app_token.get(customer_url)
customer.first_name
# => "Javier"
customer._links.self.resource_type
# => "customer"

Would you open to any enhancements to the library in some way to handle this? I'm happy to do the work. I'm wondering if you'd be willing to have a discussion around it and especially if its already come up amongst your team. Thanks.

Conflict with specifying custom middleware

If I want to create custom middleware say for parsing Dwolla responses, I can but the way the following is structured I have to specify not just the default adapter line but also the response type, otherwise my middleware receives a string, not a hash.

f.response :json, :content_type => /\bjson$/
client.faraday.call(f) if client.faraday

I have a middleware to snake_case the keys in the response body and to get it to work I have to specify:

dwolla = DwollaV2::Client.new(...) do |config|
  config.faraday do |f|
    f.use UnderscoreResponseBodyKeys
    f.response :json, :content_type => /\bjson$/
    f.adapter Faraday.default_adapter
  end
end

I don't just have to specify the default adapter as mentioned but also the response type, otherwise my middleware will receive a string, not a hash. By having the response type after it works as expected and my middleware will receive a hash that DwollaV2 had parsed earlier in the stack.

I shouldn't have to specify the response type but it has to come before specifying the default adapter. At first I tried updating the Token class to have:

    def conn
      @conn ||= Faraday.new do |f|
        # ...
        client.faraday.call(f) if client.faraday
        f.response :json, :content_type => /\bjson$/
        f.adapter Faraday.default_adapter unless client.faraday
      end
    end

But I receive a warning here, since by just specifying the client.faraday block, the default adapter isn't set but it has to be the last item otherwise you receive a warning that this won't be supported:

WARNING: Unexpected middleware set after the adapter. This won't be supported from Faraday 1.0.

While I can temporarily specify the response type on my end I don't think the library should require that and I don't think that was the intention. But it seems you can get both specifying middleware or an adapter with the current configuration setup.

I would suggest breaking off the faraday adapter into its own block or config option that way devs can specify custom middleware while still retaining the defaults. I can submit a PR for that or another solution if you prefer something else?

Faraday Response Middleware - response details missing

When using faraday response middleware (for say debug logging) in the on_complete the response details (status, headers, body etc.) are missing.

For example the middleware below (the response logger that ships with faraday gives me the same results)

def call(env)
  info "#{env.method} #{env.url.to_s}"
  debug('request') { dump_headers env.request_headers }
  debug('request') { dump_body(env[:body]) } if env[:body] && log_body?(:request)
  @app.call(env).on_complete do |response_env|
    info('Status') { response_env.status.to_s }
    debug('response') { dump_headers response_env.response_headers }
    debug('response') { dump_body response_env[:body] } if env[:body] && log_body?(:response)
  end
end

When a Dwolla request is ran response_env.response_headers is nil and the response_env.body is actually the request body.

I've used this type of response middleware logging before (with other faraday setups) without issue and it's very helpful for debugging requests.

Make Dwolla Client Initialization Arguments Coherent

The Dwolla client initialization parameter name (id) is extremely confusing to someone coming into this codebase with significant OAuth experience. I strongly suggest that:

  • The id parameter be updated to read key
  • Documentation within this repository be updated, such that instead of ENV["DWOLLA_ID"] and the like, the Client is initialized with something like ENV["DWOLLA_CONSUMER_KEY"] or ENV["DWOLLA_KEY"]
  • The sample bootstrapping code at https://gist.github.com/sausman/df58a196b3bc0381b0e8 should be updated for significant clarity
  • The official Dwolla documentation should be updated for clarity. One example of this ambiguity is here: https://developers.dwolla.com/pages/sdks.html

Generating IAV token generates odd number of arguments error

I was sent over here by my developer relations contact. I am having a weird issue that I'm struggling to diagnose. I thought it might be something related to our codebase, but a couple things is making me think it's related to this library:

  1. This flow has worked for months now, and the code has not changed. All of a sudden, it doesn't work, but did work as of 10/18/18 (nothing has changed between then and now).
  2. There's nothing on our end that's inbetween the call being made, and the actual error being produced.

We're trying to generate an IAV token to pass to the dwolla.js widget to initialize bank authorization. The entirety of our code is as follows:

response = Client.post("customers/#{customer_id}/iav-token") response.token

Where Client.post calls

$dwolla.auths.client.post endpoint, params

Where endpoint is what's being passed in in that string. I've verified that the correct customer_id is being passed, and correlates with a valid account. I asked our contact if the account not having certified beneficial ownership yet might be causing an issue, and he said no.

Any help would be appreciate on this as it's interfering with our customers experience. I'll also post the stack trace below:

By the way, the error is coming directly from the Client.post call.

[127.0.0.1] [ip-172-28-234-124.ec2.internal] odd number of arguments for Hash excluded from capture: No project_id specified [127.0.0.1] [ip-172-28-234-124.ec2.internal] [127.0.0.1] [ip-172-28-234-124.ec2.internal] ArgumentError (odd number of arguments for Hash): [127.0.0.1] [ip-172-28-234-124.ec2.internal] [127.0.0.1] [ip-172-28-234-124.ec2.internal] hashie (3.5.7) lib/hashie/extensions/indifferent_access.rb:46:in []'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] hashie (3.5.7) lib/hashie/extensions/indifferent_access.rb:46:in []' [127.0.0.1] [ip-172-28-234-124.ec2.internal] dwolla_v2 (1.2.2) lib/dwolla_v2/super_hash.rb:9:in =='
[127.0.0.1] [ip-172-28-234-124.ec2.internal] dwolla_v2 (1.2.2) lib/dwolla_v2/error.rb:44:in ==' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/rescuable.rb:94:in include?'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/rescuable.rb:94:in rescue_with_handler' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/rescuable.rb:97:in rescue_with_handler'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/rescuable.rb:164:in rescue_with_handler' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal/rescue.rb:23:in rescue in process_action'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal/rescue.rb:20:in process_action' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal/instrumentation.rb:32:in block in process_action'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/notifications.rb:166:in block in instrument' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/notifications/instrumenter.rb:21:in instrument'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/notifications.rb:166:in instrument' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal/instrumentation.rb:30:in process_action'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal/params_wrapper.rb:252:in process_action' [127.0.0.1] [ip-172-28-234-124.ec2.internal] searchkick (3.0.0) lib/searchkick/logging.rb:209:in process_action'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activerecord (5.1.6) lib/active_record/railties/controller_runtime.rb:22:in process_action' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/abstract_controller/base.rb:124:in process'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionview (5.1.6) lib/action_view/rendering.rb:30:in process' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal.rb:189:in dispatch'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_controller/metal.rb:253:in dispatch' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:49:in dispatch'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:31:in serve' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/journey/router.rb:50:in block in serve'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/journey/router.rb:33:in each' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/journey/router.rb:33:in serve'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:844:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/etag.rb:25:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/conditional_get.rb:25:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/head.rb:12:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/session/abstract/id.rb:232:in context' [127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/session/abstract/id.rb:226:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/cookies.rb:613:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activerecord (5.1.6) lib/active_record/migration.rb:556:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/callbacks.rb:26:in block in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/callbacks.rb:97:in run_callbacks'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/callbacks.rb:24:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/executor.rb:12:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/debug_exceptions.rb:59:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] web-console (3.5.1) lib/web_console/middleware.rb:135:in call_app'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] web-console (3.5.1) lib/web_console/middleware.rb:28:in block in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] web-console (3.5.1) lib/web_console/middleware.rb:18:in catch'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] web-console (3.5.1) lib/web_console/middleware.rb:18:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/show_exceptions.rb:31:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] railties (5.1.6) lib/rails/rack/logger.rb:36:in call_app' [127.0.0.1] [ip-172-28-234-124.ec2.internal] railties (5.1.6) lib/rails/rack/logger.rb:24:in block in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/tagged_logging.rb:69:in block in tagged' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/tagged_logging.rb:26:in tagged'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/tagged_logging.rb:69:in tagged' [127.0.0.1] [ip-172-28-234-124.ec2.internal] railties (5.1.6) lib/rails/rack/logger.rb:24:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/remote_ip.rb:79:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/request_id.rb:25:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/method_override.rb:22:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/runtime.rb:22:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] activesupport (5.1.6) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/executor.rb:12:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] actionpack (5.1.6) lib/action_dispatch/middleware/static.rb:125:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] rack (2.0.6) lib/rack/sendfile.rb:111:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] rack-cors (1.0.2) lib/rack/cors.rb:97:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] sentry-raven (2.7.4) lib/raven/integrations/rack.rb:51:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] webpacker (3.5.5) lib/webpacker/dev_server_proxy.rb:22:in perform_request'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] rack-proxy (0.6.5) lib/rack/proxy.rb:57:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] railties (5.1.6) lib/rails/engine.rb:522:in call'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] puma (3.10.0) lib/puma/configuration.rb:225:in call' [127.0.0.1] [ip-172-28-234-124.ec2.internal] puma (3.10.0) lib/puma/server.rb:605:in handle_request'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] puma (3.10.0) lib/puma/server.rb:437:in process_client' [127.0.0.1] [ip-172-28-234-124.ec2.internal] puma (3.10.0) lib/puma/server.rb:301:in block in run'
[127.0.0.1] [ip-172-28-234-124.ec2.internal] puma (3.10.0) lib/puma/thread_pool.rb:120:in block in spawn_thread'

Method name clash for Funding Source

Hi,

when retrieving a customer's Funding Source the "status" attribute/method clashes with Response#status (HTTP status code) and the latter is returned instead of Funding Source status (e.g "verified").

From API doc:

funding_source_url = 'https://api-sandbox.dwolla.com/funding-sources/692486f8-29f6-4516-a6a5-c69fd2ce854c'

# Using DwollaV2 - https://github.com/Dwolla/dwolla-v2-ruby (Recommended)
funding_source = app_token.get funding_source_url
funding_source.name # => "Test checking account"

funding_source.status # => 200 instead of, for example "verified"

I dug a bit the code, but I didn't find an easy solution that does not interfere with the rest of the SDK...

Knowing the issue, a workaround to get Funding Source's status may be something like this:

require 'json'

fs_data = JSON.load funding_source.to_json
fs_status = fs_data["status"]  # =>  "verified"

Odd number of arguments for hash

So I ran into an issue this morning trying to make a request to create a business customer. I thought that my hash was malformed, and passing in the wrong number of arguments, and finally threw a binding.pry in the source of the error (the hashie gem) and found the following:

` From: /Users/andrewsoep/.rvm/gems/ruby-2.4.1/gems/hashie-
3.5.7/lib/hashie/extensions/indifferent_access.rb @ line 79
Hashie::Extensions::IndifferentAccess#convert!:

77: def convert!
78:   binding.pry

=> 79: keys.each do |k|
80: regular_writer convert_key(k), indifferent_value(regular_delete(k))
81: end
82: self
83: end

[1] pry(#Pry::Config)> keys
=> ["access_token", "token_type", "expires_in"]`

It seems as if the library is passing in an odd number of arguments, which is causing the issue at hand. The odd thing is, I use this gem for a lot of requests to the API, and it works fine, so I'm confused why this would be an issue. My code can be found below, if it illustrates something I'm doing in error.

` business_params = {
firstName: name[0],
lastName: name[1],
email: params[:admin_email],
type: org.client_type,
address1: org.address_line_1,
city: org.city,
state: org.region,
postalCode: org.postal_code,
businessName: org.name,
businessType: params[:business_type],
businessClassification: params[:business_classification],
ein: params[:ein]
}

  business_params = business_params.merge({ 
      controller: {
      firstName: controller_name[0],
      lastName: controller_name[1],
      title: params[:controller][:title],
      dateOfBirth: params[:controller][:date_of_birth],
      address: {
        address1: params[:controller][:address_line_1],
        address2: params[:controller][:address_line_2],
        city: params[:controller][:city],
        state: params[:controller][:stateProvinceRegion],
        postalCode: params[:controller][:postal_code],
        country: params[:controller][:country]
      }
    }
  }) if params["business_type"] != "soleProprietorship"

  begin
    response = Client.post "customers", business_params
    location = response.headers[:location]
  rescue DwollaV2::Error => e
    location = duplicate_location_for(e) || raise
  end`

DateOfBirth value not allowed

I am testing this on heroku in my staging env which matches key for key with production. I am running everything using sandbox for Dwolla, but keep getting this same issue.

Here is there service class I am using, the error is getting triggered by this line:
customer = @access_token.post 'customers', request_body

Any clue as to why this might be happening?

# Service for managing Dwolla customers
class DwollaCustomer
  def initialize(params)
    @dwolla = set_dwolla_instance
    @user = params[:user]
    @access_token = @dwolla.auths.client
  end

  def create
    customer = @access_token.post 'customers', request_body
    @user.update_attributes(dwolla_url: customer.headers[:location])
  end

  def funding_sources
    return unless @user.merchant? || @user.dwolla_url.nil?
    sources = @access_token.get "#{@user.dwolla_url}/funding-sources"
    sources._embedded['funding-sources']
  end

  private

  def request_body
    {
      firstName: @user.first_name,
      lastName: @user.last_name,
      businessName: @user.business_name,
      email: @user.email,
      type: 'personal',
      address1: @user.street_address,
      city: @user.city,
      state: @user.state,
      postalCode: @user.zipcode,
      dateOfBirth: @user.dob.strftime('%Y-%m-%d').to_s,
      ssn: @user.ssn.last(4)
    }
  end

  def set_dwolla_instance
    @dwolla = DwollaV2::Client.new(key: ENV['dwolla_key'], secret: ENV['dwolla_secret']) do |config|
      config.environment = ENV['dwolla_env'].to_sym
    end
  end
end

Here is the entire error produced:

DwollaV2::ValidationError: {"code"=>"ValidationError", "message"=>"Validation error(s) present. See embedded errors list for more details.", "_embedded"=>{"errors"=>[{"code"=>"NotAllowed", "message"=>"DateOfBirth value not allowed.", "path"=>"/dateOfBirth", "_links"=>{}}]}}

Error in getting application token

We don't want to go over OAuth path, we are going to use White Label API.

This is my config in /initializers

$dwolla = DwollaV2::Client.new(id: "4ccmHuxwlohsKxxxxxxxxxxxxxxxxxxxxxxxxx", secret: "B0SlLhSLGsOkBl8zBjxxxxxxxxxxxxxxxxx") do |optional_config|
  optional_config.environment = :sandbox
end

When I tried to get an application token I got following error.

2.2.2 :015 >  token = $dwolla.auths.client scope: "ManagerCustomers|Funding"
TypeError: no implicit conversion of Symbol into Integer
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/dwolla_v2-0.1.0/lib/dwolla_v2/auth.rb:49:in `[]'
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/dwolla_v2-0.1.0/lib/dwolla_v2/auth.rb:49:in `request_token'
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/dwolla_v2-0.1.0/lib/dwolla_v2/auth.rb:6:in `client'
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/dwolla_v2-0.1.0/lib/dwolla_v2/portal.rb:9:in `public_send'
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/dwolla_v2-0.1.0/lib/dwolla_v2/portal.rb:9:in `method_missing'
    from (irb):15
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/railties-4.0.13/lib/rails/commands/console.rb:90:in `start'
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/railties-4.0.13/lib/rails/commands/console.rb:9:in `start'
    from /home/akaruilabs/.rvm/gems/ruby-2.2.2/gems/railties-4.0.13/lib/rails/commands.rb:62:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'

Open to PR creating a links method set

One thing I've been thinking about is how there should be an easier way to access the responses of links.

Example

DWOLLA_CLIENT = DwollaV2::Client.new(params)

funding_sources_link  =
  DWOLLA_CLIENT
    .get('customers', limit: 1)
    ._embedded.dig('customers', 0 '_links', 'funding-sources', 'href')

DWOLLA_CLIENT.get("#{funding_source_link}", params)

So, granted I understand the HAL schema y'all are working off of, the fundamental basis of the schema will remain the same, but ideally my end goal would be to go

DWOLLA_CLIENT = DwollaV2::Client.new(params)

DWOLLA_CLIENT
  .get('customers', limit: 1)
  .links[0].get.funding_sources(params) # or funding_sources('get', params)

to accomplish roughly the same thing, where .links dynamcially dispatches the link information to methods to then re-call the client.

So, codewise, I'd imagine that the https://github.com/Dwolla/dwolla-v2-ruby/blob/main/lib/dwolla_v2/response.rb would contain a method that would look directly into the response maps, pull out the direct child _links and _embedded links object and create new methods based on that. The only caveat that would be tricky to work through is carrying over the same client request for both, which I think given the way token works, by adding the token to the initializer of the Response, we should (:crossed_fingers:) be able to easily pass over the request structure to the links by going

class Response
  def initialize response, token
    @response = response
    @token = token
  end

  def links
    @response.body["_links"].keys.each do |link| 
      # ideally we'd snakecase link here
      define_method(method, link, params=nil, headers=nil) do |path, params, headers|
        full_url = self.class.full_url client, path
        @token.public_send(method, full_url, params_headers)
      end
    end
  end
end

Granted I haven't tested this, but just thought it up over lunch, but if this is something amenable, I'd be happy to fork and set up a PR.

Issue with $dwolla.get function and search customer with multiple statuses

According to the documentation (https://docs.dwolla.com/#list-and-search-customers), you can stack statuses for a unique query (such as getting all suspended and unverified customers). However when I use the dwolla gem, instead of sending the multiple status (as the Dwolla API documentation indicates), it overwrites and only the last one is sent.

Example: $dwolla.get "customers", status: "suspended", status: "unverified"

The result is only status=unverified gets sent.

I also tried to send the whole url as a string, same result: $dwolla.get "https://api.dwolla.com/customers?status=suspended&status=unverified"

Opened ticket with Dwolla support thinking it was an issue with the API documentation. Instead it's an issue with the Gem and its inability to handle same status without overwriting.

Is there a known workaround for this issue?

error handling in rails

Hi, Dwolla does not permit creating multiple customers with the same email or multiple accounts with the same routing number. However, in rails, dwolla-v2-ruby returns the error below, which does not state what the cause of the error is.
screen shot 2017-05-17 at 3 22 08 pm

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.