Hatena::ブログ(Diary)

小人さんの妄想 このページをアンテナに追加 RSSフィード Twitter

2009-10-13

PayPalでダウンロード販売する法

個人でちょっとしたダウンロード販売を行いたいと思ったとき、便利な決済サービスが PayPalです。

・初期費用、登録審査など一切不要。個人でも簡単に登録できる。

・カード決済の手数料が安い。他のカード決済サービスと比較しても、かなり安い部類。

PayPalを使えば、誰でも簡単にカード決済付きネットショップが開設できる!

そう思って PayPalの仕組みを調べてみたところ、やはり実現することが難しい機能も幾つかありました。

PayPalの使い方については、あちこちのサイトにも書かれているので、ここでは要望の割に実現するのが難しいであろう、

 ・支払いデータ転送(PDT)

 ・即時支払い通知(IPN)

の使い方をメモしておきます。

いずれもの機能も、活用するためには PHP等で処理を行うページを作る必要があります。


※ここで説明している仕組み(IPN)を実現したのが「らくらくダウンロード」というサイトです。

※ >> http://www.easypay.jp/

※ 無料で利用できるので、まずはお試しあれ。(2010/07/01追記)

※また、誰でも簡単に設置できるカートも用意しました。

* jCart日本語版 >> id:rikunora2:20100723

※ この「らくらくダウンロード+jCart」で、本当に、誰でも、無料で、お手軽に

ダウンロードショッピングサイトができちゃいます! (2010/07/23追記)


PayPalを用いてダウンロード販売サイトを作ろうと思ったとき、まず直面するのは、

PayPalで決済を済ませたお客さんを、ダウンロードのページに導くことができない」

という問題です。

通常、インターネット上のダウンロード販売は、こんな流れになっています。

 [1:買い物かご] -> [2:カード決済] -> [3:ダウンロードページ]

PayPalの場合、[1:買い物かご]は自分のホームページ、[2:カード決済]はPayPalサイト、ということになるでしょう。

問題となるのは、PayPalの場合、[2:カード決済] -> [3:ダウンロードページ] の道筋をどうするか、ということです。

PayPalの決済を終えた先に、「ありがとうございました」ページを用意することまではできます。

しかし、その際に、何を買った、どのお客さんが「ありがとうございました」ページに来たのか、簡単に区別することができないのです。

これだと(自動的な方法で)ダウンロード販売を実現することができません。


------------------------------------------------------------------------

* PDT -- Payment Data Transfer

ダウンロード販売には欠かせない、[2:カード決済]で決済したお客さんの情報を [3:ダウンロードページ] にまで持ってくる機能、

それが「支払いデータ転送(PDT)」です。

通常、PayPalアカウントを作った直後の状態では、この PDT機能は有効になっていません。

PDTを有効にするには、PayPalの管理画面にログインして、

 [マイアカウント]->[個人設定]->[販売の設定]->[ウェブ ペイメントの設定]

に行って、次の設定を行います。

・自動復帰: オン

・復帰URL: ここに[3:ダウンロードページ]となるURLを入力します。

 たぶん https://hoge.moge.co.jp/download.php みたいになると思います。

・支払いデータ転送: オン

「支払いデータ転送」の下に、IDトークン: という長い暗号のような文字列が出ています。

この「IDトークン」は、DTPで不正なアクセスを拒否するために使用します。


肝心のダウンロードページですが、PayPalからの決済情報を受け取るサンプルが、以下のURLに用意されています。

* Code Samples -- Payment Data Transfer

>> https://www.paypal.com/us/cgi-bin/webscr?cmd=p/xcl/rec/pdt-code

このサンプルを元に開発するのが早いと思います。

サンプルとほとんど同じものですが、以下に私が試したPHPソースを張っておきます。

//	決済完了、ダウンロード
//	PDTの仕組みを使ってPayPal決済情報を受け取る.
//	内容については責任持たないので、利用は自己責任で。

// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-synch';

$tx_token = $_GET['tx'];
$auth_token = "* サイトで発行されるIDトークンをここに貼り付ける *";
$req .= "&tx=$tx_token&at=$auth_token";

// post back to PayPal system to validate
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30);	// テストサイト
// $fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);	// 本番
// $fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
	// できれば HTTPS にした方が、セキュリティが高まる.

if (!$fp) {
// HTTP ERROR
	echo "ERROR: HTTP error, [" . $errno . "] " . $errstr . "\n";
	exit(1);
}

fputs ($fp, $header . $req);

// read the body data
$res = '';
$headerdone = false;
while (!feof($fp)) {
	$line = fgets ($fp, 1024);
	if (strcmp($line, "\r\n") == 0) {
		// read the header
		$headerdone = true;
	}
	else if ($headerdone) {
		// header has been read. now read the contents
		$res .= $line;
	}
}

// parse the data
$lines = explode("\n", $res);
$keyarray = array();
if (strcmp ($lines[0], "SUCCESS") == 0) {
	for ($i=1; $i<count($lines);$i++){
		list($key,$val) = explode("=", $lines[$i]);
		$keyarray[urldecode($key)] = urldecode($val);
	}
	// check the payment_status is Completed
	// check that txn_id has not been previously processed
	// check that receiver_email is your Primary PayPal email
	// check that payment_amount/payment_currency are correct
	// process payment
	$firstname = $keyarray['first_name'];
	$lastname = $keyarray['last_name'];
	$itemname = $keyarray['item_name'];
	$amount = $keyarray['payment_gross'];

?>
<html>
<body>
	毎度ありがとうございました。
print $firstname .", ". $lastname . ", ". $itemname .", ". $amount ."
\n"; var_dump( $keyarray ); // どんなパラメータが来るのか、ダンプしてみる. ?>; </body> </html> } // END "SUCCESS" else if (strcmp ($lines[0], "FAIL") == 0) { // log for manual investigation print "FAIL.\n"; } fclose ($fp); }


------------------------------------------------------------------------

* IPN -- Instant Payment Notification

とりあえず、上の PDTダウンロード販売は実現できるのですが、

これだけだとお客さんがダウンロードに失敗したときや、決済の直後にブラウザを切ってしまい、

ダウンロードページに行かなかったときに、連絡を行うことができません。

そこで、たとえお客さんが途中でブラウザを切ってしまっても、確実に決済完了した信号を受け取る仕組みが必要となります。

それが、「即時支払い通知(IPN)」です。

IPN というのは、お客さんが PayPalの決済を行ったことを、直接ショップにお知らせする機能です。

* 即時支払い通知(IPN) Instant Payment Notifications

>> https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro

なので、IPNのお知らせを自動的に受け取れば、次のような流れでダウンロード販売が実現できます。

お客さん [1:買い物かご] -> [2:カード決済] -> (ここでお客さんがブラウザを閉じた)

ショップ ->[4: IPNお知らせを受信] -> [5: お客さんにメールを送る]

お客さん ->[6: メールを受け取る] -> [7: メールの指示に従って、ダウンロードページへ]

それでは、[4: IPNお知らせを受信]は、どのようにすれば良いのでしょうか。

IPNお知らせは、http、つまり Webへのアクセスという形でやってきます。

なので、IPNお知らせを受信するための、専用のWebページを作る必要があります。

このページに、IPNお知らせを受け取るための PHPで書かれたサンプルが用意されていますので、

まずはこのサンプルをコピーしてきて修正するのが早道でしょう。

* Implementing an IPN Listener

>> https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_admin_IPNImplementation

 [4: IPNお知らせを受信] -> [5: お客さんにメールを送る]

というところは PHPなどで自作する必要があります。


サンプルとほとんど一緒なんですけど、以下に、IPNお知らせ受信ページのPHPソースを張っておきます。

以下のコードでは、受信した情報をログファイルに書き出しています。

//	PayPalのIPN機能を受けるハンドラー
//	これによって決済完了を知ることができるのだ.
//	内容については責任持たないので、利用は自己責任で。

define("IPNLOGFILE", "* ここにログファイル名を書く *");

$PAYPAL_SITE = "www.paypal.com";	// 本番サイト
// $PAYPAL_SITE = "www.sandbox.paypal.com";	// テストサイト

// Read the post from PayPal and add 'cmd' 
$req = 'cmd=_notify-validate'; 
if(function_exists('get_magic_quotes_gpc')){
	$get_magic_quotes_exits = true;
}
// Handle escape characters, which depends on setting of magic quotes 
// POSTで受けた値を、そのまま GET形式に書き直す => これをPayPal側のサイトに返す.
foreach ($_POST as $key => $value){
	if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1){
		$value = urlencode(stripslashes($value)); 
	} else { 
		$value = urlencode($value); 
	}
	$req .= "&$key=$value";  
}

// Open LogFile
$log = fopen( IPNLOGFILE, "a");	// 追記オープン
if( ! $log ){
	print "ERROR: Open Log File, " . IPNLOGFILE ;
	exit(1);
}

// Post back to PayPal to validate 
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n"; 
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; 
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n"; 

$fp = fsockopen( $PAYPAL_SITE, 80, $errno, $errstr, 30 );
	// PAYPAL_SITE の 80 番ポートに接続、タイムアウトを30秒に設定
	// エラーがあれば $errno, $errstr に入る。
// Process validation from PayPal 
if (! $fp) { // HTTP ERROR  
	fwrite( $log, "ERROR: Cannot open ". $PAYPAL_SITE . " [". $errno . "] " . $errstr );
	exit(1);
}

fputs( $fp, $header . $req );	// こちらから応答を返す.

// そして、その結果を待つ.
while( ! feof( $fp ) ) {
	
	$res = fgets( $fp, 1024 );	// その応答結果を受け取る.
	
	// 戻りは "VERIFIED" か "INVALID" のいずれかになる.
	// 成功の場合.
	if (strcmp ($res, "VERIFIED") == 0) { 
		// * TODO * ここでいろんなチェックを行うこと.
		// Check the payment_status is Completed 
		// Check that txn_id has not been previously processed 
		// Check that receiver_email is your Primary PayPal email 
		// Check that payment_amount/payment_currency are correct 
		
		// POSTパラメータを書き出す.
		$text = "";
		foreach ($_POST as $key => $value){ 
			$text .= $key . " = " .$value ."\n"; 
		}
		fwrite( $log, $text );
	}
	// 失敗の場合、とにかく値をログに書き出す.
	else if (strcmp ($res, "INVALID") == 0) { 
		// POSTパラメータを書き出す.
		$text = "";
		foreach ($_POST as $key => $value){ 
			$text .= $key . " = " .$value ."\n"; 
		}
		fwrite( $log, $text );
	}
//	else{ // 内容以外の行は読み飛ばす.
//	}
}
fclose( $fp );
fclose( $log );
exit( 0 );


上の IPNお知らせを受信ページが完成したら、PayPalから受信ページに向かって信号を送るように設定します。

PayPalの管理画面にログインして、

 [マイアカウント]->[個人設定]->[販売の設定]->[即時支払い通知の設定]

に行って、「通知URL」という項目に、上で作った IPNお知らせを受信ページのURLを入力します。

そして「IPNメッセージを受信する(有効)」にチェックを入れましょう。

これで、PayPal上で決済が行われるたびに、自作のIPNお知らせを受信ページに向かって、お知らせが送られてくるはずです。


------------------------------------------------------------------------

* 開発者用サイト -- Sandbox

上のような仕組みを実際に試してみるには、クレジットカードを使って何かお買い物をしなければなりません。

それはやってられないよ、というときのために、PayPalでは開発専用のテストサイトを用意しています。

* PayPal sandbox -- 開発者用サイト

>> https://developer.paypal.com/

開発するには、こちらのサイトのアカウントを取得しましょう。


IPN の仕組みをテストするには、

[左側メニュー] -> [Test Tools] -> [Instant Payment Notification (IPN) Simulator.]

ここから、IPNお知らせ信号を擬似的に送ることができます。

[IPN handler URL] に、自作のお知らせを受信ページを入力して、

[Transaction type] は、適当なもの、eCheck - complete とかを選んで、後は見よう見まねでやってください。


PDT, IPNの情報がまとまって書いてあるページは、意外に少なかった。(探し方が悪かったのかもしれないが)

例えば、ここに載っていた。

* PaypalPDT」「IPN」を使った決済とバックエンドの統合(2)

>> http://blog.katsuma.tv/2007/06/paypal_pdt_ipn_code.html


salon-cafesalon-cafe 2010/03/14 11:48 ありがとうございます!
助かりました。

rikunorarikunora 2010/03/15 12:46 お役に立てたようで嬉しいです。
PayPalって、サービス内容の割には日本で今ひとつ活用されていない気がします。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/rikunora/20091013/p1