Add customer and payment method on Stripe with ruby on rails

Umeriqbal
4 min readDec 4, 2023

--

here is how you can configure stripe in rails

we are using stripe element to add customer on stripe in this article.

Step 1:
add customer_id in the user model to save the stripe customer id like below

class AddCustomerIdInUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :customer_id, :string
end
end

in user model after create callback we add create_stripe_user method to create customer on stripe like below

class User < ApplicationRecord
after_create :create_stripe_user

def create_stripe_user
customer = Stripe::Customer.create(email: self.email, name: self.name)
self.update(customer_id: customer.id)
end
end

this will create customer on stripe and add its id to the database for further use.
we create payments controller to add payment method on stripe.

class PaymentsController < ApplicationController

def new
@intent = Stripe::SetupIntent.create(customer: current_user.customer_id)
@customer_id = current_user.customer_id
end
end

here is new.js.erb file for above new method

$('#stripeForm').append('<%=j render 'payments/stripe_form' %>');

and here is _stripe_form.js.erb form partial, by the way we use this approach to render form on same screen not to leave current screen.

<form id="payment-setup-form" data-secret="<%= @intent.client_secret %>" data-customer="<%= @customer_id %>"
data-publishable-key='replace this with stripe public key'
>

<div class="form-group">
<label class="">Holder Name</label>
<input class="form-control" placeholder="" id="cardholder-name" type="text" required>
</div>

<div class="form-group">
<label class="">Card Details</label>
<div class="pt-2" id="card-element"></div>
</div>

<div class="form-row ">
<button class="btn btn-accent mt-2 px-5" id="card-button">
<%='Submit' %>
</button>
</div>
</form>

<script>
$(() => {
let form = document.getElementById('payment-setup-form');
let cardholderName = document.getElementById('cardholder-name');
let cardButton = document.getElementById('card-button');
let clientSecret = form.dataset.secret;
let customerId = form.dataset.customer;
let stripe = Stripe(form.dataset.publishableKey);

let elements = stripe.elements();
let cardElement = elements.create('card');
cardElement.mount('#card-element');

cardButton.addEventListener('click', function (ev) {
ev.preventDefault();
if (cardholderName.value === '') {
showNotification('Holder Name is incomplete.', 'danger');
return
}
stripe.confirmCardSetup(
clientSecret,
{
payment_method: {
card: cardElement,
billing_details: {
name: cardholderName.value,
},
},
}
).then(function (result) {
if (result.error) {
// Display error.message in your UI.
showNotification(result.error.message, 'danger');
console.log(result.error)
} else {
// The setup has succeeded. Display a success message.
console.log(result)
showNotification('Payment method successfully added. Redirecting to Dashboard.');
setTimeout(() => {
// window.location.href = form.dataset.redirectPath
}, 1000)
}});
});
});
</script>

here is how view look like before we click to create new customer on stripe

when user click on stripe form request will go to above new method of payment and here how view will look like

after add stripe info click on submit. this will add new payment method on stripe.

then add the webhook for customer create on stripe from the developers tab in the header like below

then click on add endpoint then you will see the below screen

after that click on select event and add the payment_method.attached event and add the endpoint url where stripe will send request after payment_method create then copy the code and add it to your webhook controller like below after that click on add endpoint button to save webhook

class WebhooksController < ApplicationController
# skip_before_action :verify_authenticity_token

def create
payload = request.body.read
sig_header = request.env['HTTP_STRIPE_SIGNATURE']
event = nil

begin
event = Stripe::Webhook.construct_event(
payload, sig_header, "webhook secret key"
)
rescue JSON::ParserError => e
status 400
return
rescue Stripe::SignatureVerificationError => e
# Invalid signature
puts "Signature error"
p e
return
end

# Handle the event
case event.type
when 'payment_method.attached'
payment_method = event.data.object
clinic = Clinic.find_by(customer_id: payment_method.customer)
if clinic.payments.exists?
Payment.create(user_id: clinic.id, payment_method: payment_method)
else
Payment.create(user_id: clinic.id, payment_method: payment_method, default: true)
end
else
puts "Unhandled event type: #{event.type}"
end

render json: { message: 'success' }
end
end

you need to add webhook secret key in above begin method that you will find in api keys under develper of stripe you can copy it after click on reveal key.

after all done you will receive request after new payment method added to the stripe against a user and after successful add payment method we save the payment method id into the payment model to use it in future for payments without asking card detail.

--

--

Umeriqbal

I am a full stack software engineer specializing in Ruby on Rails, I have extensive experience in developing and deploying web applications.