GMO-PG リンクタイプPlusで決済に利用したカード情報を「決済後会員登録」によって自動でGMOに登録する

# 概要

GMO-PGのリンクタイプPlusでクレジットカード決済などの決済画面に遷移し、決済処理をすることを想定します。
たった１回だけしか利用しないECサイトでの注文であれば、カード情報を入力して決済するのもやぶさかではありませんが、
頻繁に利用するサイトでは、決済の都度カード情報を入力するなんて面倒なことをしたくありません。

リンクタイプPlusという接続方式には、決済画面の他に、GMO会員カード登録の画面もあり、
自分に紐づくカード情報を登録／追加／削除したり、デフォルトで利用するカードを設定することが可能です。

その画面にユーザー自身がアクセスして、カード情報を登録すれば、２回目以降のカード決済では、
カード情報の入力を省くことも可能ですが、
**決済処理したときにカード情報入れてるよね？ならそれそのまま登録しちゃってよ？？**という人は多いと思います。

そんな人のために用意されている機能がありますので、その実現方法について解説します。

# 事前に用意するもの

事前に用意するものとして、クレジットカード決済の結果を受け取る結果通知プログラムが必要です。
[GMO-PG リンクタイプPlusでクレジットカード決済した時の結果通知プログラムの処理](https://qiita.com/You_name_is_YU/items/7e1664ce39c8ba5c04c0)

# クレジットカード決済が正常に完了した結果を利用して決済後会員登録する

基本的な手順は以下の通り

1. ユーザーがリンクタイプPlusの決済画面に遷移し、クレジットカード決済を完了させる。
2. 結果通知プログラムがクレジットカード決済の結果を受け取る
3. カード決済が正常に完了していたら
    1. GMO会員登録を行う `SaveMember.idPass`
    1. 決済後カード登録を行う `TradedCard.idPass`

という手順で`決済後会員登録`の流れを作り込むことが可能です。
※`決済後会員登録`という1つのAPIが存在し、それを叩くだけで一発ズドンというわけにはいかないようです。

# サンプルコード

全体のソースコードはここにあります。
https://github.com/Yu-Yamaguchi/codeigniter3-app

API実行結果の検証などはだいぶ端折っていますので、実際にこれをそのまま本番環境で使うようなことは絶対にしないでください。
正しく処理結果（httpステータスコードやGMOエラーコードの結果）を判別し、適した処理にしないといけないという前提のもと、参考までにサンプルコードをご覧ください。

```php:/codeigniter3/application/controllers/Gmo_result_notification.php
<?php

/**
 * GMOから呼び出される結果通知プログラム用のコントローラです。
 */
class Gmo_result_notification extends CI_Controller {

    public function __construct()
    {
        parent::__construct();
        $this->load->helper('html');
        $this->load->helper('form');
        $this->load->helper('url_helper');
    }

    /**
     * 決済関連の結果通知プログラムから呼び出されるfacade
     */
    public function payment_facade ()
    {
        log_message('debug', 'call Gmo_result_notification#payment_facade()');
        $param = $this->input->post();

        // 決済方法により処理分岐
        switch ($param['PayType']) {
            // 0:クレジット
            case '0':
                $this->result_credit($param);
                break;
            // その他（契約している決済方法に応じて分岐）
            default:
                // ....その他の処理
                break;
        }
    }

    /**
     * 決済方法＝クレジットカード決済の結果を処理します。
     */
    private function result_credit($data) {
        log_message('debug', 'call Gmo_result_notification#result_credit()');

        // クレジットカード決済が正常に完了している場合、決済後会員登録の手続きをする。
        // 決済後会員登録を行うことで、ユーザーがGMOカード会員登録の処理をリンクタイプPlusの画面で
        // 意図的に行う必要がない。（もちろん勝手に登録しちゃまずいと思うので、なんらかの方法で
        // ユーザーに確認して同意しておく必要はあると思います。）
        if ($data['Status'] == 'CAPTURE' or $data['Status'] == 'SALES') {
            $order_id = $data['OrderID'];

            // 注文IDの先頭4桁を会員IDにしているのでそこから抽出
            // 本来はDBに登録されている注文IDから会員を識別するなどちゃんとした処理は必要ですよね。
            $member_id = intval(substr($order_id, 1, 4));

            log_message('debug', print_r($data, true));
            log_message('debug', 'order_id:'.$order_id.'/member_id:'.$member_id);

            $this->gmo_payment_save_member($member_id);
            $this->gmo_payment_traded_card($order_id, $member_id);
        }
    }

     /**
     * 会員登録を行います。
     */
    private function gmo_payment_save_member($member_id) {
        log_message('debug', 'call Gmo_result_notification#gmo_payment_save_member()');

        $param = [
            'SiteID'           => SITE_ID,
            'SitePass'         => SITE_PASS,
            'MemberID'         => $member_id,
            'MemberName'       => 'テスト　太郎'
        ];

        log_message('debug', print_r($param, true));

        // リクエストコネクションの設定
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
        curl_setopt($curl, CURLOPT_URL, 'https://pt01.mul-pay.jp/payment/SaveMember.idPass');

        // リクエスト送信
        $response = curl_exec($curl);
        $curlinfo = curl_getinfo($curl);
        curl_close($curl);

        log_message('debug', print_r($response, true));
        log_message('debug', print_r($curlinfo, true));
        
        // TODO：この辺で処理結果を正しく判別してあげる

        return true;
    }

    /**
     * 決済後会員登録の処理を行います。
     */
    private function gmo_payment_traded_card($order_id, $member_id) {
        log_message('debug', 'call Gmo_result_notification#gmo_payment_traded_card()');

        $param = [
            'ShopID'           => SHOP_ID,
            'ShopPass'         => SHOP_PASS,
            'OrderID'          => $order_id,
            'SiteID'           => SITE_ID,
            'SitePass'         => SITE_PASS,
            'MemberID'         => $member_id,
            'SeqMode'          => '0',
            'DefaultFlag'      => '1', // 洗替・継続課金フラグは、継続課金は利用しないが、洗替の機能は利用する。という場合にも`1`をセットする。
            'UseSiteMaskLevel' => '1'
        ];

        log_message('debug', print_r($param, true));

        // リクエストコネクションの設定
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
        curl_setopt($curl, CURLOPT_URL, 'https://pt01.mul-pay.jp/payment/TradedCard.idPass');

        // リクエスト送信
        $response = curl_exec($curl);
        $curlinfo = curl_getinfo($curl);
        curl_close($curl);

        log_message('debug', print_r($response, true));
        log_message('debug', print_r($curlinfo, true));

        // TODO：この辺で処理結果を正しく判別してあげる

        return true;
    }
}
```

これを実行した結果のログです

```shell-session
DEBUG - 2020-10-04 12:31:25 --> call Gmo_result_notification#payment_facade()
DEBUG - 2020-10-04 12:31:25 --> call Gmo_result_notification#result_credit()
DEBUG - 2020-10-04 12:31:25 --> Array
(
    [ShopID] => tshop999999990
    [ShopPass] => **********
    [AccessID] => 650134928a62e4d2ad6e97dceb4646ee
    [AccessPass] => ********************************
    [OrderID] => 0003OR20201004123108
    [Status] => CAPTURE
    [JobCd] => CAPTURE
    [Amount] => 10000
    [Tax] => 1000
    [Currency] => JPN
    [Forward] => 2a99662
    [Method] => 1
    [PayTimes] => 
    [TranID] => 2010041210111111111111819610
    [Approve] => 0045902
    [TranDate] => 20201004123125
    [ErrCode] => 
    [ErrInfo] => 
    [PayType] => 0
)

DEBUG - 2020-10-04 12:31:25 --> order_id:0003OR20201004123108/member_id:3
DEBUG - 2020-10-04 12:31:25 --> call Gmo_result_notification#gmo_payment_save_member()
DEBUG - 2020-10-04 12:31:25 --> Array
(
    [SiteID] => tsite99999999
    [SitePass] => sitepass
    [MemberID] => 3
    [MemberName] => テスト　太郎
)

DEBUG - 2020-10-04 12:31:25 --> MemberID=3
DEBUG - 2020-10-04 12:31:25 --> Array
(
    [url] => https://pt01.mul-pay.jp/payment/SaveMember.idPass
    [content_type] => text/plain;charset=windows-31j
    [http_code] => 200
    [header_size] => 164
    [request_size] => 187
    [filetime] => -1
    [ssl_verify_result] => 0
    [redirect_count] => 0
    [total_time] => 0.090835
    [namelookup_time] => 0.000755
    [connect_time] => 0.005827
    [pretransfer_time] => 0.031902
    [size_upload] => 474
    [size_download] => 10
    [speed_download] => 111
    [speed_upload] => 5266
    [download_content_length] => -1
    [upload_content_length] => 474
    [starttransfer_time] => 0.031906
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => 210.197.108.196
    [certinfo] => Array
        (
        )

    [primary_port] => 443
    [local_ip] => 199.19.199.199
    [local_port] => 35552
    [http_version] => 2
    [protocol] => 2
    [ssl_verifyresult] => 0
    [scheme] => HTTPS
    [appconnect_time_us] => 31848
    [connect_time_us] => 5827
    [namelookup_time_us] => 755
    [pretransfer_time_us] => 31902
    [redirect_time_us] => 0
    [starttransfer_time_us] => 31906
    [total_time_us] => 90835
)

DEBUG - 2020-10-04 12:31:25 --> call Gmo_result_notification#gmo_payment_traded_card()
DEBUG - 2020-10-04 12:31:25 --> Array
(
    [ShopID] => tshop999999990
    [ShopPass] => shoppass
    [OrderID] => 0003OR20201004123108
    [SiteID] => tsite99999999
    [SitePass] => sitepass
    [MemberID] => 3
    [SeqMode] => 0
    [DefaultFlag] => 1
    [UseSiteMaskLevel] => 1
)

DEBUG - 2020-10-04 12:31:25 --> CardSeq=0&CardNo=*************111&Forward=2a99662
DEBUG - 2020-10-04 12:31:25 --> Array
(
    [url] => https://pt01.mul-pay.jp/payment/TradedCard.idPass
    [content_type] => text/plain;charset=windows-31j
    [http_code] => 200
    [header_size] => 164
    [request_size] => 187
    [filetime] => -1
    [ssl_verify_result] => 0
    [redirect_count] => 0
    [total_time] => 0.110358
    [namelookup_time] => 2.1E-5
    [connect_time] => 0.004264
    [pretransfer_time] => 0.029225
    [size_upload] => 990
    [size_download] => 49
    [speed_download] => 445
    [speed_upload] => 9000
    [download_content_length] => -1
    [upload_content_length] => 990
    [starttransfer_time] => 0.029229
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => 210.197.108.196
    [certinfo] => Array
        (
        )

    [primary_port] => 443
    [local_ip] => 199.19.199.199
    [local_port] => 35554
    [http_version] => 2
    [protocol] => 2
    [ssl_verifyresult] => 0
    [scheme] => HTTPS
    [appconnect_time_us] => 29153
    [connect_time_us] => 4264
    [namelookup_time_us] => 21
    [pretransfer_time_us] => 29225
    [redirect_time_us] => 0
    [starttransfer_time_us] => 29229
    [total_time_us] => 110358
)

DEBUG - 2020-10-04 12:31:25 --> Total execution time: 0.2146
```

# 実際に動かして確認してみる

ログイン後、１回目の決済ではクレジットカード情報の入力が求められ、２回目の決済時には、すでに登録済みのカード情報が選択肢に出てきてくれたのを確認できます。

![gmo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/33952df9-f07e-97f2-9f22-3d3085d2c436.gif)