DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

DjangoサイトにPayPal決済を導入する 備忘録

個人で開発しているWebサービスにこの度PayPal決済サービスを付加することにしました。

 

PayPalは以前にAmazonのレビュー依頼で作っておいたのでそれをビジネスアカウントにグレードアップして使用することにしました。

dreamerdream.hateblo.jp

 

PayPalの魅力は何と言っても「会費無料」「世界でトップシェアの決済サービス」というところではないでしょうか?

手数料は決済の都度発生しますが、クレジットカードに銀行口座振り込み、おまけに仮想通貨にまで対応していて、尚かつ利用者がサイト上にカード情報を入力しなくて良いという安心感があります。

PayPalは開設費用は年会費も不要なので僕のような弱小サイトには強い見方です。口座が動かなければ完全無料運用可能なんですから、とりあえず試しに組み込むだけというのもアリ。

 

ビジネス用アカウントを作って本人確認が取れていればPayPal決済用のボタンをPayPalサービス上で作れるのですが、そのままだとこちらが支払確認をしてから顧客にサービスを提供する。という手作業が必用になってしまいます。

 

それらを自動化するためにはPayPalAPIを利用する必用があります。

 

とはいっても殆どこちらの先輩の書かれている記事の通りです。

akiyoko.hatenablog.jp

こちらの記事内容をこのまま参考資料として、僕自身が再構築する時に迷わないために以下に記すことにしました。

 

 

APIを利用するためのSDKgithubサイトで公開されているのでPython用のものをcloneするだけです。

github.com

 

SDKをダウンロードしたいディレクトリで

sudo git clone https://github.com/paypal/PayPal-Python-SDK

を実行するとその場にSDKディレクトリが展開されるので、

 

利用するpythonコードファイル(Djangoのview.pyなど)から「paypalrestsdk」ディレクトリが見えるようにインポートするだけ。

例)ソースコードファイルと同じディレクトリ内にSDKディレクトリをダウンロードした場合

import sys

sys.path.append(os.path.join(os.path.dirname(__file__), './PayPal-Python-SDK'))
import paypalrestsdk

 

とすることでpaypalrestsdkが利用出来るようになります。

paypalrestsdkには辞書形式で設定値を渡すだけだから設定が面倒であれば直書きでも動作可能。

 

実際に運用開始する前にデベロッパーページにて「sandbox」用のアカウントを売り手、買い手の2つ作って実際のお金を動かさずに動作試験することが出来ます。

developer.paypal.com

 

 

<sandboxで生成された試験用アカウント>

f:id:DreamerDream:20201105135903p:plain

下のコードのpaypalrestsdkに入力する[PAYPAL_CLIENT_ID],[ PAYPAL_CLIENT_SECRET]はここで作った売り手のアカウントのIDとシークレットキーを入力します。(実運用時には自分のアカウントのものに置き換えます。)

参考ページから転用(views.py)

paypalrestsdk.configure({
  'mode': [試験運用には'sandbox'、実際の動作には'live'を入力],
  'client_id': [PAYPAL_CLIENT_ID],
  'client_secret':[ PAYPAL_CLIENT_SECRET],
})


payment = paypalrestsdk.Payment({
  'intent': 'sale',

  # Payer
  'payer': {
    'payment_method': 'paypal',
  },

  # Redirect URLs
  'redirect_urls': {
    'return_url': '/execute-payment', #決済完了時のアドレス
    'cancel_url': '/pay', #キャンセル時のアドレス
  },

  'transactions': [{ 
    # Item List
    'item_list': {
    'items': [{
      'name': [商品の名前],
      'sku': [支払の内訳],
      'price': '100', #価格
      'currency': 'JPY', #日本円はJPY アメリカドルならUSD
      'quantity': 1, #量
    }]
  },
  # Amount
  'amount': {
    'total': '100', #合計金額
    'currency': 'JPY',
  },
  'description': [説明書き],
  }]
})

 

 

 

上のコードは参考ページのhtmlファイルに置かれているjavascryptコードの

paypal.request.post('/create-payment', {csrfmiddlewaretoken: '{{ csrf_token }}'})

この部分、例の場合であれば「/create-payment」に対してPOSTリクエストが送られ、Djangoからjson形式で決まった型でデータが返信されます。

この形式はカッチリと決まっているようで、任意のキーを付加したり削除したらエラーになります。

 

決済が完了したら

/execute-payment

が呼ばれて完了します。

 

非常にザックリした図ですけれど、

f:id:DreamerDream:20201105134501p:plain

PayPal決済ボタンでcreate情報取得するアクセスポイントを自サイトに設置して、

決済ボタンが押されたら自サイトにcreate情報アドレスへPOSTして、自サーバーからはjsonで支払情報を返す。

その後スクリプトがそのままPayPalサーバーと支払情報等をやりとりして(ブラックボックス)、決済完了後に完了画面へ推移する。

という流れのようです。

 

 

参考サイトではpaymentの情報をそのままDjangoのテンプレートで出力していますが、決済結果をjsonで保存する場合は一度str形式に変換してから辞書形式に直してjson形式で保存するという方法になるようです。

paymentはpaypalrestsdkの独自クラスのようでそのままでは辞書変換出来ませんでした。

文字列→辞書形式変換には演算可能なevelよりastクラスを使のが安全です。

note.nkmk.me

paypal形式を辞書形式に変換

import ast

 

str_payment = str( payment )
dic_payment = ast.literal_eval( str_payment )

 

あとは完了画面で有料サービスのURLやコンテンツのダウンロードを提供することができる他、取引毎に生成される固有のIDが「payment.id」で取得できるので、自サーバーにIDを保管しておけば決済完了時にユーザー毎の決済を確認することも出来ます。

これでユーザー毎にサービスを提供するなど自動的に任意のサービスをDjango側から提供することが出来ます。

 

ちなみに、コードのどこかが間違っていたり、POST出来なかったりすると以下の画像が一瞬だけ表示されて消えてしまいます。

f:id:DreamerDream:20201105140005p:plain

 

 

非常にザックリした内容でしたが、あくまで自分用の備忘録ですのでご了承ください。

 

 続↓ 実際の決済でエラーが出たので対処しました。

dreamerdream.hateblo.jp

 

kampa.me