Stripe ถูกใช้โดยล้านบริษัท ให้ Payment Infrastructure จัดการ Subscriptions และ One-time Purchases Stripe Checkout ช่วยรับชำระผ่านบัตรผ่าน Hosted Payments Page ที่ออกแบบเพื่อเพิ่ม Conversions รวมกับ Webhooks ให้นักพัฒนาขาย Products/Subscriptions และ Deliver ดิจิทัลได้
แม้จะ Handle Payments เองด้วย Provider อื่นได้ แต่ Stripe มี Benefits: เร็วกว่า (4,000+ คนทำงานเพื่อ Payments ให้ Simple, Secure, Convenient) Build Trust "Powered by Stripe" เพิ่ม Conversion Customers หวั่นให้ Card/Bank Info ทุกเว็บ ไม่ผิด
Stripe ไม่ใช่ Player คนเดียว Paddle ใหญ่เท่า Stripe Features คล้ายแต่ Fee สูงกว่า Developer Experience แย่กว่า (Anecdotally) Competitors อื่นๆ มีแต่ Popularity ของ Stripe ทำให้เลือกง่าย
SaaS มัก Recurring Subscriptions แต่มาก Products ได้ประโยชน์จาก One-time Sales Digital Products ง่ายเท่าเดิมกับ Stripe แต่ Documentation ดูเฉยๆ เรื่องนี้
Setup (การตั้งค่า)
สร้าง App ให้ Users จ่ายเงินโพสต์โฆษณา Craigslist-style Example: Website หา Roommate Browse ฟรี จ่าย Fee เพื่อโพสต์เอง
Create a Stripe Account
สมัคร Stripe Account ที่เว็บไซต์ ใช้ Info Particular หรือ Business เราสนใจ Payments and Payouts Product โดยเฉพาะ Stripe Checkout
Screenshot Stripe Sign-up Page
Create a Basic Rails App
Stripe Account Ready -> สร้าง Rails App Basic สำหรับ Integration Existing App หรือสนใจแค่ Stripe ข้ามไป "Basic Stripe Integration"
Versions: Rails 6.1, Ruby 3.0.0 Assuming Ruby/Rails Installed:
rails new roommate-board
cd roommate-board
rails s
เห็น Rails Welcome Page ที่ localhost:3000 = Ready!
Add Devise Authentication: gem 'devise' -> bundle install -> rails generate devise:install -> rails generate devise User -> rails db:migrate
Create a Roommate Board
ไม่ Focus Front-end Design สร้าง Model Posting: rails generate scaffold Post address:string rent:integer content:text -> rails db:migrate
Root Route = Posts Index ใน config/routes.rb: root 'posts#index'
Header Links Sign In/Out/Up ใน app/views/layouts/application.html.erb:
<!DOCTYPE html>
<html>
<head>
<title>RoommateBoard</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<header>
<nav>
<a href="/#" class="block">
<h1>Roommate Board</h1>
</a>
<% if user_signed_in? %>
<%= link_to "Manage Posts", manage_posts_url %>
<%= link_to "Sign Out", destroy_user_session_url, :method => 'delete' %>
<% else %>
<%= link_to "New Post", new_user_registration_url %>
<% end %>
</nav>
</header>
<%= yield %>
</body>
</html>
ไม่ Signed In -> Root Page:
Screenshot Basic Posts Index
Scope Posting ต่อ User: belongs_to :user (Post) + has_many :posts (User) Migration: rails g migration AddUserIdToPosts Edit Migration: add_column :posts, :user_id, :integer -> rails db:migrate
Models Association: belongs_to :user (Post), has_many :posts (User) Controllers: @post = current_user.posts.build (new), @post = current_user.posts.build(post_params) (create)
Ownership Check Method ใน PostsController:
def user_owns_post?
@post.user == current_user
end
Edit/Destroy Methods: Redirect ถ้าไม่ใช่ Owner
Manage Posts Feature: def manage; @posts = current_user.posts; end Route: get "/manage_posts/" => 'posts#manage' View app/views/posts/manage.html.erb Copy จาก index.html.erb เปลี่ยน Header "Manage Posts" Add Link "New Post"
Test ทุกอย่าง: Create User, Sign In/Out, View Index, Create Posting -> Ready for Stripe!
Stripe Integration
Adding the Stripe Ruby Gem
gem 'stripe'
bundle install
Stripe API Key: Stripe Console -> Developers -> API Keys -> Secret Key สร้าง Initializer config/initializers/stripe.rb: Stripe.api_key = "your-secret-key"
Storing Stripe Credentials (เก็บ Credentials อย่างปลอดภัย)
วิธีนี้ Insecure (Plaintext, Git Tracked) Rails มี Encrypted Credentials: EDITOR=vim bin/rails credentials:edit ใส่:
stripe:
secret: your-secret-key
public: your-public-key
Save Exit Vim (Esc -> :wq + Enter) Update Initializer: Stripe.api_key = Rails.application.credentials[:stripe][:secret]
Linking to Stripe Checkout From the Product
Stripe Checkout = Hosted Payment Page ไม่ต้องสร้าง UI Transaction เอง User Click Button -> Redirect Stripe -> Redirect Back App หลังจ่าย สะดวกแต่ Manage Context ยาก Stripe จัด Payment Form ให้ Maximize Conversions รองรับ Payment Types หลายอย่าง (ACH, International)
สร้าง Controller Checkout: app/controllers/checkout_controller.rb
def create
@session = Stripe::Checkout::Session.create({
success_url: root_url,
cancel_url: manage_posts_url,
payment_method_types: ['card'],
line_items: [{
name: "Roommate Posting",
amount: 2000,
currency: "usd",
quantity: 1
}],
mode: 'payment',
metadata: {post_id: params[:post_id]},
customer_email: current_user.email,
success_url: manage_posts_url,
cancel_url: manage_posts_url
})
respond_to do |format|
format.js
end
end
สร้าง Checkout Session พร้อม Context Line Item Hardcoded (Single Product) Amount = Cents ใส่ User Email Metadata = Post ID สำหรับ Fulfillment ใน Webhooks
Route: post 'checkout/create' => 'checkout#create', as: "checkout_create"
Button Submit Payment ใน Manage Posts View (app/views/manage.html.erb): Add Column "Payment" ใน Header และ Body:
<th>Payment</th>
<td><%= button_to "Submit Payment", checkout_create_path, params: {:post_id => post.id }, remote: true %></td>
JavaScript
Add Stripe JS ใน app/views/application.html.erb: <script src="https://js.stripe.com/v3/"></script>
File app/views/checkout/create.js.erb:
var stripe = Stripe("<%= Rails.application.credentials[:stripe][:public] %>")
stripe.redirectToCheckout({ sessionId: '<%= @session.id %>' }).then(function (result) {
console.log(result.error_message)
});
Setting Up Webhooks
Users จ่ายได้แล้วแต่ App ไม่รู้ว่า Post จ่ายแล้วหรือยัง Activate ยังไง Add Boolean is_paid Migration: rails g migration AddPaymentDetailsToPost -> add_column :posts, :is_paid, :boolean, default: false -> rails db:migrate
Payments ไม่ Instant Process ไม่เชื่อ API Response ได้ ใช้ Webhooks: Stripe POST Request เมื่อ Payment เสร็จ สร้าง WebhooksController:
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, Rails.application.credentials[:stripe][:webhook]
)
rescue JSON::ParserError => e
status 400; return
rescue Stripe::SignatureVerificationError => e
puts "Signature error"; p e; return
end
case event.type
when 'checkout.session.completed'
session = event.data.object
post = Post.find_by(id: session.metadata.post_id)
post.is_paid = true
post.save!
end
render json: { message: 'success' }
end
end
Route: resources :webhooks, only: [:create]
Connect Stripe Dashboard: Developers -> Webhooks -> Add Endpoint URL + /webhooks Event: checkout.session.completed ต้อง Deploy Live Site Webhooks จึงทำงาน (Local ไม่ได้)
Index Page แสดงแค่ Paid Posts: def index; @posts = Post.where(is_paid: true); end
Switching to Production
Webhooks ต้อง Deploy Live HTTPS Required สำหรับ Stripe Checkout Check JS Console หาก Issues
Test Keys -> Production Keys: Stripe Dashboard Toggle "Viewing Test Data" แล้ว Update Rails Credentials ด้วย Production Keys Test Env ช่วย Test Transactions Fake Cards Multiple Environments -> เพิ่ม Key/Value Pair ใน Credentials Dynamically Use
Conclusion (สรุป)
ใช้เวลาสร้าง Example App มาก Stripe Integration ไม่นาน Stripe Checkout โยน Responsibility (UI) ให้ Stripe ประหยัด Code ให้ Consistent Experience (Stripe Popular) Mission: Increase GDP of Internet ต้องการ Apps เหล่านี้ จึงหา Ways ให้ Process Payments Easier
Stripe Payment Links = New Product ทำให้ Checkout Integration เกือบ Unnecessary Redirect User -> Hosted Page Simple Link ทำให้ง่ายกว่า No-code Solution สำห Retailers แต่ Potential สำหรับ Use Case นี้ Stripe Checkout ยัง Flexible/Adopted มากกว่า Important To Understand
Tax Handling (International) = Struggle Stripe New Feature Auto-handle (Beta) Available -> Reduce Accounting Burden
Stripe Docs/Articles เลานไป Subscriptions มากกว่า One-time Subscriptions = Recurring Charges Implementation คล้าย One-time แต่ App Side: Subscription Management, User Manage, Periodic Status Check บทความ Focus One-time แต่ Community Resources เยอะสำหรับ Subscriptions