引用:
https://blog.csdn.net/weixin_42122979/article/details/80899552
https://github.com/dream2023/ThinkPHP5-alipay
http://www.thinkphp.cn/code/3593.html
源码在上面中下载(原文是5.0以版本以前的用法,5.1版本用法需要修改才能用)
使用说明
- 在默认配置情况下,将文件夹拷贝到根目录即可, 其中
extend
目录为支付扩展目录,application\extra\alipay.php
为配置文件 -
需要在配置文件
application\extra\alipay.php
中填写必要的参数(TP5.1版本,这个用法不行)
1.调用配置文件用法:
方法一:固定值法 并将上边代码复制进去,放到 /config/app.php文件中
// +----------------------------------------------------------------------
// | 支付宝支付参数
// +----------------------------------------------------------------------
// 'alipay'=>[
// //应用ID,您的APPID。
// 'app_id' => "2016090000241",
//
// //商户私钥
// 'merchant_private_key' => "MIIEowIBAAKCAQEAsBUqNm8NAKnukhMSFNBUlogaiZxwbM3usQQQrnpcCPhmuwqdekjkSS7eBnql5K21N641uR05G+7M1teqz1nn39lPO1pmOQj21jxwEyIZvAFn+RJGQCO+C/853nOEQ7/WACg4FUplTHZukMZA9z438yomc2C+DqcUD/j9XgIC5W2ZdQfXy0elL4Ciq7IT0HYJojS0/Vk1Qf2kPDFCNcNOB1irNL99eZ1ENsuf82CCmw9tVrcWhZ5Jt6n7vyVOp8P5IIsamTk/KvG6Px/8iqccY83Fg0wvn2sWw49vB3YHD7lTQfwBnZHwQ4WmKh6j+na/MdmEat96StMjaylf4hF/TQIDAQABAoIBAGtWG4tfGqAC9qkY8va5IDc69GJaeLX6urwxbDHP6PaW5ZizGdgHQxHRfgqZDTNfKI6upZUYWU3LvhZhFYCcn5p3D7Nwocu2yTFDWlZvfRx8BvNvzl8e6qTJim8uH7xk3oDuA3IHDcUUlMWv9L5CsTyLvzCqEnFALByofVqnAzjzGHFO/PRWpGyPw6gvQyR1yrL8BMQOn+RIUGeJl25TuJzS+X6uOVbdO3I3WKyyyJ6tZbKAxlUY0H2XruPwH/cJZ0h8sLRihRn1ZDnzhJiKPvirwAoLvVU7KwmA5wikFbU7voTqCtxzrrR+zKLzjpu/qQbOQKBgG4iZDMngawdxr/FfNA2uPJKTOniF3TD6roGDjcCmR/Y/47r1CUpEaIOLwuG4GXyRnWT3Lo7a1ve4oTudUhGKtrjPlxPGFqC7MMabknREEDnfjAqLM0tPkhnb1qvRMltepe5fS0kEDEg5Qo9aktJd1wLJ09dWnv7ijh+Bc6l+gin",
//
// //异步通知地址
// 'notify_url' => "http://www.81.com/00/notify_url.php",
//
// //同步跳转
// 'return_url' => "http://www.81.com/00/return_url.php",
//
// //编码格式
// 'charset' => "UTF-8",
//
// //签名方式
// 'sign_type'=>"RSA2",
//
// //支付宝网关
// 'gatewayUrl' => "https://openapi.alipaydev.com/gateway.do",
//
// //支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
// 'alipay_public_key' => "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QV6KrPDrmAPdFc1EqTsPO7kdlHhvs09noSEV4NUxO4XPyz5E62eJRxPlcBuSD1yzM84xBTNXo6WgTM6XuxvM74Bs6hXVm/WcZxSLIGAZ5N9tymx9+09fOMejOObydP/HIi6sfcyQeHBR7owHe9GP4fsSzMQIDAQAB",
// ]
方法二:动态值法 就是把这些参数保存到数据库中,然后再读出来
在公共文件中读出(路径:/application/common.php),组成数组$config
//支付宝支付参数
function alipay_confing(){
$config['app_id']='20100919000241';
$config['merchant_private_key']='MIIEowIBAAKCAQEAsBUqNm8NAKnukhMSFNBUlogaiZxwbM3usQQQrnpcCPhmuwqdekjkSS7eBnql5K21N641uR05G+7M1teqz1ng5Qo9aktJd1wLJ09dWnv7ijh+Bc6l+gin';
$config['notify_url']='';
$config['return_url']='';
$config['charset']='UTF-8';
$config['sign_type']='RSA2';
$config['gatewayUrl']='https://openapi.alipaydev.com/gateway.do';
$config['alipay_public_key']='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QV6KrPDrmAPdFc1EqTsPO7kdlHhvs09noSEV4NUxO4XPyz5E62eJRxPlcBuSD1yzM84xBTNXo6WgTM6XuxvM74Bs6hXVm/WcZxSLIGAZ5N9tymx9+09fOMejOObydP/GOrEAV4KF5tB65+/aq4+rvp0w/QlhfmMHwjaazLXmdBjSzXj5x1v0Ep2uzPG4SgH+XCvM6SCN0mvSIKdZDPtIowPlpLXOqzMQIDAQAB';
return $config;
}
调用:在\extend\alipay\Pagepay.php文件中调用
$config=alipay_confing();
注意
错误采用抛出异常的方式, 可根据自己的业务在统一接口进行修改
用法
电脑网站支付 Pagepay.php
调用 \alipay\Pagepay::pay($params)
即可
手机网站支付 Wappay.php
调用 \alipay\Wappay::pay($params)
即可
交易查询接口 Query.php
调用 \alipay\Query::exec($query_no)
即可
交易退款接口 Refund.php
调用 \alipay\Refund::exec($params)
即可
退款统一订单查询 RefundQuery.php
调用 \alipay\RefundQuery::exec($params)
即可
交易关闭接口 Close.php
调用 \alipay\Close::exec($query_no)
即可
查询账单下载地址接口 Datadownload.php
调用 \alipay\Datadownload::exec($bill_type, $bill_date)
即可
验签 Notify.php
调用 \alipay\Notify::check($params)
即可
通知处理步骤建议
- 验签
- 判断订单状态
- 进行业务操作
一下两个类仅为建议, 帮助理解, 可根据具体业务进行变更, 详细理解可参考官方文档和SDK
application\common\NotifyHandler.php
通知操作的处理类application\example\Notify.php
具体实现
5.1版本需要修改,
5.1版取消了import()这种引入类方法
所以需要上面用到import()方法的,都要换成require
//Loader::import('alipay.pay.service.AlipayTradeService');
require '../extend/alipay/pay/service/AlipayTradeService.php';
调用:
class Notify extends NotifyHandler
{
protected $params; // 订单信息
public function index()
{
parent::init();
}
public function play()
{
$this->getorder();
$ass = \alipay\Pagepay::pay($this->params);
}
/**
* 获取订单信息, 必须包含订单号和订单金额
*
* @return string $params['out_trade_no'] 商户订单
* @return float $params['total_amount'] 订单金额
*/
public function getOrder()
{
// 以下仅示例
// $out_trade_no = $_POST['out_trade_no'];
// $order = Db::name('order')->where('out_trade_no', $out_trade_no)->find();
$out_trade_no = time().rand(1000,9999);
// $order = arrar(
// 'out_trade_no' = $out_trade_no,
// 'total_amount' = 0.1,
// 'status' = 0,
// 'id' = 1,
// 'subject' = '测试',
// );
$params = [
'out_trade_no' => $out_trade_no,
'total_amount' => 0.1,
'status' => 0,
'id' => 1,
'subject' => '测试'
];
$this->params = $params;
}
/**
* 检查订单状态
*
* @return Boolean true表示已经处理过 false表示未处理过
*/
public function checkOrderStatus()
{
// 以下仅示例
if($this->params['status'] == 0) {
// 表示未处理
return false;
} else {
return true;
}
}
/**
* 业务处理
* @return Boolean true表示业务处理成功 false表示处理失败
*/
public function handle()
{
// 以下仅示例
$result = Db::name('order')->where('id', $this->params['id'])->update(['status'=>1]);
if($result) {
return true;
} else {
return false;
}
}
}
例子:
1、把支付宝文件放到:\extend\alipay\里边
支付宝文件包在这里下载:https://download.csdn.net/download/haibo0668/10558364
2、在TP5.1控制器中代码
<?php
namespace app\pay\controller;
use think\Db;
use think\Validate;
class Index extends Common
{
protected $params; // 订单信息
public function index()
{
$number=generate_order_number();//生成唯一订单号
//dump($number);die;
//alipay_confing('app_id');die;
//多个模板
$this->assign([
'number' => $number
]);
return $this->fetch();
}
//支付
public function payment()
{
//$params=input('post.');
$this->getOrder();
//\alipay\Pagepay::pay($this->params);//支付宝电脑PC扫码支付
\alipay\Wappay::pay($this->params);//支付宝移动支付
}
/**
* 获取订单信息, 必须包含订单号和订单金额
*
* @return string $params['out_trade_no'] 商户订单
* @return float $params['total_amount'] 订单金额
*/
public function getOrder()
{
$number=input('WIDout_trade_no');//订单号
$title=input('WIDsubject');//订单名称
$price=input('WIDtotal_amount');//总价格 如果购买多个商品,就是多个商品的总价
$state=0;//状态 0未支付 1已支付
$type=4;//支付类型 1=微信扫码 2=微信公众号 3=微信H5支付 4=支付宝PC电脑扫码 5=支付宝移动 6=银联
$type_state=0;//订单类型 0单个购买 1购买多个商品
$data['number']=$number;
$data['title']=$title;
$data['price']=$price;
$data['state']=$state;
$data['type']=$type;
$data['type_state']=$type_state;
//生成订单表
if(!$number || !$price || !$title){
$this->error('错误:订单号、价格、3标题不能为空!',url('index'));
}
$order_rs=db('order')->insert($data);
if($order_rs){
//传递参数到支付宝接口文件中
$params['out_trade_no']=$number;
$params['subject']=$title;
$params['total_amount']=$price;
}else{//生成订单失败 返回错误信息
$this->error('生成订单表失败!',url('index'));
}
$this->params = $params;
}
/* *
* 功能:支付宝页面跳转同步通知页面
* 版本:2.0
* 修改日期:2017-05-01
* 说明:
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*************************页面功能说明*************************
* 该页面可在本机电脑测试
* 可放入HTML等美化页面的代码、商户业务逻辑程序代码
*/
public function return_url()
{
$params=input('get.');
$result = \alipay\Notify::check($params);
//$result = \alipay\Notify::check($this->params);
if($result) {//验证成功
//获取支付成功后支付宝返回的参数
$out_trade_no = htmlspecialchars(input('out_trade_no'));//商户订单号
$total_amount=htmlspecialchars(input('total_amount'));//价格
$app_id=htmlspecialchars(input('app_id'));//应用ID
$trade_no = htmlspecialchars(input('trade_no'));//支付宝交易号
$data['state']=1;
$data['pay_number']=$trade_no;
$data['pay_time']=date("Y-m-d H:i:s",time());
Db::name('order')->where('number', $out_trade_no)->update($data);
echo "支付成功!";
} else {
//验证失败
echo "支付失败3";die;
}
}
/* *
* 功能:支付宝服务器异步通知页面
* 版本:2.0
* 修改日期:2017-05-01
* 说明:
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*************************页面功能说明*************************
* 创建该页面文件时,请留心该页面文件中无任何HTML代码及空格。
* 该页面不能在本机电脑测试,请到服务器上做测试。请确保外部可以访问该页面。
* 如果没有收到该页面返回的 success 信息,支付宝会在24小时内按一定的时间策略重发通知
* 必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
* 支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];
* 支付宝主动发起通知,该方式才会被启用;
* 只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);
* 服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
* 第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
* 程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
* 程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
* cookies、session等在此页面会失效,即无法获取这些数据;
* 该方式的调试与运行必须在服务器上,即互联网上能访问;
* 该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
* 当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的。
*
* 需要严格按照如下描述校验通知数据的正确性。
* 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
* 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
* 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功;
*交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买家付款成功;或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限。
*/
public function notify_url()
{
// //require_once 'config.php';
// //require_once 'pagepay/service/AlipayTradeService.php';
// require '../extend/alipay/pay/service/AlipayTradeService.php';
//
//$params=input('');
// $arr=input('post.');
// $config=alipay_confing();//传入支付宝参数
// $alipaySevice = new AlipayTradeService($config);
// $alipaySevice->writeLog(var_export($_POST,true));
$params=input('post.');
$result = \alipay\Notify::check($params);
//$result = \alipay\Notify::check($this->params);
if($result) {//验证成功
//获取支付成功后支付宝返回的参数
$out_trade_no = htmlspecialchars(input('out_trade_no'));//商户订单号
$total_amount=htmlspecialchars(input('total_amount'));//价格
$app_id=htmlspecialchars(input('app_id'));//应用ID
$trade_no = htmlspecialchars(input('trade_no'));//支付宝交易号
$trade_status = htmlspecialchars(input('trade_status'));//交易状态
$out_biz_no = htmlspecialchars(input('out_biz_no'));//商户业务ID,主要是退款通知中返回退款申请的流水号
$gmt_payment = htmlspecialchars(input('gmt_payment'));//交易付款时间
$buyer_id = htmlspecialchars(input('buyer_id'));//买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字
//判断转跳通知中是否成功记录
$rs=db('order')->where('number', $out_trade_no)->where('pay_number',$trade_no)->find();
if($rs['state']){//state=1 成功记录
$data['pay_time']=$gmt_payment;
$data['pay_biz']=$out_biz_no;
$data['pay_buyer_id']=$buyer_id;
Db::name('order')->where('number', $out_trade_no)->update($data);
echo "success"; //请不要修改或删除
}else{//转跳通知不成功记录 需要重新记录
if($trade_status=="TRADE_SUCCESS" || $trade_status=="TRADE_FINISHED"){//支付成功
$data['state']=1;
//$data['pay_number']=$trade_no;
//$data['pay_time']=date("Y-m-d H:i:s",time());
$data['pay_time']=$gmt_payment;
$data['pay_biz']=$out_biz_no;
$data['pay_buyer_id']=$buyer_id;
Db::name('order')->where('number', $out_trade_no)->update($data);
echo "success"; //请不要修改或删除
}else{
echo "fail";
}
}
}else {
//验证失败
echo "fail";
}
}
}
公共文件的函数
//生成唯一订单号
function generate_order_number(){
return date('YmdHis').rand(100,999);
//return date('YmdHis').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
模板:
<!DOCTYPE html>
<html>
<head>
<title>支付宝电脑网站支付</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
*{
margin:0;
padding:0;
}
ul,ol{
list-style:none;
}
body{
font-family: "Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;
}
.tab-head{
margin-left:120px;
margin-bottom:10px;
}
.tab-content{
clear:left;
display: none;
}
h2{
border-bottom: solid #02aaf1 2px;
width: 200px;
height: 25px;
margin: 0;
float: left;
text-align: center;
font-size: 16px;
}
.selected{
color: #FFFFFF;
background-color: #02aaf1;
}
.show{
clear:left;
display: block;
}
.hidden{
display:none;
}
.new-btn-login-sp{
padding: 1px;
display: inline-block;
width: 75%;
}
.new-btn-login {
background-color: #02aaf1;
color: #FFFFFF;
font-weight: bold;
border: none;
width: 100%;
height: 30px;
border-radius: 5px;
font-size: 16px;
}
#main{
width:100%;
margin:0 auto;
font-size:14px;
}
.red-star{
color:#f00;
width:10px;
display:inline-block;
}
.null-star{
color:#fff;
}
.content{
margin-top:5px;
}
.content dt{
width:100px;
display:inline-block;
float: left;
margin-left: 20px;
color: #666;
font-size: 13px;
margin-top: 8px;
}
.content dd{
margin-left:120px;
margin-bottom:5px;
}
.content dd input {
width: 85%;
height: 28px;
border: 0;
-webkit-border-radius: 0;
-webkit-appearance: none;
}
#foot{
margin-top:10px;
position: absolute;
bottom: 15px;
width: 100%;
}
.foot-ul{
width: 100%;
}
.foot-ul li {
width: 100%;
text-align:center;
color: #666;
}
.note-help {
color: #999999;
font-size: 12px;
line-height: 130%;
margin-top: 5px;
width: 100%;
display: block;
}
#btn-dd{
margin: 20px;
text-align: center;
}
.foot-ul{
width: 100%;
}
.one_line{
display: block;
height: 1px;
border: 0;
border-top: 1px solid #eeeeee;
width: 100%;
margin-left: 20px;
}
.am-header {
display: -webkit-box;
display: -ms-flexbox;
display: box;
width: 100%;
position: relative;
padding: 7px 0;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
background: #1D222D;
height: 50px;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
box-pack: center;
-webkit-box-align: center;
-ms-flex-align: center;
box-align: center;
}
.am-header h1 {
-webkit-box-flex: 1;
-ms-flex: 1;
box-flex: 1;
line-height: 18px;
text-align: center;
font-size: 18px;
font-weight: 300;
color: #fff;
}
</style>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=4>
<header class="am-header">
<h1>支付宝电脑网站支付体验入口页</h1>
</header>
<div id="main">
<div id="tabhead" class="tab-head">
<h2 id="tab1" class="selected" name="tab">付 款</h2>
</div>
<form name=alipayment action="{:url('payment')}" method=post target="_blank">
<div id="body1" class="show" name="divcontent">
<dl class="content">
<dt>商户订单号
:</dt>
<dd>
<input id="WIDout_trade_no" name="WIDout_trade_no" value="{$number}"/>
</dd>
<hr class="one_line">
<dt>订单名称
:</dt>
<dd>
<input id="WIDsubject" name="WIDsubject" value="测试" />
</dd>
<hr class="one_line">
<dt>付款金额
:</dt>
<dd>
<input id="WIDtotal_amount" name="WIDtotal_amount" value="0.01"/>
</dd>
<hr class="one_line">
<dt>商品描述:</dt>
<dd>
<input id="WIDbody" name="WIDbody" />
</dd>
<hr class="one_line">
<dt></dt>
<dd id="btn-dd">
<span class="new-btn-login-sp">
<button class="new-btn-login" type="submit" style="text-align:center;">付 款</button>
</span>
<span class="note-help">如果您点击“付款”按钮,即表示您同意该次的执行操作。</span>
</dd>
</dl>
</div>
</form>
<div id="foot">
<ul class="foot-ul">
<li>
支付宝版权所有 2015-2018 ALIPAY.COM
</li>
</ul>
</div>
</div>
</body>
<!--<script language="javascript">
var tabs = document.getElementsByName('tab');
var contents = document.getElementsByName('divcontent');
(function changeTab(tab) {
for(var i = 0, len = tabs.length; i < len; i++) {
tabs[i].onmouseover = showTab;
}
})();
function showTab() {
for(var i = 0, len = tabs.length; i < len; i++) {
if(tabs[i] === this) {
tabs[i].className = 'selected';
contents[i].className = 'show';
} else {
tabs[i].className = '';
contents[i].className = 'tab-content';
}
}
}
function GetDateNow() {
var vNow = new Date();
var sNow = "";
sNow += String(vNow.getFullYear());
sNow += String(vNow.getMonth() + 1);
sNow += String(vNow.getDate());
sNow += String(vNow.getHours());
sNow += String(vNow.getMinutes());
sNow += String(vNow.getSeconds());
sNow += String(vNow.getMilliseconds());
document.getElementById("WIDout_trade_no").value = sNow;
document.getElementById("WIDsubject").value = "测试";
document.getElementById("WIDtotal_amount").value = "0.01";
}
GetDateNow();
</script>-->
</html>