起因

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

后端框架: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. AngularJS1.X学习笔记13-路由

    ThinkPHP框架有路由的概念,看起来路由更多的是后端的事情,Angular怎么也会跑出个路由呢?事实上,Angular是着眼于单页应用的,他的一个应用一般来说是一个页面,你所看到的页面内容的改变, ...

  2. Bootstrap 做一个简单的母版页

    随便搭的一个母版页,不太好看,只是为了看效果....请勿吐槽. 效果如图: 一.新建母版页,引入Bootstrap相关js文件 <link href="../css/bootstrap ...

  3. Cookie、Session登陆验证相关介绍和用法

    一.Cookie和Session 首先.HTTP协议是无状态的:所谓的无状态是指每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应直接影响,也不会直接 ...

  4. 推荐几个IDEA插件,Java开发者撸码利器。

    这里只是推荐一下好用的插件,具体的使用方法不一一详细介绍. JRebel for IntelliJ 一款热部署插件,只要不是修改了项目的配置文件,用它都可以实现热部署.收费的,破解比较麻烦.不过功能确 ...

  5. 算法 排序NB二人组 堆排序 归并排序

    参考博客:基于python的七种经典排序算法     常用排序算法总结(一) 序前传 - 树与二叉树 树是一种很常见的非线性的数据结构,称为树形结构,简称树.所谓数据结构就是一组数据的集合连同它们的储 ...

  6. POJ-3421 X-factor Chains---求因子+递推 或 素因子+组合数学

    题目链接: https://vjudge.net/problem/POJ-3421 题目大意: 给你一个数X,将X分解成1~X的因子数列,前一个数可以整数后一个数,求满足条件的最大链长以及有多少条这样 ...

  7. requests-文件上传

    import requests files = {'file':open('D://tomas.jpg','rb')}#设定一个files,打开文件对象 response = requests.pos ...

  8. 集合(list、set和map)区别

    1.List,Set都是继承自Collection接口,Map则不是 2.List特点:元素有放入顺序,元素可重复:另外list支持for循环,也就是通过下标来遍历,也可以用迭代器, 3.Set特点: ...

  9. Docker下ETCD集群搭建

    搭建集群之前首先准备两台安装了CentOS 7的主机,并在其上安装好Docker. Master 10.100.97.46 Node 10.100.97.64 ETCD集群搭建有三种方式,分别是Sta ...

  10. [LeetCode] Maximum Width of Binary Tree 二叉树的最大宽度

    Given a binary tree, write a function to get the maximum width of the given tree. The width of a tre ...