DEV Community

Cover image for Embedded Stripe Checkout
Yaroslav Shmarov
Yaroslav Shmarov

Posted on • Originally published at blog.corsego.com on

Embedded Stripe Checkout

😖 I really dislike Stripe Elements: you have to build and style the checkout form yourself.

stripe-checkout-elements.png

🤩 Instead, stripe-hosted full screen checkout is a much better approach where you outsource all the payment logic to Stripe. It looks great:

stripe-checkout-hosted.gif

Heres a Github Repo where I have it implemented.

Stripe Hosted Checkout on SupeRails:

stripe-checkout-hosted-superails.gif

🥰🥰 But now you can use Embedded Stripe Checkout inside your app and keep your branding!

stripe-checkout-embedded-superails.gif

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"

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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()
  }
}

Enter fullscreen mode Exit fullscreen mode

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

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)