// gatsbyコマンドを使えるようにする
yarn global add gatsby-cli
// gatsby-starter-netlify-cmsというフォルダにgatsbyプロジェクトをクローンする
gatsby new gatsby-starter-netlify-cms https://github.com/netlify-templates/gatsby-starter-netlify-cms
cd gatsby-starter-netlify-cms
// localhost:8000で確認できるようになる
yarn start
stripeアカウントを登録し
https://dashboard.stripe.com/test/dashboard
の右下にある
をgatsby-starter-netlify-cms直下に.env.development
をいうファイルを作り書き込む
GATSBY_STRIPE_PUBLISHABLE_KEY="pk_test_..."
STRIPE_SECRET_KEY="sk_test_..."
クライアント側のみの組み込みを有効にしておく
ドメインを入力、保存。最初の商品を作成します
商品情報を入力し、商品を保存をクリック
stripe内に商品が出来ました
yarn add @stripe/stripe-js gatsby-source-stripe
ここでyarn start
でプロジェクトを立ち上げてgraphqlを確認してみます
コード追加をしていくと一番下にstripePriceと真ん中辺りにallStripePriceいう項目が表示されるようになる。ここに先ほどstripe側で作った商品の情報が格納される場所になる
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
module.exports = {
siteMetadata: {
...
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-stripe`,
options: {
objects: ["Price"],
secretKey: process.env.STRIPE_SECRET_KEY,
downloadFiles: false,
},
},
...
srcフォルダにutilsフォルダを作る
import { loadStripe } from '@stripe/stripe-js'
let stripePromise
// stripeのアカウント情報を取得する
const getStripe = () => {
if (!stripePromise) {
stripePromise = loadStripe(process.env.GATSBY_STRIPE_PUBLISHABLE_KEY)
}
return stripePromise
}
export default getStripe
商品一覧表示のコンポーネント
import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import ProductCard from "./ProductCard"
const containerStyles = {
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-between",
padding: "1rem 0 1rem 0",
}
const Products = () => {
const { prices } = useStaticQuery(
graphql`
query ProductPrices {
prices: allStripePrice(
filter: { active: { eq: true } }
sort: { fields: [unit_amount] }
) {
edges {
node {
id
active
currency
unit_amount
product {
id
name
}
}
}
}
}
`
)
// Group prices by product
const products = {}
for (const { node: price } of prices.edges) {
const product = price.product
if (!products[product.id]) {
products[product.id] = product
products[product.id].prices = []
}
products[product.id].prices.push(price)
}
return (
<div style={containerStyles}>
{Object.keys(products).map(key => (
<ProductCard key={products[key].id} product={products[key]} />
))}
</div>
)
}
export default Products
商品表示のコンポーネント(購入ボタンを押すと決済画面になる)
import React, { useState } from "react"
import getStripe from "../../utils/stripe"
const cardStyles = {
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
alignItems: "flex-start",
padding: "1rem",
marginBottom: "1rem",
boxShadow: "5px 5px 25px 0 rgba(46,61,73,.2)",
backgroundColor: "#fff",
borderRadius: "6px",
maxWidth: "300px",
}
const buttonStyles = {
display: "block",
fontSize: "13px",
textAlign: "center",
color: "#000",
padding: "12px",
boxShadow: "2px 5px 10px rgba(0,0,0,.1)",
backgroundColor: "rgb(255, 178, 56)",
borderRadius: "6px",
letterSpacing: "1.5px",
}
const buttonDisabledStyles = {
opacity: "0.5",
cursor: "not-allowed",
}
const formatPrice = (amount, currency) => {
// let price = (amount / 100).toFixed(2)
let price = amount
let numberFormat = new Intl.NumberFormat(["ja-JP"], {
style: "currency",
currency: currency,
currencyDisplay: "symbol",
})
return numberFormat.format(price)
}
const ProductCard = ({ product }) => {
const [loading, setLoading] = useState(false)
const handleSubmit = async event => {
event.preventDefault()
setLoading(true)
const price = new FormData(event.target).get("priceSelect")
const stripe = await getStripe()
const { error } = await stripe.redirectToCheckout({
mode: "payment",
lineItems: [{ price, quantity: 1 }],
successUrl: `${window.location.origin}/checkout-success/`,
cancelUrl: `${window.location.origin}/`,
})
if (error) {
console.warn("Error:", error)
setLoading(false)
}
}
return (
<div style={cardStyles}>
<form onSubmit={handleSubmit}>
<fieldset style={{ border: "none" }}>
<legend>
<h4>{product.name}</h4>
</legend>
<label>
Price{" "}
<select name="priceSelect">
{product.prices.map(price => (
<option key={price.id} value={price.id}>
{formatPrice(price.unit_amount, price.currency)}
</option>
))}
</select>
</label>
</fieldset>
<button
disabled={loading}
style={
loading
? { ...buttonStyles, ...buttonDisabledStyles }
: buttonStyles
}
>
購入する
</button>
</form>
</div>
)
}
export default ProductCard
購入完了時のリダイレクト先ページ作成
import React from "react"
import { Link } from "gatsby"
import Layout from "../components/Layout"
const CheckoutSuccessPage = () => (
<Layout>
<h1>Success!</h1>
<Link to="/products">Shop again</Link>
</Layout>
)
export default CheckoutSuccessPage
src/templates/index-page.jsの適当な場所にProducts
コンポーネントを挿入する
import React from "react";
import PropTypes from "prop-types";
import { Link, graphql } from "gatsby";
import { getImage } from "gatsby-plugin-image";
import Layout from "../components/Layout";
import Features from "../components/Features";
import BlogRoll from "../components/BlogRoll";
import FullWidthImage from "../components/FullWidthImage";
/* ↓↓↓↓ */
import Products from "../components/products/Products"
/* ↑↑↑↑ */
// eslint-disable-next-line
export const IndexPageTemplate = ({
image,
title,
heading,
subheading,
mainpitch,
description,
intro,
}) => {
const heroImage = getImage(image) || image;
return (
<div>
<FullWidthImage img={heroImage} title={title} subheading={subheading} />
<section className="section section--gradient">
<div className="container">
<div className="section">
<div className="columns">
<div className="column is-10 is-offset-1">
<div className="content">
<div className="content">
<div className="tile">
<h1 className="title">{mainpitch.title}</h1>
</div>
<div className="tile">
<h3 className="subtitle">{mainpitch.description}</h3>
</div>
</div>
<div className="columns">
<div className="column is-12">
<h3 className="has-text-weight-semibold is-size-2">
{heading}
</h3>
<p>{description}</p>
</div>
</div>
/* ↓↓↓↓ */
<Products />
/* ↑↑↑↑ */
<Features gridItems={intro.blurbs} />
<div className="columns">
<div className="column is-12 has-text-centered">
<Link className="btn" to="/products">
See all products
</Link>
</div>
</div>
<div className="column is-12">
<h3 className="has-text-weight-semibold is-size-2">
Latest stories
</h3>
<BlogRoll />
yarn start
でプロジェクトを立ち上げる
商品を表示でき購入ボタンを押すと決済ページに飛ぶ
情報を入力し支払うボタンを押す (テスト時のカード情報について #Stripe でテストのクレジットカードを追加する時の番号は? Visa なら 4242 4242 4242 4242
購入後の画面に遷移し、stripeのダッシュボードにも支払い履歴が反映されました
よかったらご購入よろしくお願いします!