【Laravel】GMO Payment Gatewayで簡単・安全・カスタマイズ可能な決済機能を実現！

叡智に富んだ皆様、ご機嫌よう。

フルスタックにメガ進化したいすぎちゃんです！

:::note info
- 自身の開発しているアプリケーションにクレカ決済機能をお手軽に導入したい！
- 会社でショッピングサイトを開発しているけど、所属する会社の資金力が乏しくクレカ情報の管理が難しい！
:::

そんな悩みを抱えている方は、この記事がピッタリです！

今回は、**GMO Payment Gateway**のAPI連携を活用して、どうすれば手軽に、そして安全にクレジットカード決済機能をアプリケーションに導入できるかをご紹介します。特に、中小企業や資金力が限られているスタートアップにとって、クレジットカード情報の管理には**PCI DSS**（*Payment Card Industry Data Security Standard*）に準拠する必要があり、この準拠には高額な初期費用や運用コストがかかります。

https://www.gmo-pg.com/

しかし、GMO Payment GatewayのAPIを使えば、カード情報を自社で管理することなく、手軽に決済機能を実装することが可能です。これにより、**PCI DSSに準拠するためのコストを大幅に削減し、安全な決済システムを実現できます**。

ぜひ最後まで読んで、GMO Payment Gatewayを使って簡単かつ安心なクレジットカード決済機能を導入しましょう！


↓[**社内勉強会で使ったスライド**](https://www.docswell.com/s/sugizaki0330/KV1PWE-2024-10-12-175624#p6)
（ちょくちょくアニメネタ混じってます。苦手な方は回れ右！）
<iframe src="https://www.docswell.com/slide/KV1PWE/embed" allowfullscreen="true" class="docswell-iframe" width="620" height="349" style="border: 1px solid #ccc; display: block; margin: 0px auto; padding: 0px; aspect-ratio: 620/349;"></iframe>

## GMO Payment Gatewayとは？
### 特徴
![スクリーンショット 2024-10-12 18.35.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/818b74ef-8ea9-a23a-c91e-2af189d642ba.png)

GMO Payment Gatewayは大企業系列の信頼性の高い決済代行業者であり、数多くのECサイトで導入されています。他の決済代行業者と比較して、**キャパシティの大きさ、決済処理スピードの速さ、そして可用性の高さ**が特に評価されています。

そのため、大規模なトラフィックを扱うECサイトや、安定した決済処理が求められるビジネスにおいて、多くの企業が安心して利用しています。

さらに、**GMO Payment Gatewayには導入支援体制**が整っており、各業種に特化した担当者が最適なソリューションを提案してくれる特典もあります。これは有料オプションですが、企業のニーズに合わせた提案を受けられるため、導入時の不安を軽減し、よりスムーズに決済システムを構築することが可能です。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/61c03f1c-59a6-9cce-de1a-d5f712c4281c.jpeg" width="50" height="50">　＜実際の現場で導入支援を使ったところ、担当者の方がご丁寧にシーケンス図を作ってくださり、どのような順番でAPIを使えばいいかまでレクチャーしてくださいました :smile: 






:::note info
クレジットカード決済の手数料は**取引金額の3.95%**。
それにプラスでトランザクション処理料として**5円**、売上処理料で**5円**課金される。

（引用：[e-shopsカート](https://cart.e-shops.jp/function/payment/gmo.html)）
:::


### 導入のメリット
![スクリーンショット 2024-10-12 18.36.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/cd1149f4-3737-8fa5-7c7e-ede2104bcfd6.png)

GMO Payment GatewayとのAPI連携を行うことで、クレジットカード決済機能の実装において非常に大きなメリットが得られます。

通常、自社でクレジットカード番号や有効期限を管理する場合、**PCI DSS**(**Payment Card Industry Data Security Standard**)への準拠が義務付けられており、この準拠には高額なコストが発生します。

:::note info
ちなみにPCI DSSでは**12要件設けられておりその下にさらに400項目ある**のだとか・・・。資金力に余裕のある企業では定期的な監査のためにセキュリティ人材までも雇っている話も耳にします・・・
:::


具体的には、**PCI DSSに対応するためには数千万円単位の初期費用がかかり、さらに運用コストも数百万円規模にのぼります。**

しかし、GMO Payment GatewayとのAPI連携を活用すれば、**クレジットカード情報の保持を外部に委託することが可能となり、自社でPCI DSSに準拠する必要がなくなります**。このため、特に中小規模の企業でも、高額な初期費用や運用コストを気にすることなく、安全かつ効率的にクレジットカード決済機能を導入できるのです。

API連携を通じて、**ビジネスに不可欠なクレジットカード決済機能を低コストで実装できる**だけでなく、セキュリティ要件も満たせるため、企業にとって非常に重要な選択肢となります。




## 会員情報とクレジットカード情報を紐づける方法
![スクリーンショット 2024-10-12 18.37.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/989d2e94-235a-ef25-a174-a03b2870c2fc.png)

:::note warn
「クレジットカード決済」と聞くと身構えてしまう読者さんもいるのでは？と想定しているので、あえて超絶カジュアルにまとめています。苦手な方は回れ右！
:::

さてこの章では実際のシナリオと絡めつつ、APIがどのタイミングで呼ばれるかについて解説していきます。

**<登場人物>**
- たけし君：本シナリオの主人公。えいち本とタイパをこよなく愛するZ世代。
- たけしのママ：たけしの母。たけしより体は小さいけどいつも息子をしばいている。

たけし君は、えいち本を安く仕入れることのできる通販サイトを見つけました。
しかしネットショッピングにはいくつかの問題がありました。

- コンビニ決済では、入金のためわざわざコンビニに行かなければ行けない
- 代金振替だと手数料として300円ほど取られてしまう

そんな問題を解消してくれる「**クレカ決済**」を使ってえいち本を購入しようと決意しました！

さて、裏側ではどのようなことが起きているのでしょうか？
一つずつステップを追って解説していきます！



### 1. 会員情報の登録 SaveMember
![スクリーンショット 2024-10-12 18.39.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/318885e9-4870-882b-ca16-437e35eec0d2.png)

早速、たけし君は会員登録のため会員IDを`gintoki1010`に設定し会員名を`gintoki_sakata`で登録しました。（流石は銀魂ファン。ハンドルネームをアニメキャラにするなんて作品に対する愛が伺えますね。）


#### リクエストパラメータ
`SiteID`：加盟店サイトID (必須)
`SitePass`：加盟店サイトパスワード（必須）
`MemberID`：会員ID （必須）
`MemberName`：会員名（任意）

:::note info
`SiteID`と`SitePass`は各クライアントに付与されるAPIキーに相当します！
人の目に触れない場所で保管してください！
:::


#### レスポンスパラメータ
`MemberID`: 会員ID

```SaveMember.php
<?php

require 'vendor/autoload.php'; // Guzzleのオートロード

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

try {
    // Guzzleクライアントの作成
    $client = new Client([
        'base_uri' => 'https://pt01.mul-pay.jp', // テスト環境のベースURI
        'timeout'  => 10.0, // タイムアウト設定
        'verify'   => false, // SSL証明書の検証（本番ではtrueにする）
    ]);

    // APIエンドポイントとリクエストデータの設定
    $response = $client->post('/payment/SaveMember.idPass', [
        'form_params' => [
            'SiteID'     => 'tshop0001234',   // GMOから提供されるサイトID
            'SitePass'   => 'password1234',   // GMOから提供されるサイトパスワード
            'MemberID'   => 'user1234',       // 登録する顧客ID
            'MemberName' => 'John Doe',       // 顧客名（任意）
            'ClientField1' => 'custom data 1', // 自由項目1
            'ClientField2' => 'custom data 2', // 自由項目2
            'ClientField3' => 'custom data 3', // 自由項目3
        ]
    ]);

    // レスポンスの取得と表示
    $body = $response->getBody()->getContents();

    // レスポンスを解析（パラメータ形式で返ってくる）
    parse_str($body, $responseArray);

    // 成功かエラーかをチェック
    if (isset($responseArray['ErrCode']) && $responseArray['ErrCode'] != "") {
        // エラーがある場合
        echo "エラーが発生しました: " . $responseArray['ErrInfo'];
    } else {
        // 成功レスポンス
        echo "顧客情報が保存されました: " . $responseArray['MemberID'];
    }

} catch (RequestException $e) {
    // リクエストエラーの処理
    echo "リクエストエラー: " . $e->getMessage();
}

?>
```

上記のコードの実行後、GMO Payment Gateway側がたけし君のアカウントが登録されたことを認知しました。正常に登録が済んだら`memberID`の値がそのまま返却されます。


### 2. クレジットカード情報のトークン化
![スクリーンショット 2024-10-12 18.39.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/0cffb118-d148-fdf9-f320-16e2b51df277.png)

会員登録をしたものの、クレジットカードの情報を登録しなければお買い物ができません。

そこでたけしくんは、ママのクレジットカードを使って無限にえいち本を仕入れてやろうと思いつきました。

クレジットカード情報の登録では一体どのような処理が走っているか、これも詳しく見ていきましょう！

#### 手順

```tokenize.html
<script type="text/javascript" src="https://stg.mul-pay.jp/ext/js/token.js"></script>

<form id="payment-form" method="POST" action="/process_payment">
    <input type="text" id="card_number" placeholder="カード番号" />
    <input type="text" id="expiry_month" placeholder="有効期限（月）" />
    <input type="text" id="expiry_year" placeholder="有効期限（年）" />
    <input type="text" id="security_code" placeholder="セキュリティコード" />
    <input type="text" id="name" placeholder="カード名義人" />
    <input type="hidden" id="token" name="token" />
    <button type="submit" id="submit-button">決済する</button>
</form>

<script>
    // TokenizationのためのGMO Payment Gatewayの設定
    var Multipayment = Multipayment.init('your_site_id'); // GMOから提供されるサイトIDを設定

    document.getElementById('payment-form').addEventListener('submit', function(event) {
        event.preventDefault(); // デフォルトの送信を防止

        // カード情報の収集
        var cardData = {
            cardno: document.getElementById('card_number').value,
            expire: document.getElementById('expiry_month').value + document.getElementById('expiry_year').value, // MMYY形式
            securitycode: document.getElementById('security_code').value,
            holdername: document.getElementById('name').value
        };

        // GMOのAPIを通してトークン化する
        Multipayment.getToken(cardData, function(response) {
            if (response.resultCode == '000') {
                // トークン化成功
                document.getElementById('token').value = response.tokenObject.token;
                document.getElementById('payment-form').submit(); // サーバーにトークンを送信
            } else {
                // エラーハンドリング
                console.error("トークン化に失敗しました: " + response.resultCode);
            }
        });
    });
</script>

```

フロントエンドでは、GMOのJavaScriptライブラリを使ってクレジットカード情報をトークン化します。**クレジットカード情報はサーバーには直接送られず、トークンが送られるため、セキュリティリスクが軽減されます**。

`Multipayment.getToken()` 関数を使ってカード情報をトークン化し、トークン化が成功したらトークンをフォームに埋め込んでサーバーに送信します。

サーバーがトークンを受け取った後、クレジットカード情報を認識して正常に処理されると、ランダムな64文字の文字列が返却されます。この文字列は**クレジットカード情報を直接扱うことなく、以降の処理で使用することができ、セキュリティが確保されたまま決済などの処理を進めることが可能です**。

:::note warn
良い子は絶対にマネしないでください。他人のクレカを使うのは犯罪です！
:::




### 3. 会員情報とクレジットカード情報の紐付け SaveCard
![スクリーンショット 2024-10-12 18.40.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/5dfcd2ed-7e2c-cf71-0e7f-c8fa2c76ac54.png)

#### リクエストパラメータ
`SiteID`：加盟店サイトID (必須)
`SitePass`：加盟店サイトパスワード（必須）
`MemberID`：会員ID （必須）
`token`：2で発行されたトークン

#### レスポンスパラメータ
`CardNo`: カード番号（特に指定のない場合、一部をマスキングされた状態で返却）
`ExpiredAt`：有効期限(YYMMで表示)

```SaveCard.php
<?php

require 'vendor/autoload.php'; // Guzzleのオートロード

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

try {
    // POSTデータとしてトークンと会員IDを受け取る
    $token = $_POST['token'];
    $memberId = $_POST['member_id'];

    // Guzzleクライアントを作成
    $client = new Client([
        'base_uri' => 'https://pt01.mul-pay.jp', // テスト環境のベースURI
        'timeout'  => 10.0, // タイムアウト設定
        'verify'   => false, // 本番環境ではtrueにする
    ]);

    // SaveCard APIへのリクエスト
    $response = $client->post('/payment/SaveCard.idPass', [
        'form_params' => [
            'SiteID'    => 'tshop0001234',  // GMOから提供されるサイトID
            'SitePass'  => 'password1234',  // GMOから提供されるサイトパスワード
            'MemberID'  => $memberId,       // ユーザーの会員ID
            'Token'     => $token,          // フロントエンドから送られたトークン
            'SeqMode'   => 0,               // 会員IDを指定するモード
            'DefaultFlag' => 1,             // デフォルトカードに設定するかどうか（1: 設定する）
        ]
    ]);

    // レスポンスの取得
    $body = $response->getBody()->getContents();
    parse_str($body, $responseArray);

    if (isset($responseArray['ErrCode']) && $responseArray['ErrCode'] != "") {
        // エラーレスポンス
        echo "カードの保存中にエラーが発生しました: " . $responseArray['ErrInfo'];
    } else {
        // カード保存成功
        echo "カードが正常に保存されました。";
    }

} catch (RequestException $e) {
    // リクエストエラーの処理
    echo "リクエストエラー: " . $e->getMessage();
}

?>

```

今度は1で登録した**会員ID**、2で返却された**トークン**をリクエストパラメータとしてSaveCardエンドポイントのAPIのリクエストを送信します。

APIの送信に成功すれば、2で入力したカード情報がマスキングされた状態で返却されます。

このAPIを用いることでユーザーのクレジットカード情報を安全に保存し、次回以降の決済をスムーズに行うことができます。会員IDとトークンを用いたこのリクエストプロセスは、セキュリティを確保しつつ、便利なユーザー体験を提供します。

## お買い物をする方法


さてクレジットカード情報の登録が終わったためにしたり顔でえいち本をカートに詰め込んでいくたけし君。

「計画通り・・・！」と言わんばかりに順風満帆でしたが、彼に大きな壁が立ちはだかります。

次は決済処理をしたときに、どのような処理が走るかを詳しくみていきましょう！


### 1. 取引登録 EntryTran
![スクリーンショット 2024-10-12 18.47.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/3d624f29-5141-da21-6b56-dd01390c82e4.png)

`EntryTran` は、クレジットカード決済の取引を開始するためのエンドポイントです。この API は、顧客が商品を購入する際に、クレジットカード情報を安全に処理し、決済を実行するために使用されます。

#### 主な機能

- **取引の開始**: EntryTran API は、クレジットカード決済を行うための取引を開始します。これにより、取引に関連する情報が記録され、後続の決済処理が可能になります。
- **セキュリティ**:顧客のクレジットカード情報を適切に処理し、トークン化や3Dセキュア認証の実施を可能にすることで、安全性を確保します。

#### リクエストパラメータ
`ShopID`：加盟店ショップID (必須)
`ShopPass`：加盟店ショップパスワード（必須）
`OrderID`：注文ID（ユニークな識別子）
`JobCd`：取引種別（例: AUTH：認証, CAPTURE：決済）
`Amount`：取引金額

#### レスポンスパラメータ
`AccessID`: 認証リクエストを送信する際に生成される一意の識別子
`AccessPass`：決済処理を継続するためのパスワードのようなもの

:::note info
`AccessID`,`AccessPass`は次項のExecTranで利用します。
:::


### 2. 取引実行 ExecTran
![スクリーンショット 2024-10-12 18.48.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/4a8edca2-113b-412c-7d21-48a0b4280e0c.png)

`ExecTran` はクレジットカード決済の実行を行うためのエンドポイントです。この API は、クレジットカード情報の処理を行い、決済を最終的に完了させるために使用されます。

#### 主な機能
- **決済の実行**: ExecTran API を使用することで、クレジットカード決済の最終処理を実行できます。これにより、取引が成立し、商品やサービスの支払いが完了します。
- **3Dセキュア認証**: この API は、3Dセキュア認証を通じて不正利用を防止し、取引の安全性を向上させます。
- **トランザクションの確認** : 決済後、取引のステータスや結果を確認できます。


#### リクエストパラメータ
`AccessID`： 認証リクエストを送信する際に生成される一意の識別子
`AccessPass`：決済処理を継続するためのパスワードのようなもの
`OrderID`： 注文ID（`EntryTran`のIDと一致する必要あり！）
`3DS`： 3Dセキュアを使用するかどうかのフラグ（`１`に設定！）

#### レスポンスパラメータ
`ACS`：？？？
`RedirectUrl`：3Dセキュア認証のためにユーザーをリダイレクトするURL

`ExecTran`では取引登録APIのレスポンスパラメータである`AcccessID`, `AccessPASS`をリクエストパラメータとし取引実行APIを叩きます。

リクエストパラメータには`AcccessID`や`AccessPASS`の他にも、
**より精度の高いリスクベース認証を行うため住所や過去の取引回数といった情報をリクエストパラメータとして受け渡すことができます。**

取引実行APIのリクエストに成功すると３Dセキュア認証画面のURLが返却され、各カード会社の認証画面にそのままリダイレクトします。

### 3. カード会社の3DS2.0画面の呼び出し
![スクリーンショット 2024-10-12 18.48.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/7a03310e-ef9c-2e22-2fba-3e382dad5460.png)

以下の流れで本人認証のプロセスが進みます！

1. クレジットカード発行会社が契約している**認証サーバー**に本人認証を要求
2. クレジットカード発行会社がユーザーにパスワードを入力を要求
3. ユーザーがパスワードを入力する
4. 認証サーバーがWebサイト・アプリに認証結果を返す

取引実行API(`ExecTran`)のレスポンスに含まれているコールバックURLに遷移すると**3Dセキュアの認証画面**が表示されます。

:::note info
3Dセキュア2.0は、2025年3月末を目処に全てのECサイトに導入が義務付けられた、クレジットカードの不正利用を防ぐためのシステムです。
:::


引用元：

https://shop-pro.jp/yomyom-colorme/99995






その際、カード発行会社が契約している認証サーバーで複数の属性(カード番号、登録済みの住所、過去の取引履歴)を元に不正利用のリスクがあるかの判断を行います。

この工程を「[**リスクベース認証**](https://www.nec-solutioninnovators.co.jp/ss/insider/security-words/24.html)」と呼び、クレジットカードを用いた取引の安全性を担保することができるのです。


不正利用の可能性が高いと判断された場合は、**チャレンジ画面に遷移しパスワードの入力が求められます**。（ステップ2）
不正利用のリスクが低いと判断された場合はパスワード入力を要求されず、そのままステップ4に進み本人認証結果をECサイトに連携します。

・・・

さて・・・不審な動きをしていると判断されたたけし君は、当然のことながら3Dセキュアのチャレンジ画面で暗証番号の入力を要求されました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/06b8db60-15df-1294-9961-acd9ba74b357.png" width="50">　<クソっ！魔法のカードでえいち本を大量購入できると思ったのに！えーっと・・・かーちゃんの誕生日？いやスリーサイz

何度も暗証番号を間違えてしまいついにはロックアウトされるハメになりました。

そしてついには・・・

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401561/8f1831ff-a8c7-3a07-8111-54e7c8fb4907.png" width="50">　<こら！！たけし！！最近熱心に勉強をしていると思ったら私のクレジットカードでえいち本を買おうと企んでたのね！この泥棒猫が！！！！

母親の携帯に「アカウントロックのお知らせ」メールが届き、こっ酷く叱られるハメになりました。

:::note warn
**2025年には3Dセキュア2.0（EMV 3Dセキュア）のEC加盟店への導入が義務付けられます！このプロセスは必ず踏むようにしましょう！**
:::


## 関連記事

https://qiita.com/wanna_be_neet/items/9835212bea2f615bef2a