2020年6月1日 更新

【ECサイトを作ろう!】Nuxt.js×Stripe×Netlifyを利用して決済処理を実装する方法

こんにちは。いかがお過ごしでしょうか?
最近は在宅時間が増え、オンラインショッピングの需要が高まってきていますね。
今回は、Nuxt.js、Stripe、Netlifyを利用して、ECサイトの決済処理を実装する方法をご紹介します。
もちろん今回もJAMstack構成で実装いたします。
最終的にはこのような決済処理が出来上がります。


では始めていきましょう。

Stripeとは?

https://stripe.com/jp
簡単に言うとオンライン決済のあらゆる機能を提供するプラットフォームです。
各種クレジットカードやApple PayやGoogle Payなどに幅広く対応しており、
一時決済だけでなく継続課金などにも対応しております。
費用は、基本的には決済手数料の3.6%のみで、初期費用や入金手数料はかかりません。
詳しくはこちらをご覧ください。
管理画面も非常に見やすく操作しやすいUIとなっております。


また、開発者向けに便利なSDKやライブラリ、ドキュメントも豊富に用意されており、
意外のほか簡単に実装できてしまいます。

今回は以前の記事「【外部API連携に】Nuxt.jsでNetlify Functionsを使用する方法」の応用で、
Netlify Functionsを利用してStripe APIを実行する流れです。
では早速進めていきます。

Stripeに登録する

まずはサービス登録します。
Stripeにアクセスし、「今すぐ始める」を選んで流れに沿って登録を進めてください。


登録が完了したらダッシュボードへアクセスします。


すると画面上部の「テストAPIキーの取得」のところに公開可能キー、及びシークレットキーが表示されます。
後ほど使用しますのでメモしておいてください。
(ダッシュボードに記載されていない場合は、メニュー > 開発者 > APIキー からも確認する事ができます。)

次は実装に入っていきます。

必要なライブラリをインストールする

今回使用するライブラリは、

の全部で5つです。
全部インストールしましょう。

yarn add @nuxtjs/axios dotenv-webpack netlify-lambda stripe vue-stripe-elements-plus


決済処理を実装する

本題の決済処理を実装していきます。

決済処理の流れとしては、

  1. カード情報入力
  2. 決済用トークン発行
  3. Netlify Functionsに決済リクエスト
  4. Stripeの決済処理APIの実行
  5. 決済完了

という流れとなります。

モジュール構成は下記です。

functions
┗api
  ┗charge.js    // Stripeの決済APIを実行する
pages
┗index.vue    // 決済画面
.env    // ローカル開発用環境変数
netlify.toml    // Netlifyのビルド設定ファイル
nuxt.config.js    // Nuxt.jsの設定ファイル
package.json    // npmプロジェクトの情報管理
webpack.functions.js    // 環境変数のbundle化


まず環境周りの設定から進めます。
.env

STRIPE_SECRET_KEY=sk_test_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ローカル開発用の環境変数です。
前手順で取得した「シークレットキー」を設定しておきます。

netlify.toml

[build]
 Command = "yarn generate"
 functions = "functions/dist"
 publish = "dist"

Netlifyのビルド設定です。
ビルドコマンド、ファンクション/ホスティングのデプロイモジュールの場所を定義しておきます。

nuxt.config.js

env: {
    FUNCTION_URL: process.env.URL || 'http://localhost:9000'
  }

環境変数にファンクションのエンドポイントを定義しておきます。
Netlifyの本番環境では自動的に「process.env.URL」が設定されます。
設定されていなければローカル環境とします。

package.json

"scripts": {
    "dev": "netlify-lambda serve functions/api --config webpack.functions.js & nuxt",
    "generate": "netlify-lambda build functions/api & nuxt generate"
  }

設定箇所は上記2つです。
ローカル環境用のコマンドと本番用の静的生成のコマンドを修正します。
ファンクションとホスティングの両方ビルド出来るようにしています。
「--config ~」は「.env」をバンドルするコマンドです。ファンクション内で環境変数を使用できるようにします。

webpack.functions.js

const Dotenv = require('dotenv-webpack')

module.exports = {
  plugins: [new Dotenv()]
}

先ほどのconfigファイルの中身です。
「dotenv-webpack」の設定をします。

次は画面の実装です。
pages/index.vue

<template>
  <div class="container">
    <h1 class="title" v-text="title" />
    <div class="card">
      <p class="image-area">
        <img :src="product.image" :alt="product.name" class="image" />
      </p>
      <div class="info-area">
        <h2 class="name" v-text="product.name" />
        <p class="desc" v-text="product.desc" />
        <p
          class="amount"
          v-text="'¥' + product.amount.toLocaleString() + '-'"
        />
        <client-only>
          <p class="stripe-area">
            <card
              :options="stripeOptions"
              :stripe="stripePK"
              class="stripe"
              @change="isEntered = $event.complete"
            />
          </p>
        </client-only>
        <div class="message" v-text="message" />
        <p class="button-area">
          <button
            class="button"
            :class="{ active: isEntered }"
            aria-label="決済する"
            @click="pay"
            v-text="'決済する'"
          />
        </p>
      </div>
      <div class="complete" :class="{ active: isComplete }">
        <p class="message" v-text="'Thank you!'" />
      </div>
    </div>
  </div>
</template>
<script>
import axios from 'axios'
import { Card, createToken } from 'vue-stripe-elements-plus'
export default {
  components: {
    Card
  },
  data() {
    return {
      title: '決済フォーム',
      product: {
        name: 'サンプル腕時計',
        desc:
          'こちらは決済フォームのサンプルのためご購入はできません。また、カード番号を入力しても請求されることはありません。ご理解いただいた上でお進みください。カード番号は「4242 4242 4242 4242」をご入力ください。※年月,CVCは任意の値で結構です。',
        amount: 12345,
        image: 'watch.jpg'
      },
      stripeOptions: { hidePostalCode: true },
      stripePK: 'pk_test_87vXgp7lt7eQx9IsTAPG6SVw00SkJVRHj7',
      message: '',
      isEntered: false,
      isComplete: false
    }
  },
  methods: {
    async pay() {
      try {
        // 決済用トークン発行
        const tokenResult = await createToken()
        if (
          !tokenResult ||
          !tokenResult.token ||
          !tokenResult.token.id ||
          tokenResult.token.id === ''
        ) {
          throw new Error('トークン発行エラー')
        }

        // 決済処理
        const chargeResult = await axios.post(
          `${process.env.FUNCTION_URL}/.netlify/functions/charge`,
          {
            amount: this.product.amount,
            token: tokenResult.token.id
          }
        )
        if (!chargeResult || chargeResult.data !== 'NORMAL') {
          throw new Error('決済エラー')
        }
        this.isComplete = true
      } catch (error) {
        this.message = error.message + 'が発生しました。'
      }
    }
  },
  head() {
    return {
      title: this.title,
      script: [{ src: '//js.stripe.com/v3/' }]
    }
  }
}
</script>

長いためCSSは割愛しています。全体はGitHubを参照ください。
「<card ~ />」の部分がカード番号入力フォームです。
「stripeOptions」では郵便番号入力フォームを無効にしています。(※詳細はこちら
「stripePK」にはStripe登録時に取得した「公開可能キー」を設定してください。
「決済する」ボタンをクリックすると「pay」メソッドの中で、
  決済用トークン発行 → 決済処理ファンクション呼び出し
を行っています。
また、head()の中で「//js.stripe.com/v3/」を読み込むよう設定します。

最後に決済処理のファンクションの実装です。
functions/api/charge.js

exports.handler = async function(event) {
  const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)

  let result = false
  try {
    const data = JSON.parse(event.body)
    const res = await stripe.charges.create({
      amount: parseInt(data.amount),
      currency: 'jpy',
      source: data.token
    })
    if (res && res.status === 'succeeded') {
      result = true
    }
  } catch (error) {
    // eslint-disable-next-line no-console
    console.log(error.message)
  }

  const headers = {
    'Access-Control-Allow-Origin': 'http://localhost:3000',
    'Access-Control-Allow-Headers': 'Content-Type'
  }
  return {
    statusCode: 200,
    headers,
    body: result ? 'NORMAL' : 'ERROR'
  }
}

画面からのリクエストを受け、Stripeライブラリを利用して決済処理をリクエスト(stripe.charges.create)します。
レスポンスにはCORS対応のヘッダーも付与しておきます。

以上でモジュール準備は完了です!

実際にローカル環境で動かしてみましょう。

yarn dev


テスト用のカード番号が用意されていますのでそちらで試してみてください。
無事に動作しましたでしょうか。
実際に決済が完了したらStripeのダッシュボードに下記のように反映されるはずです。

Netlifyにデプロイする

Netlifyへのデプロイ方法はいつも通りデプロイしてください。
不明な方は「【完全解説】Nuxt.js製の静的サイトをNetlifyで公開する方法【30分で出来る!】」や「【外部API連携に】Nuxt.jsでNetlify Functionsを使用する方法」を参照ください。
また、Settings > Build & Deploy > Environment から「STRIPE_SECRET_KEY」の環境変数も設定しておきます。

デプロイが完了し、本番環境上で決済処理がうまくいけば完了です!
実際に運用する際は、本番環境用のキーに置き換えることをお忘れなく。
自分のクレジットカードでも試しに支払いしてみましょう!

3.6%損するので間違っても大金でやらないようにご注意ください笑。

以上で、決済処理を導入する方法の説明は終了です!
DEMOサイト及びGitHubも公開しておりますので参考にしてください。

それではこの辺で!