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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Deterministic encryption trades some secrecy for the ability to query on a column, so choose it only when you need lookups.
- 2A blind index lets you search encrypted data without exposing plaintext, filling the gap left by non-deterministic encryption.
- 3Decryption is transparent in Ruby, so model methods can format and inspect sensitive values as if they were plain columns.
Related explainers
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
ruby
class LogAggregator BUCKET_FORMAT = "%Y-%m-%dT%H:%M" def initialize(entries)
Bucketing log entries by the minute in Ruby
aggregation
hashing
enumerable
Intermediate
5 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-rails-encrypts-sensitive-payment-fields-explained-ruby-105c/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.