GatsbyのプロジェクトにStripe決済をつける方法は サーバー不要Gatsbyプロジェクトで商品を売る方法。stripeの決済機能実装。
上のリンクの続きになります
yarn add use-shopping-cart
use-shopping-cartを使用するにはプロバイダーコンポーネントの中に商品を含めないといけないのでgatsby-browser.jsにプロバイダーコンポーネントを追加する
import './src/style.css'
import 'prismjs/themes/prism.css'
import React from "react"
import { CartProvider } from 'use-shopping-cart'
export const wrapRootElement = ({ {element} }) => {
return (
<CartProvider
mode={"payment"}
cartMode={"client-only"}
stripe={process.env.GATSBY_STRIPE_PUBLISHABLE_KEY}
successUrl={'https://hi1t0.com/checkout-success'}
cancelUrl={'https://hi1t0.com/products'}
currency={'jpy'}
>
{element}
</CartProvider>
)
}
プロダクトカードコンポーネントを書き換える
import React, { useState } from "react"
// import getStripe from "../../utils/stripe"
import { useShoppingCart } from 'use-shopping-cart'
...
const formatPrice = (amount, currency) => {
...
return numberFormat.format(price)
}
const ProductCard = ({ product }) => {
const [loading, setLoading] = useState(false)
const { checkoutSingleItem } = useShoppingCart()
const handleSubmit = async event => {
event.preventDefault()
setLoading(true)
// price_id
const price = new FormData(event.target).get("priceSelect")
const { error } = await checkoutSingleItem(price)
// 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}/products`,
// })
if (error) {
console.warn("Error:", error)
setLoading(false)
}
}
return (
<div className="column is-6">
<form onSubmit={handleSubmit} className="has-text-centered">
<div style={cardStyles}>
{
product.images.length > 0 ?
<img src={product.images[0]} alt={product.name}/>
: ''
}
<fieldset style={cardMain}>
<legend style={legendStyles}>
<h4>{product.name}</h4>
<p>{product.description}</p>
</legend>
<label style={priceStyles}>
価格{" "}
<select name="priceSelect" style={selectBox}>
{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>
</div>
</form>
</div>
)
}
export default ProductCard
const ProductCard = ({ product }) => {
const [loading, setLoading] = useState(false)
const [priceId, setPriceId] = useState(product.prices[0]?.id)
const { checkoutSingleItem, addItem } = useShoppingCart()
const onChangePrice = event => {
setPriceId(event.target.value)
}
const handleAddItem = event => {
setLoading(true)
const priceIndex = product.prices.findIndex(p => p.id === priceId)
addItem({price_id: product.prices[priceIndex].id, price: product.prices[priceIndex].unit_amount, currency: product.prices[priceIndex].currency, name: product?.name,})
setLoading(false)
}
const handleSubmit = async event => {
event.preventDefault()
setLoading(true)
// price_id
const price = new FormData(event.target).get("priceSelect")
const { error } = await checkoutSingleItem(price)
if (error) {
console.warn("Error:", error)
setLoading(false)
}
}
return (
<div className="column is-6">
<form onSubmit={handleSubmit} className="has-text-centered">
...
<label style={priceStyles}>
価格{" "}
<select name="priceSelect" style={selectBox} onChange={onChangePrice}>
{product.prices.map(price => (
<option key={price.id} value={price.id}>
{formatPrice(price.unit_amount, price.currency)}
</option>
))}
</select>
</label>
</fieldset>
<button
type="button"
onClick={(e) => handleAddItem(e)}
disabled={loading}
style={
loading
? { ...buttonStyles, ...buttonDisabledStyles }
: buttonStyles
}
>
追加する
</button>
<button
disabled={loading}
style={
loading
? { ...buttonStyles, ...buttonDisabledStyles }
: buttonStyles
}
>
今すぐ買う
</button>
...
src/pagesにcart.jsファイルを追加
中身は
import React from "react"
import Layout from "../components/Layout"
import { useShoppingCart } from 'use-shopping-cart'
const CartPage = () => {
const { formattedTotalPrice, cartDetails, decrementItem, incrementItem, removeItem, redirectToCheckout } = useShoppingCart()
return (
<Layout>
<div>
<ul>
{Object.values(cartDetails).map((cart) => {
return (
<li key={cart.id}>
`${cart.name} - ${cart.formattedPrice} * ${cart.quantity} = ${cart.formattedValue}`
<button onClick={() => decrementItem(cart.id)}>1つ減らす</button>
<button onClick={() => incrementItem(cart.id)}>1つ増やす</button>
<button onClick={() => removeItem(cart.id)}>削除</button>
</li>
)
})}
<li>合計: {formattedTotalPrice}</li>
</ul>
<button onClick={() => redirectToCheckout()}>
注文する
</button>
</div>
</Layout>
)
}
export default CartPage
2点分の合計金額で注文できるようになりました
error Building static HTML failed for path "/cart/"
16 | return function useDispatch() {
17 | var store = useStore();
> 18 | return store.dispatch;
| ^
19 | };
20 | }
21 | /**
WebpackError:TypeError: Cannot read property 'dispatch' of undefined
gatsby-ssr.jsファイルを作り内容をgatsby-browser.jsと同じとする
import React from "react"
import { CartProvider } from 'use-shopping-cart'
export const wrapRootElement = ({ element }) => {
return (
<CartProvider
mode={"payment"}
cartMode={"client-only"}
stripe={process.env.GATSBY_STRIPE_PUBLISHABLE_KEY}
successUrl={'https://hi1t0.com/checkout-success'}
cancelUrl={'https://hi1t0.com/cart'}
currency={'jpy'}
>
{element}
</CartProvider>
)
}
これでビルドは通り本番環境にも反映された
参考