最近应业务需求,做了支付宝支付和微信支付,今天分享一下手机端app支付宝支付对接流程,感兴趣的朋友一起看看吧
最近应业务需求,做了支付宝支付和微信支付,今天分享一下手机端app支付宝支付对接流程,实际开发过程是前后端分离,前端调用后端API接口,实现功能返回数据,我所用的跨挤啊为TP5,大致可以分为四步:
1.在蚂蚁金服开放平台创建应用,签约商户,生成应用公钥和私钥;
2.配置统一下单支付参数;
3.整合支付宝demo类文件;
4.创建Alipay支付类,类内创建两个方法(alipay_app:统一下单方法和alipay_notify:支付成功异步回调方法);
第一步主要是在蚂蚁金服开放平台登录你的支付宝账号,接入支付功能,个人就选个人,服务商就选服务商,需要填写一些材料,如手机号,邮箱等,完成后就可以创建应用啦,创建应用完成后需要进行签约,只有签约之后你应用里面开放的支付功能才会生效,签约也需要填一堆信息,签约需要审核,成功后你会拿到一个2088开头partner值,这个第三步配置参数的时候需要用到,之后还要为你的应用生成公钥和私钥,这点在开放平台开发文档中有详细描述,下载生成秘钥工具,选择对应的秘钥类型,秘钥和公钥一定要保存好,这里就不多做赘述啦,到此开放平台的准备工作就结束了。
第二步就是整合支付宝demo文件了,我这里已经整合好了,直接把代码复制到两个文件中就可以了,一个为支付类,一个为通知类:
/*此为支付类*/ class AlipayApp{ /** * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 * @param $para 需要拼接的数组 * return 拼接完成以后的字符串 */ function createLinkstring($para,$showQuotes = false) { // $arg = ""; // while (list ($key, $val) = each ($para)) { // $arg.=$key."=".$val."&"; // } ////去掉最后一个&字符 // $arg = substr($arg,0,count($arg)-2); ////如果存在转义字符,那么去掉转义 // 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; } /** * 对数组排序 * @param $para 排序前的数组 * return 排序后的数组 */ function argSort($para) { ksort($para); reset($para); return $para; } /** * 除去数组中的空值和签名参数 * @param $para 签名参数组 * return 去掉空值与签名参数后的新签名参数组 */ 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_timestamp&partner=".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; } /** * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库) * 注意:服务器需要开通fopen配置 * @param $word 要写入日志里的文本内容 默认值:空值 */ function logResult($word='') { date_default_timezone_set("PRC"); $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); } /** * 远程获取数据,POST模式 * 注意: * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了 * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem' * @param $url 指定URL完整路径地址 * @param $cacert_url 指定当前工作目录绝对路径 * @param $para 请求的数据 * @param $input_charset 编码格式。默认值:空值 * return 远程输出的数据 */ 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);// 显示输出结果 curl_setopt($curl,CURLOPT_POST,true); // post传输数据 curl_setopt($curl,CURLOPT_POSTFIELDS,$para);// post传输数据 $responseText = curl_exec($curl); //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容 curl_close($curl); return $responseText; } /** * 远程获取数据,GET模式 * 注意: * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了 * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem' * @param $url 指定URL完整路径地址 * @param $cacert_url 指定当前工作目录绝对路径 * return 远程输出的数据 */ function getHttpResponseGET($url,$cacert_url) { $curl = curl_init($url); curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头 curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果 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) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容 curl_close($curl); return $responseText; } /** * 实现多种字符编码方式 * @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; } /** * 实现多种字符解码方式 * @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 待签名数据 * @param $private_key 商户私钥字符串 * return 签名结果 */ 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 待签名数据 * @param $alipay_public_key 支付宝的公钥字符串 * @param $sign 要校对的的签名结果 * return 验证结果 */ 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 * 说明: * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。 * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考 *************************注意************************* * 调试通知返回时,可查看或改写log日志的写入TXT里的数据,来检查通知返回是否正常 */ 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); } /** * 获取返回时的签名验证结果 * @param $para_temp 通知返回来的参数数组 * @param $sign 返回的签名结果 * @return 签名验证结果 */ function getSignVeryfy($para_temp, $sign) { $alipayapp = new \Alipayapp(); //除去待签名参数数组中的空值和签名参数 $para_filter = $alipayapp->paraFilter($para_temp); //对待签名参数数组排序 $para_sort = $alipayapp->argSort($para_filter); //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串 $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; } /** * 获取远程服务器ATN结果,验证返回URL * @param $notify_id 通知校验ID * @return 服务器ATN结果 * 验证结果集: * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 * true 返回正确信息 * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟 */ 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 . "¬ify_id=" . $notify_id; $responseTxt = $alipayapp->getHttpResponseGET($veryfy_url, $this->alipay_config['cacert']); return $responseTxt; } }
第三步配置参数:
//配置参数 public $alipay_config = array( //2088开头 'partner' => '', //商户的私钥,此处填写原始私钥去头去尾,RSA公私钥生成:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.nBDxfy&treeId=58&articleId=103242&docType=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', //访问模式,根据自己的服务器是否支持ssl访问,若支持请选择https;若不支持请选择http 'transport' => 'http', );
创建$alipa_config属性,可以放到你的配置文件中方便引入,也可以直接放到Alipay类中,里面只要前三项需要自己填写,其余不变就好啦。
第四步就是创建Alipay类,类内定义两个方法,一个是alipay_app(统一下单),alipay_app中的一些参数需要自己补全,参数介绍点我,另一个是alipay_notify(支付成功后的异步回调)并引入第二步创建的两个类文件,本人开发用的是TP5框架,支付类放在第三方类库vendor目录下面,可以直接在控制器中用vendor()函数引入文件,例:vendor('Alipayapp.lib.alipay_notify');
//调用统一下单接口生成预支付订单并把数据返回给APP public function alipay_app(Request $request) { vendor('Alipayapp.lib.AlipayApp'); $param = $request->param(); $tade_no = $param['orderCode'];//订单号 调用统一下单接口需要提供一个订单号 $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']="";//回调地址,填写回调方法的绝对路径 $strOrg['service']=$this->alipay_config['service']; $strOrg['payment_type']="1"; $strOrg['_input_charset']="utf-8"; $strOrg['it_b_pay']="30m"; $alipay = new \Alipayapp(); //将post接收到的数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串。 $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'].'"'; //返回给客户端,建议在客户端使用私钥对应的公钥做一次验签,保证不是他人传输。 $datajson['mdata']=$data; echo json_encode($datajson); } //支付成功后的回调方法 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方法判断是否是支付宝发来的异步通知。 { if($alipayNotify->getSignVeryfy($_POST, $_POST['sign'])) {//使用支付宝公钥验签 //获取支付宝的通知返回参数,可参考技术文档中服务器异步通知参数列表 //商户订单号 $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') { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //如果有做过处理,不执行商户的业务程序 //注意: //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 //请务必判断请求时的out_trade_no、total_fee、seller_id与通知时获取的out_trade_no、total_fee、seller_id为一致的 if($total_amount==$total_fee){ //这里进行数据库操作,比如修改订单状态等 } }else if ($_POST['trade_status'] == 'TRADE_SUCCESS') { //判断该笔订单是否在商户网站中已经做过处理,如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程 //如果有做过处理,不执行商户的业务程序 //注意: //付款完成后,支付宝系统发送该交易状态通知 //请务必判断请求时的out_trade_no、total_fee、seller_id与通知时获取的out_trade_no、total_fee、seller_id为一致的 if($total_amount==$total_fee){ //这里进行数据库操作,比如修改订单状态等 } } echo "success"; //请不要修改或删除 }else{ //验证签名失败 echo "sign fail"; } }else{ //验证是否来自支付宝的通知失败 echo "response fail"; } }
上述getOrderN2方法是我查询订单信息用的,需要替换成你自己的查询订单方法,以上四步完成正常的话支付宝支付功能就可以实现了,需要注意的是支付功能简单的在本地测试是不可以的,需要在服务器上测试,也有说用花生壳什么的...这个我没有研究,如果没有成功查看报错提示,在对接支付时有很多坑,遇到报错不要急,逐步解决一定可以实现支付功能的,如果自己实在解决不了的可以找支付宝技术人员,如果你想查看支付宝异步通知时的返回值,可以用 file_put_contents() 函数打印返回的参数,会在你指定的路径生成一个文件,里面就是返回的值啦。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
위 내용은 PHP를 사용하여 휴대폰 APP에서 Alipay 결제 기능 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

phpsession 실패 이유에는 구성 오류, 쿠키 문제 및 세션 만료가 포함됩니다. 1. 구성 오류 : 올바른 세션을 확인하고 설정합니다. 2. 쿠키 문제 : 쿠키가 올바르게 설정되어 있는지 확인하십시오. 3. 세션 만료 : 세션 시간을 연장하기 위해 세션을 조정합니다 .GC_MAXLIFETIME 값을 조정하십시오.

PHP에서 세션 문제를 디버그하는 방법 : 1. 세션이 올바르게 시작되었는지 확인하십시오. 2. 세션 ID의 전달을 확인하십시오. 3. 세션 데이터의 저장 및 읽기를 확인하십시오. 4. 서버 구성을 확인하십시오. 세션 ID 및 데이터를 출력, 세션 파일 컨텐츠보기 등을 통해 세션 관련 문제를 효과적으로 진단하고 해결할 수 있습니다.

Session_Start ()로 여러 통화를하면 경고 메시지와 가능한 데이터 덮어 쓰기가 발생합니다. 1) PHP는 세션이 시작되었다는 경고를 발행합니다. 2) 세션 데이터의 예상치 못한 덮어 쓰기를 유발할 수 있습니다. 3) Session_status ()를 사용하여 반복 통화를 피하기 위해 세션 상태를 확인하십시오.

SESSION.GC_MAXLIFETIME 및 SESSION.COOKIE_LIFETIME을 설정하여 PHP에서 세션 수명을 구성 할 수 있습니다. 1) SESSION.GC_MAXLIFETIME 서버 측 세션 데이터의 생존 시간을 제어합니다. 2) 세션 .Cookie_Lifetime 클라이언트 쿠키의 수명주기를 제어합니다. 0으로 설정하면 브라우저가 닫히면 쿠키가 만료됩니다.

데이터베이스 스토리지 세션 사용의 주요 장점에는 지속성, 확장 성 및 보안이 포함됩니다. 1. 지속성 : 서버가 다시 시작 되더라도 세션 데이터는 변경되지 않아도됩니다. 2. 확장 성 : 분산 시스템에 적용하여 세션 데이터가 여러 서버간에 동기화되도록합니다. 3. 보안 : 데이터베이스는 민감한 정보를 보호하기 위해 암호화 된 스토리지를 제공합니다.

SessionHandlerInterface 인터페이스를 구현하여 PHP에서 사용자 정의 세션 처리 구현을 수행 할 수 있습니다. 특정 단계에는 다음이 포함됩니다. 1) CustomsessionHandler와 같은 SessionHandlerInterface를 구현하는 클래스 만들기; 2) 인터페이스의 방법 (예 : Open, Close, Read, Write, Despare, GC)의 수명주기 및 세션 데이터의 저장 방법을 정의하기 위해 방법을 다시 작성합니다. 3) PHP 스크립트에 사용자 정의 세션 프로세서를 등록하고 세션을 시작하십시오. 이를 통해 MySQL 및 Redis와 같은 미디어에 데이터를 저장하여 성능, 보안 및 확장 성을 향상시킬 수 있습니다.

SessionId는 웹 애플리케이션에 사용되는 메커니즘으로 사용자 세션 상태를 추적합니다. 1. 사용자와 서버 간의 여러 상호 작용 중에 사용자의 신원 정보를 유지하는 데 사용되는 무작위로 생성 된 문자열입니다. 2. 서버는 쿠키 또는 URL 매개 변수를 통해 클라이언트로 생성하여 보낸다. 3. 생성은 일반적으로 임의의 알고리즘을 사용하여 독창성과 예측 불가능 성을 보장합니다. 4. 실제 개발에서 Redis와 같은 메모리 내 데이터베이스를 사용하여 세션 데이터를 저장하여 성능 및 보안을 향상시킬 수 있습니다.

JWT 또는 쿠키를 사용하여 API와 같은 무국적 환경에서 세션을 관리 할 수 있습니다. 1. JWT는 무국적자 및 확장 성에 적합하지만 빅 데이터와 관련하여 크기가 크다. 2. 쿠키는보다 전통적이고 구현하기 쉽지만 보안을 보장하기 위해주의해서 구성해야합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경

SublimeText3 영어 버전
권장 사항: Win 버전, 코드 프롬프트 지원!

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

WebStorm Mac 버전
유용한 JavaScript 개발 도구
