前段时间由于项目中须要实现支付宝手机网站支付功能,因此写下这篇文章以做记录,不足之处,欢迎指教。php
适用于商家在移动端网页应用中集成支付宝支付功能。商家在网页中调用支付宝提供的网页支付接口调起支付宝客户端内的支付模块,商家网页会跳转到支付宝中完成支付,支付完后跳回到商家网页内,最后展现支付结果。若没法唤起支付宝客户端,则在必定的时间后会自动进入网页支付流程。html
连接:支付宝蚂蚁金服开放平台laravel
注意:git
进入蚂蚁金服开放平台->开发者中心->网页&移动应用。按需求建立应用,在这里我建立的是网页/移动类应用。github
建立完成后提交审核,大部分应用须要签约后才能使用,签约须要营业执照。express
配置完成后,可提交审核,开发者点击提交审核后,预计会有一个工做日的审核时间。应用上线成功后,状态变为以上线,该状态下的应用可以调用生产环境的接口。json
目前laravel中集成alipay SDK的支付接口很丰富。经常使用的有下面几种:后端
OmniPay-laravel:github OmniPay-laravel连接api
latrell/alipay:github latrell/alipay连接服务器
...
由于项目的须要,在这里我采用的是alipay的原生SDK包。
首先下载PHP版本的Demo:支付宝手机网站支付PHP demo
从index.php中能够看出该demo支持如下功能
手机网站2.0支付(接口名:alipay.trade.wap.pay)
手机网站2.0订单查询 (接口名:alipay.trade.query)
手机网站2.0订单退款 (接口名:alipay.trade.refund)
手机网站2.0订单退款查询(接口名:alipay.trade.fastpay.refund.query)
手机网站2.0帐单下载(接口名:alipay.data.dataservice.bill.downloadurl.query)
其中config.php是配置文件:
<?php $config = array ( //应用ID,您的APPID。 'app_id' => "", //商户私钥,您的原始格式RSA私钥 'merchant_private_key' => "", //异步通知地址 'notify_url' => "", //http://工程公网访问地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php //同步跳转 'return_url' => "", //http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php // jk.mrwangqi.com //编码格式 'charset' => "UTF-8", //签名方式 'sign_type'=>"RSA2", //支付宝网关 'gatewayUrl' => "https://openapi.alipay.com/gateway.do", //支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 'alipay_public_key' => "", );
配置完成后,修改demo权限
sudo chmod -R 777 alipayDemo
访问demo下的index.php
这样子这个demo就能够运行了。
如今下载SDK:支付宝手机网站支付PHP SDK
在laravel中引入SDK包的步骤:
2. 找到根目录下的composer.json文件,添加以下配置:
"autoload": { "classmap": [ "database", "app/libs/alipay" //这里是自定义包的文件位置,我将我项目中的该SDK包命名为alipay ], "psr-4": { "App\\": "app/" } },
3. 执行如下命令
composer dump-autoload //当在包中加入新的类,须要更新autoloader
在alipay目录下新建wappay目录,在wappay目录下新建buildermodel和service两个目录。将上面demo目录下的wappay/buildermodel/AlipayTradeWapPayContentBuilder.php和wappay/service/AlipayTradeService.php两个文件分别复制到本身项目SDK包中新建的wappay中的相应目录下。
AlipayTradeWapPayContentBuilder.php是alipay demo对支付宝手机网站支付接口业务参数的封装。AlipayTradeService.php是alipay demo对支付宝手机网站支付接口业务功能的封装。
在SDK目录下新建log.txt。做为支付宝支付日志存放文件
对AlipayTradeWapPayContentBuilder.php和AlipayTradeService.php设置命名空间,我设置的是
namespace App\libs\alipay\wappay\buildermodel; namespace App\libs\alipay\wappay\buildermodel;
对alipay/aop/request/AlipayTradeWapPayRequest.php和alipay/aop/AopClient.php设置命名空间,我设置的是:
namespace App\libs\alipay\aop\request; namespace App\libs\alipay\aop;
在AlipayTradeWapPayContentBuilder.php中引入上面两个命名空间:
use App\libs\alipay\aop\request\AlipayTradeWapPayRequest;
use App\libs\alipay\aop\AopClient;
将AlipayTradeService.php中的下面代码注释:
// require_once dirname ( __FILE__ ).DIRECTORY_SEPARATOR.'./../../AopSdk.php'; // require dirname ( __FILE__ ).DIRECTORY_SEPARATOR.'./../../config.php';
在上面中alipay的demo中是有一个config.php文件做为配置文件的,这里咱们不须要这个文件,咱们利用laravel的特性,在laravel项目目录下的config目录新建一个alipay.php:
return [ //应用ID,您的APPID。 'app_id' => "", //商户私钥,您的原始格式RSA私钥 'merchant_private_key' => "", //异步通知地址 'notify_url' => "", //http://工程公网访问地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php //同步跳转 'return_url' => "", //http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php // jk.mrwangqi.com //编码格式 'charset' => "UTF-8", //签名方式 'sign_type'=>"RSA2", //支付宝网关 'gatewayUrl' => "https://openapi.alipay.com/gateway.do", //支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 'alipay_public_key' => "", ];
在alipay.php中进行配置支付接口所需参数。下面咱们修改alipay/wappay/service/AlipayTradeService.php:
class AlipayTradeService { //支付宝网关地址 public $gateway_url = "https://openapi.alipay.com/gateway.do"; //支付宝公钥 public $alipay_public_key; //商户私钥 public $private_key; //应用id public $appid; //编码格式 public $charset = "UTF-8"; public $token = NULL; //返回数据格式 public $format = "json"; //签名方式 public $signtype = "RSA"; function __construct(){ $this->gateway_url = config('alipay.gatewayUrl'); //得到config文件夹下的alipay.php中的gatewayUrl参数,下同。 $this->appid = config('alipay.app_id'); $this->private_key = config('alipay.merchant_private_key'); $this->alipay_public_key = config('alipay.alipay_public_key'); $this->charset = config('alipay.charset'); $this->signtype= config('alipay.sign_type'); if(empty($this->appid)||trim($this->appid)==""){ throw new Exception("appid should not be NULL!"); } if(empty($this->private_key)||trim($this->private_key)==""){ throw new Exception("private_key should not be NULL!"); } if(empty($this->alipay_public_key)||trim($this->alipay_public_key)==""){ throw new Exception("alipay_public_key should not be NULL!"); } if(empty($this->charset)||trim($this->charset)==""){ throw new Exception("charset should not be NULL!"); } if(empty($this->gateway_url)||trim($this->gateway_url)==""){ throw new Exception("gateway_url should not be NULL!"); } } function AlipayWapPayService($alipay_config) { $this->__construct($alipay_config); } /** * alipay.trade.wap.pay * @param $builder 业务参数,使用buildmodel中的对象生成。 * @param $return_url 同步跳转地址,公网可访问 * @param $notify_url 异步通知地址,公网能够访问 * @return $response 支付宝返回的信息 */ function wapPay($builder,$return_url,$notify_url) { $biz_content=$builder->getBizContent(); //打印业务参数 $this->writeLog($biz_content); $request = new AlipayTradeWapPayRequest(); $request->setNotifyUrl($notify_url); $request->setReturnUrl($return_url); $request->setBizContent ( $biz_content ); // 首先调用支付api $response = $this->aopclientRequestExecute ($request,true); // $response = $response->alipay_trade_wap_pay_response; return $response; } function aopclientRequestExecute($request,$ispage=false) { $aop = new AopClient (); $aop->gatewayUrl = $this->gateway_url; $aop->appId = $this->appid; $aop->rsaPrivateKey = $this->private_key; $aop->alipayrsaPublicKey = $this->alipay_public_key; $aop->apiVersion ="1.0"; $aop->postCharset = $this->charset; $aop->format= $this->format; $aop->signType=$this->signtype; // 开启页面信息输出 $aop->debugInfo=true; if($ispage) { $result = $aop->pageExecute($request,"post"); echo $result; } else { $result = $aop->Execute($request); } //打开后,将报文写入log文件 $this->writeLog("response: ".var_export($result,true)); return $result; } //请确保项目文件有可写权限,否则打印不了日志。 function writeLog($text) { // $text=iconv("GBK", "UTF-8//IGNORE", $text); //$text = characet ( $text ); file_put_contents ( dirname ( __FILE__ ).DIRECTORY_SEPARATOR."./../../log.txt", date ( "Y-m-d H:i:s" ) . " " . $text . "\r\n", FILE_APPEND ); } } ?>
其余接口暂时用不到,因此在这里我将其隐去。
php artisan make:controller AlipayController
由于须要实现手机网站支付,因此须要定义支付接口:
<?php namespace App\Http\Controllers\User\Alipay; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\libs\alipay\wappay\buildermodel\AlipayTradeWapPayContentBuilder; use App\libs\alipay\wappay\service\AlipayTradeService; class AlipayWapController extends Controller { /** *支付接口 */ public function alipayWapPay(Request $request) { $out_trade_no = getTradeNOString(); //公共方法生成惟一订单号 $subject = 'test'; //数据仅供测试,下同 $total_amount = 0.01; $body = 'test test!'; $timeout_express="1m"; $payRequestBuilder = new AlipayTradeWapPayContentBuilder(); $payRequestBuilder->setBody($body); $payRequestBuilder->setSubject($subject); $payRequestBuilder->setOutTradeNo($out_trade_no); $payRequestBuilder->setTotalAmount($total_amount); $payRequestBuilder->setTimeExpress($timeout_express); $payResponse = new AlipayTradeService(); $result=$payResponse->wapPay($payRequestBuilder,config('alipay.return_url'),config('alipay.notify_url')); } /** *支付同步回调接口,在config/alipay.php的return_url参数进行配置 */ public function alipayReturn() { } /** *支付异步回调接口,在config/alipay.php的notify_url参数进行配置 */ public function alipayNotify() { } }
定义支付路由及同步和异步回调路由
Route::group(['prefix' => 'alipay'],function() { Route::get('wappay','AlipayWapController@alipayWapPay'); Route::get('return','AlipayWapController@alipayReturn'); Route::get('notify','AlipayWapController@alipayNotify'); });
要注意的一点是同步路由是GET形式调用,而异步路由是POST形式调用,在调用支付接口的时候会出现CSRF错误,如今最简单的方法是利用laravel的中间件避免CSRF,在app/Http/Middleware/VerifyCsrfToken.php中增长路由
protected $except = [ // 'alipay/pay', 'alipay/return', 'alipay/notify' ];
这时就能够经过定义路由进行调用支付接口,可是在调用时会报下面这个错误:
Cannot redeclare Encrypt() (previously declared in .../vendor/laravel/lumen-framework/src/helpers.php:126) //或: Cannot redeclare Decrypt() (previously declared in .../vendor/laravel/lumen-framework/src/helpers.php:126)
这是由于Laravel 5使用Alipay SDK时,Laravel内带的加密解密函数Encrypt()/Decrypt()函数和Alipay SDK中的加密解密函数Encrypt()/Decrypt()函数命名冲突
解决方法:只需修改Alipay SDK中定义的函数名称,修改引用的函数名称。
修改步骤:
在Alipay SDK中,一共有须要修改三个文件的内容:
aop/AopEncrypt.php
aop/AopClient.php
lotusphp_runtime/Cookie/Cookie.php
在文件中查找encrypt/decrypt替换为alipayEncrypt/alipayDecrypt便可。
注:若是服务器是在Linux下,可能会报一个没有权限的错误,这是由于咱们以前在SDK包中新建了一个log.txt,在alipay/wappay/service/AlipayTradeService.php中的writeLog()函数中向该文件写入支付日志时没有写入权限,给它个权限就行了。
到此,在Laravel中支付宝手机网站支付功能就实现了,不足之处,欢迎请教。