LINE Pay APIをPythonから叩いてみた

こんにちは、こちらは[国産Web API Advent Calendar 2018](https://qiita.com/advent-calendar/2018/apijp)の1日目の記事です (※誰がなんと言おうと1日目の記事です、依頼日は12/4、投稿日は12/17でも1日目の記事です) 。普段はLINE株式会社で[Clova](https://clova.line.me/)のSoftware engineerをしています。本日はLINE Pay API (Clova APIじゃない!?) をPythonから叩いてみたというお題目で記事を書きたいと思います。

## はじめに
本記事はLINE株式会社の許諾を得て投稿していますが、本記事で紹介するソースコードはLINE公式ではありません。ご利用の際は自己責任でお願い致します。

本記事は「[LINE Pay APIを使ってアプリに決済を組み込む方法](https://qiita.com/nkjm/items/b4f70b4daaf343a2bedc)」をPythonで書いてみたものになります。LINE Payを使うまでの下準備 (アカウント登録、Sandbox環境の準備、など) は[こちら](https://qiita.com/nkjm/items/b4f70b4daaf343a2bedc)が大変分かりやすいのでそちらを参照してください。

前置きが長くなりましたが、LINE Pay APIは誰でも無償で申請でき、簡単に利用できます。組み込み実装も簡単でした。なぜかネット上でLINE Pay API使ってみた系の情報があまり見つからず、唯一[こちら](https://github.com/nkjm/line-pay)のNode.js版の非公式SDKだけが有用な情報だったので、今回は[PythonでSDK的な何か](https://github.com/keigohtr/line-pay-python-example)を作ってLINE Pay APIを使ってみたいと思います。


## 必要なもの
- LINE Pay Sandbox環境
- 固定IPがあるサーバー
- Python 3.6


## ソースコード
https://github.com/keigohtr/line-pay-python-example


## 下準備 (LINE Pay Sandbox環境の準備)
[こちら](https://qiita.com/nkjm/items/b4f70b4daaf343a2bedc#line-pay-sandbox%E3%81%AE%E7%94%B3%E8%AB%8B%E3%81%A8%E8%A8%AD%E5%AE%9A)を参考にしてください。


## LINE Pay APIをPythonから叩いてみる
今回のデモの決済の流れは、ざっくりと以下になります。

```text
支払い予約 (Server) → ユーザーの認可 (Client) → 支払いの実行 (Server)
```

とにかく一旦デモを動かしてみましょう。

```bash
$ git clone https://github.com/keigohtr/line-pay-python-example.git
$ cd line-pay-python-example
```

`app.py`というのがデモの本体です。LINE Pay Sandbox環境のパラメータを設定してください。

```python
LINE_PAY_CHANNEL_ID = 'your channel'
LINE_PAY_CHANNEL_SECRET = 'your secret'
```

下記コマンドでデモを起動します。デモを起動すると `http://localhost:5000/` に「LINE Pay」ボタンがあります。押すとLINE認証の要求画面が出て、認証後に「チョコレート」を「1円」で決済できます。これはデモなので安心して決済してください。Sandbox環境では実際にお金を支払うことはありません。

```bash
$ pip install -r requirements.txt
$ python app.py
$ open http://localhost:5000/
```

### デモの構成
```text
line-pay-python-example
 |- app.py          Flask Webサービス
 |- line_pay.py     自家製LINE Pay SDK的な何か
 |- models/         sqliteでtransaction IDを管理
 |- static/         今回はLINE Payのiconを格納
 |- templates/      Flask + Jinja2のテンプレート
```

Webフレームワークとして[Flask](http://flask.pocoo.org/)を使いました。SQLAlchemyでsqliteのDBを立て、transactionの管理をしています。非常にシンプルな構成です。`line_pay.py` というLINE Pay SDK的な何かを作りましてLINE Pay APIの全てを網羅しましたが、動作確認できているのは今回のデモで使用した「支払い予約 `POST: /v2/payments/request`」と「支払い実行 `POST: /v2/payments/{transaction_id}/confirm`」だけですのでご注意ください。

### デモの実装を追ってみる
設定項目は以下の4つです。

```python
LINE_PAY_URL = 'https://sandbox-api-pay.line.me'
LINE_PAY_CHANNEL_ID = 'your channel'
LINE_PAY_CHANNEL_SECRET = 'your secret'
LINE_PAY_CONFIRM_URL = 'http://localhost:5000/pay/confirm'
pay = LinePay(channel_id=LINE_PAY_CHANNEL_ID, channel_secret=LINE_PAY_CHANNEL_SECRET,
              line_pay_url=LINE_PAY_URL, confirm_url=LINE_PAY_CONFIRM_URL)
```

今回のデモでは`LINE_PAY_URL`がsandbox環境に向いています。Productionで利用する場合はProduction用のURLを使いますが、その前にLINE Pay APIの利用審査があります。`LINE_PAY_CONFIRM_URL`は「支払い予約」をしてユーザーが支払いOKと決めたとき、LINE Pay側からデモサーバーにcallbackするURLになります。デモでは`localhost`を向いていますが、Productionで利用する場合はLINE Pay Developersに設定したとおり「固定IP」を持つ「https」のエンドポイントである必要があります。以上の4つの設定項目が`LinePay`classの最低限必要な引数となります。

続いて、支払い予約の部分を解説します。

```
@app.route("/pay/reserve", methods=['POST'])
def pay_reserve():
    product_name = "チョコレート"
    amount = 1
    currency = "JPY"

    (order_id, response) = pay.request_payments(product_name=product_name, amount=amount, currency=currency)
    transaction_id = response["info"]["transactionId"]
    obj = Transactions(transaction_id=transaction_id, order_id=order_id,
                       product_name=product_name, amount=amount, currency=currency)
    db.session.add(obj)
    db.session.commit()
    db.session.close()
    redirect_url = response["info"]["paymentUrl"]["web"]
    return redirect(redirect_url)
```

今回のデモでは`product_name`と`amount`、`currency`を決め打ちにしました。支払い予約をするためには、`product_name`と`amount`、`currency`、`order_id`をLINE Pay APIに`POST: /v2/payments/request`する必要があります。`order_id`はorder毎に振られるユニークな値でユーザーが自由に定義することができます。デモでは`pay.request_payments()`の中で`order_id`を自動生成し、APIリクエストを行っています。

APIレスポンスには色々と含まれているのですが、支払い予約が成功しているならば`response["info"]["transactionId"]`に`transaction_id`が含まれています。`transaction_id`はLINE Pay側から吐き出されたユニークな値です。自分たちのサービスで吐き出されたtransactionであることをちゃんと管理したいので、`transaction_id`はDBに保存しておきます。最後に、`response["info"]["paymentUrl"]["web"]`に「ユーザーの認可」用の`redirect_url`が含まれていますので、ClientをredirectしてユーザーにLINE Payで認可してもらいます。

続いて、支払い実行部分を解説します。

```
@app.route("/pay/confirm", methods=['GET'])
def pay_confirm():
    transaction_id = request.args.get('transactionId')
    obj = Transactions.query.filter_by(transaction_id=transaction_id).one_or_none()
    if obj is None:
        raise Exception("Error: transaction_id not found.")

    response = pay.confirm_payments(transaction_id=transaction_id, amount=obj.amount, currency=obj.currency)
    db.session.query(Transactions).filter(Transactions.transaction_id == transaction_id).delete()
    db.session.commit()
    db.session.close()
    return "Payment successfully finished."
```

ユーザーがLINE Payで認可していれば、callbackに指定したURLにGETでcallbackが返ってきます。Query parameterに`transaction_id`が含まれているので、先程DBに保存した値と一致するかチェックします。自分たちのサービスで吐き出されたtransactionであることが確認できたら、実際に支払い実行を行います。支払い実行をするためには、`amount`、`currency`をLINE Pay APIに`POST: /v2/payments/{transaction_id}/confirm`する必要があります。デモでは`pay.confirm_payments()`の中で適切なURLにAPIリクエストを行っています。

APIリクエストに成功したら、DBから当該`transaction_id`のエントリを削除します。ただ、実際にProduction環境で利用する場合は、DBエントリを削除するのではなく"支払い済み"statusでもつけてください。`transaction_id`か`order_id`は過去に実行したTransactionの参照や管理にも使いますので。


## おわりに
今回はLINE Pay APIをPythonから叩いてみました。APIドキュメントは非常に読みやすく、構成もシンプルです。今回のデモを作るにあたって詰まった場所はありませんでしたし、組み込み難易度は非常に低いと思います。まだまだLINE Pay APIを使ってみた系の有用な情報が少ないと感じましたので、今回の記事が参考になれば幸いです。


## 参考情報
- [LINE Pay APIを使ってアプリに決済を組み込む方法](https://qiita.com/nkjm/items/b4f70b4daaf343a2bedc)
- [LINE Pay 技術連動ガイド](https://pay.line.me/file/guidebook/technicallinking/LINE_Pay_Integration_Guide_for_Merchant-v1.1.2-JP.pdf)


## 筆者について
LINE株式会社でSoftware engineerをしています、服部 ([keigohtr](https://twitter.com/keigohtr?lang=ja)) です。担当プロダクトは[Clova](https://clova.line.me/)で、担当領域はNLU (Natural Language Understanding) および機械学習まわりです。Kubernetes上で機械学習モジュールの配信基盤を作っていて、一連のソフトウェアを[Rekcurd](https://github.com/rekcurd)と名付けてOSSとして公開しています。**スター**ください(迫真)。また個人的に情報科学技術をWebAPIで売り買いできるWebAPIのマーケットプレイス [Apitore](https://apitore.com) を運営しています。世の中のあらゆる技術を簡単にデプロイでき、かつ誰でも簡単に利用できる、そんな世界を夢見ています。

- [Rekcurd](https://github.com/rekcurd)
- [Apitore](https://apitore.com)
- [Kubernetes上で動作する機械学習モジュールの配信＆管理基盤Rekcurd について](https://www.slideshare.net/linecorp/kubernetesrekcurd)