ข้อผิดพลาด deploy ไม่มีอาการ. สี่รั้ว — credentials แยก, สคริปต์ EDITOR, อ่านกลับตรวจ, พลิก runtime ตาม env — ให้ Claude ใช้งานได้โดยไม่เผาทุกอย่าง
ความแตกต่างใหญ่ที่สุดระหว่าง deploy กับการเขียนโค้ด: deploy ทำครั้งเดียว เดิมพันสูง rollback ยาก เขียนโค้ดผิดหนึ่งบรรทัด รันเทสครั้งเดียวก็จับได้ เขียน credentials ผิดหนึ่งบรรทัด กว่าจะรู้คือตอนที่จ่ายเงินจริงครั้งแรกล้มเหลว — ผู้ใช้รูดบัตรไปแล้ว เงินไม่เข้าบัญชีคุณ log เต็มไปด้วย 400
ช่วงนี้พา how2claude จาก dev local ขึ้น production กับ Claude: บัญชี Stripe live, wallet x402 mainnet, Google OAuth, Kamal secrets. Claude ไม่รู้ว่าค่าไหนคือ test ค่าไหนคือของจริง ไม่รู้ว่า sk_live_ กับ sk_test_ ต่างกันตัวอักษรเดียวแต่ผลลัพธ์หายนะคนละโลก รั้วต้องตั้งเอง
Default ของ Rails คือ config/credentials.yml.enc ถอดรหัสด้วย config/master.key ใช้ default นี้คือแพ้แล้ว
ถ้าเอา sk_live_xxx ใส่ไฟล์นี้แล้วรันเทสที่ local โค้ดเทสคุณจะใช้ Stripe production ทุกครั้งที่รันเทสคือรูดบัตรจริงหนึ่งครั้ง
แยกเป็นสอง:
- config/credentials.yml.enc + config/master.key: dev/test เก็บ sk_test_xxx
- config/credentials/production.yml.enc + config/credentials/production.key: prod เก็บ sk_live_xxx
ทั้งสอง .key อยู่ใน .gitignore, ทั้งสอง .enc commit. production.key อยู่แค่บนเครื่อง deploy
แล้ว Kamal secrets ต้องชี้ไปที่ไฟล์ที่ถูกต้อง:
# .kamal/secrets
-RAILS_MASTER_KEY=$(cat config/master.key)
+RAILS_MASTER_KEY=$(cat config/credentials/production.key)
ภายใน container RAILS_MASTER_KEY ตอนนี้ชี้ไปที่ key ของ production และสิ่งที่ถูกถอดรหัสคือ credentials ของ production ผมดู Claude เขียนบรรทัดนี้อย่างใกล้ชิด — template default ของ Kamal คือ config/master.key (ของ dev) ซึ่งจะ deploy Stripe key ของ dev ขึ้น production อย่างเงียบ ๆ
bin/rails credentials:edit --environment production เปิด editor แบบ interactive Claude ขับ editor interactive ไม่ได้ คุณเองก็ไม่อยาก paste ด้วยมือสิบกว่า secret (พิมพ์ผิดตัวเดียวพังยกแผง)
ใช้ pattern นี้:
EDITOR="ruby script/set_prod_webhook_secret.rb" \
bin/rails credentials:edit --environment production
rm script/set_prod_webhook_secret.rb
script/set_prod_webhook_secret.rb หน้าตาแบบนี้:
# ARGV[0] คือ path ของไฟล์ YAML ชั่วคราวที่ถอดรหัสแล้ว
file = ARGV[0]
require "yaml"
data = YAML.load_file(file) || {}
data["stripe"] ||= {}
data["stripe"]["webhook_secret"] = "whsec_GHWObNAKFh2HPOlJpbGmlYfIiKz1C8EY"
File.write(file, data.to_yaml)
Rails เขียน YAML ที่ถอดรหัสแล้วลง temp file เรียก "EDITOR" ของคุณโดยส่ง path นั้นเป็น argument แล้วเข้ารหัสใหม่เมื่อออก "EDITOR" ของเราคือสคริปต์ Ruby ที่แก้ key เดียวอย่างแม่นยำแล้วเซฟ
ดีตรงไหน:
- แม่นยำ: แตะ stripe.webhook_secret อย่างเดียว ไม่แตะอย่างอื่น
- Idempotent: รันสองครั้งผลเหมือนครั้งเดียว
- ตรวจสอบได้: สคริปต์คือ diff Claude เขียน คุณเหลือบดู รู้ทันทีว่าจะเปลี่ยนอะไร
- หายเมื่อลบ: หลัง rm ไม่มี credential plaintext บน disk ไม่มี whsec_... ใน shell history
สคริปต์หนึ่งตัวต่อ credential: set_stripe_live_key.rb, set_webhook_secret.rb, set_price_ids.rb, set_wallet_address.rb. rm แต่ละตัวทันทีหลังใช้
หลังเขียน อย่าเชื่อว่ามันเขียนถูก อ่านกลับ:
bin/rails runner -e production "
c = Rails.application.credentials
puts 'sk_live set: ' + c.dig(:stripe, :secret_key).to_s.start_with?('sk_live_').to_s
puts 'webhook_secret set: ' + c.dig(:stripe, :webhook_secret).to_s.start_with?('whsec_').to_s
puts 'wallet prefix: ' + c.dig(:x402, :wallet_address).to_s[0..5]
puts 'wallet len: ' + c.dig(:x402, :wallet_address).to_s.length.to_s
"
หัวใจคือการตรวจ prefix และ ความยาว ไม่ใช่แค่ print ออกมาดู
sk_live_ เสมอ ถ้าอ่านกลับได้ sk_test_ แปลว่า Claude เอา test key ใส่ไฟล์ prod — บั๊กนี้จะไม่เจอจนกว่าการจ่ายเงินจริงครั้งแรกwhsec_ เสมอ รูปแบบถูกหรือไม่เห็นทันที0x... เสมอ ความยาวผิดแปลว่ามีตัวอักษรอื่นแอบเข้ามาเพิ่มการตรวจ prefix ก็บล็อกได้ส่วนใหญ่ทั้งพิมพ์ผิด, field หาย, environment ปน
มักคิดว่า "ก่อน deploy ฉันจะเปลี่ยน network เป็น mainnet" — สวิตช์แบบนี้พึ่งความทรงจำของมนุษย์ ช้าหรือเร็วก็เผาคุณ
ฝังกฎไว้ใน initializer:
# config/initializers/x402.rb
X402.configure do |c|
c.wallet_address = Rails.application.credentials.dig(:x402, :wallet_address)
c.chain = Rails.env.production? ? "base" : "base-sepolia"
end
โค้ดเดียวกัน dev วิ่ง testnet (sepolia) prod วิ่ง mainnet (base) ตอน deploy ไม่ต้องเปลี่ยนอะไรเลย Claude ก็ "ลืมสลับ" ไม่ได้เพราะการสลับไม่ใช่งานของมัน
กลเดียวกันใช้กับ basescan_tx_url, การแสดง Plan (plan dev-only ไม่โชว์ใน prod), การเลือก Stripe price ID เป็นต้น อะไรที่ต่างกันระหว่าง dev กับ prod ให้พลิกด้วย Rails.env อย่าพึ่งว่าจะจำได้ตอน deploy
รั้วสี่ตัวครบแล้ว คลิกจ่ายเงินจริงครั้งแรกยังคงต้องคุณเองอยู่ดี
บทความก่อน ดีบักบั๊กเงียบกับ Claude ผมเล่าถึงคลิกจ่าย x402 ครั้งแรกใน production: console ฟ้อง invalid_string at payTo ทันที ตัวอักษรที่ 43 ของ wallet address คือเครื่องหมายคำถาม fullwidth ที่แอบเข้ามาจาก IME ภาษาจีน การตรวจ prefix จับไม่ได้ (0x ยังอยู่) test จับไม่ได้ (test ไม่ยิงธุรกรรมจริง) มีแต่ คลิกจริง เท่านั้นที่เปิดเผยออกมา
รั้วของ deploy ไม่ใช่เพื่อ Claude รั้วเพื่อคุณ — ทำสิ่งที่ automate ได้ให้ automate (prefix, ความยาว, การพลิก env) เพื่อให้ความสนใจที่ประหยัดได้ไปกับการโต้ตอบจริงที่ automation ปิดไม่ได้
Flow เต็มของ Claude-deploy:
.enc + .key ต่างหากEDITOR=script ลบทันทีRails.env ไม่พลิกตอน deployDeploy ไม่ใช่ "เขียนโค้ดที่เดิมพันสูงขึ้น" Deploy คือ "เขียนโค้ดที่ไม่มีใครบอกคุณว่าผิด" รั้วมีไว้เพื่อเปลี่ยน "ไม่มีใครบอก" ให้เป็น "คุณรู้ภายใน 15 วินาที"