laravel实现支付宝支付功能

本文链接(包含源码示例)若需查看,请点击文章左下角的阅读原文。

起因

前段时间因为项目中需要实现支付宝手机网站支付功能,所以写下这篇文章以作记录,不足之处,欢迎指教。

后端框架:Laravel 5.5

业务功能

适用于商家在移动端网页应用中集成支付宝支付功能。商家在网页中调用支付宝提供的网页支付接口调起支付宝客户端内的支付模块,商家网页会跳转到支付宝中完成支付,支付完后跳回到商家网页内,最后展示支付结果。若无法唤起支付宝客户端,则在一定的时间后会自动进入网页支付流程。

一. 创建应用

链接:支付宝蚂蚁金服开放平台

注意:

需拥有实名认证的支付宝账户。

企业或个体工商户可申请

需要有真实有效的营业执照,切网站必须通过ICP备案

进入蚂蚁金服开放平台->开发者中心->网页&移动应用。按需求创建应用,在这里我创建的是网页/移动类应用。

创建完成后提交审核,大部分应用需要签约后才能使用,签约需要营业执照。

二. 配置应用环境

配置完成后,可提交审核,开发者点击提交审核后,预计会有一个工作日的审核时间。应用上线成功后,状态变为以上线,该状态下的应用能够调用生产环境的接口。

三. 接口调用配置

目前laravel中集成alipay SDK的支付接口很丰富。常用的有下面几种:

OmniPay-laravel:github OmniPay-laravel链接

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

一. 引入SDK包

在laravel中引入SDK包的步骤:

在app/新建libs文件夹,将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';

四. 配置config(alipay.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' => "",

];

五. 对应config修改函数

在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 );

}

}

?>

其他接口暂时用不到,所以在这里我将其隐去。

六. 新建控制器(AlipayController)

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中支付宝手机网站支付功能就实现了,不足之处,欢迎请教。

本文分享自微信公众号 - PHP技术大全(phpgod)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-04-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Devops专栏

Django 2.1.7 使用富文本编辑器 tinymce

Django 2.1.7 Admin - 注册模型、自定义显示列表字段 Django 2.1.7 上传图片 - Admin后台管理 https://djan...

12710
来自专栏PHP饭米粒

手把手撸PHP扩展 0x03: 理解PHP生命周期的过程

我们现在所处的目录就是这个服务器的根目录。我们请求这个服务器的时候,服务器会在这个目录里面查找资源。

7010
来自专栏PHP修行之路

【多进程】php多进程编程

php实现多进程需要安装pcntl模块,这个模块是php官方提供的,所以我们可以在PHP源码中找到,下载 php7.3.7 源码并解压到 /home 目录下,...

10120
来自专栏Web技术布道师

php 解压缩 zip 和 rar 压缩包文件

项目涉及文档处理,用户上传的包括 zip 和 rar 压缩包,需要先将压缩包解压后再作处理。对于 zip 压缩包,由于 php 自带 zip 扩展,可以直接解压...

23220
来自专栏跟Qt君学编程

QML界面嵌入QWidget使用

51320
来自专栏Web技术布道师

开源推荐 - soar-PHP - SQL 语句优化器和重写器的 PHP 扩展包、 方便框架应用中 SQL 调优

soar-php 是一个基于小米公司开源的 soar 开发的 PHP 扩展包,方便框架中 SQL 语句调优。

13310
来自专栏matlab爱好者

matlab调用IP Cam网络摄像头

感谢大家关注matlab爱好者微信公众号,今天给大家介绍一下如何使用matlab调用网络摄像头。在聊天栏中回复“017”、“摄像头”或“IP”即可快速获取本视频...

18120
来自专栏复盘总结文章集合

MyEclipse生成javadoc文档

三。create javadoc for members with visibility解释 private 所有类和成员都生成

10720
来自专栏跟Qt君学编程

Qt开源网络库[2]-接口篇

上一篇介绍了Qt开源网络库,有兴趣的可以翻开往期推送.今篇主要介绍该开源网络库接口的用法.

22430
来自专栏Web技术布道师

开源推荐 - Swoft 2.0.3 重大更新,发布优雅的微服务治理

Swoft 是一款基于 Swoole 扩展实现的 PHP 微服务协程框架。Swoft 能像 Go 一样,内置协程网络服务器及常用的协程客户端且常驻内存,不依赖传...

11510

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励