Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Stripe & Next.js + AWS Amplify で会員 + 定期課金機能 / ...

Stripe & Next.js + AWS Amplify で会員 + 定期課金機能 / JP_Stripes20210903

Hidetaka Okamoto

September 03, 2021
Tweet

More Decks by Hidetaka Okamoto

Other Decks in Programming

Transcript

  1. H i d e t a k a O k

    a m o t o • Digitalcube Co. Ltd. • JavaScript Developer • WordPress 4.7 / 5.0 / 5.3 Core contributor
  2. H i d e t a k a O k

    a m o t o • Digitalcube Co. Ltd. • JavaScript Developer • WordPress 4.7 / 5.0 / 5.3 Core contributor https://zenn.dev/hideokamoto/books/e961b4bad92429
  3. Agenda • ࠷খݶͷ։ൃ޻਺Ͱ༗ྉձһϝσΟΞΛ࡞Δ • Next.js / Stripe / AWS AmplifyͰ࣮૷͢Δ

    • ઃܭɾ։ൃ࣌ͷ஫ҙ఺ • ʮਖ਼͘͠SaaSʹґଘ͢Δʯ #JP_Stripes
  4. Agenda • ࠷খݶͷ։ൃ޻਺Ͱ༗ྉձһϝσΟΞΛ࡞Δ • Next.js / Stripe / AWS AmplifyͰ࣮૷͢Δ

    • ઃܭɾ։ൃ࣌ͷ஫ҙ఺ • ʮਖ਼͘͠SaaSʹґଘ͢Δʯ #JP_Stripes
  5. • AWS Amplify: Authentication • AWS CDK: IaC • Stripe:

    Subscription • Algolia: Advanced Search • Next.js: Framework • Ionic: UI library • WordPress: Headless CMS • Capacitor / Sentry / etc… #JP_Stripes ٕज़ελοΫ
  6. SaaS APIΛ׆༻ͯ͠ɺίʔυͷྔΛ཈͑Δ • ܾࡁͱܾࡁ؅ཧ͸΄΅ඞͣඞཁʹͳΔػೳཁ݅ • StripeͰܾࡁ͢Δ৔߹ɺҎԼͷ2఺͕Low CodeԽͰ͖Δ • ஫จϑΥʔϜ(Χʔυ /

    ߪೖऀ৘ใೖྗ) -> Checkout • ఆظ՝ۚ৘ใ؅ཧ -> Customer Portal • ʮࣗ༝ͱޮ཰ʯͲͬͪΛॏࢹ͢Δ͔ͰಓΛબ΅͏ #JP_Stripes
  7. Agenda • ࠷খݶͷ։ൃ޻਺Ͱ༗ྉձһϝσΟΞΛ࡞Δ • Next.js / Stripe / AWS AmplifyͰ࣮૷͢Δ

    • ઃܭɾ։ൃ࣌ͷ஫ҙ఺ • ʮਖ਼͘͠SaaSʹґଘ͢Δʯ #JP_Stripes
  8. Next.js & AWS AmplifyʹStripeΛ૊ΈࠐΉ • React AppଆʹStripe JS SDK •

    REST APIଆʹStripe Nodejs SDK • CheckoutͱCustomer Portal͚ͩͳΒReact SDK͸ෆཁ • React SDK͸Stripe ElementΛ࢖͏ͨΊͷSDK • APIΩʔͷऔΓѻ͍ʹ஫ҙʢޙड़ʣ #JP_Stripes
  9. SSM·ͨ͸Secret Manager͔ΒAPIΩʔΛऔಘ export const createStripeClient = async () => {

    const data = await new SSM({ region: “us-east-1"ɹ}) .getParameter({ Name: !!process.env.AWS_LAMBDA_FUNCTION_NAME ? "STRIPE_LIVE_SECRET_KEY": "STRIPE_TEST_SECRET_KEY", WithDecryption: true, }).promise(); const stripe = new Stripe(data.Parameter?.Value;, { apiVersion: "2020-08-27", maxNetworkRetries: 3, }); return stripe; }; #JP_Stripes
  10. Stripe CustomerͱCognito User poolͷ࿈ܞ // Authorization HeaderͳͲͰऔಘͨ͠tokenͰCognito UserΛGet const user

    = await cognito.getUser({ AccessToken: req.headers.authorization || “" }).promise() // User attributes͔ΒEmailΛऔಘ const emailAttribute = user.UserAttributes.find((data) => data.Name === "email"); const email = emailAttribute?.Value || “"; // EmailΛ͔ͭͬͯStripe CustomerΛ࡞੒ const customer = await stripe.customers.create({ email }); #JP_Stripes
  11. Stripe CustomerͱCognito User poolͷ࿈ܞ // ࡞੒ͨ͠Stripe Customer const customer =

    await stripe.customers.create({ email }); // User Attributeʹอଘ͢Δ await cognito .updateUserAttributes({ AccessToken: req.headers.authorization, UserAttributes: [{ Name: "custom:stripeCustomerId", Value: customer.id, }], }).promise(); #JP_Stripes
  12. Customer idΛηοτͯ͠checkout.sessions.create const session = await stripe.checkout.sessions.create({ customer: customerId, allow_promotion_codes:

    true, line_items: [{ price: price.id, quantity: 1, }], mode: "subscription", success_url: `${appUrl.replace(/\/$/, "")}/mypages/subscriptions`, cancel_url: `${appUrl.replace(/\/$/, "")}/mypages/plans`, payment_method_types: ["card"], }); res.status(200).json({ session_id: session.id }); #JP_Stripes
  13. Customer Portal͸Customer IDΛ࢖͏ // Cognito͔ΒϢʔβʔΛऔಘ const user = await cognito.getUser({

    AccessToken: req.headers.authorization }).promise() const attribute = user.UserAttributes.find((data) => { return data.Name === “custom:stripeCustomerId" }); // Customer Poral SessionΛ࡞੒ const session = await stripe.billingPortal.sessions.create({ customer: attribute?.Value, return_url: `${appUrl.replace(/\/$/, "")}/mypages/subscriptions`, }); res.status(200).json({ url: session.url }); #JP_Stripes
  14. APIͰϙʔλϧͷURLΛੜ੒ͯ͠ϦμΠϨΫτ const { push } = useRouter(); return (<button onClick={async

    () => { const session = await Auth.currentSession(); const token = session.getAccessToken().getJwtToken(); const data = await fetch("/api/portal", { method: "POST", headers: { "Content-Type": "application/json", Authorization: token, }, }).then(data => data.json()); push(data.url); }}>Button</button>) #JP_Stripes
  15. ࣮૷࣌ͷϙΠϯτ 1. StripeͷCustomer IDΛϢʔβʔ৘ใͱඥ͚ͮΔඞཁ͋Γ • Customer Portal Linkͷੜ੒͸αʔόʔଆॲཧ 2. Customer

    Portalʹग़͢ίϯςϯπͷ੍ޚ͸ݪଇDashboard • API͔ΒͳΒઃఆ͸ෳ਺࡞ΕΔ໛༷ʢະݕূʣ 3. Stripe Secret APIΩʔͷѻ͍͕ෆ҆ͳΒ੍ݶ෇͖ΩʔΛ࢖͓͏ #JP_Stripes
  16. Agenda • ࠷খݶͷ։ൃ޻਺Ͱ༗ྉձһϝσΟΞΛ࡞Δ • Next.js / Stripe / AWS AmplifyͰ࣮૷͢Δ

    • ઃܭɾ։ൃ࣌ͷ஫ҙ఺ • ʮਖ਼͘͠SaaSʹґଘ͢Δʯ #JP_Stripes
  17. ΞϓϦ֎ͰStripeͷσʔλ͕มΘΔ͜ͱʹ஫ҙ • ϓϥϯมߋɾղ໿ɾϢʔβʔ৘ใͷมߋͳͲͷૢ࡞ • Customer PortalͰى͖ͨมߋΛγεςϜʹ൓өͤ͞Δඞཁ͕͋Δ • Stripe WebhookͰΠϕϯτΛड͚ͯσʔλΛมߋ͠Α͏ •

    Customer Portalʹ͸ʮCustomer࡟আʯ͕ݱঢ়ͳ͍ 1. Subscriptions.delete WebhookͰσʔλΛফ͢ 2. CognitoͳͲͷϢʔβʔ࡟আ࣌ʹফ͢Α͏ʹγεςϜΛߏங #JP_Stripes
  18. Agenda • ࠷খݶͷ։ൃ޻਺Ͱ༗ྉձһϝσΟΞΛ࡞Δ • Next.js / Stripe / AWS AmplifyͰ࣮૷͢Δ

    • ઃܭɾ։ൃ࣌ͷ஫ҙ఺ • ʮਖ਼͘͠SaaSʹґଘ͢Δʯ #JP_Stripes
  19. ίΞϏδωεྖҬ֎ͷػೳΛ୭͕࡞Δ͔ʁ A. ࣗྗ։ൃ͢Δ͜ͱͰɺσβΠϯ΍࢓༷ͷࣗ༝౓Λ֬อͰ͖Δ • ػೳ͕૿͑Δͱอकɾӡ༻ͷൣғ΋૿͑Δ • ͦͷࣗ༝Λ׆༻Ͱ͖Δ͚ͩͷ։ൃϦιʔε͕͋Δ͔൱͔ B. SaaS /

    Low Codeʹҕৡͯ͠ɺॏཁͳػೳʹूத͢Δ • ϕϯμʔ͕࠷దͱ͢ΔUI΍ػೳΛৗʹड͚औΕΔ • υϝΠϯ΍σβΠϯɾػೳͷ੍໿Λڐ༰Ͱ͖Δ͔൱͔ #JP_Stripes
  20. ΠϕϯτۦಈͳγεςϜ͔ͩΒͰ͖Δ͜ͱ #JP_Stripes • Checkout / Customer Portal͸WebhookͰγεςϜͱ࿈ܞ͢Δ࢓૊Έ • ϓϥϯมߋͳͲͷΠϕϯτΛड͚ͯಈ࡞͢ΔγεςϜʹͳΔ •

    ʮಉ͡ΠϕϯτΛൃՐͰ͖Ε͹ʯ͍ͭͰ΋ஔ͖׵͑Ͱ͖Δ • ্ཱͪ͛࣌͸྆ػೳΛ࢖ͬͯίΞ։ൃʹϦιʔεΛूத͢Δ • Ҡߦ͢Δ࣌͸ɺΠϕϯτ୯ҐͰஈ֊తʹ੾Γସ͍͑ͯ͘ • ʮΠϕϯτۦಈΞʔΩςΫνϟʯ΁ͷୈҰาʹ
  21. Thanks! • ࠷খݶͷ։ൃ޻਺Ͱ༗ྉձһϝσΟΞΛ࡞Δ • Next.js / Stripe / AWS AmplifyͰ࣮૷͢Δ

    • ઃܭɾ։ൃ࣌ͷ஫ҙ఺ • ʮਖ਼͘͠SaaSʹґଘ͢Δʯ #JP_Stripes