Analog Studio

PayPal API (version 2) を使ってクレジットカード決済を実装しよう!

概要

8月になり全国的に梅雨も明けて非常に暑い日が続いておりますが、皆様体調如何でしょうか?
1ヶ月前は寒い日があったのですが、急な環境の変化は辛いですね。

さて、1ヶ月前と云えば「7pay」がセキュリティ対策ができておらず大変な被害が出てしまいまったことは記憶に新しいです。(そして9月末で終了)
このような新しいシステムの導入は慎重に進めて貰いたいところです。
なにやら「UNIQLO Pay」なる決済も商標出願したことで一時話題になりました。

さて、そんな新規参入したキャッシュレス決済システムにはセキュリティ上の懸念がどうしても付きまといます。対して、古くから使われているシステムはその点で信頼と実績が高いです。
こうした古参のシステムとして世界的にも広く使われているシステムに「PayPal」(ペイパル) があります。(PayPayじゃないですよ)
EC や決済用の API が多数用意されており、基本無料で利用できますので、この便利な API を使った決済方法をまとめていこうと思います。

PayPal(ペイパル)とは?20世紀からある老舗です!

PayPal(ペイパル)

PayPal (ペイパル) とは、1998年にアメリカで起業された電子決済サービスを展開する会社です。
アメリカをはじめ、全世界190の国と地域で広く利用されている老舗企業です。
通貨の異なる場合でも PayPal で自動的に為替レートが適応されて取引が行えます。(レートは悪いですが)

・公式サイト - PayPal
・Wikipedia - PayPal

日本ではあまり流行っていないように感じますが、決済サービスとしては国際的に浸透しています。
クレジットカード情報を PayPal アカウントで管理する為、EC サイト側にクレジットカード情報が残らずセキュリティが高いです。
店側としても管理の難しいクレジットカード情報を保持する必要がなく、またユーザ目線でも店側に番号を教えることなく決済を終えることができるのでどちらにとってもメリットがあります。

PayPalでの決済方法

そんな便利な PayPal では電子決済サービスとして大きく以下の3つの決済方法が提供されています。

メールで決済

最も手軽で簡単に請求が行えるのが、この「メールで決済」です。
PayPal のビジネスアカウントを開設すれば PayPal の自分のアカウントツールから簡単に請求書を作成・発行できます。

・PayPal 公式 - メールで決済 (請求書ツール)

請け負った仕事に対する請求書として使う機会も多く、先方が PayPal 払いに慣れている場合は両者ともに圧倒的に楽な決済が実現できます。
個人店でクレジットカード決済を導入していない場合などにも使えると思います。

かんたん決済ボタン(HTML)

自分の Web サイトを持っていたり、ブログを運用している方はこの決済ボタンが便利です。
商品情報と価格を設定するだけで決済を開始するボタンの HTML コードが自動的に生成され、コピペで決済ボタンを実装できます。

・PayPal 公式 - かんたん決済ボタン (ウェブペイメントスタンダード)

ボタンのデザインもある程度はカスタマイズできますので、基本的にはこの仕組みを使うと便利にすぐに始められます。
また、定期購入やショッピングカート機能も実装できますので、小規模な EC サイトなら PayPal にお任せしてしまっても良いかもしれません。
データベースが使えないレンタルサーバを使っている場合にも PayPal 内でデータ管理されるので心配はいりません。

API決済

最後に API 決済です。これは自由度は断然高いですが、決済用のプログラムを組む必要があり導入の難易度が高いです。
決済後に自動で詳細な処理を行いたい (自分の DB に詳細データ保存して購入者にメールを送るなど) 場合には API 決済を使う必要があります。

ただ、SDK (開発用キット) も提供されており、中でも Javascript SDK を使ったものは非常に手軽に使うことができます。
Javascript SDK では僅かなコードで決済ボタンを生成でき、動的な処理も可能になっています。

・PayPal 公式 - API 決済 (エクスプレスチェックアウト) (情報なしのリンクのみ)
・PayPal 公式 (Qiita) - PayPal API決済の実装方法 (これも詳細情報はない)
・PayPal 公式 (英語) - PayPal Developer (全部英語ですが、詳細あり)

SDK を導入しなくても、決済を作成して決済するための URL を生成したり、詳細情報の取得、返金処理、請求書発行など PayPal の機能を利用可能です。

この記事では SDK を利用しないで決済開始からデータ取得、返金処理を実現する方法をまとめます。
これだけのために SDK を導入するのはちょっとムダ (SDK の PHP ファイルは約1MB) なので素の PHP で実装していきます。

API の最新は Ver.2 ですが、Javascript SDK はどうやら Ver.1 のようなのも気になる、という背景もあります。

PayPalは月額無料だけど、決済手数料は掛かります!

PayPal は月額料金や基本料金というものは設定されていません。なので、売り上げの少ないスタートアップにはもってこいです。
普通は月額でクレジットカード決済代行の最低費用は○○円~、とか月額料金が掛かったりしますのでとてもありがたいですね。

初期費用・月額費用

しかし、決済ごとに決済手数料は掛かってきます。当然といえば当然です。
PayPal の決済手数料は他のサービスと比較しても高くないので月額費用のない分、お得だと思います。

決済手数料

少額の決済だと固定の40円の比率が大きくなりますので、ある程度の金額以上での決済となる工夫や価格設定が必要ですね。
少額決済のみであれば手数料の計算方法が異なる決済も選べるようです。(要利用申請)

少額決済手数料

PayPal決済の流れ

まずは、PayPal での決済の流れを確認しましょう。
PayPal の高いセキュリティを享受するためにクレジットカード情報の入力などの決済処理はすべて PayPal サイトで行う方法を採用します。
(自サイト完結型もできるようですが、セキュリティ的にやりたくないので詳しくは調べていません)

PayPalでの決済フロー

・PayPal Developer - PayPal Checkout Overview - How the buttons work

決済情報を作成する

まずは、決済情報を作成します。これには Order API を利用します。(上図の③)

決済情報を作成するタイミングは決済ボタンをクリックしたタイミングが良いでしょう。
ページの読込時に作成すると無駄になってしまうことがあるからです。
(高確率で決済が始められる場合はクリック前に作成すると遅れが少なくなってユーザビリティが上がると思います)

決済情報を作成するには商品情報や価格、税額、総額などを設定します。
オプションで戻りの URL を指定することもできます。
(最低限は図にあるように総額と通貨単位のみでも作成できます)

決済が作成できると決済ごとのIDや決済処理を行う PayPal サイトの URL などが返ってきます。

ユーザに決済処理をしてもらう

続いて、作成した決済情報からユーザが実際に決済する PayPal サイトの URL にリダイレクトしましょう。
ここからは PayPal が決済関連の処理を全て代行してくれます。 (上図の④~⑤)

決済処理が完了すれば (PayPal 側で) 処理を受け付けた状態となり、戻り URL を指定していればそこにリダイレクトされて戻ってきます。

決済処理完了を確認し確定処理する

最後に決済処理を受け付けた状態で PayPal からリダイレクトされてくるので、自サイトで確認したことを PayPal に通知して決済を完了させます。(上図の⑥)

完了すると決済情報の詳細や支払いごとの ID が返ってきます。
返金処理をする場合にはこの ID が必要になりますので、データベースなどに保存しておきましょう。
(決済処理の ID から取得することもできます)

APIを使ってみよう!

ではようやく本題です。API を使っていきましょう。
公式ドキュメントの PHP では cURL を使った方法がまとめられていますが、cURL が使えない方もいると思うので cURL を使わない方法で接続していきます。

ちなみにですが、このサイトを運用している Xserver でははじめから cURL を使うことができます。

ビジネスアカウントの取得

API の利用にはビジネスアカウントが必要になってきます。
PAyPal サイトから簡単にビジネスアカウントは取得できますので事前にご用意をお願いします。

・PayPal 公式 - 新規登録

※ビジネスアカウントの開設には住所の確認があります (認証番号が郵送で送られてくる) ので、時間に余裕をもって登録して下さい。

APIへのアクセスをまとめたClass

このセクションは有料となっています。以下よりログインするか購入 (記事単位) をお願いします。

cURLを使わないでfile_get_contents()関数でリクエストを送信する

PayPal のサンプルコードでは cURL でのリクエストですからこれを PHP の組み込み関数で実現させます。
PHP では、GET や POST は file_get_contents() 関数を使うことができます。

cURL オプションの "-H" はリクエストヘッダ、"-u" は Basic 認証、"-d" は送信データを表しています。
従ってこれらを file_get_contents() で送信できるようにしていきます。

file_get_contents() 関数にヘッダやリクエストボディを添付するには第3引数にストリームコンテキストと呼ばれるリクエストの挙動を制御するオプションなどをまとめたものを渡します。
これにはコンテキストを生成する元になる連想配列と stream_context_create() 関数を使います。
指定できるオプションなどは HTTP コンテキストオプションをご参照下さい。

まずは、ヘッダを準備しましょう。
ヘッダは1行に1つの値を格納した文字列を準備しておけばOKです。
ここでは分かりやすいように配列に一つずつ入れておきます。

$header = array( 'Accept: application/json', 'Accept-Language: en_US', 'Authorization: Basic '. base64_encode('<client_id>:<secret>'), );

指定する値は cURL の場合と同じで良いです。
ただし、cURL では Basic 認証を "-u" オプションで指定できますが、ヘッダに含める場合は "ユーザ名:パスワード" という文字列を Base64 エンコードした文字列を使います。

続いてリクエストボディ (POST や GET で送信する値) を配列の形で用意しておきます。
"key=value" という情報を送信する場合は、array('key' => 'value') とします。

$data = array( 'grant_type' => 'client_credentials', );

送信するデータを通常であれば、URL エンコードして使います。(http_build_query() 関数)

最後に HTTP リクエスト用の配列にまとめて、ストリームコンテキストを生成します。

$options = array( 'http' => array( 'method' => 'POST' や 'GET', 'header' => implode("\r\n", $header), // 改行を追加 'content' => http_build_query($data), // 送信値をURLエンコード ) ); // ただし、PayPal API は上記のオプションだけではリクエストが通りません $result = file_get_contents('APIのリクエストURL', false, stream_context_create($options)); // JSON形式の結果が返ってくる

アクセストークンを取得

このセクションは有料となっています。以下よりログインするか購入 (記事単位) をお願いします。

アクセストークンの取得に成功すると以下のような JSON 形式のレスポンスが返ってきます。
(実際には改行やインデントはされていません)

{ "scope" : "https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller https://uri.paypal.com/services/payments/refund https://api.paypal.com/v1/vault/credit-card https://api.paypal.com/v1/payments/.* https://uri.paypal.com/payments/payouts https://api.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks", "access_token" : "A21AAF2nSA0uCSdT4_yM18U20dc2INA20XVpLeBGFcq_gxBM9oDf8YSlSL2DrWhvdQRx7Iv8XUOusuAxsXbBXWPs2MUxbZsHA", "token_type" : "Bearer", "app_id" : "APP-80W857390P519543T", "expires_in" : 32333, "nonce" : "2019-08-01T07:46:46ZcEuf0bu3ht-pKcnyM_uKXGBlo9f8UyyNAbhNE_khcMY" }

上記 Class を利用する場合は、$paypal->getToken() と引数なしで呼び出すとアクセストークンの文字列のみを返します。
引数に true を設定した $paypal->getToken(true) では上記 JSON を配列に展開したものを返します。

PayPal決済を作成

このセクションは有料となっています。以下よりログインするか購入 (記事単位) をお願いします。

上記で定義した Class を使うと、以下のように呼び出せば PayPal での決済用ページ (チェックアウトページ) のリダイレクト先 URL が取得できます。

$order = $paypal->createOrder( array( 'items' => array( array( 'name' => 'テスト商品1', 'description' => '商品1。商品説明がここに表示されます。', 'sku' => 'paypal-test'. rand(1111,9999), 'unit_amount' => array( 'currency_code' => 'JPY', 'value' => 2500, ), 'quantity' => 1, 'category' => 'DIGITAL_GOODS', 'tax' => array( 'currency_code' => 'JPY', 'value' => ceil(2500 * 0.08), ), ), array( 'name' => 'テスト商品2', 'description' => '商品2。商品説明がここに表示されます。', 'sku' => 'paypal-test'. rand(1111,9999), 'unit_amount' => array( 'currency_code' => 'JPY', 'value' => 1200, ), 'quantity' => 5, 'category' => 'DIGITAL_GOODS', 'tax' => array( 'currency_code' => 'JPY', 'value' => ceil(1200 * 0.08), ), ), ), 'shipping' => array( 'address' => '', // 発送の場合は住所情報必須 /* 'address' => array( // 住所情報 'address_line_1' : '2211 North Street', 'address_line_2' : '', 'admin_area_2' : 'San Jose', 'admin_area_1' : 'CA7, 'postal_code' : '951723', 'country_code' : 'US', */ ), 'cost' => 1500, ), 'url' => array( 'completed_url' => 'ユーザの決済が完了したときに戻ってくるURL', 'canceled_url' => 'ユーザが決済を途中で中断した時に戻るURL', ), 'shopinfo' => array( 'name' => 'Analog Studio (ショップ名など)', 'shipment' => 発送するかどうかの真偽値, ), ) // , '取得済みのアクセストークン' // アクセストークンを指定する場合 // , API のレスポンスを配列に展開したものをそのまま返す場合は true を指定 );

成功すると以下の配列が $order に代入されます。

<第3引数を省略した場合> { "id" : "7MX4180721396502V", "token" : "A21AAFwoFNikL3iOf98tgeJqrMsdl8jUUlrlKsfcTlBNbIRmzDeInXZiHIZEerp9dnEkgn5FMZvc_B35Gi1wTgnhGxbnkY2Ig", "login" : "https://www.sandbox.paypal.com/checkoutnow?token=7MX4180721396502V", "card" : "https://www.sandbox.paypal.com/checkoutnow?token=7MX4180721396502V&fundingSource=card" } <第3引数を true にした場合> { "id" : "7MX4180721396502V", "links" : [ { "href" : "https://api.sandbox.paypal.com/v2/checkout/orders/7MX4180721396502V", "rel" : "self", "method" : "GET" }, { "href" : "https://www.sandbox.paypal.com/checkoutnow?token=7MX4180721396502V", "rel" : "approve", "method" : "GET", }, { "href" : "https://api.sandbox.paypal.com/v2/checkout/orders/7MX4180721396502V", "rel" : "update", "method" : "PATCH" }, { "href" : "https://api.sandbox.paypal.com/v2/checkout/orders/7MX4180721396502V/capture", "rel" : "capture", "method" : "POST" } ], "status" : "CREATED" }

決済の詳細情報取得

PayPal で作成した決済の詳細情報を取得するのにも Orders API を使います。
リクエスト URL は、本番環境 (Live) では "https://api.paypal.com/v2/checkout/orders/{Order ID}"、テスト環境 (Sandbox) では "https://api.sandbox.paypal.com/v2/checkout/orders/{Order ID}" です。リクエストメソッドは GET です。

・PayPal Developer - Orders API - Show order details

リクエストボディは必要ないので認証ヘッダだけ付加して GET でリクエストを送りましょう。
これだけで決済の詳細を取得することができます。

{ "id": "5O190127TN364715T", "status": "CREATED", "intent": "CAPTURE", "purchase_units": [ { "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b", "amount": { "currency_code": "USD", "value": "100.00" } } ], "create_time": "2018-04-01T21:18:49Z", "links": [ { "href": "https://api.paypal.com/v2/checkout/orders/5O190127TN364715T", "rel": "self", "method": "GET" }, { "href": "https://api.sandbox.paypal.com/checkoutnow?token=5O190127TN364715T", "rel": "approve", "method": "GET" }, { "href": "https://api.paypal.com/v2/checkout/orders/5O190127TN364715T/capture", "rel": "capture", "method": "POST" } ] }

PayPal で決済を作成した直後には上記のようなデータを得ることができます。
ユーザが決済処理 (チェックアウト) を進めると "status" が "APPROVED" (ユーザの決済処理が完了) や "COMPLETED" (入金済み) に変化していきます。
また、決済の処理を進めるごとにお支払いしてくれた方の情報 (ID や メールアドレス) や支払い ID が追加されていきます。

上記 Class では取得した JSON を配列に展開して返します。
アクセストークンを引数に与えても良いです。(空の場合や期限切れ等の場合は内部で再取得)

決済の完了

ユーザの処理が終了したら決済が完了したことを API に送信します。
これにも Orders API を使います。
リクエスト URL は、本番環境 (Live) では "https://api.paypal.com/v2/checkout/orders/{Order ID}/capture"、テスト環境 (Sandbox) では "https://api.sandbox.paypal.com/v2/checkout/orders/{Order ID}/capture" です。リクエストメソッドは POST です。

・PayPal Developer - Orders API - Capture payment for order

詳細情報の取得と同様にリクエストボディを空で送信すれば決済が完了し、入金されてきます。
リクエストボディにカード情報などを添付してやれば自サイト完結型もできそうですが、試していないので興味があればやってみて下さい。
セキュリティの担保が難しいので採用はしないですが、興味はあるので Sandbox で試してみたいとは思っています。

成功すると以下のような取引の詳細情報などが取得できます。

{ "id": "5O190127TN364715T", "status": "COMPLETED", "payer": { "name": { "given_name": "John", "surname": "Doe" }, "email_address": "customer@example.com", "payer_id": "QYR5Z8XDVJNXQ" }, "purchase_units": [ { "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b", "shipping": { "address": { "address_line_1": "2211 N First Street", "address_line_2": "Building 17", "admin_area_2": "San Jose", "admin_area_1": "CA", "postal_code": "95131", "country_code": "US" } }, "payments": { "captures": [ { "id": "3C679366HH908993F", "status": "COMPLETED", "amount": { "currency_code": "USD", "value": "100.00" }, "seller_protection": { "status": "ELIGIBLE", "dispute_categories": [ "ITEM_NOT_RECEIVED", "UNAUTHORIZED_TRANSACTION" ] }, "final_capture": true, "disbursement_mode": "INSTANT", "seller_receivable_breakdown": { "gross_amount": { "currency_code": "USD", "value": "100.00" }, "paypal_fee": { "currency_code": "USD", "value": "3.00" }, "net_amount": { "currency_code": "USD", "value": "97.00" } }, "create_time": "2018-04-01T21:20:49Z", "update_time": "2018-04-01T21:20:49Z", "links": [ { "href": "https://api.paypal.com/v2/payments/captures/3C679366HH908993F", "rel": "self", "method": "GET" }, { "href": "https://api.paypal.com/v2/payments/captures/3C679366HH908993F/refund", "rel": "refund", "method": "POST" } ] } ] } } ], "links": [ { "href": "https://api.paypal.com/v2/checkout/orders/5O190127TN364715T", "rel": "self", "method": "GET" } ] }

上記 Class ではこの取得したデータを詳細情報と同じように配列に展開して返します。
もちろん、アクセストークンを引数に与えても良いのは同じです。

完了した取引に対しての返金

このセクションは有料となっています。以下よりログインするか購入 (記事単位) をお願いします。

ただし、完了した取引は PayPal のアカウントページから確認することができ、ここから返金 (全額返金でも部分返金でも) ができますのでシステム的に自動での返金が必要な場合以外はアカウントページから返金すれば良いと思います。
というのも、返金しても PayPal 手数料の固定費 "40円" は売り手の負担のままですので、ユーザから自由に全額返金できてしまうと最悪の場合にはかなりの損をさせられてしまう可能性があります。
それを防ぐ意味でも自動化はせずに逐一返金をした方が良いと思います。

EC システムに組み込む場合に、その管理画面からできるようにしたい、という場合には返金用 API を使うと便利ですね。

決済処理を実行するサンプル

上記 Class を使った一連の決済を行う単一商品購入のサンプルプログラムを以下に提示します。
これらを使えば任意の価格の決済がすぐに始められます。
公式の Javasript SDK を使ったものよりもずっと軽量で必要な操作以外は実装していませんのでわかりやすいのが特徴です。

このセクションは有料となっています。以下よりログインするか購入 (記事単位) をお願いします。

PayPal公式のとても参考になるページ

このセクションは有料となっています。以下よりログインするか購入 (記事単位) をお願いします。

クレジットカード決済のテストに使える番号一覧

クレジットカード決済をテストする時に使える番号の一覧を Web クリエーターボックスさんで一覧にしてくれていますので活用しましょう。

・Web クリエーターボックス - ECサイトの動作テストに使える、クレジットカードのテスト番号一覧

一番わかりやすい番号は VISA の「4111111111111111」です。一番広く使われているようです。
セキュリティコードは3桁の任意の数字が使えます。有効期限は未来の日付ならOKです。

まとめ

以上、PayPal API を使った決済を行う方法でした。
PayPal が普及すれば個人でも EC 運営が簡単にできるようになりますね。面倒な審査もありません。

EC 以外でも予約システムに組み込んでドタキャンを防止したり、事前決済も選べるようにするなど、使い方は自由です。

決済の核となる部分を実績のある会社 (PayPal) が行ってくれるのは非常に安心ですし、簡単に使うことができます。
ぜひ、導入をご検討ください。