Swift愛好会合宿で活躍したLINE PayとそのAPIを利用してみる(Node.js) #love_swift


こんにちは、 @n0bisukeです。

[Swift愛好会](https://love-swift.connpass.com)の運営を手伝ったりしてます。[Swift愛好会アドベントカレンダー](http://qiita.com/advent-calendar/2016/love_swift)ということで、Swift愛好会合宿に関する記事を書こうと思います。

![](https://i.gyazo.com/b1ae437f56aa4002c63f246381f30317.png)

>ちなみにこの写真は、奇跡的なベストショットをこの時[話題だったEverfilter](http://nlab.itmedia.co.jp/nl/articles/1612/07/news080.html)で加工したものです。

## Swift愛好会合宿とは

先日の2016年12月3日~4日で行われたSwift愛好会の一周年記念合宿です。

* [【１周年・合宿】集まれSwift好き！Swift愛好会 vol13](https://love-swift.connpass.com/event/43865/)

### 合宿はまさに伝説的な盛り上がりに

**この合宿はものすごく参加者満足度が高く、様々な記事が書かれています。**

* [2016 年の Swift 愛好会を、自分が談義した話を中心にざっくり振り返ってみる #love_swift](http://qiita.com/es_kumagai/items/49039f9507d268030596)

>合宿会場は千葉・笹川の 土善旅館 さまでしたけど、Wi-Fi は 22 人を余裕でさばく完璧な性能だったし、大画面スクリーンはあるし、外部ディスプレイはあるし、椅子でなにか疲れを感じた記憶もないし、テーブルの高さはちょうど良いし、猫は来襲するし。それに、電源タップも不足をまったく感じさせないし、なんなら外付けキーボードまで貸してもらえたりするくらい。そんな 設備の充実さが光る土善旅館さま でした。

と@es_kumagaiさんもべた褒めです。

* [22名で開発合宿いったけど土善旅館さん、なんだこれまじで最高か？](http://qiita.com/gotou015/items/40081f33b68f616491b8)

運営メンバーの@gotou015さんによる記事ですが **88いいね、31はてぶ**と普通にバズってます

* [Swift愛好会1周年記念合宿に参加してきました！ – 1日目 #love_swift](http://dev.classmethod.jp/etc/event-report-love-swift-camp-day-1/)
* [Swift愛好会1周年記念合宿に参加してきました！ – 2日目 #love_swift](http://dev.classmethod.jp/etc/event-report-love-swift-camp-day-2/)

@ktanaka117さんはまさかの[Developers.IO](http://dev.classmethod.jp)に合宿記事を書いてくれました。すごい。

* [Swift愛好会合宿 1日目 レポート](http://qiita.com/tamaki/items/a5a7a733718497c6be48)
* [Swift愛好会合宿 2日目 レポート](http://qiita.com/tamaki/items/c64270a6b89860cedaa9)

たまきさんによるレポートたちです。

* [【１周年・合宿】集まれSwift好き！Swift愛好会 vol13 中のツイートまとめ #love_swift](http://togetter.com/li/1056256)

@es_kumagaiさんによるトゥギャッターまとめも。

* [Swift愛好会合宿にビールを送り込んだ話](http://cozynooks.hatenablog.jp/entry/2016/12/06/101744)

@cozynooksさんは合宿にビールを送り込んで下さいました。めちゃ美味しくいただきました！

### 帰還してから多方面から合宿問い合わせがくるなど

こんな感じで色々な人から合宿楽しそう！やりたい！という話が来ました笑

こちら[Goカンファレンス](https://gocon.connpass.com/)主催の@tenntennさん。

![](https://i.gyazo.com/26cfcf5c4e12885bc71a5135f10b3fb2.png)

とまぁ、 **めちゃ盛り上がった合宿**でした。

## 合宿を支えた「LINE Pay」

さて、ここまではSwift愛好会合宿がいかに盛り上がったかを書きましたが、実際の裏側の運営話もあったりします。

僕は流れで会計担当をやっていたのですが、集金に`LINE Pay`を採用しました。

### LINE Payって?

LINEが提供している個人間送金が可能なプリペードの電子マネーのようなものです。

https://line.me/ja/pay

![](https://i.gyazo.com/8c1b7e4aee9a7450649cc047e00b95fb.png)

**個人間送金の手数料が無料**なので、僕の場合は飲み会やフットサルの割り勘などによく使っています。
お金の貸し借りのログも残るので便利です。

チャージや送金で貯めたお金は[LINE Payカード](https://line.me/ja/pay/card)でJCBのクレジットカードとして使うことも可能です。ちなみに[今はポイント還元率がなんと4%らしい](http://japanese.engadget.com/2016/12/22/line-pay-4-12-23-1-9-18/)ですね！

### なぜLINE Payを採用したのか

イベントをやると必ずと言っていいほど発生するのが **ドタキャン**です。まぁ仕方ないと思います。
ただ、今回の合宿は一人当たり13000円の参加費用がかかりました。この参加費でドタキャンが発生すると運営側がかなり厳しいので前払いを前提としました。

#### PayPalのビジネスアカウント用意はけっこう大変

connpassの場合前払いでお金を徴集する場合、PayPalのビジネスアカウントが必要になります。
これはけっこう用意するのが大変で、(僕は持ってましたが)通常はあまり持っていないと思います。

#### 大体の人がPayPalアカウントは持ってないけどLINEアカウントは持ってる

PayPalアカウントはWeb業界の人でも持ってる人と持ってない人が半々くらいの割合な気がしてます。
LINEアカウントはほぼ100%で所持しているのでLINEアプリがあれば使えるという点もLINE Pay採用ポイントでした。

#### 手数料が0円

PayPalだと3.6%+40円～という手数料で130000円の集金だと手数料が500円強かかることになります。20人参加だと10000円を超える金額になるため運営費用で見るとけっこう大きな額です。

LINE Payの個人間送金だと手数料が0円ということもあり、手数料の節約になりました。

## 実際の合宿時の運用

### PayPalとLINE Payの併用

PayPalアカウントを持ってる人もいると思ったのでPayPalでの支払いもできるようにしました。

![](https://i.gyazo.com/fe56d15d176e4ed4dcbed7c05f73d840.png)

ただ、PayPal支払いの場合は手数料が発生して運営側が受け取れるお金が減ってしまうため500円多めに集金という強め設定です。

* PayPal支払い -> 13500円
* LINE Pay支払い -> 13000円

という形にしましたが、500円高くてもPayPal払いの人も半数くらいいました。
イベントによく参加する人はPayPal払い慣れてるんですね。

### 合宿の連絡もLINEグループで展開

LINEは当たり前のように全員が使ってる認識でLINEグループを作成し、そこで連絡しつつお金の集計もしました。

![](https://i.gyazo.com/10ff3b90bb9dcc7ce2c1500385537b76.png)

ちなみに振り返りながら書いてますがこのキャプチャは **僕が成田駅で一人はぐれてしまった**ときの会話です。苦笑

### 運営側のお金やりとりも

当日発生したお金のやりとりもLINE Pay送金です。

![](https://i.gyazo.com/b3809d9ccb607ef72a4c8073552177d3.png)

合宿の連絡自体をLINEでしていたので当然運営側もLINE Payのやりとりで成り立ちます。

### キャッシュバックもLINE Payで

会が終わった後に、若干お金が余ったのでなんと **一人当たり555円のキャッシュバック**を実施。
555円って現金で返すと大変だけど、LINE Payだと数字の移動だけなので楽ですね。

![](https://i.gyazo.com/604ede714369a2d4e594c25dd54ba727.png)

## [本題] LINE PayのAPIを触ってみよう

こんな感じでLINE Payはだいぶ役立ってくれました。
LINE PayはAPIもあるのでもっと便利に出来そうです。

ちなみに[Swift愛好会](https://love-swift.connpass.com/)にいますが僕はSwiftはほぼ書いてません。ということでNode.jsで実装してみたいと思います。(合宿中に書いていたネタでもあります)

### 申請が必要

2016年12月時点だとLINE PayのAPIを実際に使う場合は申請が必要で、僕が申請したときに運営しているサイトに独自カートシステムがないという理由でリジェクトされてしまいました。

![](https://i.gyazo.com/bcf839a7e35f2512e3e4b4cc6bf497aa.png)

イベントの集金とかで使えたらって思ったけど現時点だと難しそうですね苦笑
(運営側の人みてないかなぁ)

本当ならAPIを使って合宿の決済を済ませられないかと検討していました。

### サンドボックス利用してみる

サンドボックス利用なら試すことはできるので、触ってみた記録を残します。


[LINE Pay Developers](https://pay.line.me/jp/developers/techsupport/sandbox/creation?locale=ja_JP#)からSandboxを生成しましょう。
ちなみにこのリンクがLINE Payのページのどこから辿れるのかがよく分からないです苦笑

APIの詳細は[LINE Pay技術連動ガイド](https://pay.line.me/file/guidebook/technicallinking/LINE_Pay_Technical_Linking_Guide_for_Normal_Merchant_v1.0.1_jp.pdf)を参照して進めましょう。現時点だとPDFしかないみたいですが、そのうちページが用意される気がします。

#### 利用の流れ

[テストフロー](https://pay.line.me/jp/developers/techsupport/sandbox/testflow?locale=ja_JP)の通りに進めると実装ができます。この辺りの詳細はまた別記事に書こうと思います。

APIの流れとしては以下の図にあるような流れになります。

払い戻しなどを省いて、シンプルなのは`決済reserve API` -> `paymentUrl / (ユーザー側)ログイン認証&決済処理` -> `決済confirm API`という流れです。今回はこの流れだけで実装してみます。

![](https://i.gyazo.com/afc9239514ed0614fc854761e9513ec6.png)
>LINE Pay技術連動ガイドより

### Node.jsで実装してみる

エンドポイントや必須のリクエストヘッダ情報を埋めます。

#### API認証部分

`X-LINE-ChannelId`と`X-LINE-ChannelSecret`のヘッダ情報でLINEのサーバーとの認証をさせ、HTTPリクエストを司る部分です。

```httpclient.js
'use strict'

const client = require('https');

const LINE_CHANNEL_ID = `xxxxxxxxxxxxx`;
const LINE_CHANNEL_SECRET = `xxxxxxxxxxxxxxxxxxxxx`;
const HOST = `sandbox-api-pay.line.me`;
const PATH = `/v2/payments/request`;

module.exports = ($requestData, $path, $method = 'POST') => {
    let postDataStr = JSON.stringify($requestData);
    let options = {
        host: HOST,
        port: 443,
        path: $path,
        method: $method,
        headers: {
            'Content-Type': 'application/json; charset=UTF-8',
            'X-LINE-ChannelId': LINE_CHANNEL_ID,
            'X-LINE-ChannelSecret': LINE_CHANNEL_SECRET
        }
    };
    
    return new Promise((resolve, reject) => {        
        let req = client.request(options, (res) => {
            let body = '';
            res.setEncoding('utf8');
            res.on('data', (chunk) => {
                body += chunk;
            });
            res.on('end', () => {
                let response = JSON.parse(body);
                resolve(response);
            });
        });
        req.on('error', (e) => {
            reject(e);
        });
        req.write(postDataStr);
        req.end();
    });
}
```

#### メインの処理

説明すると以下の流れになります。

* ユーザーが`/`にアクセスした際に`購入リンク`を表示させ、`/reserve`に飛ばします。
* `/reserve`では商品情報などをLINE側に送信し、専用のURL(paymentUrl)を発行してもらいます。
* 発行されたpaymentUrlからユーザーは認証&決済をして`/confirm`に戻り、決済完了処理を行います。

```app.js
'use strict'

const uuid = require('node-uuid');
const app = require('express')();
const http = require('http').Server(app);
const PORT = process.env.PORT || 1337;
const httprequest = require('./httpclient');
const DOMAIN = 'http://localhost:${PORT}';

app.get(`/`, (req, res) => {
  let html = `<h1>LINEで決済</h1>
  <a href="/reserve">購入!</a>`;
  res.send(html);
});

//決済reserve API
app.get(`/reserve`, (req, res) => {
  let requestData = {
    "productName": "募金します。",
    "amount": 1,
    "currency": "JPY",
    "confirmUrl": `${DOMAIN}/confirm`,
    "orderId": uuid.v1()
  };

  httprequest(requestData,'/v2/payments/request')
  .then((responseData)=>{    
    //成功
    if(responseData.returnCode === `0000`){
      res.redirect(responseData.info.paymentUrl.web);
    }

    //失敗
    else if(responseData.returnCode === `1106`){
      console.log(responseData.returnMessage);
    }

  },(e)=>{console.log(e)});
});

//決済confirm API
app.get(`/confirm`, (req, res) => {
    let transactionId = req.query.transactionId;
    let requestData = {
      "amount": 1,
      "currency": "JPY"
    };
    let path = `/v2/payments/${transactionId}/confirm`;
  
    httprequest(requestData, path)
    .then((responseData)=>{
      console.log(responseData);
      let html = `ご購入ありがとうございました。<a href="/">戻る</a>`;
      res.send(html);
    },(e)=>{console.log(e)});

});

http.listen(PORT, () => {
  console.log(`listening on *:${PORT}`);
});
```

##### 決済reserve API
`/reserve`の箇所です。

POSTリクエストの情報に`productName(商品名)`や`amount(金額)`などを指定しています。
成功するとLINEのサーバーから`paymentUrl`と呼ばれるURLが送られてくるのでそこにリダイレクトさせています。

今回は「募金します。」という商品を「1円」で売っていることにしました。

##### 決済confirm API

`/confirm`の箇所です。
ユーザーが認証&決済を済ませた後にリダイレクトしてきます。このときに`transactionId`という決済ごとのIDがLINEのサーバーから渡ってくるのでキャッチし、このIDを`confirm api`へのリクエストに渡してます。

このとき`reserve api`を読んだ時点の情報と違いがあると決済が成功しません。
LINE Pay APIではここで二段階で確認をすることで安全性を担保しているようです。

### 実際に使って確認

今の実装を試すとこんな感じで動作します。

![](https://i.gyazo.com/1db01c3fbed0a7bb51dd045775b0eb3d.gif)

>解説してないですが、実際にはIPのホワイトリスト登録なども必要です。

SandBoxの場合は[こちら](https://sandbox-pay.line.me/ja_JP/main)から決済の内容が確認できます。

![](https://i.gyazo.com/db3126e7f7d90ec9acfd23eaf77c5a0b.png)

**取引金額1円がちゃんと履歴に入ってますね！**

## おわりに

こんな感じでLINE Payでの商品の購入をNode.jsで実装することができました。

この記事の内容をまとめると、

* LINE Payは集金とかに向いてる
* API使って実装もできる

という感じです。みなさんも普段使いLINE Payから始めてみましょう。

最後になりますが、Swift愛好会のアドベントカレンダーでSwift書かずすみません苦笑
(SSSwiftで書いてみようか悩みましたが時間がかかりそうだったのでry)