最近使用CodeIgniter寫商城系統的代碼需要集成支付寶即時到賬功能進去
看了下支付寶官方發佈的代碼主要包括2部分
第一部分:
支付寶外部服務接口控制類
alipay_service.php
付款過程中服務器通知類
alipay_notify.php
以及支付寶接口公用函數
alipay_function.php
第二部分:
支付寶主動通知調用頁面(服務器異步通知頁面)
notify_url.php
付完款後跳轉的頁面(頁面跳轉同步通知頁面)
return_url.php
其次就是alipay的配置文件
alipay_config.php
下面是我按codeigniter框架的思路構建:
1.創建自己的類庫到codeigniter, 同樣是兩個類Alipay_service和Alipay_notify
2.創建alipay模型,主要功能是構建支付提交表單,通知函數,及數據庫更新支付寶交易號功能
3.創建alipay配置文件
4.最後就是調用了,控制層調用alipay模型就可以了
使用示例:
控制層代碼
application/controllers/product.php 代碼片段
//生成在線支付鏈接
$this
->load->model(
'alipay_model'
);
$data
[
'alipay_form'
] =
$this
->alipay_model->build_form(
$data
[
'order_sn'
],
$data
[
'product_name'
],
$data
[
'total'
]);
$this
->load->view(
'product/quick_buy'
,
$data
);
上面代碼的意思是給alipay_model的build_form函數傳入參數訂單號,產品名,金額
顯示層代碼
application/views/product/quick_buy.php 代碼片段
<?php
if
(
'online'
==
$payment
) {
echo
$alipay_form
;
}?>
application/libraries/alipay/Alipay_service.php
<span style="font-size:14px;"><?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Codeigiter的支付寶外部服務接口控制
* @author http://blog.runphp.net
* @version 0.1
*/
require_once("alipay_function.php");
class Alipay_service {
var $gateway; //網關地址
var $_key; //安全校驗碼
var $mysign; //簽名結果
var $sign_type; //簽名類型
var $parameter; //需要簽名的參數數組
var $_input_charset; //字符編碼格式
function __construct($parameter)
{
self::Alipay_service(
$parameter['parameter'],
$parameter['key'],
$parameter['sign_type']
);
}
/**
* 構造函數
* 從配置文件及入口文件中初始化變量
* @param array $parameter 需要簽名的參數數組
* @param string $key 安全校驗碼
* @param string $sign_type 簽名類型
*/
public function Alipay_service($parameter, $key, $sign_type)
{
$this->gateway = "https://www.alipay.com/cooperate/gateway.do?";
$this->_key = $key;
$this->sign_type = $sign_type;
$this->parameter = para_filter($parameter);
//設定_input_charset的值,爲空值的情況下默認爲GBK
if($this->parameter['_input_charset'] == '')
$this->parameter['_input_charset'] = 'GBK';
$this->_input_charset = $this->parameter['_input_charset'];
//獲得簽名結果
$sort_array = arg_sort($this->parameter); //得到從字母a到z排序後的簽名參數數組
$this->mysign = build_mysign($sort_array,$this->_key,$this->sign_type);
}
/**
*
* Enter description here ...
*/
function build_form() {
//GET方式傳遞
$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gateway."_input_charset=".$this->parameter['_input_charset']."' method='get'>";
//POST方式傳遞(GET與POST二必選一)
//$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gateway."_input_charset=".$this->parameter['_input_charset']."' method='post'>";
while (list ($key, $val) = each ($this->parameter)) {
$sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
}
$sHtml = $sHtml."<input type='hidden' name='sign' value='".$this->mysign."'/>";
$sHtml = $sHtml."<input type='hidden' name='sign_type' value='".$this->sign_type."'/>";
//submit按鈕控件請不要含有name屬性
$sHtml = $sHtml."<input type='submit' value='支付寶確認付款'></form>";
//$sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
return $sHtml;
}
}</span>
application/libraries/alipay/Alipay_notify.php
<span style="font-size:14px;"><?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Codeigiter付款過程中服務器通知類
* @author http://blog.runphp.net
* @version 0.1
*/
require_once("alipay_function.php");
class Alipay_notify {
var $gateway; //網關地址
var $_key; //安全校驗碼
var $partner; //合作伙伴ID
var $sign_type; //簽名方式 系統默認
var $mysign; //簽名結果
var $_input_charset; //字符編碼格式
var $transport; //訪問模式
function __construct($parameter)
{
self::Alipay_notify(
$parameter['partner'],
$parameter['key'],
$parameter['sign_type'],
$parameter['_input_charset'],
$parameter['transport']
);
}
/**構造函數
*從配置文件中初始化變量
*$partner 合作身份者ID
*$key 安全校驗碼
*$sign_type 簽名類型
*$_input_charset 字符編碼格式
*$transport 訪問模式
*/
function Alipay_notify($partner,$key,$sign_type,$_input_charset = "GBK",$transport= "https") {
$this->transport = $transport;
if($this->transport == "https") {
$this->gateway = "https://www.alipay.com/cooperate/gateway.do?";
}else {
$this->gateway = "http://notify.alipay.com/trade/notify_query.do?";
}
$this->partner = $partner;
$this->_key = $key;
$this->mysign = "";
$this->sign_type = $sign_type;
$this->_input_charset = $_input_charset;
}
/********************************************************************************/
/**對notify_url的認證
*返回的驗證結果:true/false
*/
function notify_verify() {
//獲取遠程服務器ATN結果,驗證是否是支付寶服務器發來的請求
if($this->transport == "https") {
$veryfy_url = $this->gateway. "service=notify_verify" ."&partner=" .$this->partner. "¬ify_id=".$_POST["notify_id"];
} else {
$veryfy_url = $this->gateway. "partner=".$this->partner."¬ify_id=".$_POST["notify_id"];
}
$veryfy_result = $this->get_verify($veryfy_url);
//生成簽名結果
if(empty($_POST)) { //判斷POST來的數組是否爲空
return false;
}
else {
$post = para_filter($_POST); //對所有POST返回的參數去空
$sort_post = arg_sort($post); //對所有POST反饋回來的數據排序
$this->mysign = build_mysign($sort_post,$this->_key,$this->sign_type); //生成簽名結果
//寫日誌記錄
log_result("veryfy_result=".$veryfy_result."\n notify_url_log:sign=".$_POST["sign"]."&mysign=".$this->mysign.",".create_linkstring($sort_post));
//判斷veryfy_result是否爲ture,生成的簽名結果mysign與獲得的簽名結果sign是否一致
//$veryfy_result的結果不是true,與服務器設置問題、合作身份者ID、notify_id一分鐘失效有關
//mysign與sign不等,與安全校驗碼、請求時的參數格式(如:帶自定義參數等)、編碼格式有關
if (preg_match("/true$/i",$veryfy_result) && $this->mysign == $_POST["sign"]) {
return true;
} else {
return false;
}
}
}
/********************************************************************************/
/**對return_url的認證
*return 驗證結果:true/false
*/
function return_verify() {
//獲取遠程服務器ATN結果,驗證是否是支付寶服務器發來的請求
if($this->transport == "https") {
$veryfy_url = $this->gateway. "service=notify_verify" ."&partner=" .$this->partner. "¬ify_id=".$_GET["notify_id"];
} else {
$veryfy_url = $this->gateway. "partner=".$this->partner."¬ify_id=".$_GET["notify_id"];
}
$veryfy_result = $this->get_verify($veryfy_url);
//生成簽名結果
if(empty($_GET)) { //判斷GET來的數組是否爲空
return false;
}
else {
$get = para_filter($_GET); //對所有GET反饋回來的數據去空
$sort_get = arg_sort($get); //對所有GET反饋回來的數據排序
$this->mysign = build_mysign($sort_get,$this->_key,$this->sign_type); //生成簽名結果
//寫日誌記錄
//log_result("veryfy_result=".$veryfy_result."\n return_url_log:sign=".$_GET["sign"]."&mysign=".$this->mysign."&".create_linkstring($sort_get));
//判斷veryfy_result是否爲ture,生成的簽名結果mysign與獲得的簽名結果sign是否一致
//$veryfy_result的結果不是true,與服務器設置問題、合作身份者ID、notify_id一分鐘失效有關
//mysign與sign不等,與安全校驗碼、請求時的參數格式(如:帶自定義參數等)、編碼格式有關
if (preg_match("/true$/i",$veryfy_result) && $this->mysign == $_GET["sign"]) {
return true;
}else {
return false;
}
}
}
/********************************************************************************/
/**獲取遠程服務器ATN結果
*$url 指定URL路徑地址
*return 服務器ATN結果集
*/
function get_verify($url,$time_out = "60") {
$urlarr = parse_url($url);
$errno = "";
$errstr = "";
$transports = "";
if($urlarr["scheme"] == "https") {
$transports = "ssl://";
$urlarr["port"] = "443";
} else {
$transports = "tcp://";
$urlarr["port"] = "80";
}
$fp=@fsockopen($transports . $urlarr['host'],$urlarr['port'],$errno,$errstr,$time_out);
if(!$fp) {
die("ERROR: $errno - $errstr<br />\n");
} else {
fputs($fp, "POST ".$urlarr["path"]." HTTP/1.1\r\n");
fputs($fp, "Host: ".$urlarr["host"]."\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ".strlen($urlarr["query"])."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $urlarr["query"] . "\r\n\r\n");
while(!feof($fp)) {
$info[]=@fgets($fp, 1024);
}
fclose($fp);
$info = implode(",",$info);
return $info;
}
}
/********************************************************************************/
}
?></span>
application/libraries/alipay/alipay_function.php
<span style="font-size:14px;"><?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
*功能:支付寶接口公用函數
* @author http://blog.runphp.net
* @version 0.1
*/
/**生成簽名結果
*$array要簽名的數組
*return 簽名結果字符串
*/
function build_mysign($sort_array,$key,$sign_type = "MD5") {
$prestr = create_linkstring($sort_array); //把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串
$prestr = $prestr.$key; //把拼接後的字符串再與安全校驗碼直接連接起來
$mysgin = sign($prestr,$sign_type); //把最終的字符串簽名,獲得簽名結果
return $mysgin;
}
/********************************************************************************/
/**把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串
*$array 需要拼接的數組
*return 拼接完成以後的字符串
*/
function create_linkstring($array) {
$arg = "";
while (list ($key, $val) = each ($array)) {
$arg.=$key."=".$val."&";
}
$arg = substr($arg,0,count($arg)-2); //去掉最後一個&字符
return $arg;
}
/********************************************************************************/
/**除去數組中的空值和簽名參數
*$parameter 簽名參數組
*return 去掉空值與簽名參數後的新簽名參數組
*/
function para_filter($parameter) {
$para = array();
while (list ($key, $val) = each ($parameter)) {
if($key == "sign" || $key == "sign_type" || $val == "")continue;
else $para[$key] = $parameter[$key];
}
return $para;
}
/********************************************************************************/
/**對數組排序
*$array 排序前的數組
*return 排序後的數組
*/
function arg_sort($array) {
ksort($array);
reset($array);
return $array;
}
/********************************************************************************/
/**簽名字符串
*$prestr 需要簽名的字符串
*return 簽名結果
*/
function sign($prestr,$sign_type) {
$sign='';
if($sign_type == 'MD5') {
$sign = md5($prestr);
}elseif($sign_type =='DSA') {
//DSA 簽名方法待後續開發
die("DSA 簽名方法待後續開發,請先使用MD5簽名方式");
}else {
die("支付寶暫不支持".$sign_type."類型的簽名方式");
}
return $sign;
}
/********************************************************************************/
// 日誌消息,把支付寶返回的參數記錄下來
// 請注意服務器是否開通fopen配置
function log_result($word) {
$fp = fopen("log.txt","a");
flock($fp, LOCK_EX) ;
fwrite($fp,"執行日期:".strftime("%Y%m%d%H%M%S",time())."\n".$word."\n");
flock($fp, LOCK_UN);
fclose($fp);
}
/********************************************************************************/
/**實現多種字符編碼方式
*$input 需要編碼的字符串
*$_output_charset 輸出的編碼格式
*$_input_charset 輸入的編碼格式
*return 編碼後的字符串
*/
function charset_encode($input,$_output_charset ,$_input_charset) {
$output = "";
if(!isset($_output_charset) )$_output_charset = $_input_charset;
if($_input_charset == $_output_charset || $input ==null ) {
$output = $input;
} elseif (function_exists("mb_convert_encoding")) {
$output = mb_convert_encoding($input,$_output_charset,$_input_charset);
} elseif(function_exists("iconv")) {
$output = iconv($_input_charset,$_output_charset,$input);
} else die("sorry, you have no libs support for charset change.");
return $output;
}
/********************************************************************************/
/**實現多種字符解碼方式
*$input 需要解碼的字符串
*$_output_charset 輸出的解碼格式
*$_input_charset 輸入的解碼格式
*return 解碼後的字符串
*/
function charset_decode($input,$_input_charset ,$_output_charset) {
$output = "";
if(!isset($_input_charset) )$_input_charset = $_input_charset ;
if($_input_charset == $_output_charset || $input ==null ) {
$output = $input;
} elseif (function_exists("mb_convert_encoding")) {
$output = mb_convert_encoding($input,$_output_charset,$_input_charset);
} elseif(function_exists("iconv")) {
$output = iconv($_input_charset,$_output_charset,$input);
} else die("sorry, you have no libs support for charset changes.");
return $output;
}
/*********************************************************************************/
/**用於防釣魚,調用接口query_timestamp來獲取時間戳的處理函數
注意:由於低版本的PHP配置環境不支持遠程XML解析,因此必須服務器、本地電腦中裝有支持DOMDocument、SSL的PHP配置環境。建議本地調試時使用PHP開發軟件
*$partner 合作身份者ID
*return 時間戳字符串
*/
function query_timestamp($partner) {
$URL = "https://mapi.alipay.com/gateway.do?service=query_timestamp&partner=".$partner;
$encrypt_key = "";
//若要使用防釣魚,請取消下面的4行註釋
// $doc = new DOMDocument();
// $doc->load($URL);
// $itemEncrypt_key = $doc->getElementsByTagName( "encrypt_key" );
// $encrypt_key = $itemEncrypt_key->item(0)->nodeValue;
// return $encrypt_key;
}
?></span>
application/models/alipay_model.php
<span style="font-size:14px;"><?php
/**
* 支付寶模型
* @version 0.1
* @author http://blog.runphp.net
*
*/
class Alipay_model extends CI_Model{
var $alipay_config;
function __construct ()
{
parent::__construct();
$this->config->load('alipay', TRUE);
$this->alipay_config = $this->config->item('alipay');
}
/**
*
* 服務器異步通知
*/
function notify_verify ()
{
$tmp = array(
'partner' => $this->alipay_config['partner'],
'key' => $this->alipay_config['key'],
'sign_type' => $this->alipay_config['sign_type'],
'_input_charset' => $this->alipay_config['sign_type'],
'transport' => $this->alipay_config['transport'],
);
$this->load->library('alipay/alipay_notify', $tmp);
$verify_result = $this->alipay_notify->notify_verify();
if($verify_result) {
$dingdan = $_POST['out_trade_no']; //獲取支付寶傳遞過來的訂單號
$total = $_POST['total_fee']; //獲取支付寶傳遞過來的總價格
if($_POST['trade_status'] == 'TRADE_FINISHED' ||$_POST['trade_status'] == 'TRADE_SUCCESS') { //交易成功結束
echo "success"; //請不要修改或刪除
} else {
echo "success"; //其他狀態判斷。普通即時到帳中,其他狀態不用判斷,直接打印success。
}
self::update_status($_POST['trade_no'], $dingdan);
} else {
echo "fail";
}
}
/**
*
* 頁面跳轉同步通知
*/
function return_verify ()
{
$tmp = array(
'partner' => $this->alipay_config['partner'],
'key' => $this->alipay_config['key'],
'sign_type' => $this->alipay_config['sign_type'],
'_input_charset' => $this->alipay_config['sign_type'],
'transport' => $this->alipay_config['transport'],
);
$this->load->library('alipay/alipay_notify', $tmp);
$verify_result = $this->alipay_notify->return_verify();
if($verify_result) {//驗證成功
$dingdan = $_GET['out_trade_no']; //獲取訂單號
$total_fee = $_GET['total_fee']; //獲取總價格
if($_GET['trade_status'] == 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') {
} else {
return $_GET['trade_status'];
}
self::update_status($_GET['trade_no'], $dingdan);
} else {
return "fail";
}
}
/**
*
* 更新訂單狀態(更新支付寶訂單號)
* @param string $alipay_trade_no 支付寶訂單號
* @param string $order_sn 商戶訂單號
*/
function update_status ($alipay_trade_no, $order_id)
{
//請在這裏加上商戶的業務邏輯程序代
$this->db->where('name', $order_id);
$this->db->update('sl_order', array('trade_no' => $alipay_trade_no));
}
/**
*
* 構造提交表單
* @param 訂單編號 $order_id
* @param 訂單名 $order_name
* @param 訂單金額 $money
*/
function build_form ($order_id, $order_name, $money)
{
/*以下參數是需要通過下單時的訂單數據傳入進來獲得*/
//必填參數
$out_trade_no = $order_id; //請與貴網站訂單系統中的唯一訂單號匹配
$subject = $order_name; //訂單名稱,顯示在支付寶收銀臺裏的“商品名稱”裏,顯示在支付寶的交易管理的“商品名稱”的列表裏。
$body = ''; //訂單描述、訂單詳細、訂單備註,顯示在支付寶收銀臺裏的“商品描述”裏
$total_fee = $money; //訂單總金額,顯示在支付寶收銀臺裏的“應付總額”裏
//擴展功能參數——默認支付方式
/*
$pay_mode = $_POST['pay_bank'];
if ($pay_mode == "directPay") {
$paymethod = "directPay"; //默認支付方式,四個值可選:bankPay(網銀); cartoon(卡通); directPay(餘額); CASH(網點支付)
$defaultbank = "";
}
else {
$paymethod = "bankPay"; //默認支付方式,四個值可選:bankPay(網銀); cartoon(卡通); directPay(餘額); CASH(網點支付)
$defaultbank = $pay_mode; //默認網銀代號,代號列表見http://club.alipay.com/read.php?tid=8681379
}*/
$paymethod = "directPay";
$defaultbank = "";
//擴展功能參數——防釣魚
//請慎重選擇是否開啓防釣魚功能
//exter_invoke_ip、anti_phishing_key一旦被使用過,那麼它們就會成爲必填參數
//開啓防釣魚功能後,服務器、本機電腦必須支持遠程XML解析,請配置好該環境。
//若要使用防釣魚功能,請打開class文件夾中alipay_function.php文件,找到該文件最下方的query_timestamp函數,根據註釋對該函數進行修改
//建議使用POST方式請求數據
$anti_phishing_key = ''; //防釣魚時間戳
$exter_invoke_ip = $this->input->ip_address(); //獲取客戶端的IP地址,建議:編寫獲取客戶端IP地址的程序
//如:
//$exter_invoke_ip = '202.1.1.1';
//$anti_phishing_key = query_timestamp($partner); //獲取防釣魚時間戳函數
//擴展功能參數——其他
$extra_common_param = ''; //自定義參數,可存放任何內容(除=、&等特殊字符外),不會顯示在頁面上
$buyer_email = ''; //默認買家支付寶賬號
//擴展功能參數——分潤(若要使用,請按照註釋要求的格式賦值)
$royalty_type = ""; //提成類型,該值爲固定值:10,不需要修改
$royalty_parameters = "";
//提成信息集,與需要結合商戶網站自身情況動態獲取每筆交易的各分潤收款賬號、各分潤金額、各分潤說明。最多隻能設置10條
//各分潤金額的總和須小於等於total_fee
//提成信息集格式爲:收款方Email_1^金額1^備註1|收款方Email_2^金額2^備註2
//如:
//royalty_type = "10"
//royalty_parameters = "[email protected]^0.01^分潤備註一|[email protected]^0.01^分潤備註二"
//$alipay_config = $this->alipay_config;
$parameter = array(
"service" => "create_direct_pay_by_user", //接口名稱,不需要修改
"payment_type" => "1", //交易類型,不需要修改
//獲取配置文件中的值
"partner" => $this->alipay_config['partner'],
"seller_email" => $this->alipay_config['seller_email'],
"return_url" => $this->alipay_config['return_url'],
"notify_url" => $this->alipay_config['notify_url'],
"_input_charset" => $this->alipay_config['_input_charset'],
"show_url" => $this->alipay_config['show_url'],
//從訂單數據中動態獲取到的必填參數
"out_trade_no" => $out_trade_no,
"subject" => $subject,
"body" => $body,
"total_fee" => $total_fee,
//擴展功能參數——網銀提前
"paymethod" => $paymethod,
"defaultbank" => $defaultbank,
//擴展功能參數——防釣魚
"anti_phishing_key" => $anti_phishing_key,
"exter_invoke_ip" => $exter_invoke_ip,
//擴展功能參數——自定義參數
"buyer_email" => $buyer_email,
"extra_common_param"=> $extra_common_param,
//擴展功能參數——分潤
"royalty_type" => $royalty_type,
"royalty_parameters"=> $royalty_parameters
);
$tmp = array('parameter' => $parameter, 'key' => $this->alipay_config['key'], 'sign_type' =>$this->alipay_config['sign_type']);
$this->load->library('alipay/alipay_service', $tmp);
return $this->alipay_service->build_form();
}
}</span>
最好別丟了配置文件了(自己按註釋填好)
application/config/alipay.php
<span style="font-size:14px;"><?php
//合作身份者ID,以2088開頭的16位純數字
$config['partner'] = "";
//安全檢驗碼,以數字和字母組成的32位字符
$config['key'] = "";
//簽約支付寶賬號或賣家支付寶帳戶
$config['seller_email'] = "";
//交易過程中服務器通知的頁面 要用 http://格式的完整路徑,不允許加?id=123這類自定義參數
$config['notify_url'] = "";
//付完款後跳轉的頁面 要用 http://格式的完整路徑,不允許加?id=123這類自定義參數
$config['return_url'] = "";
//網站商品的展示地址,不允許加?id=123這類自定義參數
$config['show_url'] = "http://blog.runphp.net";
//收款方名稱,如:公司名稱、網站名稱、收款人姓名等
$config['mainname'] = "heui";
//↑↑↑↑↑↑↑↑↑↑請在這裏配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//$config['total_fee'] = 1; //訂單總金額,顯示在支付寶收銀臺裏的“應付總額”裏
//簽名方式 不需修改
$config['sign_type'] = "MD5";
//字符編碼格式 目前支持 GBK 或 utf-8
$config['_input_charset'] = "utf-8";
//訪問模式,根據自己的服務器是否支持ssl訪問,若支持請選擇https;若不支持請選擇http
$config['transport'] = "http";
?></span>