決済サービスの選定を悩んでいる方必見!決済サービス選定方法からPayJPのTypeScript実装方法を決済構築エキスパートが解説 ✍️ !

決済サービスの選定を悩んでいる方必見!決済サービス選定方法からPayJPのTypeScript実装方法を決済構築エキスパートが解説 ✍️ !

2021.12.03

こんにちは!

DX が流行っている影響で、今まで SaaS サービスを利用していたけど自社システム運用をしたい企業様が大変増えてきました。今まで Amazon を使用して商品販売していたけど、自社ブランドで商品販売したいので EC システムを0から構築〜運用する、というお客様が多くいらっしゃいます。

本記事では、決済サービス選定の思考フローから、多種多様なサービスでよく利用される決済サービス「PayJP」を使用した決済処理構築手順を紹介&解説します。

TypeScript で綺麗に書いたコードも後述しますので、PayJPで実装を検討されている方は必見です。

想定する読者

  • 決済サービスの選定を悩んでいるヒト
  • PayJPを使用して決済サービス構築を検討しているヒト
  • PayJPの利用を検討しているヒト

決済サービス選定思考フロー

決済サービスには、ソフトバンク社の提供する SBPS や、ペイパル社の提供する PayPal、キャリア決済 ( Docomo, Softbank, Au ) 等様々なサービスが存在しますが、どれを選べば良いのか悩みどころだと思います。

私たちは、決済サービスを以下の指標で選定しています。

  • 想定する決済手段が提供されていること(クレジットカード決済、口座振替、携帯料金振替等々)
  • 手数料はビジネスの予算に合っていること
  • SandBox ( 試験環境 ) の提供がされていること
  • バーストアクセス閾値が想定内であること ( 想定する同時実行数に耐えられるか )
  • クレジットカード決済で VISA / MASTER 両方対応していること
  • 過去に大きなセキュリティインシデントの事例がないこと
  • 定期決済に対応していること

特に決済サービスがベンダーロックインになってしまうケースが度々ありますので、将来的な展望を見据えてしっかり慎重に選定することをお勧めします。ここを慎重にやらずに進めると、後々実装面で痛手を被ります。

クレジットカード決済の PayJP

PayJP の主要機能

PayJP を利用する上で重要となる3つの機能を紹介します。以下3つを抑えれば、基本的な実装は全て対応できるかと思います。

機能概要
カード情報のトークン化ユーザーが入力したクレジットカード情報をトークン文字列化します。これにより、ユーザーは一度入力したクレジットカード情報について、トークンを使用して呼び出すことが可能となります。
支払い処理クレジットカードのトークンを使用して、即時決済を行います。
定期課金クレジットカードのトークンを使用して、定期決済プランの作成を行います。
定期課金の停止から再開、課金間隔の指定など柔軟に行うことが可能です。

PayJPの支払い処理のワークフロー

  1. [ Frontend ] クレジットカード情報を入力後に PayJP SDK にて PayJP サーバーへトークン要求リクエスト
  2. [ Frontend ] 決済金額とトークンをサーバーサイドへリクエスト
  3. [ ServerSide ] 決済金額+トークン+シークレットトークンを使用して PayJP へ SDK にて支払い要求

PayJP では、WEB フロントエンド、NodeJS 向けに SDK が提供されていますので、それを呼び出すだけで簡単にクレジットカードのトークンを取得することができます。

取得したクレジットカードのトークンと、PayJP 管理画面で生成したシークレットトークンを使用して、サーバーサイド(NodeJS)で支払い要求を行うワークフローとなります。

※ よくある決済サービスと同様にクレジットカード番号を直接 PayJP へリクエストする形ではなくトークンを使用して支払い要求を行います

※ 定期支払いの場合は別途定期決済用のプラン作成などが必要です

PayJP の実装方法& TypeScript を紹介

まずはじめに、PayJP にはサーバーサイド用 SDK、フロントエンド用 SDK の2種類が提供されています。

ここで1点、実装方法の紹介の前に、残念なお知らせがあります。

フロントエンドのSDK ( payjp.js ) には TypeScript が提供されていません

こちらのIssueで取り上げているように、TypeScript 形式のモジュールの提供はありません。

そのため、後述しますが自前で TypeScript に対応させることが必要 ( ≒ 推奨 ) なのと、加えて CDN 提供しかされていないので DOM のマウント後に CDN を読み込むカスタム実装が必要となります。

NuxtJS / Vue3 / payjp.js の実装方法

私たち独自の、TypeScript を使用した実装方法を紹介します。先ほどあげた残念なところもうまく回避していますので、ぜひ参考にしてください。

src/services/payjpService.ts

appendPayJpScriptTag 関数に payjp.js を読み込む Script タグを生成処理実装しています。これにより、CDN 提供しかされていないモジュールに対応することが可能です。また、payjp.js のコールバックを受ける関数をグローバルに定義することが必要なため、window オブジェクトに関数をアサインして対応しています。

import _ from "lodash";

type Option = {
  id?: string;
  src?: string;
  class?: string;
  "data-partial"?: boolean; //入力後にtokenを作成しwindowを閉じるか否か
  "data-text"?: string;
  "data-submit-text"?: string;
  "data-lang"?: string;
  "data-on-created"?: string;
  "data-on-failed"?: string;
  "data-name-placeholder"?: string;
};

export default class PayjpService {
  private dataKey: string;
  private scriptTagAppendDom: HTMLElement;
  private option?: Option;
  private defaultOption: Option = {
    id: "payjp-script",
    src: "https://checkout.pay.jp/",
    class: "payjp-button",
    "data-partial": true,
    "data-text": "決済画面に進む",
    "data-submit-text": "支払い",
    "data-lang": "ja",
    "data-on-created": "onTokenCreated",
    "data-on-failed": "onTokenFailed",
  };

  constructor(
    dataKey: string,
    scriptTagAppendDom: HTMLElement,
    onTokenCreated: Function,
    onTokenFailed: Function,
    option?: Option
  ) {
    this.dataKey = dataKey;
    this.scriptTagAppendDom = scriptTagAppendDom;
    this.option = option;
    window.onTokenCreated = onTokenCreated;
    window.onTokenFailed = onTokenFailed;
  }

  public appendPayJpScriptTag() {
    const option: Option = Object.assign(
      {
        ...this.defaultOption,
        "data-key": this.dataKey,
      },
      this.option || {}
    );
    const scriptEl = document.createElement("script");
    _.forEach(option, (v, k) => {
      scriptEl.setAttribute(k, v as string);
    });
    this.scriptTagAppendDom.appendChild(scriptEl);
  }
}

Vue ファイル

Vue ファイルでは、先程の appendPayJpScriptTag 関数を DOM のマウント後に実行し、Script タグを生成しています。

// Vue

import {
  defineComponent,
  onMounted,
  useContext
} from "@nuxtjs/composition-api";
import _ from "lodash";
import PayjpService from "~/services/payjpService";

export default defineComponent({
  middleware: "authenticated",
  setup() {
    const context = useContext();
   
    const onTokenCreated = () => {
      settlementLoading.value = true;
      const payjpTokenElm = document.getElementsByName(
        "payjp-token"
      )[0] as HTMLInputElement;
      if (!payjpTokenElm) throw new Error("not found token element.");
      const token = payjpTokenElm.value;
      console.log(token);
      // TODO: トークンをサーバーへ送信し決済処理を完了させること(以下API送信処理を参考)
      setTimeout(() => {
        settlementLoading.value = false;
        settlementComplete.value = true;
      }, 2000);
    };

    const onTokenFailed = (res: Error) => {
      throw new Error(JSON.stringify(res.message, null, 2));
    };

    onMounted(() => {
      // HTML描画のDOMに依存する処理なのでMauntedにて実行
      const scriptTagAppendDom = document.getElementById("payjp-area");
      if (scriptTagAppendDom === null)
        throw new Error("not found scriptTagAppendDom.");
      const payjpService = new PayjpService(
        context.$config.PAYJP_PUBLIC_KEY, // nuxt.config.jsのpublicRuntimeConfigに要設定
        scriptTagAppendDom,
        onTokenCreated,
        onTokenFailed
      );
      payjpService.appendPayJpScriptTag();
    });

    return {
      columns,
      settlementLoading,
      settlementComplete,
      onClickBackToMypage,
    };
  }
});

TIPS

PayJP は独自に UI 実装を施すことも可能

本記事では、チェックアウトと呼ばれる PayJP が用意してくれている UI を使用するサンプルとなっていますが、独自に UI 実装を行いたい場合は、payjp.js 実装ガイドを見ながら実装してください。独自の UI・デザインへも柔軟に対応可能となっています。

まとめ

本記事では PayJP を使用した決済処理を紹介しましたが、口座振替などの要件に対応する場合は SBPA 等々のサービスの利用を検討してください。

PayJP は手軽にクレジットカード決済を実現できますのでオススメのサービスとなっています。

DX に関する決済処理・決済プラットフォーム・AWS の開発はお気軽にお問い合わせください。