起因

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

后端框架:Laravel 5.5

业务功能

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

一. 创建应用

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

注意:

  1. 需拥有实名认证的支付宝账户。
  2. 企业或个体工商户可申请
  3. 需要有真实有效的营业执照,切网站必须通过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  alipayDemo

访问demo下的index.php

这样子这个demo就可以运行了。

具体开发

现在下载SDK:支付宝手机网站支付PHP SDK

一.  引入SDK包

在laravel中引入SDK包的步骤:

  1. 在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:)
//或:
Cannot redeclare Decrypt() (previously declared in .../vendor/laravel/lumen-framework/src/helpers.php:)

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

laravel实现支付宝支付功能的更多相关文章

  1. django中使用事务以及接入支付宝支付功能

    之前一直想记录一下在项目中使用到的事务以及支付宝支付功能,自己一直犯懒没有完,趁今天有点兴致,在这记录一下. 商城项目必备的就是支付订单的功能,所以就会涉及到订单的保存以及支付接口的引入.先来看看订单 ...

  2. 支付宝支付功能(使用支付宝sdk)

    1.准备参数        新建一个公共参数配置类NewAlipayconfig (可将参数存放到config配置文件中读取)          public class NewAlipayconfi ...

  3. SSM 实现支付宝支付功能(图文详解+完整代码)

    阅读本文大概需要 4 分钟. 前言 本教程详细介绍了如何使用ssm框架实现支付宝支付功能.本文章分为两大部分,分别是「支付宝测试环境代码测试」和「将支付宝支付整合到ssm框架」,详细的代码和图文解释, ...

  4. ThinkPHP接入支付宝支付功能

    最近做系统,需要实现在线支付功能,毫不犹豫,选择的是支付宝的接口支付功能.这里我用的是即时到帐的接口,具体实现的步骤如下: 一.下载支付宝接口包 下载地址:https://b.alipay.com/o ...

  5. mui实现支付宝支付功能

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>H ...

  6. ThinkPHP5.0 实现 app支付宝支付功能

    前几天做项目,要求要用到支付宝接口,第一次做,弄了好几天 各种坑啊,简单写一下我做支付宝支付的过程,希望对也是第一次做支付宝支付的童鞋有帮助, 不懂的可以先去支付平台看一下支付宝支付的文档,我是下的d ...

  7. Laravel 开发支付宝支付与提现转账问题小结

    由于项目需要,所以需要开发支付宝支付与微信支付,支付部分采用了 yansongda/pay    https://packagist.org/packages/yansongda/pay  https ...

  8. Django——支付宝支付功能

    前期准备 首先我们需要获得支付宝提供的权限与接口,在蚂蚁开放平台进行相关申请:https://openhome.alipay.com/platform/appDaily.htm?tab=info 申请 ...

  9. cocos2d-x C++ iOS工程集成第三方支付宝支付功能

      一.在支付宝开放平台下载支付宝SDK(https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.WWgVz8&tr ...

随机推荐

  1. angular2 学习笔记 ( server-side rendering, angular universal, 服务端渲染 )

    更新 : 2018-01-10  大半年过去了依然没有做 server side render 的冲动,但是一直有关注这方便的做法. 今天领悟了一些道理, 这里做个记入. server side re ...

  2. SpringCloud的Archaius - 动态管理属性配置

    参考链接:http://www.th7.cn/Program/java/201608/919853.shtml 一.Archaius是什么? Archaius用于动态管理属性配置文件. 参考自Gett ...

  3. spring-oauth-server实践:授权方式四:client_credentials 模式的refresh_token?

    spring-oauth-server入门(1-13)授权方式四:client_credentials 模式的refresh_token? 有效期内的反复申请access_token获取失效日期不变! ...

  4. 【52ABP实战教程】0.2-- VSTS中的账号迁移到东亚

    需求从哪里来! VSTS的全称是Visual Studio Team Services. 在上一篇的文章中已经给大家说了VSTS之前是没有香港节点.大家的访问速度回比较慢.但是11月10号微软就宣布开 ...

  5. 不错的ngix/redis/java/android学习地址

    http://blog.csdn.net/xlgen157387/article/details/50051543 徐刘根的博客,好像是“Java后端技术”微信公众号的建立者,反正看到不少关于他的博文 ...

  6. Java面试题—初级(1)

    1.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致. 2.Java有 ...

  7. urllib.error

    #捕获异常 import urllib.request import urllib.error try: response = urllib.request.urlopen('http://sasd. ...

  8. The first week CorelDRAW 课总结:

    1.这节课学到了什么知识? 答:(1)认识了CorelDRAW X4的工作界面(由标题栏 菜单栏 工具栏 属性栏 工具箱 页面控制栏 状态栏 绘图区和调色板组成): (2)CorelDRAW X4的基 ...

  9. Python面向对象——重写与Super

    1本文的意义 如果给已经存在的类添加新的行为,采用继承方案 如果改变已经存在类的行为,采用重写方案 2图解继承.重写与Super 注:上面代码层层关联.super()可以用到任何方法里进行调用,本文只 ...

  10. Unity中的基础光照

    渲染包含了两大部分:决定一个像素的可见性,决定这个像素上的光照计算. 光照模型就是用于决定在一个像素上进行怎样的光照计算. 一.光源 在实时渲染中我们通常把光源当做一个没有体积的点. 1.1 辐照度 ...