Free

ให้ Claude deploy ขึ้น production

ข้อผิดพลาด 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_ ต่างกันตัวอักษรเดียวแต่ผลลัพธ์หายนะคนละโลก รั้วต้องตั้งเอง

รั้ว #1: แยกไฟล์ credentials สำหรับ dev กับ prod

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 อย่างเงียบ ๆ

รั้ว #2: ใช้สคริปต์ EDITOR ไม่ copy-paste

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 แต่ละตัวทันทีหลังใช้

รั้ว #3: ตรวจสอบด้วยการอ่านกลับผ่าน Rails runner

หลังเขียน อย่าเชื่อว่ามันเขียนถูก อ่านกลับ:

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 ออกมาดู

  • Stripe live secret จะขึ้นต้นด้วย sk_live_ เสมอ ถ้าอ่านกลับได้ sk_test_ แปลว่า Claude เอา test key ใส่ไฟล์ prod — บั๊กนี้จะไม่เจอจนกว่าการจ่ายเงินจริงครั้งแรก
  • Webhook secrets ขึ้นต้นด้วย whsec_ เสมอ รูปแบบถูกหรือไม่เห็นทันที
  • EVM wallet address 42 ตัวอักษร 0x... เสมอ ความยาวผิดแปลว่ามีตัวอักษรอื่นแอบเข้ามา

เพิ่มการตรวจ prefix ก็บล็อกได้ส่วนใหญ่ทั้งพิมพ์ผิด, field หาย, environment ปน

รั้ว #4: สลับใน runtime ตาม env ไม่สลับด้วยมือตอน deploy

มักคิดว่า "ก่อน 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

ขั้นสุดท้าย: เดิน flow จริงด้วยตัวเอง

รั้วสี่ตัวครบแล้ว คลิกจ่ายเงินจริงครั้งแรกยังคงต้องคุณเองอยู่ดี

บทความก่อน ดีบักบั๊กเงียบกับ 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:

  1. แยก credentials dev กับ prod เป็นไฟล์ .enc + .key ต่างหาก
  2. ค่าแต่ละตัวที่จะ set เขียนสคริปต์ Ruby ใช้ครั้งเดียว ป้อนผ่าน EDITOR=script ลบทันที
  3. หลังเขียนทุกครั้ง อ่านกลับด้วย Rails runner ตรวจ prefix และความยาว
  4. ความต่าง dev/prod ทั้งหมดพลิกผ่าน Rails.env ไม่พลิกตอน deploy
  5. ปุ่มเงินจริงครั้งแรก คุณคลิกเอง

Deploy ไม่ใช่ "เขียนโค้ดที่เดิมพันสูงขึ้น" Deploy คือ "เขียนโค้ดที่ไม่มีใครบอกคุณว่าผิด" รั้วมีไว้เพื่อเปลี่ยน "ไม่มีใครบอก" ให้เป็น "คุณรู้ภายใน 15 วินาที"