最近應(yīng)業(yè)務(wù)需求,做了支付寶支付和微信支付,今天分享一下手機端app支付寶支付對接流程,實際開發(fā)過程是前后端分離,前端調(diào)用后端API接口,實現(xiàn)功能返回數(shù)據(jù),我所用的跨擠啊為TP5,大致可以分為四步:
1.在螞蟻金服開放平臺創(chuàng)建應(yīng)用,簽約商戶,生成應(yīng)用公鑰和私鑰;
2.配置統(tǒng)一下單支付參數(shù);
3.整合支付寶demo類文件;
4.創(chuàng)建Alipay支付類,類內(nèi)創(chuàng)建兩個方法(alipay_app:統(tǒng)一下單方法和alipay_notify:支付成功異步回調(diào)方法);
第一步主要是在螞蟻金服開放平臺登錄你的支付寶賬號,接入支付功能,個人就選個人,服務(wù)商就選服務(wù)商,需要填寫一些材料,如手機號,郵箱等,完成后就可以創(chuàng)建應(yīng)用啦,創(chuàng)建應(yīng)用完成后需要進行簽約,只有簽約之后你應(yīng)用里面開放的支付功能才會生效,簽約也需要填一堆信息,簽約需要審核,成功后你會拿到一個2088開頭partner值,這個第三步配置參數(shù)的時候需要用到,之后還要為你的應(yīng)用生成公鑰和私鑰,這點在開放平臺開發(fā)文檔中有詳細描述,下載生成秘鑰工具,選擇對應(yīng)的秘鑰類型,秘鑰和公鑰一定要保存好,這里就不多做贅述啦,到此開放平臺的準備工作就結(jié)束了。
第二步就是整合支付寶demo文件了,我這里已經(jīng)整合好了,直接把代碼復(fù)制到兩個文件中就可以了,一個為支付類,一個為通知類:
/*此為支付類*/
class AlipayApp{
/**
* 把數(shù)組所有元素,按照“參數(shù)=參數(shù)值”的模式用“”字符拼接成字符串
* @param $para 需要拼接的數(shù)組
* return 拼接完成以后的字符串
*/
function createLinkstring($para,$showQuotes = false) {
// $arg = "";
// while (list ($key, $val) = each ($para)) {
// $arg.=$key."=".$val."";
// }
////去掉最后一個字符
// $arg = substr($arg,0,count($arg)-2);
////如果存在轉(zhuǎn)義字符,那么去掉轉(zhuǎn)義
// if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
// return $arg;
$arg = "";
$quotes = '';
if($showQuotes){
$quotes = '"';
}
foreach ($para as $key => $val) {
if($arg == ''){
$arg = $key.'='.$quotes.$val.$quotes;
}else{
$arg = $arg.''.$key.'='.$quotes.$val.$quotes;
}
}
if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
return $arg;
}
/**
* 對數(shù)組排序
* @param $para 排序前的數(shù)組
* return 排序后的數(shù)組
*/
function argSort($para) {
ksort($para);
reset($para);
return $para;
}
/**
* 除去數(shù)組中的空值和簽名參數(shù)
* @param $para 簽名參數(shù)組
* return 去掉空值與簽名參數(shù)后的新簽名參數(shù)組
*/
function paraFilter($para) {
$para_filter = array();
while (list ($key, $val) = each ($para)) {
if($key == "sign" || $key == "sign_type" || $val == "")continue;
else$para_filter[$key] = $para[$key];
}
return $para_filter;
}
function query_timestamp() {
$url = $this->alipay_gateway_new."service=query_timestamppartner=".trim(strtolower($this->alipay_config['partner']))."_input_charset=".trim(strtolower($this->alipay_config['input_charset']));
$encrypt_key = "";
$doc = new DOMDocument();
$doc->load($url);
$itemEncrypt_key = $doc->getElementsByTagName( "encrypt_key" );
$encrypt_key = $itemEncrypt_key->item(0)->nodeValue;
return $encrypt_key;
}
/**
* 寫日志,方便測試(看網(wǎng)站需求,也可以改成把記錄存入數(shù)據(jù)庫)
* 注意:服務(wù)器需要開通fopen配置
* @param $word 要寫入日志里的文本內(nèi)容 默認值:空值
*/
function logResult($word='') {
date_default_timezone_set("PRC");
$fp = fopen("log.txt","a");
flock($fp, LOCK_EX) ;
fwrite($fp,"執(zhí)行日期:".strftime("%Y%m%d%H%M%S",time())."\n".$word."\n");
flock($fp, LOCK_UN);
fclose($fp);
}
/**
* 遠程獲取數(shù)據(jù),POST模式
* 注意:
* 1.使用Crul需要修改服務(wù)器中php.ini文件的設(shè)置,找到php_curl.dll去掉前面的";"就行了
* 2.文件夾中cacert.pem是SSL證書請保證其路徑有效,目前默認路徑是:getcwd().'\\cacert.pem'
* @param $url 指定URL完整路徑地址
* @param $cacert_url 指定當前工作目錄絕對路徑
* @param $para 請求的數(shù)據(jù)
* @param $input_charset 編碼格式。默認值:空值
* return 遠程輸出的數(shù)據(jù)
*/
function getHttpResponsePOST($url, $cacert_url, $para, $input_charset = '') {
if (trim($input_charset) != '') {
$url = $url."_input_charset=".$input_charset;
}
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL證書認證
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//嚴格認證
curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//證書地址
curl_setopt($curl, CURLOPT_HEADER, 0 ); // 過濾HTTP頭
curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 顯示輸出結(jié)果
curl_setopt($curl,CURLOPT_POST,true); // post傳輸數(shù)據(jù)
curl_setopt($curl,CURLOPT_POSTFIELDS,$para);// post傳輸數(shù)據(jù)
$responseText = curl_exec($curl);
//var_dump( curl_error($curl) );//如果執(zhí)行curl過程中出現(xiàn)異常,可打開此開關(guān),以便查看異常內(nèi)容
curl_close($curl);
return $responseText;
}
/**
* 遠程獲取數(shù)據(jù),GET模式
* 注意:
* 1.使用Crul需要修改服務(wù)器中php.ini文件的設(shè)置,找到php_curl.dll去掉前面的";"就行了
* 2.文件夾中cacert.pem是SSL證書請保證其路徑有效,目前默認路徑是:getcwd().'\\cacert.pem'
* @param $url 指定URL完整路徑地址
* @param $cacert_url 指定當前工作目錄絕對路徑
* return 遠程輸出的數(shù)據(jù)
*/
function getHttpResponseGET($url,$cacert_url) {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, 0 ); // 過濾HTTP頭
curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 顯示輸出結(jié)果
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL證書認證
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//嚴格認證
curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//證書地址
$responseText = curl_exec($curl);
//var_dump( curl_error($curl) );//如果執(zhí)行curl過程中出現(xiàn)異常,可打開此開關(guān),以便查看異常內(nèi)容
curl_close($curl);
return $responseText;
}
/**
* 實現(xiàn)多種字符編碼方式
* @param $input 需要編碼的字符串
* @param $_output_charset 輸出的編碼格式
* @param $_input_charset 輸入的編碼格式
* return 編碼后的字符串
*/
function charsetEncode($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;
}
/**
* 實現(xiàn)多種字符解碼方式
* @param $input 需要解碼的字符串
* @param $_output_charset 輸出的解碼格式
* @param $_input_charset 輸入的解碼格式
* return 解碼后的字符串
*/
function charsetDecode($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;
}
/**
* RSA簽名
* @param $data 待簽名數(shù)據(jù)
* @param $private_key 商戶私鑰字符串
* return 簽名結(jié)果
*/
function rsaSign($data, $private_key) {
//以下為了初始化私鑰,保證在您填寫私鑰時不管是帶格式還是不帶格式都可以通過驗證。
$private_key=str_replace("-----BEGIN RSA PRIVATE KEY-----","",$private_key);
$private_key=str_replace("-----END RSA PRIVATE KEY-----","",$private_key);
$private_key=str_replace("\n","",$private_key);
$private_key="-----BEGIN RSA PRIVATE KEY-----".PHP_EOL .wordwrap($private_key, 64, "\n", true). PHP_EOL."-----END RSA PRIVATE KEY-----";
$res=openssl_get_privatekey($private_key);
if($res)
{
openssl_sign($data, $sign,$res);
}
else {
echo "您的私鑰格式不正確!"."br/>"."The format of your private_key is incorrect!";
exit();
}
openssl_free_key($res);
//base64編碼
$sign = base64_encode($sign);
return $sign;
}
/**
* RSA驗簽
* @param $data 待簽名數(shù)據(jù)
* @param $alipay_public_key 支付寶的公鑰字符串
* @param $sign 要校對的的簽名結(jié)果
* return 驗證結(jié)果
*/
function rsaVerify($data, $alipay_public_key, $sign) {
//以下為了初始化私鑰,保證在您填寫私鑰時不管是帶格式還是不帶格式都可以通過驗證。
$alipay_public_key=str_replace("-----BEGIN PUBLIC KEY-----","",$alipay_public_key);
$alipay_public_key=str_replace("-----END PUBLIC KEY-----","",$alipay_public_key);
$alipay_public_key=str_replace("\n","",$alipay_public_key);
$alipay_public_key='-----BEGIN PUBLIC KEY-----'.PHP_EOL.wordwrap($alipay_public_key, 64, "\n", true) .PHP_EOL.'-----END PUBLIC KEY-----';
$res=openssl_get_publickey($alipay_public_key);
if($res)
{
$result = (bool)openssl_verify($data, base64_decode($sign), $res);
}
else {
echo "您的支付寶公鑰格式不正確!"."br/>"."The format of your alipay_public_key is incorrect!";
exit();
}
openssl_free_key($res);
return $result;
}
}
/* *
* 類名:AlipayNotify
* 功能:支付寶通知處理類
* 詳細:處理支付寶各接口通知返回
* 版本:1.0
* 日期:2016-06-06
* 說明:
* 以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據(jù)自己網(wǎng)站的需要,按照技術(shù)文檔編寫,并非一定要使用該代碼。
* 該代碼僅供學(xué)習(xí)和研究支付寶接口使用,只是提供一個參考
*************************注意*************************
* 調(diào)試通知返回時,可查看或改寫log日志的寫入TXT里的數(shù)據(jù),來檢查通知返回是否正常
*/
class AlipayNotify {
/**
* HTTPS形式消息驗證地址
*/
var $https_verify_url = 'https://mapi.alipay.com/gateway.do?service=notify_verify';
/**
* HTTP形式消息驗證地址
*/
var $http_verify_url = 'http://notify.alipay.com/trade/notify_query.do?';
var $alipay_config;
function __construct($alipay_config){
$this->alipay_config = $alipay_config;
}
function AlipayNotify($alipay_config) {
$this->__construct($alipay_config);
}
/**
* 獲取返回時的簽名驗證結(jié)果
* @param $para_temp 通知返回來的參數(shù)數(shù)組
* @param $sign 返回的簽名結(jié)果
* @return 簽名驗證結(jié)果
*/
function getSignVeryfy($para_temp, $sign) {
$alipayapp = new \Alipayapp();
//除去待簽名參數(shù)數(shù)組中的空值和簽名參數(shù)
$para_filter = $alipayapp->paraFilter($para_temp);
//對待簽名參數(shù)數(shù)組排序
$para_sort = $alipayapp->argSort($para_filter);
//把數(shù)組所有元素,按照“參數(shù)=參數(shù)值”的模式用“”字符拼接成字符串
$prestr = $alipayapp->createLinkstring($para_sort);
$isSgin = false;
switch (strtoupper(trim($this->alipay_config['sign_type']))) {
case "RSA" :
$isSgin = $alipayapp->rsaVerify($prestr, trim($this->alipay_config['alipay_public_key']), $sign);
break;
default :
$isSgin = false;
}
return $isSgin;
}
/**
* 獲取遠程服務(wù)器ATN結(jié)果,驗證返回URL
* @param $notify_id 通知校驗ID
* @return 服務(wù)器ATN結(jié)果
* 驗證結(jié)果集:
* invalid命令參數(shù)不對 出現(xiàn)這個錯誤,請檢測返回處理中partner和key是否為空
* true 返回正確信息
* false 請檢查防火墻或者是服務(wù)器阻止端口問題以及驗證時間是否超過一分鐘
*/
function getResponse($notify_id) {
$alipayapp = new \Alipayapp();
$transport = strtolower(trim($this->alipay_config['transport']));
$partner = trim($this->alipay_config['partner']);
$veryfy_url = '';
if($transport == 'https') {
$veryfy_url = $this->https_verify_url;
}
else {
$veryfy_url = $this->http_verify_url;
}
$veryfy_url = $veryfy_url."partner=" . $partner . "notify_id=" . $notify_id;
$responseTxt = $alipayapp->getHttpResponseGET($veryfy_url, $this->alipay_config['cacert']);
return $responseTxt;
}
}
第三步配置參數(shù):
//配置參數(shù)
public $alipay_config = array(
//2088開頭
'partner' => '',
//商戶的私鑰,此處填寫原始私鑰去頭去尾,RSA公私鑰生成:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.nBDxfytreeId=58articleId=103242docType=1
'private_key' => '',
//支付寶的公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
'alipay_public_key' => '',
//異步通知接口
'service'=> 'mobile.securitypay.pay',
//字符編碼格式 目前支持 gbk 或 utf-8
'input_charset' => 'utf-8',
//簽名方式 不需修改
'sign_type' => 'RSA',
//ca證書路徑地址,用于curl中ssl校驗
//請保證cacert.pem文件在當前文件夾目錄中
'cacert' => '/cacert.pem',
//訪問模式,根據(jù)自己的服務(wù)器是否支持ssl訪問,若支持請選擇https;若不支持請選擇http
'transport' => 'http',
);
創(chuàng)建$alipa_config屬性,可以放到你的配置文件中方便引入,也可以直接放到Alipay類中,里面只要前三項需要自己填寫,其余不變就好啦。
第四步就是創(chuàng)建Alipay類,類內(nèi)定義兩個方法,一個是alipay_app(統(tǒng)一下單),alipay_app中的一些參數(shù)需要自己補全,參數(shù)介紹點我,另一個是alipay_notify(支付成功后的異步回調(diào))并引入第二步創(chuàng)建的兩個類文件,本人開發(fā)用的是TP5框架,支付類放在第三方類庫vendor目錄下面,可以直接在控制器中用vendor()函數(shù)引入文件,例:vendor('Alipayapp.lib.alipay_notify');
//調(diào)用統(tǒng)一下單接口生成預(yù)支付訂單并把數(shù)據(jù)返回給APP
public function alipay_app(Request $request)
{
vendor('Alipayapp.lib.AlipayApp');
$param = $request->param();
$tade_no = $param['orderCode'];//訂單號 調(diào)用統(tǒng)一下單接口需要提供一個訂單號
$order = new Order(); //實例化訂單
$ret = $order->getOrderN2($tade_no); //查詢訂單信息 此處為我自己的查詢訂單信息方法,可以替換為你自己的
$strOrg['partner']=$this->alipay_config['partner']; // 配置的partner
$strOrg['seller_id']=$this->alipay_config['partner']; // 此處和partner值一樣即可
$strOrg['out_trade_no']=$tade_no; // 訂單號
$strOrg['subject']=""; // 商品的標題
$strOrg['body']="";//商品名
$strOrg['total_fee']=$ret['money'];// 金額
$strOrg['notify_url']="";//回調(diào)地址,填寫回調(diào)方法的絕對路徑
$strOrg['service']=$this->alipay_config['service'];
$strOrg['payment_type']="1";
$strOrg['_input_charset']="utf-8";
$strOrg['it_b_pay']="30m";
$alipay = new \Alipayapp();
//將post接收到的數(shù)組所有元素,按照“參數(shù)=參數(shù)值”的模式用“”字符拼接成字符串。
$data=$alipay->createLinkstring($strOrg,true);//,true
//將待簽名字符串使用私鑰簽名,且做urlencode. 注意:請求到支付寶只需要做一次urlencode.
$rsa_sign=urlencode($alipay->rsaSign($data, $this->alipay_config['private_key']));
//把簽名得到的sign和簽名類型sign_type拼接在待簽名字符串后面。
$data = $data.'sign='.'"'.$rsa_sign.'"'.'sign_type='.'"'.$this->alipay_config['sign_type'].'"';
//返回給客戶端,建議在客戶端使用私鑰對應(yīng)的公鑰做一次驗簽,保證不是他人傳輸。
$datajson['mdata']=$data;
echo json_encode($datajson);
}
//支付成功后的回調(diào)方法
public function alipay_notify()
{
vendor('Alipayapp.lib.alipay_notify');//引入支付通知類文件
$alipayNotify = new \AlipayNotify($this->alipay_config);
$order = new Order(); //實例化訂單
if($alipayNotify->getResponse($_POST['notify_id'])) //判斷成功之后使用getResponse方法判斷是否是支付寶發(fā)來的異步通知。
{
if($alipayNotify->getSignVeryfy($_POST, $_POST['sign'])) {//使用支付寶公鑰驗簽
//獲取支付寶的通知返回參數(shù),可參考技術(shù)文檔中服務(wù)器異步通知參數(shù)列表
//商戶訂單號
$out_trade_no = $_POST['out_trade_no'];
$ret = $order->getOrderN2($out_trade_no); //查詢訂單信息
$total_amount=$ret['money']; //訂單金額
$total_fee = $_POST['total_fee']; //支付寶返回金額
if($_POST['trade_status'] == 'TRADE_FINISHED') {
//判斷該筆訂單是否在商戶網(wǎng)站中已經(jīng)做過處理
//如果沒有做過處理,根據(jù)訂單號(out_trade_no)在商戶網(wǎng)站的訂單系統(tǒng)中查到該筆訂單的詳細,并執(zhí)行商戶的業(yè)務(wù)程序
//如果有做過處理,不執(zhí)行商戶的業(yè)務(wù)程序
//注意:
//退款日期超過可退款期限后(如三個月可退款),支付寶系統(tǒng)發(fā)送該交易狀態(tài)通知
//請務(wù)必判斷請求時的out_trade_no、total_fee、seller_id與通知時獲取的out_trade_no、total_fee、seller_id為一致的
if($total_amount==$total_fee){
//這里進行數(shù)據(jù)庫操作,比如修改訂單狀態(tài)等
}
}else if ($_POST['trade_status'] == 'TRADE_SUCCESS') {
//判斷該筆訂單是否在商戶網(wǎng)站中已經(jīng)做過處理,如果沒有做過處理,根據(jù)訂單號(out_trade_no)在商戶網(wǎng)站的訂單系統(tǒng)中查到該筆訂單的詳細,并執(zhí)行商戶的業(yè)務(wù)程
//如果有做過處理,不執(zhí)行商戶的業(yè)務(wù)程序
//注意:
//付款完成后,支付寶系統(tǒng)發(fā)送該交易狀態(tài)通知
//請務(wù)必判斷請求時的out_trade_no、total_fee、seller_id與通知時獲取的out_trade_no、total_fee、seller_id為一致的
if($total_amount==$total_fee){
//這里進行數(shù)據(jù)庫操作,比如修改訂單狀態(tài)等
}
}
echo "success"; //請不要修改或刪除
}else{ //驗證簽名失敗
echo "sign fail";
}
}else{ //驗證是否來自支付寶的通知失敗
echo "response fail";
}
}
上述getOrderN2方法是我查詢訂單信息用的,需要替換成你自己的查詢訂單方法,以上四步完成正常的話支付寶支付功能就可以實現(xiàn)了,需要注意的是支付功能簡單的在本地測試是不可以的,需要在服務(wù)器上測試,也有說用花生殼什么的...這個我沒有研究,如果沒有成功查看報錯提示,在對接支付時有很多坑,遇到報錯不要急,逐步解決一定可以實現(xiàn)支付功能的,如果自己實在解決不了的可以找支付寶技術(shù)人員,如果你想查看支付寶異步通知時的返回值,可以用 file_put_contents() 函數(shù)打印返回的參數(shù),會在你指定的路徑生成一個文件,里面就是返回的值啦。
總結(jié)
以上所述是小編給大家介紹的PHP 實現(xiàn)手機端APP支付寶支付功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
您可能感興趣的文章:- thinkPHP框架對接支付寶即時到賬接口回調(diào)操作示例
- php支付寶在線支付接口開發(fā)教程
- PHP 接入支付寶即時到賬功能
- 支付寶服務(wù)窗API接口開發(fā)php版本
- 純PHP代碼實現(xiàn)支付寶批量付款
- php支付寶手機網(wǎng)頁支付類實例
- php支付寶接口用法分析
- php進行支付寶開發(fā)中return_url和notify_url的區(qū)別分析
- ThinkPHP實現(xiàn)支付寶接口功能實例
- 支付寶 接口開發(fā)幫助(asp,php,asp.net,jsp)