😖 I really dislike Stripe Elements: you have to build and style the checkout form yourself.
🤩 Instead, stripe-hosted full screen checkout is a much better approach where you outsource all the payment logic to Stripe. It looks great:
Heres a Github Repo where I have it implemented.
Stripe Hosted Checkout on SupeRails:
🥰🥰 But now you can use Embedded Stripe Checkout inside your app and keep your branding!
Here’s how you can implement Embedded Stripe Checkout:
Create a pricing page that redirects to the checkout page
# config/routes.rb
get "pricing", to: "stripe/checkout#pricing"
get "checkout/new", to: "stripe/checkout#checkout", as: "new_checkout"
Stripe Embedded Checkout API has ui_mode: :embedded
and return_url
params that you should set:
# app/controllers/stripe/checkout_controller.rb
# GET
def pricing
lookup_key = %w[month year lifetime]
@prices = Stripe::Price.list(lookup_keys: lookup_key, active: true,
expand: ['data.product']).data.sort_by(&:unit_amount)
end
# GET
def checkout
price = Stripe::Price.retrieve(params[:price_id])
@session = Stripe::Checkout::Session.create({
customer: current_user.stripe_customer_id,
# allow_promotion_codes: true,
# automatic_tax: {enabled: @plan.taxed?},
# consent_collection: {terms_of_service: :required},
# customer_update: {address: :auto},
# payment_method_collection: :if_required,
line_items: [{
price:,
quantity: 1
}],
mode: mode(price),
return_url: user_url(current_user),
ui_mode: :embedded
})
end
private
MODES = {
'recurring' => 'subscription',
'one_time' => 'payment',
'setup' => 'setup'
}.freeze
def mode(price)
MODES[price.type]
end
Display links to checkout for each different price:
# app/views/stripe/checkout/pricing.html.erb
<% @prices.each do |price| %>
<%= link_to "Checkout" new_checkout_path(price_id: price.id) %>
<% end %>
This will redirect to the checkout page. You will need some JS to embed the Stripe Checkout.
# app/views/stripe/checkout/checkout.html.erb
<%= javascript_include_tag "https://js.stripe.com/v3/" %>
<%= tag.div data: {
controller: "stripe--embedded-checkout",
stripe__embedded_checkout_public_key_value: Rails.application.credentials.dig(Rails.env, :stripe, :public),
stripe__embedded_checkout_client_secret_value: @session.client_secret
} %>
rails g stimulus stripe/embedded_checkout
// app/javascript/controllers/stripe/embedded_checkout_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = {
publicKey: String,
clientSecret: String,
}
async connect() {
this.stripe = Stripe(this.publicKeyValue)
this.checkout = await this.stripe.initEmbeddedCheckout({clientSecret: this.clientSecretValue})
this.checkout.mount(this.element)
}
disconnect() {
this.checkout.destroy()
}
}
That’s it! Now you have the latest, coolest Stripe Checkout!
Don’t forget to also add:
- Webhooks to create customers, handle successful and failed payments, subscription state changes
- Billing portal for user to manage plan, change payment methods, see invoice history
See examples here: github.com/corsego/rails-7-stripe-subscriptions
Top comments (0)