สร้างรายได้จากซอฟต์แวร์โอเพ่นซอร์สด้วยฟังก์ชัน Gatsby และ Stripe
เผยแพร่แล้ว: 2022-03-10ในบทความนี้ ฉันจะอธิบายวิธีที่ฉันใช้ Gatsby Functions และ Stripe API เพื่อเปิดใช้งานการบริจาค "จ่ายสิ่งที่คุณต้องการ" ที่ปลอดภัยซึ่งช่วยจัดหาเงินทุนสำหรับโครงการโอเพ่นซอร์ส MDX Embed ของฉัน
หมายเหตุ : MDX Embed ช่วยให้คุณสามารถฝังเนื้อหาสื่อของบุคคลที่สามที่เป็นที่นิยม เช่น วิดีโอ YouTube, ทวีต, โพสต์ใน Instagram, บทเรียน Egghead, Spotify, TikTok และอื่นๆ อีกมากมายใน .mdx
ของคุณโดยตรงโดยไม่ต้องนำเข้า
Gatsby Serverless Functions
Gatsby Functions เปิดโลกใบใหม่ให้กับนักพัฒนา front-end เนื่องจากมีวิธีการเขียนและใช้โค้ดฝั่งเซิร์ฟเวอร์โดยไม่ต้องวุ่นวายกับการดูแลเซิร์ฟเวอร์ การใช้งานสำหรับฟังก์ชันไร้เซิร์ฟเวอร์มีตั้งแต่การสมัครรับจดหมายข่าวด้วย ConvertKit การส่งอีเมลโดยใช้ SendGrid บันทึกข้อมูลในฐานข้อมูล เช่น Fauna หรือในกรณีนี้ การรับการชำระเงินที่ปลอดภัยโดยใช้ Stripe — รายการนี้มีมากมายไม่รู้จบ!
บริการของบุคคลที่สามเช่นบริการที่กล่าวถึงข้างต้นจะยอมรับเฉพาะคำขอที่ส่งทางฝั่งเซิร์ฟเวอร์เท่านั้น มีหลายสาเหตุ แต่โดยทั่วไปแล้วการใช้คีย์ความปลอดภัยหรือคีย์ส่วนตัวนั้นมีเหตุผลเดียว การใช้คีย์เหล่านี้ฝั่งเซิร์ฟเวอร์หมายความว่าจะไม่เปิดเผยต่อไคลเอ็นต์ (เบราว์เซอร์) และไม่สามารถใช้ในทางที่ผิด และนี่คือจุดที่ Serverless Functions ของ Gatsby สามารถช่วยได้
Gatsby ให้แนวทางเชิงตรรกะเดียวกันกับ Serverless Functions เช่นเดียวกับที่ทำกับเพจ ตัวอย่างเช่น หน้าเว็บไซต์จะอยู่ใน src/pages
และ Serverless Functions จะอยู่ใน src/api
ย่อมมีอะไรมากกว่านั้นเล็กน้อย แต่ประสบการณ์สำหรับนักพัฒนาซอฟต์แวร์ของ Gatsby นั้นทั้งสมเหตุสมผลและสม่ำเสมอ และฉันก็ชอบสิ่งนั้นมาก!
ฟังก์ชันต้นกำเนิดเดียวกัน
เก้าในสิบครั้งเมื่อทำงานกับ Serverless Functions คุณจะใช้มันอย่างที่ควรจะเป็น เช่น เว็บไซต์ของคุณใช้ฟังก์ชันของตัวเอง ฉันเรียกการใช้งานนี้ว่า Same Origin Functions หรือ SOF เรียกสั้น ๆ ว่า ในสถานการณ์สมมตินี้ ทั้ง Front-end และ API ถูกนำไปใช้กับจุดเริ่มต้นเดียวกัน เช่น www.my-website.com และ www.my-website.com/api และการสื่อสารระหว่างทั้งสองนั้นราบรื่นและแน่นอน , รวดเร็วทันใจ!
นี่คือไดอะแกรมเพื่อช่วยแสดงให้เห็นว่ามีลักษณะอย่างไร:
ฟังก์ชัน Cross-Origin
อย่างไรก็ตาม มีอย่างน้อยสองสถานการณ์ที่ฉันพบซึ่งฉันต้องการสิ่งที่ฉันเรียกว่า "ฟังก์ชันข้ามแหล่งกำเนิด" (หรือ COF สั้น ๆ ) สองสถานการณ์ที่ฉันต้องการ COF มีดังนี้:
- ฉันต้องการความสามารถฝั่งเซิร์ฟเวอร์ แต่เว็บไซต์ต้นทางไม่สามารถเรียกใช้ฟังก์ชันแบบไร้เซิร์ฟเวอร์ได้
- ฟังก์ชัน Serverless ถูกใช้โดยแหล่งกำเนิดมากกว่าหนึ่งแห่ง
หมายเหตุ : การใช้ Gatsby ไม่ใช่วิธีเดียวในการเขียน Serverless Functions แต่เพิ่มเติมในอีกสักครู่
ฉันได้ทดลองใช้วิธีการนี้ครั้งแรกในเดือนพฤศจิกายน 2020 ก่อนการเปิดตัว Gatsby Functions และใช้ Netlify Functions เพื่อให้การสื่อสารแบบเซิร์ฟเวอร์กับเซิร์ฟเวอร์กับ Twitter API และบล็อก Gatsby และผลงานเชิงพาณิชย์ของฉัน คุณสามารถอ่านวิธีการนี้ได้ที่นี่: ใช้ Netlify Functions และ Twitter API v2 เป็น CMS สำหรับบล็อก Gatsby ของคุณ
หลังจากเปิดตัว Gatsby Functions ในเดือนมิถุนายน 2021 ฉันได้ปรับโครงสร้างข้างต้นเพื่อทำงานกับ Gatsby Functions และนี่คือข้อมูลเพิ่มเติมเล็กน้อยเกี่ยวกับวิธีที่ฉันดำเนินการและเหตุผล: การใช้ Gatsby Functions เป็น API แบบนามธรรม
นี่คือไดอะแกรมเพื่อแสดงแนวทางทั่วไปได้ดียิ่งขึ้น
ในไดอะแกรมด้านบน website-1.com
นั้นสร้างด้วย Gatsby และ สามารถ ใช้ Serverless Functions (แต่ใช้ไม่ได้) และ website-2.com
ถูกสร้างขึ้นโดยใช้สิ่งที่ไม่มีความสามารถของ Serverless Function
หมายเหตุ : ในทั้งสองกรณี ทั้งคู่จำเป็นต้องใช้บริการบุคคลที่สามเดียวกัน ดังนั้นจึงควรสรุปการทำงานนี้เป็น API แบบสแตนด์อโลน
ตัวอย่าง API แบบสแตนด์อโลน ( my-api.com
) ยังเป็นไซต์ Gatsby และมีความสามารถของ Serverless Function แต่ที่สำคัญกว่านั้นคือช่วยให้เว็บไซต์จากต้นทางอื่นใช้ Serverless Functions ได้
ฉันรู้ว่าคุณคิดอะไรอยู่: CORS! อืม นั่งนิ่งๆ ฉันจะครอบคลุมเรื่องนี้ในไม่ช้า
การสร้างรายได้ MDX Embed
นี่คือสถานการณ์ที่ฉันพบด้วย MDX Embed เว็บไซต์เอกสารสำหรับโครงการนี้สร้างขึ้นโดยใช้ Storybook Storybook ไม่มีความสามารถแบบไร้เซิร์ฟเวอร์ แต่ฉันต้องการการสื่อสารระหว่างเซิร์ฟเวอร์กับเซิร์ฟเวอร์จริงๆ ทางออกของฉัน? ฉันสร้าง API แบบสแตนด์อโลนที่เรียกว่า Paulie API
Paulie API
Paulie API (เช่น ตัวอย่าง API แบบสแตนด์อโลนที่กล่าวถึงข้างต้น) สามารถรับคำขอจากเว็บไซต์จากแหล่งที่มาต่างๆ กัน และสามารถเชื่อมต่อกับบริการต่างๆ ของบุคคลที่สามได้หลายบริการ ซึ่งหนึ่งในนั้นคือ Stripe
ในการเปิดใช้งานการชำระเงิน Stripe จาก MDX Embed ฉันได้สร้างปลายทาง api/make-stripe-payment
บน Paulie API ซึ่งสามารถส่งข้อมูลที่เกี่ยวข้องจาก MDX Embed ผ่านฟังก์ชัน Serverless ของตัวเอง และบน Stripe API เพื่อสร้าง “การชำระเงิน” คุณสามารถดูรหัส src ได้ที่นี่
เมื่อสร้างการชำระเงินสำเร็จแล้ว Stripe API จะส่งกลับ URL URL นี้จะถูกส่งกลับไปยัง MDX Embed ซึ่งจะเปิดหน้าต่างใหม่ในเบราว์เซอร์ที่ "ลูกค้า" สามารถป้อนรายละเอียดการชำระเงินได้อย่างปลอดภัยในหน้าเว็บ Stripe... และบูม! คุณได้รับเงิน!
นี่คือไดอะแกรมที่อธิบายวิธีการทำงานได้ดีขึ้น:
วิธีการนี้เหมือนกับที่กล่าวไว้ข้างต้นโดยที่ https://mdx-embed.com ส่งคำขอไปที่ https://paulieapi.gatsbyjs.io ซึ่งจะเชื่อมต่อกับ Stripe API โดยใช้การสื่อสารระหว่างเซิร์ฟเวอร์กับเซิร์ฟเวอร์ แต่ก่อนที่เราจะไปไกลกว่านี้ มันคุ้มค่าที่จะอธิบายว่าทำไมฉันถึงไม่ใช้ react-stripe-js
react-stripe-js
react-stripe-js
เป็นชุดเครื่องมือฝั่งไคลเอ็นต์ (เบราว์เซอร์) ที่ให้คุณสร้างการชำระเงิน Stripe และองค์ประกอบในโครงการ React ของคุณ ด้วย react-stripe-js คุณสามารถตั้งค่าวิธีการรับการชำระเงินได้อย่างปลอดภัยโดยไม่จำเป็นต้องใช้การสื่อสารทางฝั่งเซิร์ฟเวอร์ แต่... และก็มี แต่ ฉันต้องการใช้การบริจาค "จ่ายตามที่คุณต้องการ" ให้ฉันอธิบาย
นี่คือภาพหน้าจอของ MDX Embed “ผลิตภัณฑ์” ที่ฉันตั้งค่าไว้ในแดชบอร์ด Stripe สังเกตว่าราคาอยู่ที่ $1.00
ถ้าฉันใช้ react-stripe-js เพื่อเปิดใช้งานการชำระเงิน "ลูกค้า" ทั้งหมดจะถูกขอให้จ่ายเงินในจำนวนเท่ากัน ในกรณีนี้ มันเป็นเพียง $1.00 และนั่นจะไม่จ่ายบิลมัน!
ในการเปิดใช้งาน "จ่ายเท่าที่คุณต้องการ" (เช่น จำนวนเล็กน้อยที่เลือกโดย "ลูกค้า") คุณต้องเจาะลึกลงไปอีกเล็กน้อยและใช้การสื่อสารระหว่างเซิร์ฟเวอร์กับเซิร์ฟเวอร์ และส่งจำนวนนี้ไปยัง Stripe API โดยใช้คำขอ HTTP ที่กำหนดเอง นี่คือที่ที่ฉันใช้ฟังก์ชัน Gatsby และส่งผ่านค่าไดนามิกซึ่งจะถูกใช้เพื่อสร้างประสบการณ์ "การชำระเงิน" และเขียนทับราคาที่กำหนดไว้ในแดชบอร์ด Stripe ของฉัน
ใน MDX Embed ฉันได้เพิ่ม HTML <input type="number" />
ซึ่งอนุญาตให้ “ลูกค้า” กำหนดจำนวนเงินแทนที่จะจ่ายตามจำนวนที่กำหนดไว้ล่วงหน้า — ถ้าอีคอมเมิร์ซทั้งหมดเป็นแบบนี้!
นี่เป็นวิดีโอเล็กๆ ที่ฉันทำขึ้นซึ่งแสดงให้เห็นว่า MDX Embed, Paulie API และ Stripe API ทำงานร่วมกันอย่างไร:
โดยการส่งค่าอินพุตจาก MDX Embed ไปยัง Paulie API ซึ่งจะเชื่อมต่อกับ Stripe API ฉันสามารถสร้างการชำระเงินแบบ "ไดนามิก" ได้
หมายเหตุ : ตอนนี้หมายความว่า “ลูกค้า” สามารถตัดสินใจได้ว่าโครงการใดมีค่าสำหรับพวกเขาและกำหนดจำนวนเงินที่เหมาะสมเพื่อบริจาค
ฉันอยากจะพูดถึง Benedicte Raae ณ จุดนี้ซึ่งแสดงให้ฉันเห็นถึงแนวทางนี้เป็นครั้งแรกในระหว่างหลักสูตร Summer Functions อันยอดเยี่ยมของเธอ คุณสามารถหาข้อมูลเพิ่มเติมได้โดยไปที่ Queen Raae Codes ( ขอบคุณเบเนดิกต์ คุณเก่งที่สุด! )
มาคุยกันเรื่องCORS .กันเถอะ
ตามค่าเริ่มต้น CORS จะไม่บล็อกฟังก์ชัน Gatsby Serverless เนื่องจาก Front-end และ API ถูกปรับใช้กับต้นทางเดียวกัน อย่างไรก็ตาม เมื่อพัฒนา Cross-Origin Functions คุณจะต้องกำหนดค่า API ของคุณ เพื่อให้ API ยอมรับคำขอจากต้นทางที่แตกต่างจากของตัวเอง
นี่คือข้อมูลโค้ดที่แสดงวิธีที่ฉันจัดการ CORS ในจุดสิ้นสุด api/make-stripe-payment
:
// src/api/make-stripe-payment const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY) import Cors from 'cors' const allowedOrigins = [ 'https://www.mdx-embed.com', 'https://paulie.dev', ] const cors = Cors({ origin: (origin, callback) => { if (allowedOrigins.includes(origin)) { callback(null, true) } else { callback(new Error()) } }, }) const runCorsMiddleware = (req, res) => { return new Promise((resolve, reject) => { cors(req, res, (result) => { if (result instanceof Error) { return reject(result) } return resolve(result) }) }) } export default async function handler(req, res) { const { success_url, cancel_url, amount, product } = req.body try { await runCorsMiddleware(req, res) try { const session = await stripe.checkout.sessions.create({ success_url: success_url, cancel_url: cancel_url, payment_method_types: ['card'], line_items: [ { quantity: 1, price_data: { unit_amount: amount * 100, currency: 'usd', product: product, }, }, ], mode: 'payment', }) res.status(200).json({ message: ' Stripe checkout created ok', url: session.url }) } catch (error) { res.status(500).json({ message: ' Stripe checkout error' }) } } catch (error) { res.status(403).json({ message: ' Request blocked by CORS' }) } }
ในข้อมูลโค้ดด้านบนนี้ คุณควรจะสามารถเห็นได้ว่าฉันได้กำหนดอาร์เรย์ของ allowedOrigins
แล้ว นี่เป็นจุดเริ่มต้นเดียวที่ได้รับอนุญาตให้ใช้จุดปลายนี้ คำขอจากแหล่งอื่น ๆ จะได้รับรหัสสถานะ 403
และข้อความ Request blocked by CORS
ฟังก์ชันนี้ยังยอมรับพารามิเตอร์เนื้อหาจำนวนหนึ่ง ซึ่งหนึ่งในนั้นคือ amount
ที่ "ลูกค้า" ตัดสินใจจ่าย ซึ่งเป็นค่าจากอินพุต HTML บนไซต์ MDX Embed คุณจะสังเกตเห็นพารามิเตอร์ product
ซึ่งเป็นรหัสผลิตภัณฑ์ที่กำหนดไว้ในแดชบอร์ด Stripe ของฉัน และวิธีที่ Stripe API สร้าง URL "การชำระเงิน" ที่ถูกต้อง การส่งผ่านค่านี้เป็นพารามิเตอร์ body แทนที่จะฮาร์ดโค้ดในฟังก์ชัน ทำให้ฉันสามารถใช้ปลายทางนี้ซ้ำสำหรับผลิตภัณฑ์ Stripe อื่นๆ
น้ำผลไม้คุ้มค่ากับการบีบหรือไม่?
ฉันได้กล่าวถึงบางสิ่งระหว่างทางว่าทำไมฉันจึงตัดสินใจเลือกเส้นทางนี้ ท้ายที่สุด มันอาจดูเหมือนวิธีที่ซับซ้อนกว่าในการใช้ Serverless Functions แต่ฉันมีเหตุผลของฉัน และฉันคิดว่ามันคุ้มค่า นี่คือเหตุผล
Paulie API เป็นทั้ง Cross-Origin API และไซต์เอกสาร แน่นอน หากคุณกำลังจะเขียน API จะต้องมีการจัดทำเป็นเอกสารใช่ไหม
นี่คือจุดที่ฉันชอบที่จะใช้ Gatsby เพื่อขับเคลื่อน API ของฉันเพราะพร้อมด้วยความสามารถแบบไร้เซิร์ฟเวอร์ Paulie API ยังเป็นเว็บไซต์ Gatsby และเนื่องจากเป็นเว็บไซต์จริงๆ ฉันจึงสามารถเติมเนื้อหาและทำให้ดูสวยงามได้ แต่เดี๋ยวก่อน ยังมีอีกมาก …
หมายเหตุ: Paulie API ยังเป็นสนามเด็กเล่น API แบบโต้ตอบอีกด้วย!
แต่ละฟังก์ชันมีลิงก์ Run in browser
ซึ่งจะนำคุณไปยังหน้าบนไซต์ที่คุณสามารถโต้ตอบกับฟังก์ชันได้ มันทำหน้าที่เป็นทั้งพื้นที่ทดสอบที่มีประโยชน์ในขณะที่ฉันกำลังพัฒนาฟังก์ชันและวิธีง่ายๆ ในการสาธิตวิธีการทำงานของฟังก์ชัน เอกสารนั้นดี เอกสารแบบโต้ตอบก็ดีกว่า!
ฉันยังใช้ API นี้เพื่อให้การทำงานฝั่งเซิร์ฟเวอร์ที่คล้ายกันสำหรับเว็บไซต์อื่นๆ ของฉัน ดูหน้าเกี่ยวกับซึ่งฉันได้บันทึกไว้ว่าไซต์ใดของฉันใช้ฟังก์ชันใดบ้าง และนี่คือไดอะแกรมที่แสดงให้เห็นว่าไซต์ทั้งหมดมารวมกันได้อย่างไร
คุณควรเห็นจากแผนภาพด้านบนว่า https://paulie.dev ใช้ปลายทาง Stripe ด้วย ฉันได้ใช้วิธีเดียวกันกับ MDX Embed เพื่อเปิดใช้งานฟังก์ชัน “จ่ายในสิ่งที่คุณต้องการ” เป็นเรื่องเล็กน้อย แต่เนื่องจากจุดสิ้นสุด make-stripe-payment
มีการเขียนและใช้งานได้แล้ว ฉันจึงสามารถใช้ซ้ำได้และหลีกเลี่ยงการทำซ้ำฟังก์ชันนี้
เว็บไซต์ https://paulie.dev ยังมี Gatsby Serverless Functions ของตัวเอง ซึ่งฉันใช้เพื่อโพสต์ปฏิกิริยาของผู้ใช้ต่อ Fauna และบันทึกการสมัครรับจดหมายข่าว ฟังก์ชันนี้มีเอกลักษณ์เฉพาะสำหรับไซต์นี้ ดังนั้นฉันจึงยังไม่ได้สรุปสิ่งนี้ อย่างไรก็ตาม หากฉันต้องการสมัครรับจดหมายข่าวใน https://www.pauliescanlon.io นี่จะเป็นจุดที่ฉันย้ายฟังก์ชันไปยัง Paulie API
สิ่งที่เป็นนามธรรม
นี่อาจดูเหมือนเป็นการถอยหลังเพื่อสรุปฟังก์ชัน Serverless ของคุณ ท้ายที่สุด หนึ่งในสิ่งที่ยอดเยี่ยมที่สุดเกี่ยวกับการใช้เซิร์ฟเวอร์แบบไร้เซิร์ฟเวอร์ก็คือทั้งโค้ดส่วนหน้าและส่วนหลังของคุณอยู่ในที่เดียวกัน ดังที่ฉันได้แสดงให้เห็นแล้ว มีบางครั้งที่การสรุปเป็นนามธรรมก็สมเหตุสมผล — สำหรับฉันอยู่ดี
ฉันได้รับประโยชน์อย่างแน่นอนจากการใช้แนวทางนี้และวางแผนที่จะพัฒนา API ของฉันต่อไปเพื่อให้มีฟังก์ชันการทำงานมากขึ้นกับเว็บไซต์ของฉันเองจำนวนมาก แต่ถ้าการทำเงินจากโอเพ่นซอร์สเป็นที่สนใจสำหรับคุณและไซต์ของคุณไม่ได้สร้างโดยใช้ Gatsby แนวทางนี้อาจเป็นคำตอบที่คุณต้องการ
ต้องการเริ่มต้นใช้งาน Gatsby Functions หรือไม่? ดูเอกสาร Gatsby Functions เพื่อเริ่มต้น!
อ่านเพิ่มเติม
หากคุณสนใจที่จะเรียนรู้เพิ่มเติมเกี่ยวกับ Serverless Functions ฉันขอแนะนำ:
- หนังสือของ Swizec Teller “คู่มือไร้เซิร์ฟเวอร์สำหรับวิศวกรส่วนหน้า”
- หลักสูตร Summer Functions ของเบเนดิกต์
- …และแน่นอน เอกสารของแกสบี้
FuncJam
ตั้งแต่วันที่ 17 สิงหาคม ถึง 30 กันยายน เหล่าชาว Gatsby จะจัดการแข่งขันระดับชุมชนพร้อมของรางวัลมากมายที่จะคว้าไป หากยังมีเวลา ให้ไปที่ FuncJam และเข้าร่วม นอกจากนี้ ให้ตรวจสอบส่วนขนาดไบต์ของโพสต์ในบล็อกนี้ มันมีวิดีโอที่เป็นประโยชน์และลิงก์ไปยังฟังก์ชันตัวอย่างมากมาย
ขอขอบคุณที่อ่าน และหากคุณต้องการพูดคุยเรื่องใด ๆ ที่กล่าวถึงในบทความนี้ แสดงความคิดเห็นด้านล่างหรือหาฉันบน Twitter