<?php
/*
 * shopping/order/settlement/MethodEpsilon.class.php
 * 
 * CopyRight(C) 2010 Shopformer Development Team. All Right Reserved
 * URL: http://sourceforge.jp/projects/shopformer/
 * 
 * 
 * Mail: m_nakashima@users.sourceforge.jp
 * Auther: Masanori Nakashima
 * Last Update: 2010-06-18
 */
require_once(dirname(__FILE__).DIRECTORY_SEPARATOR.'MethodBase.class.php');
/**
 * shopping_order_settlement_MethodEpsilon
 * 決済プラグイン: イプシロンクレジットカード決済用クラス
 * 
 * イプシロン株式会社が提供する決済代行サービス、クレジットカード決済用の決済プラグイン実装クラスです。
 * http://www.epsilon.jp/
 * 注文が完了した場合にエラーがなければで注文状態を「新規」、決済状態を「決済済」に設定します。
 * エラーが発生していた場合は注文状態を「無効」、決済状態を「決済エラー」に設定します。
 */
class shopping_order_settlement_MethodEpsilon extends shopping_order_settlement_MethodBase {
	/**
	 * コンストラクタ
	 */
	function shopping_order_settlement_MethodEpsilon() {
		$this->viewName			= 'クレジットカード';
		$this->enable			= false;
		$this->statusReady		= 211;
		$this->statusNomal		= 0;
		$this->statusError		= 211;
		$this->statusSettle		= 101;
		$this->settingValues	= array(
			'contract_code'		=> '',
			'contract_password'	=> '',
			// オーダー情報送信先URL(試験用)
			// 本番環境でご利用の場合は契約時に弊社からお送りするURLに変更してください。
			'order_url'		=> 'http://beta.epsilon.jp/cgi-bin/order/receive_order3.cgi',
			// 決済状態確認用URL
			'getsales_url'	=> 'https://beta.epsilon.jp/client/getsales.cgi',
		);
		$this->settingLabels	= array(
			'contract_code'		=> '契約コード',
			'contract_password'	=> '契約パスワード',
			'order_url'			=> 'オーダー情報送信先URL',
			'getsales_url'		=> '決済状態確認用URL',
		);
		$this->feeType			= 0;
		$this->feeValue			= 0;
		$this->mailTemplateName	= 'thanks';
		$this->mailComment	= "クレジットカードにてお支払いが完了しています。";
		$this->mailOrder	= "クレジットカードにてお支払いが完了しています。\n";
		$this->explanation	= <<< __METHOD_EXPLANATION__
<div>
GMOインターネットグループの決済代行イプシロン株式会社を利用したお支払いです。
クレジットカードのみ対応しています。VISAカード、MASTERカードでお支払いいただけます。
</div>
<div>
<img src="/shopping/images/logo_master.gif" alt="MASTERカード" />
<img src="/shopping/images/logo_visa.gif" alt="VISAカード" />
</div>
<div>
カード情報の入力画面はイプシロン株式会社でおこなって頂きます。当方ではカード情報は一切保持いたしません。
</div>
__METHOD_EXPLANATION__;

		$this->attention	= <<< __METHOD_EXPLANATION__
<div>
決済代行会社イプシロン株式会社の決済用画面に移動します。決済画面でお支払いが完了したらご注文完了となります。
</div>
__METHOD_EXPLANATION__;
	}
	/**
	 * 決済方法名を取得
	 */
	function getMethodName() {
		return 'イプシロン株式会社:クレジットカード決済プラグイン';
	}
	/**
	 * 決済方法固有の番号を取得します。
	 */
	function getMethodNumber() {
		return '21';
	}
	/**
	 * Complete画面が必要か確認します
	 * @return true/false
	 */
	function needComplete(){
		return true;
	}
	/**
	 * Complete処理の画面表示自体が必要か確認
	 * API経由のURLコールの場合はfalseを返してください
	 * 決済プロバイダからのリダイレクト戻りで画面表示が必要な場合はtrueを返してください
	 * @return boolean true/false
	 */
	function needCompleteView(){
		return true;
	}
	/**
	 * 指定された注文情報に対して本決済プラグインを利用可能か確認します
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 利用可能ならtrue/不可ならfalse
	 */
	function enableOnOrder( & $request, & $shoppingOrderObject ) {
		if( !is_null($shoppingOrderObject) ) {
			return true;
		}
		return false;
	}
	/**
	 * execute時の処理
	 */
	function execute( & $request, & $shoppingOrderObject ) {
		// 決済実行日を設定
		$shoppingOrderObject->settle_date	= date('Y-m-d H:i:s');
		// HttpRequestクラス
		spider_Controller::loadClassDefinition('util_HttpRequest');
		$httpRequest	= new util_HttpRequest($this->settingValues['order_url'],'post');
		// 基本情報設定
		$httpRequest->addPostData('contract_code', $this->settingValues['contract_code']);
		// 決済区分はクレジットカードのみで固定
		$httpRequest->addPostData('st_code', '10000-0000-00000');
		// 処理区分は固定で初回課金のみ
		// 処理区分 (1:初回課金 2:登録済み課金 3:登録のみ 4:登録変更 8:月次課金解除 9:退会)
		// 月次課金をご利用にならない場合は1:初回課金をご利用ください。
		// 各処理区分のご利用に関してはCGI設定マニュアルの「処理区分について」を参照してください。
		$httpRequest->addPostData('process_code', '1');
		// 課金区分 (1:一回のみ 2〜10:月次課金)
		$missionCode	= '1';
		$productUnitHash	= $shoppingOrderObject->getProductUnitHash( $request );
		foreach( $productUnitHash as $key => $unitObject ) {
			if( $unitObject->delivery_type >= 110 ) {
				$missionCode	= ( $unitObject->delivery_type - 110 );
				break;
			}
		}
		$httpRequest->addPostData('mission_code', $missionCode);
		$httpRequest->addPostData('xml', '1');

		// 注文固有情報設定
		$memberObject	= $shoppingOrderObject->memberObject;
		$email			= null;
		if( strlen($memberObject->pc_mail) > 0 ) {
			$email	= $memberObject->pc_mail;
		} else if( strlen($memberObject->mb_mail) > 0 ) {
			$email	= $memberObject->mb_mail;
		}
		$orderId = $shoppingOrderObject->order_id;
		$httpRequest->addPostData('order_number', $orderId.date('YmdHis',strtotime($shoppingOrderObject->settle_date)));
		$httpRequest->addPostData('user_id', $orderId);
		$httpRequest->addPostData('user_name', mb_convert_encoding($memberObject->family_name.$memberObject->first_name, 'EUC-JP', 'UTF-8'));
		$httpRequest->addPostData('user_mail_add', $email);
		// 注文商品追加
		$itemNameArray	= array();
		$productUnitHash	= $shoppingOrderObject->getProductUnitHash( $request );
		foreach( $productUnitHash as $key => $unitObject ) {
			array_push($itemNameArray,$unitObject->shoppingProductObject->product_name.$unitObject->unit_name);
		}
		$itemName	= implode(',',$itemNameArray);
		if( mb_strlen($itemName) > 32 ) {
			$itemName	= mb_substr($itemName,0,32);
		}
		$httpRequest->addPostData('item_code', $orderId);
		$httpRequest->addPostData('item_name', mb_convert_encoding($itemName, 'EUC-JP', 'UTF-8'));
		$httpRequest->addPostData('item_price', $shoppingOrderObject->payment_total);
		
		// HTTPリクエスト実行
		$redirectUrl	= null;
		if( $httpRequest->send(null,60) ) {
			// 応答内容XMLの解析
			$responseCode	= $httpRequest->statusCode;
			$responseBody	= trim($httpRequest->responseBody);
			$resultHash		= $this->epsilonConvertXml2Hash( $request, $responseBody );
			// 結果処理
			if( '1' == $resultHash['result'] ) {
				// 正常終了の場合
				$redirectUrl	= rawurldecode($resultHash['redirect']);
				// リダイレクトURLからトランザクションコードを抜き出す
				list( $redirectPage, $paramStrings )	= explode('?',$redirectUrl);
				$paramArray	= explode('&',$paramStrings);
				$paramHash	= array();
				$settleTransactionCode	= '';
				foreach( $paramArray as $paramStr ) {
					list( $key, $val )	= explode('=',$paramStr);
					$key	= trim( $key );
					$val	= trim( $val );
					if( preg_match('/^[tT][rR][aA][nN][sS]\\_[cC][oO][dD][eE]$/',$key) > 0 ) {
						$settleTransactionCode	= $val;
						break;
					}
				}
				if( strlen($settleTransactionCode) == 0 ) {
					$shoppingOrderObject->addColumnError($request,'settle_method_errors','決済は終了しましたがトランザクションコードを確認できませんでした。');
				} else {
					$shoppingOrderObject->setSettleTransactionCode( $request, $val );
				}
			} else {
				// エラーの場合
				$shoppingOrderObject->addColumnError($request,'settle_method_errors','決済を正常に行えませんでした。'.$resultHash['err_code'].':'.$resultHash['err_detail']);
			}
		} else {
			// リクエストエラー
			$shoppingOrderObject->addColumnError($request,'settle_method_errors','決済サーバーが正常な応答を返しませんでした。お手数ですがしばらくたってからやりなおしてください。');
		}
		// 通信ログの記録
		$logMessage	= "OK\r\n";
		if( $request->isError() ) {
			$logMessage	= "NG\r\n".implode("\n",$request->errors);
		}
		$logMessage	.= "\r\n".$responseBody;
		$shoppingOrderObject->settleLogLn( $request, $logMessage );
		// 決済実行日を更新
		$shoppingOrderObject->setSettleDate( $request, $shoppingOrderObject->settle_date );
		if( $request->isError() ) {
			return false;
		} else {
			$request->redirectTo($redirectUrl);
			return true;
		}
	}
	
	/**
	 * complete時にパラメータから判断して指定の注文オブジェクトを作成します。
	 */
	function getCompleteShoppingObject( & $request ) {
		$dbo	= $request->getAttribute('dbo');
		// パラメータを取得
		$transactionCode	= trim(stripslashes($_GET['trans_code']));
		$orderId			= trim(stripslashes($_GET['order_number']));
		$settleResult		= trim(stripslashes($_GET['result']));
		$userId				= trim(stripslashes($_GET['user_id']));
		if( strlen($transactionCode) == 0 ) {
			return null;
		}
		if( strlen($orderId) == 0 ) {
			return null;
		}
		if( strlen($settleResult) == 0 ) {
			return null;
		}
		if( strlen($userId) == 0 ) {
			return null;
		}
		if( preg_match('/^[0-9a-zA-Z\\-\\_\\.]+$/',$orderId) > 0 ) {
			// 注文IDがあるなら読み込み
			$orderId	= substr( $orderId, 0, strlen($orderId)-14 );
			$shoppingOrderObject	= spider_Controller::createObject('shopping_DaoShoppingOrder');
			if( $dbo->loadById( $shoppingOrderObject, $orderId, true ) ) {
				if( $shoppingOrderObject->settle_method == $this->getMethodNumber() ) {
					return $shoppingOrderObject;
				} else {
					return null;
				}
			}	
		}
		return null;
	}
	/**
	 * complete時の処理
	 */
	function settlement( & $request, & $shoppingOrderObject ) {
		$dbo				= $request->getAttribute('dbo');
		$logMessage	= date('Y-m-d H:i:s')."\r\n";
		// パラメータを取得
		$transactionCode	= trim(stripslashes($_GET['trans_code']));
		$orderId			= trim(stripslashes($_GET['order_number']));
		$settleResult		= trim(stripslashes($_GET['result']));
		$userId				= trim(stripslashes($_GET['user_id']));
		if( is_null($shoppingOrderObject) ) {
			return false;
		}
		if( $shoppingOrderObject->settle_transaction_code != $transactionCode
		|| $shoppingOrderObject->order_id != $userId ) {
			$shoppingOrderObject->addColumnError($request,'settle_method_errors','アクセスパラメータが正しくありません。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		$httpRequest	= $this->epsilonGetOrderStatus( $request, $shoppingOrderObject );
		if( false === $httpRequest ) {
			$logMessage	.= "CONFIRM=NG\r\n";
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		} else if( $request->isError() ) {
			// 通信エラーが発生している場合
			$logMessage	.= "CONFIRM=NG\r\n";
			$logMessage	.= "Request Error No=".$httpRequest->errorNumber." ".$httpRequest->errorMessage;
			$shoppingOrderObject->addColumnError($request,'通信エラー番号:'.$httpRequest->errorNumber.' '.$httpRequest->errorMessage);
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		// 通信に成功しているなら結果を取得
		$responseCode	= $httpRequest->statusCode;
		$responseBody	= trim($httpRequest->responseBody);
		$resultHash		= $this->epsilonConvertXml2Hash( $request, $responseBody );
		// 結果処理
		if( $shoppingOrderObject->settle_transaction_code != trim($resultHash['trans_code']) ) {
			// トランザクションコードが一致しない
			$logMessage	.= "CONFIRM=NG\r\n";
			$logMessage	.= "trans_code [".trim($resultHash['trans_code'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済サーバーの応答と注文内容に相違が発生しています。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( $shoppingOrderObject->payment_total != trim($resultHash['item_price']) ) {
			// 金額が一致しない
			$logMessage	.= "CONFIRM=NG\r\n";
			$logMessage	.= "order price [".trim($resultHash['item_price'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済サーバーの応答と注文金額に相違が発生しています。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( '1' != $resultHash['state'] ) {
			// 支払済みでない：クレジットカードの場合はエラーにする
			$logMessage	.= "CONFIRM=NG\r\n";
			$logMessage	.= "epsilon state [".trim($resultHash['state'])."]!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' イプシロンの決済状態が支払済みになっていません。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		// 支払済
		$logMessage	.= "CONFIRM=OK\r\n";
		$logMessage	.= "SETTLE STATUS=101\r\n".$responseBody;
		$shoppingOrderObject->setSettlePrice($request,trim($resultHash['item_price']));
		$shoppingOrderObject->settleLogLn( $request, $logMessage );
		return true;
	}
	/**
	 * 妥当性検査時の処理
	 */
	function validate( & $request ) {
		$this->enable	= true;
		$isError	= false;
		$this->settingValues['contract_code']	= mb_convert_kana($this->settingValues['contract_code'],'a');
		if( strlen(trim($this->settingValues['contract_code'])) == 0 ) {
			$this->enable	= false;
		} else if( preg_match('/^[0-9]+$/',$this->settingValues['contract_code']) == 0 ) {
			$request->addLocaledError('shopping.error.plugin.epsilon.setting.invalidcode',SPIDER_LOG_LEVEL_ERROR,array());
		}
		if( $this->enable ) {
			if( 0 != $this->feeType ) {
				$request->addLocaledError('shopping.error.plugin.epsilon.setting.nocharge',SPIDER_LOG_LEVEL_ERROR,array());
				$isError	= false;
			} else {
				$this->feeType	= 0;
				$this->feeValue	= 0;
			}
			if( preg_match('/^[0-9a-zA-Z]+$/',$this->settingValues['contract_password']) == 0 ) {
				$request->addLocaledError('shopping.error.plugin.epsilon.setting.invalidpasswd',SPIDER_LOG_LEVEL_ERROR,array());
				$isError	= false;
			}
			if( strlen(trim($this->settingValues['order_url'])) == 0 ) {
				$request->addLocaledError('shopping.error.plugin.epsilon.setting.requireurl1',SPIDER_LOG_LEVEL_ERROR,array());
				$isError	= false;
			}
			if( strlen(trim($this->settingValues['getsales_url'])) == 0 ) {
				$request->addLocaledError('shopping.error.plugin.epsilon.setting.requireurl2',SPIDER_LOG_LEVEL_ERROR,array());
				$isError	= false;
			}
			if( $isError ) {
				$this->enable	= false;
			}
		}
		if( $request->isError() ) {
			return false;
		} else {
			return true;
		}
	}
	/**
	 * 決済状態同期が有効か確認する抽象メソッド
	 * 注文オブジェクトを引数にとって外部サービスの決済状態と状態を同期します
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 成功したらtrue/失敗したらfalse
	 */
	function enableSynchronize( $shoppingOrderObject = null ) {
		return true;
	}
	/**
	 * 決済状態同期が有効か確認する抽象メソッド
	 * 注文オブジェクトを引数にとって外部サービスの決済状態と状態を同期します
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 成功したらtrue/失敗したらfalse
	 */
	function enableChangeAmount( $shoppingOrderObject = null ) {
		return false;
	}
	/**
	 * 決済状態同期のメソッド実装
	 * 注文オブジェクトを引数にとって外部サービスの決済状態と状態を同期します
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 成功したらtrue/失敗したらfalse
	 */
	function settleSynchronize( & $request, & $shoppingOrderObject ) {
		$dbo		= $request->getAttribute('dbo');
		$logMessage	= date('Y-m-d H:i:s')."\r\n";
		if( !is_object($shoppingOrderObject) && strlen($shoppingOrderObject->order_id) == 0 ) {
			return true;
		}
		$httpRequest	= $this->epsilonGetOrderStatus( $request, $shoppingOrderObject );
		if( false === $httpRequest ) {
			$shoppingOrderObject->addColumnError($request,'決済状態の同期リクエストに失敗しました。');
			return false;
		} else if( $request->isError() ) {
			$shoppingOrderObject->addColumnError($request,'決済状態の同期リクエストに失敗しました 通信エラー番号:'.$httpRequest->errorNumber.' '.$httpRequest->errorMessage);
			return false;
		}
		// 通信に成功しているなら結果を取得
		$responseCode	= $httpRequest->statusCode;
		$responseBody	= trim($httpRequest->responseBody);
		$resultHash		= $this->epsilonConvertXml2Hash( $request, $responseBody );
		// 結果処理
		if( $shoppingOrderObject->settle_transaction_code != trim($resultHash['trans_code']) ) {
			// トランザクションコードが一致しない
			$logMessage	.= "SYNCHRONIZE CONFIRM=NG\r\n";
			$logMessage	.= "trans_code [".trim($resultHash['trans_code'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済サーバーの応答と注文内容に相違が発生しています。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( $shoppingOrderObject->payment_total != trim($resultHash['item_price']) ) {
			// 金額が一致しない
			$logMessage	.= "SYNCHRONIZE CONFIRM=NG\r\n";
			$logMessage	.= "order price [".trim($resultHash['item_price'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済サーバーの応答と注文金額に相違が発生しています。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( '1' != $resultHash['state'] ) {
			// 支払済みでない：クレジットカードの場合はエラーにする
			$shoppingOrderObject->status_settle = 0;
			$logMessage	.= "SYNCHRONIZE CONFIRM=NG\r\n";
			$logMessage	.= "epsilon state [".trim($resultHash['state'])."]!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' イプシロンの決済状態が支払済みになっていません。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		// 支払済
		if( $shoppingOrderObject->status_settle != 101 ) {
			// 注文の決済状態が支払済みでないなら支払済みに変更する
			$shoppingOrderObject->setSettlePrice($request,trim($resultHash['item_price']));
			$shoppingOrderObject->status_settle = 101;
			$logMessage	.= "SYNCHRONIZE CONFIRM=OK\r\n";
			$logMessage	.= "SETTLE STATUS=101\r\n".$responseBody;
			$shoppingOrderObject->settleLogLn( $request, $logMessage );
		}
		// 決済ログと状態更新
		$sql	= 'UPDATE '.TABLE_NAME_SHOPPING_ORDER.' SET '
			.' status_settle='.$shoppingOrderObject->status_settle
			.', updated_date=NOW()'
			.' WHERE order_number='.$shoppingOrderObject->order_number;
		if( $dbo->query($sql) ){
			$shoppingOrderObject->setSettlePrice($request,trim($resultHash['item_price']));
		} else {
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済状態のデータベース変更に失敗しました。');
			return false;
		}
		return true;
	}
	/**
	 * 決済キャンセルメソッド実装
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 成功したらtrue/失敗したらfalse
	 */
	function settleCancel( & $request, & $shoppingOrderObject ){
		// 同期して決済がキャンセルになっているか確認し有効なままだとキャンセルできないようにする
		$dbo		= $request->getAttribute('dbo');
		$logMessage	= date('Y-m-d H:i:s')."\r\n";
		if( !is_object($shoppingOrderObject) && strlen($shoppingOrderObject->order_id) == 0 ) {
			return true;
		}
		$httpRequest	= $this->epsilonGetOrderStatus( $request, $shoppingOrderObject );
		if( false === $httpRequest ) {
			$shoppingOrderObject->addColumnError($request,'決済状態の同期リクエストに失敗しました。');
			return false;
		} else if( $request->isError() ) {
			$shoppingOrderObject->addColumnError($request,'決済状態の同期リクエストに失敗しました 通信エラー番号:'.$httpRequest->errorNumber.' '.$httpRequest->errorMessage);
			return false;
		}
		// 通信に成功しているなら結果を取得
		$responseCode	= $httpRequest->statusCode;
		$responseBody	= trim($httpRequest->responseBody);
		$resultHash		= $this->epsilonConvertXml2Hash( $request, $responseBody );
		// 結果処理
		if( $shoppingOrderObject->settle_transaction_code != trim($resultHash['trans_code']) ) {
			// トランザクションコードが一致しない
			$logMessage	.= "CANCEL CONFIRM=NG\r\n";
			$logMessage	.= "trans_code [".trim($resultHash['trans_code'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済サーバーの応答と注文内容に相違が発生しています。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( '1' == $resultHash['state'] ) {
			// 支払済みのままの場合はエラーにする
			$logMessage	.= "CANCEL CONFIRM=NG\r\n";
			$logMessage	.= "epsilon state [".trim($resultHash['state'])."]!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' イプシロンクレジットカード決済でキャンセルするには先にイプシロン管理画面で決済をキャンセルしてください。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		// 支払済
		if( $shoppingOrderObject->status_settle != 0 ) {
			// 注文の決済状態が支払済みなら未決済に変更する
			$shoppingOrderObject->setSettlePrice($request,trim($resultHash['item_price']));
			$shoppingOrderObject->status_settle = 0;
			$logMessage	.= "CANCEL CONFIRM=OK\r\n";
			$logMessage	.= "SETTLE STATUS=101\r\n".$responseBody;
			$shoppingOrderObject->settleLogLn( $request, $logMessage );
		}
		// 決済ログと状態更新
		$sql	= 'UPDATE '.TABLE_NAME_SHOPPING_ORDER.' SET '
			.' status_settle=0'
			.', updated_date=NOW()'
			.' WHERE order_number='.$shoppingOrderObject->order_number;
		if( $dbo->query($sql) ){
		} else {
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済状態のデータベース変更に失敗しました。');
			return false;
		}
		return true;
	}
	/**
	 * 決済金額変更メソッド実装
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 成功したらtrue/失敗したらfalse
	 */
	function settleChangeAmount( & $request, & $shoppingOrderObject ){
		$dbo		= $request->getAttribute('dbo');
		$logMessage	= date('Y-m-d H:i:s')."\r\n";
		if( !is_object($shoppingOrderObject) && strlen($shoppingOrderObject->order_id) == 0 ) {
			return true;
		}
		$httpRequest	= $this->epsilonGetOrderStatus( $request, $shoppingOrderObject );
		if( false === $httpRequest ) {
			$shoppingOrderObject->addColumnError($request,'決済状態の同期リクエストに失敗しました。');
			return false;
		} else if( $request->isError() ) {
			$shoppingOrderObject->addColumnError($request,'決済状態の同期リクエストに失敗しました 通信エラー番号:'.$httpRequest->errorNumber.' '.$httpRequest->errorMessage);
			return false;
		}
		// 通信に成功しているなら結果を取得
		$responseCode	= $httpRequest->statusCode;
		$responseBody	= trim($httpRequest->responseBody);
		$resultHash		= $this->epsilonConvertXml2Hash( $request, $responseBody );
		// 結果処理
		if( $shoppingOrderObject->settle_transaction_code != trim($resultHash['trans_code']) ) {
			// トランザクションコードが一致しない
			$logMessage	.= "CHANGE AMOUNT CONFIRM=NG\r\n";
			$logMessage	.= "trans_code [".trim($resultHash['trans_code'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' 決済サーバーの応答と注文内容に相違が発生しています。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( $shoppingOrderObject->payment_total != trim($resultHash['item_price']) ) {
			// 金額が一致しない
			$logMessage	.= "CHANGE AMOUNT CONFIRM=NG\r\n";
			$logMessage	.= "order price [".trim($resultHash['item_price'])."] is invalid value!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' イプシロンクレジットカード決済で金額変更するには先にイプシロン管理画面で決済金額変更してください。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		if( '1' != $resultHash['state'] ) {
			// 支払済みでない：クレジットカードの場合はエラーにする
			$shoppingOrderObject->status_settle = 0;
			$logMessage	.= "CHANGE AMOUNT CONFIRM=NG\r\n";
			$logMessage	.= "epsilon state [".trim($resultHash['state'])."]!";
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.' イプシロンの決済状態が支払済みになっていません。');
			$shoppingOrderObject->settleLogLn( $request, $logMessage."\r\n".implode("\n",$request->errors) );
			return false;
		}
		// 支払済
		if( $shoppingOrderObject->status_settle != 101 ) {
			// 注文の決済状態が支払済みでないなら支払済みに変更する
			$shoppingOrderObject->setSettlePrice($request,trim($resultHash['item_price']));
			$shoppingOrderObject->status_settle = 101;
			$logMessage	.= "CHANGE AMOUNT CONFIRM=OK\r\n";
			$logMessage	.= "SETTLE STATUS=101\r\n".$responseBody;
			$shoppingOrderObject->settleLogLn( $request, $logMessage );
		}
		// 決済ログと状態更新
		$sql	= 'UPDATE '.TABLE_NAME_SHOPPING_ORDER.' SET '
			.' status_settle='.$shoppingOrderObject->status_settle
			.', updated_date=NOW()'
			.' WHERE order_number='.$shoppingOrderObject->order_number;
		if( $dbo->query($sql) ){
		} else {
			$shoppingOrderObject->addColumnError($request,'注文番号:'.$shoppingOrderObject->order_id.'  決済状態のデータベース変更に失敗しました。');
			return false;
		}
		return true;
	}
	/**
	 * 決済復帰メソッド実装
	 * @param $request spider_HttpRequestインスタンス参照
	 * @param $shoppingOrderObject shopping_DaoShoppingOrderインスタンス参照
	 * @return boolean 成功したらtrue/失敗したらfalse
	 */
	function settleRecover( & $request, & $shoppingOrderObject ){
		$shoppingOrderObject->addColumnError($request,'イプシロン株式会社カード決済は注文キャンセル削除からの復帰に対応していません。新しく注文を作成してください。');
		return false;
	}
	/**
	 * イプシロン固有メソッド: 指定の注文オブジェクトの支払状態を元に状態確認CGIに問い合わせします
	 */
	function epsilonGetOrderStatus( & $request, & $shoppingOrderObject ) {
		if( strlen($shoppingOrderObject->order_id) > 0 ) {
			// Basic認証
			$headerParms	= array();
			$headerParms['Authorization']	= 'Basic '.base64_encode($this->settingValues['contract_code']
				.':'.$this->settingValues['contract_password']);
			// HttpRequestクラス
			spider_Controller::loadClassDefinition('util_HttpRequest');
			$httpRequest	= new util_HttpRequest($this->settingValues['getsales_url'],'post',$headerParms);
			$httpRequest->addPostData('order_number', $shoppingOrderObject->order_id.date('YmdHis',strtotime($shoppingOrderObject->settle_date)));
			// HTTPリクエスト実行
			if( $httpRequest->send(null,60) ) {
				// 問い合わせ成功の場合
				return $httpRequest;
			} else {
				// 問い合わせ失敗の場合
				$shoppingOrderObject->addColumnError($request,'イプシロン決済サーバーへの状態確認問い合わせに失敗しました。');
				return $httpRequest;
			}
		} else {
			$shoppingOrderObject->addColumnError($request,'注文情報の指定が正しくありません。');
			return false;
		}
	}
	/**
	 * イプシロン固有メソッド: DOMノードから結果ハッシュを生成して返します
	 */
	function epsilonConvertXml2Hash( & $request, $responseBody ) {
		// 本文解析
		$responseBody	= str_replace("x-sjis-cp932", "EUC-JP", $responseBody);
		$responseBody	= str_replace("\r\n","\n",$responseBody);
		$responseBody	= str_replace("\r","\n",$responseBody);
		$xmlNodeObject	= simplexml_load_string($responseBody);
		// DOMノードの結果をまとめる
		$resultHash		= array();
		foreach( $xmlNodeObject->result as $key => $node ) {
			$attributes	= $node->attributes();
			foreach( $attributes as $key => $value ) {
				$resultHash[$key]	= mb_convert_encoding(urldecode($value),'UTF-8','auto');
			}
		}
		return $resultHash;
	}
}
?>