pay-rails / pay Goto Github PK
View Code? Open in Web Editor NEWPayments for Ruby on Rails apps
Home Page: https://github.com/pay-rails/pay
License: MIT License
Payments for Ruby on Rails apps
Home Page: https://github.com/pay-rails/pay
License: MIT License
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
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.
# 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
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:
subscription.hosted_authentication_url
subscription.payment_intent_id
and subscription.payment_intent_client_secret
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")
Stripe::PaymentMethod.attach
and then stripe_customer.invoice_settings.default_payment_method = payment_method
to set the default payment method 😓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.
❤️
Additional table or column. what do you recommend?
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.
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)
We probably should set self.card_token = nil
after the update.
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.
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.
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?
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 swap an user's subscription plan at end of billing cycle instead of immediately?
current_user.subscription.swap(plan)
This swaps the plan immediately.
I want to apply a discount to a subscription when a user signs up. How can I do this?
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? :/
I followed instructions here: https://github.com/jasoncharnes/pay/wiki/Using-Stripe-Elements-and-Devise
STRIPE_SIGNING_SECRET is set
However, customer card_token is not getting injected to form.
Any suggestions?
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?
Is there a method to set the user as "subscribed" bypassing processors?
Something like:
user.subscribe(name: 'Plan1')
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
Hi there!
I'm following the "Using Stripe Elements and Devise" wiki entry but I'm facing a few issues.
NoMethodError
's (e.g: NoMethodError: undefined method application_name=' for Pay:Module
)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! :)
Pay.stripe_public_key
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.
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?
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"
}
If a user went from Stripe to Braintree or vice versa, we need to reset the processor_id.
Getting Cannot verify signature without a 'StripeEvent.signing_secret'
. Even though signing_secret
is correctly setup on credentials
file.
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?
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 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!
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
.
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!
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!
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.
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:
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:
This [4.2]
crashes the migrations
i have just stumbled on this... wondering if @excid3 would be kind enough to make a video on how to use it.. Thanks
Hi,
What do you think about storing Stripe invoices with this gem?
Invoices provides the link between subscriptions and payments.
Stripe uses webhooks to tell your application if a payment has failed. We should listen for that and act accordingly.
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.
We need to update this link to use customer
instead so that it goes through the main Pay interface instead. This will automatically handle updating cards for us.
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?
Noticed that subscription charges are not posted to pay_charges table.
Is there a way to access subscription related charges, to also include coupons?
Would be nice to be able to customize the emails sent via UserMailer (receipt.html.erb and subscription_renewing.html.erb)
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.
Noticed that coupon cannot be applied to subscription upon creation. Is it on the things to do list?
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.
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?
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?
Does anyone know what could be cause of this error?
I added include Pay::Billable to my Team model.
When trying to process the payment I get
undefined method
processor' for Team`
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?
Like Laravel Cashier, we should sync any core updates made directly in the Stripe or Braintree dashboards.
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.
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.
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.
If the customer is deleted from the Stripe dashboard, their subscriptions will be cancelled and their Stripe information will be removed from your database.
Currently, the subscribe method is looking for an email attribute on the Pay.billable_table
table.
However, in my case, I have a team with multiple users in it and there is no email in the team table.
rails generate pay:email_views
. It says "Could not find generator 'pay:email_views'."config.send_emails = false
in the initializer. I get error "undefined method `send_emails=' for Pay:Module (NoMethodError)"How do I handle this use case?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.