Giter VIP home page Giter VIP logo

pay's Introduction

💳 Pay - Payments engine for Ruby on Rails

Build Status Gem Version

Pay is a payments engine for Ruby on Rails 6.0 and higher.

⚠️ Upgrading? Check the UPGRADE guide for required changes and/or migration when upgrading from a previous version of Pay.

🧑‍💻 Tutorial

Want to see how Pay works? Check out our video getting started guide.

🏦 Payment Processors

Our supported payment processors are:

  • Stripe (SCA Compatible using API version 2022-11-15)
  • Paddle (SCA Compatible & supports PayPal)
  • Braintree (supports PayPal)
  • Fake Processor (used for generic trials without cards, free subscriptions, testing, etc)

Want to add a new payment provider? Contributions are welcome.

We make our best attempt to standardize the different payment providers. They function differently so keep that in mind if you plan on doing more complex payments. It would be best to stick with a single payment provider in that case so you don't run into discrepancies.

📚 Docs

🙏 Contributing

If you have an issue you'd like to submit, please do so using the issue tracker in GitHub. In order for us to help you in the best way possible, please be as detailed as you can.

If you'd like to open a PR please make sure the following things pass:

bin/rails db:test:prepare
bin/rails test
bundle exec standardrb

📝 License

The gem is available as open source under the terms of the MIT License.

pay's People

Contributors

ameft avatar bilalbudhani avatar cjilbert504 avatar clemoun avatar deanpcmad avatar dependabot-preview[bot] avatar dependabot[bot] avatar digitalmoksha avatar dwarfmondo avatar excid3 avatar feliperaul avatar guillaumebriday avatar hboon avatar jasoncharnes avatar jespr avatar joemasilotti avatar kieranklaassen avatar le-doude avatar ledermann avatar mguidetti avatar mpvosseller avatar nm avatar olimart avatar rameerez avatar rathboma avatar ro31337 avatar rockwellll avatar rodreegez avatar shawnaukstak avatar stevepolitodesign 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pay's Issues

Braintree JS Elements

Could not get Braintree/Paypal CC to populate, using Braintree payments resource links.

Does anybody have a Braintree client presentation JS elements to share?

Thanks!

Can't get emails to send

Emails are not sending. This is my pay.rb in config/initializers

Pay.setup do |config|
  config.billable_class = 'User'
  config.billable_table = 'users'

  config.chargeable_class = 'Pay::Charge'
  config.chargeable_table = 'pay_charges'

  # For use in the receipt/refund/renewal mailers
  config.business_name = "Example"
  config.business_address = "1600 Pennsylvania Avenue NW"
  config.application_name = "Example App"
  config.support_email = "[email protected]"

  config.send_emails = true

  config.automount_webhook_routes = true
  config.webhooks_path = "/webhooks" # Only when automount_webhook_routes is true
end

I have Sendgrid on Heroku. Emails are working for other parts of my app like devise confirmable. Is there anything else I need to config like config/environments/production.rb?

How to use the gem to have a user linked to multiple items each with one subscription?

Hi,

I don't know how to use this gem to support the following idea: I want a user to have a subscription per item.

I have the current prototype, that allows one item to be billable.

class Item < ApplicationRecord
  include Pay::Billable
  belongs_to :use
  ...
end

class User < ApplicationRecord
  has_many :items
  ... 
end

With this prototype, the subscription can be created.

But if the user creates a new item and a subscription for it, In Stripe a new customer(user) is created

I don't want one cutomer per Item, I want one customer per User.

How should I structure my code to make it work?

class Item < ApplicationRecord
  belongs_to :use
  ...
end

class User < ApplicationRecord
  include Pay::Billable
  has_many :items
  ...
end

I suppose this will make my user unique, but how do I link the item to the subscription?

Stripe update subscription webhook doesn't use canceled_at field

The following stripe event is handled by the stripe update subscription webhook, but the webhook doesn't use canceled_at field so the ends_at field is set to nil.
https://github.com/pay-rails/pay/blob/master/lib/pay/stripe/webhooks/subscription_updated.rb

The subscription is marked as: incomplete_expired and the "cancel_at": null, and "cancel_at_period_end": false,

but canceled_at is set: "canceled_at": 1568998560,

{
  "id": "evt_1FKpO",
  "object": "event",
  "api_version": "2019-09-09",
  "created": 1568998561,
  "data": {
    "object": {
      "id": "sub_FqW",
      "object": "subscription",
      "application_fee_percent": null,
      "billing": "charge_automatically",
      "billing_cycle_anchor": 1568998489,
      "billing_thresholds": null,
      "cancel_at": null,
      "cancel_at_period_end": false,
      "canceled_at": 1568998560,
      "collection_method": "charge_automatically",
      "created": 1568998489,
      "current_period_end": 1571590489,
      "current_period_start": 1568998489,
      "customer": "cus_FpM1",
      "days_until_due": null,
      "default_payment_method": null,
      "default_source": null,
      "default_tax_rates": [
        {
          "id": "txr_1FKo",
          "object": "tax_rate",
          "active": true,
          "created": 1568994589,
          "description": null,
          "display_name": "Tax",
          "inclusive": false,
          "jurisdiction": null,
          "livemode": false,
          "metadata": {
          },
          "percentage": 20
        }
      ],
      "discount": null,
      "ended_at": 1568998560,
      "items": {
        "object": "list",
        "data": [
          {
            "id": "si_FqWQC",
            "object": "subscription_item",
            "billing_thresholds": null,
            "created": 1568998490,
            "metadata": {
            },
            "plan": {
              "id": "monthly",
              "object": "plan",
              "active": true,
              "aggregate_usage": null,
              "amount": 2900,
              "amount_decimal": "2900",
              "billing_scheme": "per_unit",
              "created": 1568993616,
              "currency": "eur",
              "interval": "month",
              "interval_count": 1,
              "livemode": false,
              "metadata": {
              },
              "nickname": "Monthly",
              "product": "prod_EPKg",
              "tiers": null,
              "tiers_mode": null,
              "transform_usage": null,
              "trial_period_days": null,
              "usage_type": "licensed"
            },
            "quantity": 1,
            "subscription": "sub_FqW",
            "tax_rates": [
            ]
          }
        ],
        "has_more": false,
        "total_count": 1,
        "url": "/v1/subscription_items?subscription=sub_FqW"
      },
      "latest_invoice": "in_1FKpNdLB8",
      "livemode": false,
      "metadata": {
      },
      "pending_setup_intent": null,
      "plan": {
        "id": "monthly",
        "object": "plan",
        "active": true,
        "aggregate_usage": null,
        "amount": 2900,
        "amount_decimal": "2900",
        "billing_scheme": "per_unit",
        "created": 1568993616,
        "currency": "eur",
        "interval": "month",
        "interval_count": 1,
        "livemode": false,
        "metadata": {
        },
        "nickname": "Monthly",
        "product": "prod_EPKg",
        "tiers": null,
        "tiers_mode": null,
        "transform_usage": null,
        "trial_period_days": null,
        "usage_type": "licensed"
      },
      "quantity": 1,
      "schedule": null,
      "start": 1568998489,
      "start_date": 1568998489,
      "status": "incomplete_expired",
      "tax_percent": 20,
      "trial_end": null,
      "trial_start": null
    },
    "previous_attributes": {
      "canceled_at": null,
      "ended_at": null,
      "status": "incomplete"
    }
  },
  "livemode": false,
  "pending_webhooks": 1,
  "request": {
    "id": "req_R7",
    "idempotency_key": null
  },
  "type": "customer.subscription.updated"
}

Remove the processor argument for subscribe

I'm adding a charge method and realized if we require the processor every time for the charge and subscribe methods it'll get pretty annoying. Should be simple enough if we just have you assign processor independently ahead of time like we do with card_token.

Randomly failing builds

Hi, has anyone seen this kind of build failure?
https://travis-ci.org/jean-francois-labbe/pay/builds/482508048

Pay::Stripe::Webhooks::ChargeRefundedTest#test_a_charge_isn't_updated_with_the_refunded_amount_if_a_corresponding_charge_can't_be_found_(obviously):
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: users: INSERT INTO "users" ("email", "processor", "processor_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)
    sqlite3 (1.3.13) lib/sqlite3/database.rb:91:in `initialize'

I've not been able to consitently reproduce the issue even by setting the seed parameter
At first I thought it could be due to an issue due to ruby version but it's not.

Storing card data in the Subscription instead of User

Hi,

I've noticed that the follow fields are added to the user table.

add_column Pay.billable_table, :card_type, :string
add_column Pay.billable_table, :card_last4, :string
add_column Pay.billable_table, :card_exp_month, :string
add_column Pay.billable_table, :card_exp_year, :string

Why don't you store them in the subscription?
Because a user can have multiple subscription and a different card per subscription.
That will allow us to display which card has been registered for a specific subscription.

Catch stripe and braintree errors and re-raise as Pay::Error

In order to provide a simpler interface that standardizes the APIs, we should catch errors on the following methods at a minimum and re-raise as Pay::Error:

  • customer
  • charge
  • subscribe
  • subscription.swap
  • subscription.cancel
  • subscription.resume

We should also make sure Pay::Error provides access to the original error object so you can inspect it directly if needed. This might look like exception.original_exception.

We could also maybe define exception.stripe? and exception.braintree? methods to help determine if the error came from Stripe or Braintree if that seems useful.

The message from the original error should be propagated upwords as the error message for Pay::Error as well.

Safe for production use?

Hello!

This gem looks really promising but currently does not have a stable release, although it appears to be getting close to one. Would you recommend avoiding use in a production environment at this time? Are there any considerations we should take into account if we do right now?

Thanks!

Retreiving subscriptions

How are subscriptions going to be called when trying to retrieve them to update or delete them??

#lib/pay/billable/stripe.rb

      def stripe_subscription(subscription_id)
        ::Stripe::Subscription.retrieve(subscription_id)
      end

A possible solution would be to add to the user a processor_subscription_id or so so we can call

  ::Stripe::Subscription.retrieve(current_user.processor_subscription_id)

But that also means a user can only have one subscription at a time, I believe. Is that OK?

Could not find generator 'pay:email_views'

rails generate pay:email_views
Running via Spring preloader in process 16092
Could not find generator 'pay:email_views'. Maybe you meant 'kaminari:views', 'devise:views' or 'erb:mailer'

Seems like migrations ghost run on the gem until another proceeding migration is run. No rows were added to project schema.

Issue happened when using Jumpstart Rails basic

How do I add idempotency_key to new subscription?

How do I prevent someone from accidentitly getting charged multiple times?

Can I send an idempotency_key while subscribing the user? I tried the code below, but it only created the customer and didn't subscribe the user.

idempotency_key = SecureRandom.hex(6)

user.subscribe(plan: plan.id, idempotency_key: idempotency_key)

If this is not how it should be done, what way do you recommend to prevent duplicate charges? Thanks!

Allow env variables to override configuration

Hi,

What do you think about giving the highest priority to configuration from env variables?
here the priority is: secrets, credentials, env variables.

This doesn't allow us to use env variables to override configuration in review env or staging env

That would be interresting to use the same priority as in rails for DATABASE_URL for example

What do you think?

Make email content customizable

Would be nice to be able to customize the emails sent via UserMailer (receipt.html.erb and subscription_renewing.html.erb)

Issue running tests from master, Pay::Subscription::BraintreeTest#test_resume_on_grace_period:

Hello,

I'm facing the following issue when running tests from master 976cbad.

The error is visible here https://travis-ci.org/jean-francois-labbe/pay/jobs/588079100

Error:
Pay::Subscription::BraintreeTest#test_resume_on_grace_period:
VCR::Errors::UnhandledHTTPRequestError: 

================================================================================

An HTTP request has been made that VCR does not know how to handle:
  PUT https://api.sandbox.braintreegateway.com/merchants/zyfwpztymjqdcc5g/subscriptions/47grp2

VCR is currently using the following cassette:
  - /home/travis/build/jean-francois-labbe/pay/test/vcr_cassettes/braintree-resume.yml
    - :record => :once
    - :match_requests_on => [:method, :uri]

Under the current configuration VCR can not find a suitable HTTP interaction
to replay and is prevented from recording new requests. There are a few ways
you can deal with this:

  * If you're surprised VCR is raising this error
    and want insight about how VCR attempted to handle the request,
    you can use the debug_logger configuration option to log more details [1].

  * You can use the :new_episodes record mode to allow VCR to
    record this new request to the existing cassette [2].

  * If you want VCR to ignore this request (and others like it), you can
    set an `ignore_request` callback [3].
  * The current record mode (:once) does not allow new requests to be recorded
    to a previously recorded cassette. You can delete the cassette file and re-run
    your tests to allow the cassette to be recorded with this request [4].

  * The cassette contains 4 HTTP interactions that have not been
    played back. If your request is non-deterministic, you may need to
    change your :match_requests_on cassette option to be more lenient
    or use a custom request matcher to allow it to match [5].
[1] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/configuration/debug-logging
[2] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/record-modes/new-episodes
[3] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/configuration/ignore-request
[4] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/record-modes/once
[5] https://www.relishapp.com/vcr/vcr/v/4-0-0/docs/request-matching

================================================================================
    vcr (4.0.0) lib/vcr/request_handler.rb:97:in `on_unhandled_request'
    vcr (4.0.0) lib/vcr/library_hooks/webmock.rb:129:in `on_unhandled_request'
    vcr (4.0.0) lib/vcr/request_handler.rb:24:in `handle'
    vcr (4.0.0) lib/vcr/library_hooks/webmock.rb:144:in `block in <module:WebMock>'
    webmock (3.5.1) lib/webmock/stub_registry.rb:28:in `block in register_global_stub'
    webmock (3.5.1) lib/webmock/request_pattern.rb:40:in `matches?'
    webmock (3.5.1) lib/webmock/stub_registry.rb:58:in `block in request_stub_for'
    webmock (3.5.1) lib/webmock/stub_registry.rb:57:in `each'
    webmock (3.5.1) lib/webmock/stub_registry.rb:57:in `detect'
    webmock (3.5.1) lib/webmock/stub_registry.rb:57:in `request_stub_for'
    webmock (3.5.1) lib/webmock/stub_registry.rb:50:in `response_for_request'
    webmock (3.5.1) lib/webmock/http_lib_adapters/net_http.rb:79:in `request'
    braintree (2.96.0) lib/braintree/http.rb:137:in `block in _http_do'
    webmock (3.5.1) lib/webmock/http_lib_adapters/net_http.rb:123:in `start_without_connect'
    webmock (3.5.1) lib/webmock/http_lib_adapters/net_http.rb:150:in `start'
    braintree (2.96.0) lib/braintree/http.rb:109:in `_http_do'
    braintree (2.96.0) lib/braintree/http.rb:46:in `put'
    braintree (2.96.0) lib/braintree/subscription_gateway.rb:59:in `update'
    /home/travis/build/jean-francois-labbe/pay/lib/pay/braintree/subscription.rb:15:in `braintree_cancel'
    /home/travis/build/jean-francois-labbe/pay/app/models/pay/subscription.rb:49:in `cancel'
    /home/travis/build/jean-francois-labbe/pay/test/models/braintree_test.rb:32:in `block (2 levels) in <class:BraintreeTest>'
    vcr (4.0.0) lib/vcr/util/variable_args_block_caller.rb:9:in `call_block'
    vcr (4.0.0) lib/vcr.rb:188:in `use_cassette'
    /home/travis/build/jean-francois-labbe/pay/test/models/braintree_test.rb:29:in `block in <class:BraintreeTest>'
    minitest (5.11.3) lib/minitest/test.rb:98:in `block (3 levels) in run'
    minitest (5.11.3) lib/minitest/test.rb:195:in `capture_exceptions'
    minitest (5.11.3) lib/minitest/test.rb:95:in `block (2 levels) in run'
    minitest (5.11.3) lib/minitest.rb:265:in `time_it'
    minitest (5.11.3) lib/minitest/test.rb:94:in `block in run'
    minitest (5.11.3) lib/minitest.rb:360:in `on_signal'
    minitest (5.11.3) lib/minitest/test.rb:211:in `with_info_handler'
    minitest (5.11.3) lib/minitest/test.rb:93:in `run'
    minitest (5.11.3) lib/minitest.rb:960:in `run_one_method'
    minitest (5.11.3) lib/minitest.rb:334:in `run_one_method'
    minitest (5.11.3) lib/minitest.rb:321:in `block (2 levels) in run'
    minitest (5.11.3) lib/minitest.rb:320:in `each'
    minitest (5.11.3) lib/minitest.rb:320:in `block in run'
    minitest (5.11.3) lib/minitest.rb:360:in `on_signal'
    minitest (5.11.3) lib/minitest.rb:347:in `with_info_handler'
    minitest (5.11.3) lib/minitest.rb:319:in `run'
    railties (5.2.3) lib/rails/test_unit/line_filtering.rb:10:in `run'
    minitest (5.11.3) lib/minitest.rb:159:in `block in __run'
    minitest (5.11.3) lib/minitest.rb:159:in `map'
    minitest (5.11.3) lib/minitest.rb:159:in `__run'
    minitest (5.11.3) lib/minitest.rb:136:in `run'
    minitest (5.11.3) lib/minitest.rb:63:in `block in autorun'
bin/rails test /home/travis/build/jean-francois-labbe/pay/test/models/braintree_test.rb:28

Does anyone know why?

Coupon Option?

Noticed that coupon cannot be applied to subscription upon creation. Is it on the things to do list?

undefined method `create_stripe_subscription' for User.

Hi,
I'm facing this issue undefined method `create_stripe_subscription' for #User:0x0000557d8d2a3518 Did you mean? create_subscription

It works few times then it crash. Do you have any idea why?

Pay version: 1.0.0.alpha1, master@0c93698

How to check if a users subscription is active?

What's the preferred way to see if the users subscription is active? Here I'm thinking we want to show a link to a let's say billing page if the user is subscribed.

The current api sort of expects us to pass in a name for a subscription to user.subscription - and since we can have many different plans, that'll be a bit tedious.

Would the preferred way be to do user.subscriptions.last.active? or could we change user.subscription to just take the latest subscription if no name is given?

Dealing With Duplicated Customers

My failed form token retries created lot of duplicated customers in Stripe.

Is there a convenient way to prevent duplicates if user.subscribe failed? Another way would be to test for duplicates in Stripe using email?

Dependency on rails-html-sanitizer

Question: Is there a need to have rails-html-sanitizer as a dependency? Doesn't look like it being used anywhere.
This dependency conflicts with Rails 6.0 dependency on a higher version of rails-html-sanitizer.

Sync dashboard updates to app

Like Laravel Cashier, we should sync any core updates made directly in the Stripe or Braintree dashboards.

customer.updated

If the default card on the customer is changed in the Stripe dashboard, the customer information stored in your application’s database will automatically be updated.

customer.subscription.updated

If the plan, quantity, or trial ending date is updated on the Stripe dashboard, this information will automatically be updated in your application’s database.

customer.source.deleted

If the customer’s payment source is deleted in the Stripe dashboard, the source information will be set to NULL in your application’s database.

customer.deleted

If the customer is deleted from the Stripe dashboard, their subscriptions will be cancelled and their Stripe information will be removed from your database.

Add webhooks controller

Stripe uses webhooks to tell your application if a payment has failed. We should listen for that and act accordingly.

How to customize webhooks user notifications

Hello,

Your gem can send emails to user upon some webhooks, is there any way to customize/hook into the user notification?

Use case could be to create a notification in the webapp and/or a push notification on mobile.

Send email to all team admins upon subscribe.

Currently, the subscribe method is looking for an email attribute on the Pay.billable_tabletable.
However, in my case, I have a team with multiple users in it and there is no email in the team table.

  • I also tried running rails generate pay:email_views. It says "Could not find generator 'pay:email_views'."
  • I also tried using config.send_emails = false in the initializer. I get error "undefined method `send_emails=' for Pay:Module (NoMethodError)"
    Looks like the code is there in the master but not released to RubyGems.

How do I handle this use case?

stripe_event webhook not working

I seem to have a problem when trying to hit /webhooks/stripe. I get the following:

Started POST "/webhooks/stripe" for 127.0.0.1 at 2018-11-23 21:43:19 -0800

ActionController::RoutingError (No route matches [POST] "/webhooks/stripe"):

actionpack (5.2.1) lib/action_dispatch/middleware/debug_exceptions.rb:65:in `call'
web-console (3.7.0) lib/web_console/middleware.rb:135:in `call_app'
web-console (3.7.0) lib/web_console/middleware.rb:30:in `block in call'
web-console (3.7.0) lib/web_console/middleware.rb:20:in `catch'
web-console (3.7.0) lib/web_console/middleware.rb:20:in `call'
actionpack (5.2.1) lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
railties (5.2.1) lib/rails/rack/logger.rb:38:in `call_app'
railties (5.2.1) lib/rails/rack/logger.rb:26:in `block in call'
activesupport (5.2.1) lib/active_support/tagged_logging.rb:71:in `block in tagged'
activesupport (5.2.1) lib/active_support/tagged_logging.rb:28:in `tagged'
activesupport (5.2.1) lib/active_support/tagged_logging.rb:71:in `tagged'
railties (5.2.1) lib/rails/rack/logger.rb:26:in `call'
sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (5.2.1) lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
actionpack (5.2.1) lib/action_dispatch/middleware/request_id.rb:27:in `call'
rack (2.0.6) lib/rack/method_override.rb:22:in `call'
rack (2.0.6) lib/rack/runtime.rb:22:in `call'
activesupport (5.2.1) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (5.2.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (5.2.1) lib/action_dispatch/middleware/static.rb:127:in `call'
rack (2.0.6) lib/rack/sendfile.rb:111:in `call'
railties (5.2.1) lib/rails/engine.rb:524:in `call'
puma (3.12.0) lib/puma/configuration.rb:225:in `call'
puma (3.12.0) lib/puma/server.rb:658:in `handle_request'
puma (3.12.0) lib/puma/server.rb:472:in `process_client'
puma (3.12.0) lib/puma/server.rb:332:in `block in run'
puma (3.12.0) lib/puma/thread_pool.rb:133:in `block in spawn_thread'

I have tried installing stripe_event directly in my project, without pay installed - and that seems to work fine. My app is running on ruby 2.5.3p105 and rails 5.2.1.

The version of pay I'm using is the latest, pointing to github using:
gem 'pay', github: 'jasoncharnes/pay'

Any ideas what could be going on? :/

NoMethodError: undefined method `credentials' for #<Russ::Application:0x000055bed0ab3c88>

Hi there,
I am install pay in my Rails application following the step to use with Devise gem, but when run rails db:migrate

$ rake db:migrate --trace
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
rake aborted!
NoMethodError: undefined method `credentials' for #<Russ::Application:0x000055bed0ab3c88>
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/pay-1.0.0/lib/pay/env.rb:32:in `credentials'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/pay-1.0.0/lib/pay/env.rb:17:in `find_value_by_name'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/pay-1.0.0/lib/pay/stripe.rb:27:in `private_key'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/pay-1.0.0/lib/pay/stripe.rb:14:in `setup'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/pay-1.0.0/lib/pay/engine.rb:33:in `block in <class:Engine>'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:413:in `instance_exec'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:413:in `block in make_lambda'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:197:in `block (2 levels) in halting'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:601:in `block (2 levels) in default_terminator'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:600:in `catch'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:600:in `block in default_terminator'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:198:in `block in halting'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:507:in `block in invoke_before'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:507:in `each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:507:in `invoke_before'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/callbacks.rb:130:in `run_callbacks'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/reloader.rb:87:in `prepare!'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/application/finisher.rb:61:in `block in <module:Finisher>'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/initializable.rb:30:in `instance_exec'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/initializable.rb:30:in `run'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/initializable.rb:59:in `block in run_initializers'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:228:in `block in tsort_each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:431:in `each_strongly_connected_component_from'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:349:in `block in each_strongly_connected_component'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:347:in `each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:347:in `call'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:347:in `each_strongly_connected_component'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:226:in `tsort_each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/tsort.rb:205:in `tsort_each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/initializable.rb:58:in `run_initializers'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/application.rb:353:in `initialize!'
/home/daniel0318/app/russ/config/environment.rb:5:in `<top (required)>'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `require'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `block in require'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:258:in `load_dependency'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activesupport-5.1.7/lib/active_support/dependencies.rb:292:in `require'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/application.rb:329:in `require_environment!'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/railties-5.1.7/lib/rails/application.rb:445:in `block in run_tasks_blocks'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:273:in `block in execute'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:273:in `each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:273:in `execute'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:214:in `block in invoke_with_call_chain'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/monitor.rb:226:in `mon_synchronize'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:194:in `invoke_with_call_chain'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:238:in `block in invoke_prerequisites'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:236:in `each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:236:in `invoke_prerequisites'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:213:in `block in invoke_with_call_chain'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/2.5.0/monitor.rb:226:in `mon_synchronize'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:194:in `invoke_with_call_chain'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/task.rb:183:in `invoke'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:160:in `invoke_task'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:116:in `each'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:116:in `block in top_level'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:125:in `run_with_threads'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:110:in `top_level'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:83:in `block in run'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:186:in `standard_exception_handling'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/lib/rake/application.rb:80:in `run'
/home/daniel0318/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/rake-12.3.2/exe/rake:27:in `<top (required)>'
/home/daniel0318/.rbenv/versions/2.5.5/bin/rake:23:in `load'
/home/daniel0318/.rbenv/versions/2.5.5/bin/rake:23:in `<main>'
Tasks: TOP => db:migrate => environment

and before when run rails pay:install:migrations

Copied migration 20190724182043_create_subscriptions.pay.rb from pay
Copied migration 20190724182044_add_fields_to_billable.pay.rb from pay
Copied migration 20190724182045_create_charges.pay.rb from pay

And the /config/environment.rb and on line 5 where the error is:

# Load the Rails application.
require_relative 'application'

# Initialize the Rails application.
Rails.application.initialize!

In my Gemfile Add:

gem 'pay'
gem 'stripe', '< 5.0', '>= 2.8'
gem 'stripe_event', '~> 2.2'

Also add include Pay::Billable in my user model and secret stripe_api_key both public and private.

update_card_on_file when creating subscriptions??

Right now, calling

user = current_user
    user.card_token = params[:stripeToken]
    user.subscribe('default', 'monthly')

does not update the database for the brand, last_4, exp_year and exp_month. Should that be done automatically when you create the subscription? Or should leave it up to you? Maybe we should leave it up to the user in case they just want to make a payment with the card that they just submitted but not necessarily change their default card.

    user = current_user
    user.card_token = params[:stripeToken]
    user.subscribe('default', 'monthly')

    user.update_card_on_file(params)

Undefined method 'type' when module is included to User

Hi,

When I'm trying to include Pay::Billable module into User and it crashes the app.

class User < ActiveRecord::Base
  include Pay::Billable
end

I have the following error:

photoshootings 2017-11-14 21-06-56

The backtrace:

NoMethodError - undefined method `type' for :string:Symbol:
  activerecord (4.2.6) lib/active_record/attribute_methods/time_zone_conversion.rb:64:in `create_time_zone_conversion_attribute?'
  activerecord (4.2.6) lib/active_record/attribute_methods/time_zone_conversion.rb:53:in `block (2 levels) in inherited'
  activerecord (4.2.6) lib/active_record/attribute_decorators.rb:61:in `block in matching'
  activerecord (4.2.6) lib/active_record/attribute_decorators.rb:60:in `matching'
  activerecord (4.2.6) lib/active_record/attribute_decorators.rb:56:in `decorators_for'
  activerecord (4.2.6) lib/active_record/attribute_decorators.rb:47:in `apply'
  activerecord (4.2.6) lib/active_record/attribute_decorators.rb:29:in `block in add_user_provided_columns'
  activerecord (4.2.6) lib/active_record/attribute_decorators.rb:28:in `add_user_provided_columns'
  activerecord (4.2.6) lib/active_record/attributes.rb:93:in `columns'
  activerecord (4.2.6) lib/active_record/attributes.rb:98:in `columns_hash'
  activerecord (4.2.6) lib/active_record/relation/query_methods.rb:969:in `block in create_binds'

I use rails 4.2.6.

Have no idea how to fix. Any help would be appreciated :)

By the way, I've noticed another problem with migrations. Generator creates the next migration:

tomsvideo 2017-11-14 21-14-05

This [4.2] crashes the migrations

SCA

Now that things are slowing down at work from implementing Strong Customer Authentication (SCA), I want to start the process of trying to add it to Pay.

While I'm familiar with the regular and how Stripe implements it, I'm not sure (yet) how Braintree handles SCA. With that said, my contribution (at first) will likely be a Stripe-focused solution. If anyone would like to rubber-duck to make sure the approach with Stripe makes sense at a high-level since we also implement SCA for Braintree, too. 👍

Currently, pre-SCA (the good ol' days), you would receive a token from Stripe, send it to a Stripe::Charge, Stripe::Customer, or Stripe::Subscription (right?).

I just spent a massive amount of time regurgitating the Stripe docs in this issue, which is irrelevant to the PR (because I link to it above). I deleted it and what I would like to do, instead, is talk about the potential API within Pay.

Before I get started, I've considered making SCA an optional buy-in, at first. I don't have all the logistics worked out in my head, yet.

Pay.setup do |config|
  config.use_strong_customer_authentication = false
end

Creating a charge with Stripe

I think we can keep the existing API. Though, I think it might be worth making payment_method_id and payment_intent_id arguments to charge versus attributes on the object.

user.charge(5000, payment_method: "pm_card_visa")

But, instead of returning the charge*, return a Pay::Payment object (since Pay::Charge is a model with a table and all that) that can respond to SCA-related methods.

* unless they choose not to opt-in to SCA
# Does not require authentication
charge = user.charge(5000, payment_method: "pm_card_visa")
charge.success? #=> true, no authentication required

# Requires authentication
charge = user.charge(5000, payment_method: "pm_card_visa")
charge.success? #=> false, requires authentication
charge.requires_action? #=> true (user can take this and do something with it)
charge.errors #=> ['This payment requires authentication'] 🤷‍♂ 
charge.payment_intent_id #=> "pi_id"
charge.client_secret #=> "client_secret"
# ... go to the client and authenticate
charge = user.charge(5000, payment_intent: "pi_id")
charge.success? #=> true

Creating a subscription with Stripe

I'd like to (mostly) keep the existing API.

user.subscribe(payment_method: "pm_card_visa")

Here, we continue to return Pay::Subscription and add the appropriate methods.

# Does not require authentication
subscription = user.subscribe(payment_method: "pm_card_visa")
subscription.active? #=> true, no authentication required

# Requires authentication
subscription = user.subscribe(payment_method: "pm_card_visa")
subscription.active? #=> false, requires authentication
subscription.requires_action? #=> true (user can take this and do something with it)
subscription.errors #=> ['This subscription requires authentication'] 🤷‍♂ 
subscription.payment_intent_id #=> "pi_id"
subscription.payment_intent_client_secret #=> "client_secret"
# ... go to the client and authenticate, but have a webhook that handles making the subscription active again

☝️ In the case of required authentication, we should persist the subscription in an invalid state. This is because we've created a PaymentIntent specifically against that subscription, versus a PaymentIntent created as a one-off payment.

I think it would be cool to give the developer two options:

  1. If the subscription object requires authentication, let them grab the link to Stripe's hosted authentication page. subscription.hosted_authentication_url
  2. The ability grab the information needed to build their own authentication screen subscription.payment_intent_id and subscription.payment_intent_client_secret

Updating a card with Stripe

As above, I think we can keep this mostly the same.

We need to provide a SetupIntent from Stripe:

class PaymentMethodsController < ApplicationController
  def edit
    # under the hood, this will reach out to stripe for a off-session SetupIntent
    @payment_intent = Pay::SetupIntent.new
  end
end

We need the setup intent for Stripe elements to know how to generate the proper token. Then, when we go back to the server:

user.update_card(payment_method: "pm_card_visa")
Under the hood, we'll need to call Stripe::PaymentMethod.attach and then stripe_customer.invoice_settings.default_payment_method = payment_method to set the default payment method 😓

Conclusion

There's without a doubt ground I'm not covering here, but I think this is a decent start. I plan to start coding on this before the end of this week.

❤️

Unable to subscribe with plan 'name'

If I do something like:
organization.subscribe(plan: "plan_xyz")
Everything works correctly, but if instead, I do:
organization.subscribe(name: "starter", plan: "plan_xyz")
The organization doesn't get subscribed, so that:
organization.subscribed? returns false, but in my Stripe dashboard, it does get subscribed.
Any explanation for the issue?

Access Subscription Charges

Noticed that subscription charges are not posted to pay_charges table.

Is there a way to access subscription related charges, to also include coupons?

Rename card_brand to card_type

We've got both card_brand and card_type right now, so it'd be useful to just standardize this to the same thing on both Billable and Charge models. I think card_type is most useful considering Braintree allows things like Venmo, PayPal, etc which may not really be a card.

In fact, long-term it might make sense to have this called like payment_method instead.

Devise reconfirmable email address not syncing

When config.reconfirmable is set to true and after the new email address has been confirmed, the email that is synced with stripe is the previous address and not the most recent changed address.

Multiple errors and issues with Devise and Stripe

Hi there!

I'm following the "Using Stripe Elements and Devise" wiki entry but I'm facing a few issues.

  1. Adding a pay initializer causes NoMethodError's (e.g: NoMethodError: undefined method application_name=' for Pay:Module)
  2. Stripe wasn't picking up credentials until I've updated my Gemfile to fetch the gem directly from jasoncharnes/pay
  3. Upon submit I'm faced with a Faraday::ClientError at /users the server responded with status 400 error.

Any ideas how to fix the last one?
I do appreciate and love the work you guys put into this gem though! :)

Error in solidus

I trying to add this extension to solidus (fork of spree commerce) but Give me error:

I add this:
module Spree
class User < Spree::Base
include Pay::Billable
end
end

But if I use:
<% user = Spree::User.find_by(email: @user.email)%>
<%@user_subscriptions = Spree::User.subscription %>

Give this error:
undefined method `subscription' for #Class:0x007f922674b200

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.