Stripe has multiple options for payments
- Stripe Checkout - Hosted pages for payments (you'll redirect users to Stripe)
- Stripe Elements - Payment fields on your site
Stripe introduced Products & Prices to support more payment options. Previously, they had a concept called Plan that was for subscriptions. Pay supports both Price IDs
and Plan IDs
when subscribing.
@user.payment_processor.subscribe(plan: "price_1234")
@user.payment_processor.subscribe(plan: "plan_1234")
See: https://stripe.com/docs/api/subscriptions/create
Stripe Checkout allows you to simply redirect to Stripe for handling payments. The main benefit is that it's super fast to setup payments in your application, they're SCA compatible, and they will get improved automatically by Stripe.
📝 Warning: You need to configure webhooks before using Stripe Checkout otherwise your application won't be updated with the correct data.
Choose the checkout button mode you need and pass any required arguments. Read the Stripe Checkout Session API docs to see what options are available. For instance:
class SubscriptionsController < ApplicationController
def checkout
# Make sure the user's payment processor is Stripe
current_user.set_payment_processor :stripe
# One-time payments (https://stripe.com/docs/payments/accept-a-payment)
@checkout_session = current_user.payment_processor.checkout(mode: "payment", line_items: "price_1ILVZaKXBGcbgpbZQ26kgXWG")
# Or Subscriptions (https://stripe.com/docs/billing/subscriptions/build-subscription)
@checkout_session = current_user.payment_processor.checkout(
mode: 'subscription',
locale: I18n.locale,
line_items: [{
price: 'price_1ILVZaKXBGcbgpbZQ26kgXWG',
quantity: 4
}],
subscription_data: {
trial_period_days: 15,
pay_name: "base" # Optional. Overrides the Pay::Subscription name attribute
},
success_url: root_url,
cancel_url: root_url
)
# Or Setup a new card for future use (https://stripe.com/docs/payments/save-and-reuse)
@checkout_session = current_user.payment_processor.checkout(mode: "setup")
# If you want to redirect directly to checkout
redirect_to @checkout_session.url, allow_other_host: true, status: :see_other
end
end
Alternatively, you can use Pay & Stripe.js to render a button that will take the user to Stripe Checkout instead of redirecting immediately.
<%= render "pay/stripe/checkout_button", session: @checkout_session, title: "Checkout" %>
The session_id
param will be included on success and cancel URLs automatically. This allows you to lookup the checkout session on your success page and confirm the payment was successful before fulfilling the customer's purchase.
https://stripe.com/docs/payments/checkout/custom-success-page
Customers will want to update their payment method, subscription, etc. This can be done with the Customer Billing Portal. It works the same as the other Stripe Checkout pages.
First, create a session in your controller:
class SubscriptionsController < ApplicationController
def index
@portal_session = current_user.payment_processor.billing_portal
end
end
Then link to it in your view:
<%= link_to "Billing Portal", @portal_session.url %>
Or redirect to it in your controller:
redirect_to @portal_session.url, allow_other_host: true, status: :see_other
For one-time payments, you'll need to add a webhook listener for the Checkout stripe.checkout.session.completed
and stripe.checkout.session.async_payment_succeeded
events. Some payment methods are delayed so you need to verify the payment_status == "paid"
. The async payment succeeded event fires when delayed payments are complete.
For subscriptions, Pay will automatically create the Pay::Subscription
record for you.
Pay::Webhooks.delegator.subscribe "stripe.checkout.session.completed", FulfillCheckout.new
Pay::Webhooks.delegator.subscribe "stripe.checkout.session.async_payment_succeeded", FulfillCheckout.new
class FulfillCheckout
def call(event)
object = event.data.object
return object.payment_status != "paid"
# Handle fulfillment
end
end
That's it!
See Credentials