🕌

Stripeを用いた決済処理の実装方法①

2023/07/16に公開

背景

今回は、react,railsアプリでオンライン決済サービスの「Stripe」を用いた決済処理を実装してみました。これから決済処理を実装するためStripeの使用を検討している方の参考になれば幸いです。

 

実装方法

まず、大まかな実装手順は下記のようになります。

①Stripeのセットアップ

②決済処理の作成

③決済完了後、注文内容をデータベースに保存(別の記事で紹介予定)

今回は、③まで説明するととても説明が長くなってしまうので、①,②の実装手順のみを解説していきます。(③に関する実装はこちらのリンクです。)

まず、Stripeを使用するためにStripeのセットアップ方法について説明していきます。

 

Stripeのセットアップ

まずは、こちらのリンクからStripeのアカウントを作成するために新規登録します。

次に、rails の Credentials という機密情報を暗号化し、安全に保護する機能を用いて、公開鍵や秘密鍵などの機密情報を登録していきます。

Credentials の作成コマンドは以下の通りです。

ターミナル
$ EDITOR="vi" bin/rails credentials:edit -e development

今回は、-e development とオプションをつけることでdevelopment環境のみの設定として環境設定を指定することができます。

上記のコマンドを実行すると、config/credentials/development.yml.enc(公開鍵)とconfig/credentials/development.key(秘密鍵)が作成されます。

そして、下記のような画面になるので vimコマンドを用いて公開鍵(publishable_key)と秘密鍵(secret_key)を設定してください。(設定方法は vim コマンドで検索すると多くの記事に記載されているのでそちらを参考にしてみてください。)

# aws:
#   access_key_id: 123
#   secret_access_key: 345

# ここらへんにStripeの公開鍵と秘密鍵の記述をする。

設定すべき公開鍵(publishable_key)と秘密鍵(secret_key)の各値は、Stripeダッシュボード > ホーム に記載してあります。(下記画像を参照してください。)

公開鍵は「公開可能キー」の値、秘密鍵は「シークレットキー」の値を設定してください。

# aws:
#   access_key_id: 123
#   secret_access_key: 345

stripe:
  publishable_key: pk_test_xxxxx
  secret_key: sk_test_xxxxx

設定が完了したら rails c で 適切に設定できているか確認してみます。コマンドは下記の通りです。

ターミナル
$ rails c

[1] pry(main)> Rails.application.credentials.dig(:stripe, :publishable_key)
=> "pk_test_51NN..."

[2] pry(main)> Rails.application.credentials.dig(:stripe, :secret_key)
=> "sk_test_51NN..."

さらに、サーバ起動時に設定した公開鍵(publishable_key)と秘密鍵(secret_key)を読み込ませるために Stripe専用の config/initializers/stripe.rb を作成します。

config/initializers/ について説明すると、config/initializers/ 配下にあるファイルは、rails アプリ起動時に自動的に読み込まれます。

読み込まれるタイミングは、フレームワークの読み込み&gemの読み込みが終わったタイミングです。

また、config/initializers/ 配下にあるファイルは、開発環境・テスト環境・本番環境全てで読み込まれます。

作成すべきconfig/initializers/stripe.rb は下記の通りです。

config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)
Stripe.api_version = '2022-11-15'

Stripe.api_versionは、こちらのリンクを参考にしてください。

以上でStripeを使用できるようになるまでの初期設定は完了となります。次に決済処理の実装方法について説明します。

 

決済処理の作成

Stripeのドキュメントに「Checkout(支払い)の仕組み」というページがあり、決済処理の流れを確認することができます。大まかな処理の流れは下記の通りです。

①顧客が購入を完了する準備ができると、アプリケーションは新しい Checkout セッション(Webサイトの購入者に対する単一のアクティブなセッションまたはエンゲージメントのこと)を作成します。

②Checkout セッションは、顧客を Stripe がオンラインで提供する決済ページにリダイレクトする URL を提供します。

③顧客は決済ページに決済情報を入力し、取引を完了します。

④取引終了後、Webhookは checkout.session.completed イベントを使用して注文のフルフィルメント(商品を受注してから発送するまでの一連の工程)を実行します。

 
Checkout(支払い)のライフサイクル(ドキュメントより抜粋)

大まかなCheckout(支払い)の仕組みを把握することができたので、まず React,JavaScript側の処理を実装していきます。

実装すべき内容は、Checkoutセッションを作成し、決済ページへリダイレクトさせる処理の API を呼び出すことです。実装内容は下記の通りです。

frontend/src/components/cart/Cart.jsx
import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { Card, CardContent, CardMedia, Typography, Grid, Button } from '@mui/material';
import { fetchCartData } from '../../apis/fetchCartData';
import { confirmedOrder } from '../../apis/confirmedOrder';
import logo from '../..//20230416_シャンプー画像.jpg'

const Cart = () => {

  -- 省略 --

  // 決済ページへ遷移
  const GoToOrderPage = () => {
    confirmedOrder()
  }

  return (
    <div style={{ padding: '10px'}}>

      -- 省略 --

      <div style={{ margin: '20px', display: 'flex' }}>
        <Typography style={{ minWidth: '50%' }}>合計金額:¥{cart.total_price}</Typography>
        <Button variant="contained" style={{ minWidth: '50%' }} onClick={GoToOrderPage} disabled={cart.total_price <= 0}>注文手続きへ</Button>
      </div>
    </div>
  )
}

export default Cart

上記では、作成した「注文手続きへ」のボタンを押下することで、GoToOrderPage関数の中にある confirmedOrder 関数(決済ページへリダイレクトするための処理)が実行されます。

Checkoutセッションを作成し、決済ページへリダイレクトさせる処理のAPI(上記のcheckouts_controller.rb の createアクション)を実行させる JavaScript 側の処理は下記になります。

frontend/src/apis/confirmedOrder.js
import axios from 'axios';
import { confirmOrderurl } from '../urls/index'

// 決済ページへ
export const confirmedOrder = async() => {
  await axios.post(confirmOrderurl)
  .then(data => {
    window.location.href = data.data.session.url
  }).catch(error => {
    console.log(error);
  });
};

また、ルーティングは下記のようになります。

config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do

      -- 省略 --

      resources :checkouts, only: [:create]

    end
  end
end

次に、Checkoutセッションを作成するためのcontrollerを作成します。実装内容は下記の通りです。

app/controllers/api/v1/checkouts_controller.rb
class Api::V1::CheckoutsController < ApplicationController
  before_action :authenticate_api_v1_user!

  # ここにCheckoutセッションを作成する処理を実装予定
end

今回は、devise-token-authというトークンベース認証(サーバから生成されたトークンによりそのユーザーが誰であるのか確認・特定すること)を行うことができる gem を用いています。devise-token-auth が提供しているメソッドの before_action :authenticate_api_v1_user! を用いて、ログインユーザーかどうか判断しユーザー情報を取得しています。

次に、createアクションを定義し、Checkout セッションを作成し、決済ページへリダイレクトする処理を実装していきます。下記が実装したCheckout セッションの作成処理となります。

app/controllers/api/v1/checkouts_controller.rb
class Api::V1::CheckoutsController < ApplicationController
  before_action :authenticate_api_v1_user!

  def create
    line_items = current_api_v1_user.line_items_checkout
    session = create_session(line_items)
    render json: { session: session }, status: :ok
  end

  private

  def create_session(line_items)
    Stripe::Checkout::Session.create(
      client_reference_id: current_api_v1_user.id,
      customer_email: current_api_v1_user.email,
      mode: 'payment',
      payment_method_types: ['card'],
      line_items: line_items,
      shipping_address_collection: {
        allowed_countries: ['JP']
      },
      shipping_options: [
        {
          shipping_rate_data: {
            type: 'fixed_amount',
            fixed_amount: {
              amount: 500,
              currency: 'jpy'
            },
            display_name: 'Single rate'
          }
        }
      ],
      success_url: '決済成功時に遷移させたいURL',
      cancel_url: "決済失敗時に遷移させたいURL"
    )
  end
end
app/models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable
  include DeviseTokenAuth::Concerns::User

  has_one :cart, dependent: :destroy
  has_many :orders, dependent: :destroy

  -- 省略 --

  def line_items_checkout
    cart.cart_items.not_order_confirm.map do |cart_item|
      {
        quantity: cart_item.quantity,
        price_data: {
          currency: 'jpy',
          unit_amount: cart_item.item.price,
          product_data: {
            name: cart_item.item.name,
            metadata: {
              product_id: cart_item.item_id
            }
          }
        }
      }
    end
  end
end

実装したコードについて説明します。

まず、createアクション1行目の line_items = current_api_v1_user.line_items_checkout では、ログインユーザー(User)が持つカート(Cart)のカートアイテム(CartItem、ItemとCartを関連づける中間テーブルのデータ)のデータを取得しています。

このように取得する理由は、Checkout セッションを作成する時にユーザーが選択した商品情報をStripe 側で指定された形と同じような形で取得するためです。

作成すべきCheckout セッションの内容(ドキュメントより抜粋)

Stripeドキュメント
session = Stripe::Checkout::Session.create(
  line_items: [{
    name: 'T-shirt',
    description: 'Comfortable cotton t-shirt',
    images: ['https://example.com/t-shirt.png'],
    amount: 2000,
    currency: 'usd',
    price_data: {
      currency: 'usd',
      unit_amount: 2000,
      product_data: {
        name: 'T-shirt',
        description: 'Comfortable cotton t-shirt',
        images: ['https://example.com/t-shirt.png'],
      },
    },
    quantity: 1,
  }],
  mode: 'payment',
  success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
  cancel_url: 'https://example.com/cancel',
)

次に、createアクション2行目 session = create_session(line_items) で、Checkout セッションを作成しています。

そして、createアクション3行目 render json: { session: session }, status: :ok で、Javascript側に返したい内容をJSON形式で返しています。

これは、決済ページのURL(session の中にある url プロパティの値)へリダイレクトさせるために行なっています。

以上がStripeの決済処理の実装方法になります。

上記のように実装することで、下記の決済ページへリダイレクトすることができるようになります。

 

まとめ

今回は、react,railsアプリでオンライン決済サービスの「Stripe」を用いた決済処理を実装する方法を紹介してみました。

今回はCheckoutセッションの作成から決済ページへリダイレクトする処理の実装方法までしか解説できなかったので、次回は決済後の処理の実装方法を解説したいと思います。

Discussion