Python
WebAPI
api
Line
LINEPay

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

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

はじめに

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

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

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

必要なもの

  • LINE Pay Sandbox環境
  • 固定IPがあるサーバー
  • Python 3.6

ソースコード

https://github.com/keigohtr/line-pay-python-example

下準備 (LINE Pay Sandbox環境の準備)

こちらを参考にしてください。

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

今回のデモの決済の流れは、ざっくりと以下になります。

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

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

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

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

LINE_PAY_CHANNEL_ID = 'your channel'
LINE_PAY_CHANNEL_SECRET = 'your secret'

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

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

デモの構成

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を使いました。SQLAlchemyでsqliteのDBを立て、transactionの管理をしています。非常にシンプルな構成です。line_pay.py というLINE Pay SDK的な何かを作りましてLINE Pay APIの全てを網羅しましたが、動作確認できているのは今回のデモで使用した「支払い予約 POST: /v2/payments/request」と「支払い実行 POST: /v2/payments/{transaction_id}/confirm」だけですのでご注意ください。

デモの実装を追ってみる

設定項目は以下の4つです。

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つの設定項目がLinePayclassの最低限必要な引数となります。

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

@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_nameamountcurrencyを決め打ちにしました。支払い予約をするためには、product_nameamountcurrencyorder_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であることが確認できたら、実際に支払い実行を行います。支払い実行をするためには、amountcurrencyをLINE Pay APIにPOST: /v2/payments/{transaction_id}/confirmする必要があります。デモではpay.confirm_payments()の中で適切なURLにAPIリクエストを行っています。

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

おわりに

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

参考情報

筆者について

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