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.