How to get paid

How to get paid
Photo by Clay Banks / Unsplash

Got a great product and now you would like your consumers to start paying for it! Doesn't sound too far fetched and quite reasonable in fact – but how does the mechanics work at all? Like, where do I sign to make this happen? If you are like I was, right now you got a lot of questions!

This post is about how you go from running/providing a service to see money getting into your bank account.

Disclaimer: I did not receive any kick backs or advertisement benefits for doing this post nor am I an affiliate partner of any service provider mentioned in this post!

Who will collect the money on my behalf?

One of my first questions was just that! I knew I (designed an inter-National Banks cheque exchange system back in the early 90's, so I probably) could build an integration with a bank – a number of banks to be more precise – using the PSD2 (EU 2015/2366 Directive) but that would require me to handle a lot of regulation and compliance myself!

An easier (and a lot faster) way is to pay someone else to do that for me just like I plan to get paid by my customers to do something for them. A payment gateway that is called. The one that comes to mind (and mentioned a lot when you google it) obviously is Stripe. There are many like Stripe and some that are less costly but one reason to go with Stripe is the api documentation which Stripe has taken to a whole different level! 🥳

Where to start?

There are a number of issues to tick off and I made a list for your benefit:

  • get a bank account
  • setup an account with Stripe
  • install the 'stripe' gem
  • generate the payment link
  • add callback route

The rest of this post will go through each issue and solve them.

Get a bank account

This may sound crazy but even in 2025 you still need to have one of these archaic constructs: a bank account! I have a lot to say about that but let's focus here.

You need a bank account that will do IBAN/SWIFT payments (that's international money wiring). I'm doing this from Denmark so I'm not sure if you can get away with just your Apple Pay account in the US but anyways...

An account with Stripe

Setting up an account with Stripe is fairly easy. Go to stripe.com and sign up and then start setting up your account by following a very crafty made setup guide (thank you Stripe - job well done)

Install the 'stripe' gem

This one was easy as pie – I just ran bundle add stripe and almost was done!

From somewhere, probably the UI, you'll call the generation of the Payment Link and you can make it as customized and fancy as you like (there is a ton of API calls to that end) or you can lock in on a select few things, like a product, a price and boom. Once generated you'll show that to the user somehow - a modal, an email, whatever - and then just sit tight and wait for the callback 😆

Add callback route

You could argue that a callback route is of less value in a workflow like this one – being variably asynchronous; but it actually makes a lot of sense – if you take the same route I did, that is!

I use the Payment Link API to generate a unique link to every new customer (a user that selects one of the payable services to choose from in Mortimer) and this link may live (forever?) a long time, until the user decides to use it. This way I get to do this, eventually/hopefully always:


class Stripe::PaymentController < ApplicationController
  # Stripe callback from completed payment
  # GET "/stripe/payment?ci=cs_test_a1M01tZueaLHFtv...Cwn5tgV61"
  #
  # param 
  #   ui = user_id
  #   ci = checkout_session_id
  #
  def new
    Current.system_user = User.find(params["ui"])
    ss = Stripe::Service.new user: Current.get_user
    if ss.confirm_payment(params["ci"])
      redirect_to edit_tenant_url(Current.get_tenant), 
        success: t("payment_successfully_confirmed")
    else
      redirect_to edit_tenant_url(Current.get_tenant), 
        warning: t("payment_not_confirmed")
    end
  end
end

Done

That's about it actually – you'll want to keep stuff together (I keep stuff in a folder labeled 'services' and below that I have a 'stripe' folder with a class like this

class Stripe::Service < SaasService

  def payment_link url, price
    Stripe.api_key = settings["api_key"]
    pl = Stripe::PaymentLink.create({
      line_items: [
        {
          price: price.id,
          quantity: 1
        }
      ],
      after_completion: {
        type: "redirect",
        redirect: {
          url: url
        }
      }
    })
    return pl.url if pl.active
    false
  end
  
  def confirm_payment checkout_session_id
    Stripe.api_key = settings["api_key"]
    scs = Stripe::Checkout::Session.retrieve checkout_session_id
    if scs["payment_status"] == "paid"
      invoice = Stripe::Invoice.retrieve scs["invoice"]
      prod = Stripe::Product.retrieve(invoice
        .lines.data.first.pricing.price_details.product)
      if prod.present?
        case prod.name
        when /pro/; Current.get_tenant.update license: 3
        when /essential/; Current.get_tenant.update license: 2
        end
      end

      TenantMailer.with(tenant: Current.get_tenant, 
        user: Current.get_user, 
        recipient: Current.get_tenant.email, 
        invoice_pdf: invoice.invoice_pdf)
        .send_invoice.deliver_later
      Current.get_tenant.update license_changed_at: Time.current,
        license_expires_at: Time.at(invoice
          .lines.data.first.period.end).utc
      true
    else
      false
    end
  end

but that's just smoke and mirrors 😉 - the Stripe gem does all the heavy lifting, really!

Walther H Diechmann

Walther H Diechmann

Got on the train just about when the IBM PC 5150 got introduced and never really got off again - switched to macOS about 2006 though, and never looked back. It's been such a ride!
Silkeborg, Denmark