We've moved discussions to Discord

Request: Some more documentation about how billing works

David Verhasselt
Hello 👋

I was wondering if it was possible to have some more documentation about how the billing system works, especially edge cases. Where should we rely on the payment processor and what does and doesn't the Pay gem do?

Some questions I have (all in context of Stripe, but could be generic)
- What happens if a payment fails? How does that reflect in the subscription?
- Is Stripe considered the single source of truth? E.g. if I change the Plan, change the next billing date, or cancel a subscription in the Stripe UI, will this mess up the jumpstart app or will jumpstart recognise these changes and execute them?
- In the SCA description it says Jumpstart will send emails for further authentication. I'm guessing this means we need to turn those emails off in Stripe, so the user does not receive two emails?

Apologies if this is described somewhere and I missed it.
Jim Jones
Hello David! 

Why not just run through those edge cases locally with your Stripe test account?  Then you can observe first-hand?
John Quarto-vonTivadar
I think the first question can be easily tested as Jim suggested. "I don't know what I don't know ...but I know how I can change that".

however the second and third questions are legitimately the sort of thing one would prefer to hear from someone with experience, since they depend on "I don't know what I don't know ... and I don't think I know how to change that"
David Verhasselt
Yes, I could find out everything myself but that takes time and leaves me open for bugs in case I'm not correct or forgot about edge-cases. I could also have built a Rails app from scratch, but I didn't, I paid for Jumpstart Pro to save time and bugs :)
Chris Oliver
What happens if a payment fails? How does that reflect in the subscription?
Stripe marks it as "past_due" and retries payments a few times. If the card doesn't get updated and the charges continue to fail, the subscription is automatically cancelled.

Is Stripe considered the single source of truth? E.g. if I change the Plan, change the next billing date, or cancel a subscription in the Stripe UI, will this mess up the jumpstart app or will jumpstart recognise these changes and execute them?
Yep, you'll just want to make sure any updates are synced via webhook to the Pay::Subscription model. We already save most of them automatically. All that's done using the Pay gem.

In the SCA description it says Jumpstart will send emails for further authentication. I'm guessing this means we need to turn those emails off in Stripe, so the user does not receive two emails?
Yep, or turn off the emails in Pay. Up to you.
Mark Nelson
I observed a different error today: 

KeyError in ChargesController#show

key not found: :details

Debugging this shows that the @charge variable is set and there is a receipt filename, but there is no @charge.receipt value present:

  ↳ app/controllers/charges_controller.rb:31:in `set_charge'
[2, 11] in ~/ritual-moon/app/controllers/charges_controller.rb
     2|   before_action :authenticate_user!
     3|   before_action :set_charge
     4|
     5|   def show
     6|     respond_to do |format|
=>   7|       debugger
     8|       format.pdf {
     9|         send_data @charge.receipt,
    10|           filename: @charge.filename,
    11|           type: "application/pdf",
=>#0 block {|format=#<ActionController::MimeResponds::Collec...|} in show at ~/ritual-moon/app/controllers/charges_controller.rb:7
  #1 ActionController::MimeResponds#respond_to(mimes=[]) at ~/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/actionpack-7.0.2.2/lib/action_controller/metal/mime_responds.rb:205
  # and 80 frames (use `bt' command for all frames)
(rdbg) @charge.receipt    # ruby
  Pay::Customer Load (0.4ms)  SELECT "pay_customers".* FROM "pay_customers" WHERE "pay_customers"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
  Account Load (0.4ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2  [["id", 5], ["LIMIT", 1]]
  ↳ (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
  AccountUser Load (0.5ms)  SELECT "account_users".* FROM "account_users" WHERE "account_users"."account_id" = $1 ORDER BY "account_users"."created_at" ASC LIMIT $2  [["account_id", 5], ["LIMIT", 1]]
  ↳ app/models/account.rb:48:in `email'
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1  [["id", 5]]
  ↳ app/models/account.rb:48:in `email'
eval error: key not found: :details
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `fetch'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `generate_from'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:15:in `initialize'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `new'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `receipt_pdf'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:13:in `receipt'
  (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
nil
(rdbg) @charge    # ruby
#<Pay::Charge:0x0000000119f25bb0
 id: 2,
 processor_id: "ch_3KaWlOIjBtlieNXS19qmH53k",
 amount: 700,
 amount_refunded: 0,
 created_at: Mon, 07 Mar 2022 10:57:34.000000000 CST +08:00,
 updated_at: Mon, 07 Mar 2022 10:57:38.028238000 CST +08:00,
 data:
  {"brand"=>"Visa",
   "last4"=>"1111",
   "exp_year"=>"2023",
   "exp_month"=>"7",
   "payment_method_type"=>"card"},
 application_fee_amount: nil,
 currency: "usd",
 metadata: {},
 subscription_id: 2,
 customer_id: 2>

(rdbg) @charge.filename    # ruby
"receipt-2022-03-07.pdf"

(rdbg) @charge.receipt    # ruby
  CACHE AccountUser Load (0.0ms)  SELECT "account_users".* FROM "account_users" WHERE "account_users"."account_id" = $1 ORDER BY "account_users"."created_at" ASC LIMIT $2  [["account_id", 5], ["LIMIT", 1]]
  ↳ app/models/account.rb:48:in `email'
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1  [["id", 5]]
  ↳ app/models/account.rb:48:in `email'
eval error: key not found: :details
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `fetch'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `generate_from'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:15:in `initialize'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `new'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `receipt_pdf'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:13:in `receipt'
  (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
nil
David Verhasselt
Hello 👋

I was wondering if it was possible to have some more documentation about how the billing system works, especially edge cases. Where should we rely on the payment processor and what does and doesn't the Pay gem do?

Some questions I have (all in context of Stripe, but could be generic)
- What happens if a payment fails? How does that reflect in the subscription?
- Is Stripe considered the single source of truth? E.g. if I change the Plan, change the next billing date, or cancel a subscription in the Stripe UI, will this mess up the jumpstart app or will jumpstart recognise these changes and execute them?
- In the SCA description it says Jumpstart will send emails for further authentication. I'm guessing this means we need to turn those emails off in Stripe, so the user does not receive two emails?

Apologies if this is described somewhere and I missed it.
Jim Jones
Hello David! 

Why not just run through those edge cases locally with your Stripe test account?  Then you can observe first-hand?
John Quarto-vonTivadar
I think the first question can be easily tested as Jim suggested. "I don't know what I don't know ...but I know how I can change that".

however the second and third questions are legitimately the sort of thing one would prefer to hear from someone with experience, since they depend on "I don't know what I don't know ... and I don't think I know how to change that"
David Verhasselt
Yes, I could find out everything myself but that takes time and leaves me open for bugs in case I'm not correct or forgot about edge-cases. I could also have built a Rails app from scratch, but I didn't, I paid for Jumpstart Pro to save time and bugs :)
Chris Oliver
What happens if a payment fails? How does that reflect in the subscription?
Stripe marks it as "past_due" and retries payments a few times. If the card doesn't get updated and the charges continue to fail, the subscription is automatically cancelled.

Is Stripe considered the single source of truth? E.g. if I change the Plan, change the next billing date, or cancel a subscription in the Stripe UI, will this mess up the jumpstart app or will jumpstart recognise these changes and execute them?
Yep, you'll just want to make sure any updates are synced via webhook to the Pay::Subscription model. We already save most of them automatically. All that's done using the Pay gem.

In the SCA description it says Jumpstart will send emails for further authentication. I'm guessing this means we need to turn those emails off in Stripe, so the user does not receive two emails?
Yep, or turn off the emails in Pay. Up to you.
Mark Nelson
I observed a different error today: 

KeyError in ChargesController#show

key not found: :details

Debugging this shows that the @charge variable is set and there is a receipt filename, but there is no @charge.receipt value present:

  ↳ app/controllers/charges_controller.rb:31:in `set_charge'
[2, 11] in ~/ritual-moon/app/controllers/charges_controller.rb
     2|   before_action :authenticate_user!
     3|   before_action :set_charge
     4|
     5|   def show
     6|     respond_to do |format|
=>   7|       debugger
     8|       format.pdf {
     9|         send_data @charge.receipt,
    10|           filename: @charge.filename,
    11|           type: "application/pdf",
=>#0 block {|format=#<ActionController::MimeResponds::Collec...|} in show at ~/ritual-moon/app/controllers/charges_controller.rb:7
  #1 ActionController::MimeResponds#respond_to(mimes=[]) at ~/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/actionpack-7.0.2.2/lib/action_controller/metal/mime_responds.rb:205
  # and 80 frames (use `bt' command for all frames)
(rdbg) @charge.receipt    # ruby
  Pay::Customer Load (0.4ms)  SELECT "pay_customers".* FROM "pay_customers" WHERE "pay_customers"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
  Account Load (0.4ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2  [["id", 5], ["LIMIT", 1]]
  ↳ (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
  AccountUser Load (0.5ms)  SELECT "account_users".* FROM "account_users" WHERE "account_users"."account_id" = $1 ORDER BY "account_users"."created_at" ASC LIMIT $2  [["account_id", 5], ["LIMIT", 1]]
  ↳ app/models/account.rb:48:in `email'
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1  [["id", 5]]
  ↳ app/models/account.rb:48:in `email'
eval error: key not found: :details
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `fetch'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `generate_from'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:15:in `initialize'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `new'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `receipt_pdf'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:13:in `receipt'
  (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
nil
(rdbg) @charge    # ruby
#<Pay::Charge:0x0000000119f25bb0
 id: 2,
 processor_id: "ch_3KaWlOIjBtlieNXS19qmH53k",
 amount: 700,
 amount_refunded: 0,
 created_at: Mon, 07 Mar 2022 10:57:34.000000000 CST +08:00,
 updated_at: Mon, 07 Mar 2022 10:57:38.028238000 CST +08:00,
 data:
  {"brand"=>"Visa",
   "last4"=>"1111",
   "exp_year"=>"2023",
   "exp_month"=>"7",
   "payment_method_type"=>"card"},
 application_fee_amount: nil,
 currency: "usd",
 metadata: {},
 subscription_id: 2,
 customer_id: 2>

(rdbg) @charge.filename    # ruby
"receipt-2022-03-07.pdf"

(rdbg) @charge.receipt    # ruby
  CACHE AccountUser Load (0.0ms)  SELECT "account_users".* FROM "account_users" WHERE "account_users"."account_id" = $1 ORDER BY "account_users"."created_at" ASC LIMIT $2  [["account_id", 5], ["LIMIT", 1]]
  ↳ app/models/account.rb:48:in `email'
  CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1  [["id", 5]]
  ↳ app/models/account.rb:48:in `email'
eval error: key not found: :details
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `fetch'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:23:in `generate_from'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/receipts-2.0.0/lib/receipts/base.rb:15:in `initialize'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `new'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:39:in `receipt_pdf'
  /Users/marknelson/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/pay-3.0.24/lib/pay/receipts.rb:13:in `receipt'
  (rdbg)//Users/marknelson/ritual-moon/app/controllers/charges_controller.rb:1:in `block in show'
nil
Notifications
You’re not receiving notifications from this thread.
© 2022 Jumpstart Pro by GoRails, LLC