ruby 35 lines · 9 steps

How Rails encrypts sensitive payment fields

A PaymentMethod model uses Active Record Encryption to store card and bank data while keeping it queryable and maskable.

Explained by highlit
1class PaymentMethod < ApplicationRecord
2 belongs_to :account
3 
4 encrypts :card_number, deterministic: false
5 encrypts :routing_number, deterministic: true, downcase: false
6 encrypts :account_holder_name
7 
8 blind_index :routing_number
9 
10 validates :card_number, presence: true, format: { with: /\A\d{13,19}\z/ }
11 validates :routing_number, presence: true
12 
13 before_validation :normalize_card_number
14 
15 scope :for_routing, ->(number) { where(routing_number: number) }
16 
17 def masked_card_number
18 "**** **** **** #{card_number.last(4)}"
19 end
20 
21 def brand
22 case card_number
23 when /\A4/ then :visa
24 when /\A5[1-5]/ then :mastercard
25 when /\A3[47]/ then :amex
26 else :unknown
27 end
28 end
29 
30 private
31 
32 def normalize_card_number
33 self.card_number = card_number.to_s.gsub(/[\s-]/, "").presence
34 end
35end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Deterministic encryption trades some secrecy for the ability to query on a column, so choose it only when you need lookups.
  2. 2A blind index lets you search encrypted data without exposing plaintext, filling the gap left by non-deterministic encryption.
  3. 3Decryption is transparent in Ruby, so model methods can format and inspect sensitive values as if they were plain columns.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How Rails encrypts sensitive payment fields — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code