PHP对接支付宝当面付详细教程

本文详细介绍了如何为个人或小型站点接入支付宝的当面付功能,包括申请条件、步骤、代码实现和注意事项。即使没有企业资质,也能通过设置个人支付宝账号和门面照片来实现收款。此外,还提供了当面付的SDK简化示例和异步通知的处理方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读:

作为一名小站长或开发者,网站需要接入支付功能,才能实现网站的变现盈利,一般有以下几个方案:

1、第三方支付平台: 截止2020年11月23日,我国拥有第三方支付牌照的公司共有237家,从2015年至今,先后有34家第三方支付公司支付牌照被注销。第三方支付不容易申请,基本都需要企业资格才能开通,审核比较严。

第三方支付公司排名TOP50

排名第三方支付公司APP或支付品牌
1支付宝(中国)网络技术有限公司支付宝
2财付通支付科技有限公司财付通和微信支付
3中国银联股份有限公司银联商务
4平安付科技服务有限公司壹钱包
5快钱支付清算信息有限公司快钱
6苏宁消费金融有限公司苏宁金融(苏宁支付)
7联动优势电子商务有限公司联动优势
8京东数字科技控股股份有限公司京东支付
9拉卡拉支付股份有限公司拉卡拉
10通联支付网络服务股份有限公司通联支付
11易宝支付有限公司易宝支付(Yeepay)
12迅付信息科技有限公司环迅支付
13北京度小满支付科技有限公司度小满支付
14天翼电子商务有限公司翼支付
15中移电子商务有限公司和包支付
16网银在线(北京)科技有限公司网银在线
17联通支付有限公司联通支付
18上海盛付通电子支付服务有限公司盛付通
19北京海科融通支付服务股份有限公司海科融通
20易生支付有限公司易生支付
21随行付支付有限公司随行付Plus
22海南海岛一卡通支付网络有限公司海南一卡通
23国付宝信息科技有限公司国付宝
24银盛支付服务股份有限公司银盛支付
25网易宝有限公司网易宝
26连连银通电子支付有限公司连连支付
27北京钱袋宝支付技术有限公司钱袋宝
28宝付网络科技(上海)有限公司宝付
29杉德支付网络服务发展有限公司杉德支付
30智付电子支付有限公司智付Dinpay
31中付支付科技有限公司中付支付
32上海付费通信息服务有限公司付费通
33汇付天下有限公司汇付天下
34瑞银信支付技术有限公司瑞银信
35北京新浪支付科技有限公司新浪支付
36上海富友支付服务股份有限公司富友支付
37易智付科技(北京)有限公司首信易支付
38快捷通支付服务有限公司快捷通
39中金支付有限公司中金支付
40广州市汇聚支付电子科技有限公司汇聚支付
41得仕股份有限公司得仕通
42资和信电子支付有限公司资和信
43易联支付有限公司易联支付
44上海偶可贝网络科技有限公司Allpay
45优钱付(浙江)信息科技有限公司优钱付
46四川商通实业有限公司四川商通
47卡友支付服务有限公司卡友支付
48重庆易极付科技有限公司易极付
49双乾网络支付有限公司双乾支付
50新生支付有限公司新生支付

2、第四方支付(聚合支付):第四方支付是相对第三方而言的,作为对第三方支付平台服务的拓展,第三方支付介于银行和商户之间,而第四方支付是介于第三方支付和商户之间,没有支付许可牌照的限制。第三方支付提供的是资金清算通道,而第四方支付提供的是支付基础上的多种衍生服务。作为连接着第三方支付机构和商户的中间商,聚合支付只是完成支付环节的信息流转和商户操作的承载,从事的是“支付、结算、清算”服务之外的“支付服务”,并不提供资金清算通道。《中国人民银行支付结算司关于开展违规“聚合支付”服务清理整治工作的通知》,将聚合支付定位于“收单外包机构”,对聚合支付划了四条明确的红线,即“四个不得”,其中之一是“不得以任何形式经手特约商户结算资金,从事或变相从事特约商户资金结算”。换句话理解,聚合支付公司只是为商户提供第三方支付通道的技术服务机构,不能沉淀资金,更不能为商户提供支付和资金清算。聚合支付本身并不违法,但如果聚合支付平台从事了结算业务,对商家的资金进行了截留,形成所谓“资金池”,就是非法行为,风险很大。正是因为风险的存在,所以很多未经国家有关主管部门批准的小型聚合支付平台都是做一段时间就跑路,就是为了规避这个风险。大家要选择第四方聚合支付平台时,一定要谨慎!

3、个人免签约系统:使用个人的支付宝或微信收款码,然后安卓监听APP收款通知,实现收款。本质上是采用挂机监听的策略,但针对的是移动端支付宝或微信的收款通知消息,成本高,配置麻烦,需24小时挂台安卓手机,不免费。

对于个人开发者来说,以上方案要么没有企业资质,要么太麻烦,要么太贵,有没有更好的方案呢?答案是:当然有,那就是支付宝的当面付,下面让我们一步一步来探讨。

当面付申请条件:

1.  支持的账户类型:经过实名认证的个人/企业支付宝账号  

2.  签约申请提交资料: 

   1) 经营场所照片

     a. 有店铺门头的经营场所,需提供门头照;

     b. 无店铺门头的经营场所,需提供内景照或场景照

提醒:若未规范提交经营场景照片,商家收款将受限,商家需在30天内补全资料,否则将影响正常收款。

   2) 与实名认证的支付宝账号持有人同名的营业执照(即营业执照的法人代表与支付宝账号持有人姓名一致)

提醒:若未提供同名营业执照,可长期使用但商家收款有限额。

3.费率

服务名称费率服务期限
单笔费率0.6%1年

说明:

  • 申请当面付其实很简单,只需要找一张店铺门面照片即可,可以到外面随便拍一张,或者网站搜索。
  • 营业执照是可选的,不上传的话,限制单笔收款≤1000,单日收款≤5W,对于小网站来说,这个额度已经够用了。

补充:

如果你的支付宝账号在申请时,提示风险过高,那就换另一个账号。支付宝的规则系统一般会检测你账号近期的交易,看是否有风险,新注册的支付宝账号,一般都申请不了当面付。

当面付申请流程:

1、点击这里进入,登陆支付宝账户,然后点击“立即开通”。

2、填写基本信息,红色星号的必填:

  • 经营内容选择百货零售 → 超市 → 超市,或者选其他的,建议不要选平台类、支付类、游戏类的,这些审查严格,没有企业资质和电信增值许可证是通过不了。
  • 营业执照可以不上传。
  • 店铺招牌,就是门店的照片,可以去外面拍一张,也可以百度搜索。
  • 提交申请后十多分钟就可收到通过通知。

当面付开发流程:

一、准备工作

1、首先是要申请成功开发者,可以登录支付宝开放平台申请。

2、当面付申请成功后,可以在蚂蚁金服开放平台网页&移动应用中,看到我的应用列表中多了一个 “应用2.0签约20210203*********” 的应用

二、配置当面付公钥和私钥

1、利用 “支付宝开放平台开发助手” 生成RSA密钥(包括应用公钥和应用私钥),官方教程地址:第一步:生成密钥并上传 | 网页&移动应用

在线生成密钥:MiniU 研发工作台

2、点击进入应用2.0签约20210203*********

3、在 左侧菜单栏的“应用信息” 中设置公钥,设置“接口加签方式”

4、把前面生成的应用公钥复制进去,然后保存设置即可

三、官方API参数

网关地址:支付宝 - 网上支付 安全快速!

1、公共请求参数:

参数类型是否必填最大长度描述
app_idString32支付宝分配给开发者的应用ID
methodString128接口名称
formatString40仅支持JSON
charsetString10请求使用的编码格式,如utf-8,gbk,gb2312等
sign_typeString10商户生成签名字符串所使用的签名算法类型,目前支持RSA
signString256商户请求参数的签名串,详见签名
timestampString19发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
versionString3调用的接口版本,固定为:1.0
notify_urlString256支付宝服务器主动通知商户服务器里指定的页面http/https路径。
app_auth_tokenString40详见应用授权概述
biz_contentString-请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档

2、请求参数:

参数类型是否必填最大长度描述
out_trade_noString必须64商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复
sceneString必须32支付场景 条码支付,取值:bar_code 声波支付,取值:wave_code
auth_codeString必须32支付授权码
seller_idString可选28如果该值为空,则默认为商户签约账号对应的支付宝用户ID
total_amountPrice可选11订单总金额,单位为元,
discountable_amountPrice可选11参与优惠计算的金额,单位为元
undiscountable_amountPrice可选11不参与优惠计算的金额,单位为元
subjectString必须256订单标题
bodyString可选128订单描述
goods_detailGoodsDetail []可选-订单包含的商品列表信息,Json格式,其它说明详见商品明细说明
operator_idString可选28商户操作员编号
store_idString可选32商户门店编号
terminal_idString可选32商户机具终端编号
alipay_store_idString可选32支付宝的店铺编号
extend_paramsExtendParams可选-业务扩展参数
timeout_expressString可选6该笔订单允许的最晚付款时间,逾期将关闭交易。
royalty_infoRoyaltyInfo可选-描述分账信息,Json格式,其它说明详见分账说明
sub_merchantSubMerchant可选-二级商户信息,当前只对特殊银行机构特定场景下使用此字段

3、公共响应参数:

参数类型是否必填最大长度描述
codeString-网关返回码,详见文档
msgString-网关返回码描述,详见文档
sub_codeString-业务返回码,详见文档
sub_msgString-业务返回码描述,详见文档
signString-签名,详见文档

4、响应参数

参数类型是否必填最大长度描述
trade_noString必填64支付宝交易号
out_trade_noString必填64商户订单号
buyer_logon_idString必填100买家支付宝账号
total_amountPrice必填11交易金额
receipt_amountString必填11实收金额
buyer_pay_amountPrice选填11买家付款的金额
point_amountPrice选填11使用积分宝付款的金额
invoice_amountPrice选填11交易中可给用户开具发票的金额
gmt_paymentDate必填32交易支付时间
fund_bill_listTradeFundBill []必填-交易支付使用的资金渠道
card_balancePrice选填11支付宝卡余额
store_nameString选填512发生支付交易的商户门店名称
buyer_user_idString必填28买家在支付宝的用户id
discount_goods_detailString必填-本次交易支付所使用的单品券优惠的商品优惠信息

三、沙箱测试环境

如果当面付暂时还没有签约成功,可以先使用沙箱账号进行测试,方法如下:

1、登录支付宝开发平台 支付宝开放平台,开发服务 → 研发服务

2、沙箱应用里,设置RSA2(SHA256)密钥,和上面设置应用公钥是一样的。

3、设置应用网关,用于接收支付宝异步通知

四、 代码编写

可以下载官方当面付的SDK示例,下载链接:网页&移动应用学习路径 | 网页&移动应用

我大概地看了一下官方给的示例,涉及的类文件比较多,对于新手而言还是比较复杂的,所以我简化了一下,大家可以参考我以下的Thinkphp框架代码。

1、在extend扩展目录下新建一个pay文件夹,里面创建一个Alipay.php类,代码如下:

<?php
namespace pay;
use \think\Db;
use app\common\model\Order as OrderModel;
 
/**
 * 支付宝支付类
 */
class Alipay {
	//是否沙盒环境
	public $is_sandbox = true;
	//沙盒地址
	private $sandurl = 'https://openapi.alipaydev.com/gateway.do';
	//正式地址
	private $apiurl  = 'https://openapi.alipay.com/gateway.do';
	//网关地址
	private $gateway;
	//支付宝的APPID
	private $appid;
	//应用私钥
	private $rsaPrivateKey = '商户设置的私钥';
	//支付宝公钥
	private $alipayPublicKey= '支付宝自动生成的公钥';
 
	private $charset = 'utf-8';
	
	public function setAppid($appid)
    {
        $this->appid = $appid;
    }
	
	public function setRsaPrivateKey($rsaPrivateKey)
    {
        $this->rsaPrivateKey = $rsaPrivateKey;
    }
	
	public function setAlipayPublicKey($alipayPublicKey)
    {
        $this->alipayPublicKey = $alipayPublicKey;
    }
	//构造方法
	public function __construct($account){
		//如果是沙箱测试
		if($this->is_sandbox){
			$this->gateway = $this->sandurl;
			$this->appid = '2021000117612368';
			$this->rsaPrivateKey ='MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDF0Bwt0712ogHbqp245rlz6Rj7LePxxhO4HoIUZhmZS7OeC1muhLK44e9kznFGT6YvOE2rasZT+EixPLifYqUML0Ecmxxvt3dr/WrAIIM941ODhjCaV+CDjZYNBZmsK+nPojFNWuyZCq9ygzX43XOjk3oTNbws5/tJdA4fzKIhlufuvpeGL5tgzoLKxUy2oweVluIyqkhFZUs9/9vgiUwFVt/iPf4JREGJ56Vq1grOBd+ZGqrljCcx6mEOm7GGd0lcDw038L//MYICdAgLfmCF+aojkRm3qkcTt7bl0jYsYnAWqxkBvtpCWQzFKMbwVsqbOM5EUzle8csNuguXqZ3TAgMBAAECggEAQXoSacTluOgFT23ZSUPt7QUiRLo68mOLUW2fxxrIrGpXaR/+rnq0Ieh/clG3QvvIWWb8ppnwTgkoHqqnvfJMCY12sv5Q2lXTTMaUWEYqywh9M1LusZgW89Sg72yEIm5itVzNjvpiyIG182SlL9w1k9aUGNm3kPG0xuqIZlQ48n7IZSeDGg+5ic4J+U7PGaCV99Q2zq7mkuIrbot+p478oFR7aM1/24OfxdJ1eh4FZZevwmLHpwqZEhecZFGrKx6/wlzDOhaiR7PgGcjH0/q6tiw6g5TCWfAzBDfFl/ZdRToNP6mOe57qt7jKhQz+2Lxz0qLNdNDacyTSYIkFg9cE0QKBgQD3AyFhrFk0VaTN2Xt6OAOEn1UIf1aBPenFQfV8+tIlbL5Io3VHHmycMuctW7c2BGkp7HF9OwcYGtURQdaBRCyKAYnW1nbZRgLHhkFC9rLyG04/yv6pN6/8EZ4bow7aUiCGoR/mV5GehiWTjb5/thCS+enaaalTdAfQBGZOl7VyGwKBgQDNArKycIbtXl2BEqcIqwW2nzv0RA8FhK7LkRy1CFIAeZfr1O4FiSXM/3V/EuocISCwOLWRq5Qyx7mZoMZ4FSRlQFbo9LUmYtIXtKHekwYZEpxleXJOVudRRQa3GkMvWAd3s1MSCGQ44jHFc/vbap6fxfR/GWHupoMOvW4N06h+qQKBgAFzekG5oniFPEedTHVmWNbxnK1FGjv+Ih5vicKnMo7XubJxi2HUkuzD+8mvbT8a/YcqhWwn6Z3BopjMWzc9MEnLQoUJk38pQyDq7/01t81mTapgRei2lAkWDWi6J38u1lQUJDzVLNzSiuv3/DOB3U0PvMj0r/L2jokxTWyOxZkbAoGBAK8DuqHyxn0lrhVeYxJXTg1Vas1gvKItXzuRqjwx2i9lnnhJ0tkO+CJFg3z0HL/e2BUYlIjDPUUMlDm+szAYfHWjs440OeGHQ2vRXM6yHOaSqMlIIHkYX2lV0CHIXcqxD870W9ptJ4IYN/0kwsHSB5DGYa+Pb7dYl1GiDa3oH1PhAoGBAPCSjFfXd45BPYDNL+4N9zS2wT5pjxlSo3Jrg3NKOqBZLS52ygZ6xeANXrG7lzitJ75prUS/JyXS83SM8au0vcO1Fko9BbW7MkuKFZH7FSpOlv5/PJQ6tI55TV7gGl8l4ND7V9OSdAMhEBQ/uT2gmddFoRTB14MeLIpe2iIVonan';
			$this->alipayPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs9AE75V+F242HcN/SlcGUxUNv3zGpXjai4u2ZMkOL1nolr4/v4BGEsE8tLaYq6lecG/of5taQ27WbWXB5PMB3s6emesTEn5yvZPh/HqmKACHi7js+Dtu123J/z0DyCukMm1ZHJe7jT9VEU9w4gIdNi64VxdaRF3ZMT6DXnboUEegyRKGmJc58h+O30P/UYqdCW/Gl+380o80e6Fs0rX33AixkNTRNgnQ3n2er1Nrqan/9sfUCnqqxgpc1+GTT+vyn3x4Xwvch2pRIkcK4BFCrWVVMKVXx/icj1njTVwWm1KYrXDVYrxf4ZLgpMHyW3SfhnXFKuxvSYnvm5EHjxtNEQIDAQAB';
		}else{
			$this->gateway = $this->apiurl;
			$this->appid = $account['appid'];
			$this->rsaPrivateKey = $account['privateKey'];
			$this->alipayPublicKey = $account['publicKey'];
		}
	}
	/**
	 *
	 * 发起当面付
	 * $params 传输的数据
	 */
	public function facepay($params){
		//请求参数的集合,参考https://mp.csdn.net/editor/html/113599448
		$biz_content = [
			"scene" =>"bar_code",//支付场景 条码支付,取值:bar_code 声波支付,取值:wave_code(必须)
			"out_trade_no" =>$params['pay_id'],//唯一标识,订单编号(必须)
			"total_amount" =>$params['money'],//订单金额(可选)
			"subject" =>"购买商品",//商品名称
			//"seller_id" =>'',	//如果该值为空,则默认为商户签约账号对应的支付宝用户ID	
			"timeout_express" =>"5m",//该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天
 		];
 		//公共请求参数
		$param = [
			'app_id' => $this->appid,//支付宝分配给开发者的应用ID
			'method' => 'alipay.trade.precreate',//接口名称
			//'format' => 'JSON',//仅支持JSON
			'charset' => 'utf-8',//请求使用的编码格式
			'sign_type' => 'RSA2',//商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
			'sign' => '',//商户请求参数的签名串
			'timestamp' => date('Y-m-d H:i:s', time()),//发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
			'version' => '1.0',//调用的接口版本,固定为1.0
			'notify_url' => $params['notify_url'],//异步通知地址,支付宝服务器主动通知商户服务器里指定的页面http/https路径
			//'app_auth_token' => '',//app_auth_token
			'biz_content' => json_encode($biz_content),	//请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
		];
		//组合生成签名参数
		$signdata = [];
		$signdata['app_id'] = $param['app_id'];
		$signdata['method'] = $param['method'];
		$signdata['charset'] = $param['charset'];
		$signdata['sign_type'] = $param['sign_type'];
		$signdata['timestamp'] = $param['timestamp'];
		$signdata['version'] = $param['version'];
		$signdata['notify_url'] = $param['notify_url'];
		$signdata['biz_content'] = $param['biz_content'];
		//生成签名
		$sign = $this->generateSign($signdata, 'RSA2');
		$param['sign'] = $sign;
		//echo "<pre>";
		//var_dump($param);die;
		//发起请求
		$content = $this->curlPost($this->gateway,$param);
		$alipayData  = json_decode($content, true);	
		//公共响应参数
		$responseData = $alipayData['alipay_trade_precreate_response'];
		if($responseData['code'] == 10000){
			//生成成功,返回结果给前端
			$data = [];
			$data['out_trade_no'] = $responseData['out_trade_no'];
			$data['qr_code'] = $responseData['qr_code'];
			return ['code' => 1 , 'msg' => '成功' , 'data' => $data];
 
		}else {
			//file_put_contents(LOG_PATH .'alipayFacepay.log', 'err code:' . $responseData['code'] . ', err msg:' . $responseData['msg'] . '\r\n', FILE_APPEND);
			return ['code' => 0 , 'msg' => '错误码:' . $responseData['code'] . ',错误信息:' . $responseData['msg']];
		}
	}
 
 
	/**
	 * 支付宝支付通知
	 * @param $data 通知的数据
	 */
	public function notify($data){
		$falg=false;
		//验证签名
		$param = $data;
		unset($param['sign']);
		unset($param['sign_type']);
		$rst = $this->rsaCheck($param, $data['sign'] , $data['sign_type']);
		if(!$rst){
			file_put_contents(LOG_PATH .'alipaynotify.log', '验签失败\r\n' , FILE_APPEND );
			return false;
		}
		//查询支付订单状态
		try{
			$rst = $this->orderquery($data, 'TRADE_SUCCESS');
		} catch (\Exception $e) {
			printLog("查询支付订单状态失败:".$e);
		}
		if($rst){
			$falg=true;
		}else {
			file_put_contents(LOG_PATH .'alipaynotify.log', '查询订单状态错误\r\n', FILE_APPEND);
			$falg=false;
		}
		return $falg;
	}
 
	/**
	 *
	 * 支付查询接口 
	 * @param  data   支付宝响应的参数集合
	 * @param  status 要验证的状态  
	 * WAIT_BUYER_PAY 交易创建等待买家付款 
	 * TRADE_CLOSED   未付款交易超时关闭或支付完成后全额退款  
	 * TRADE_SUCCESS  交易支付成功 
	 * TRADE_FINISHED 交易结束不可退款
	 */
	public function orderquery($data , $status){
		$biz_content = [
			'out_trade_no' => $data['out_trade_no'],
			'trade_no' => $data['trade_no'],
			//'org_pid' => '',
		];
 
		$param = [
			'app_id' => $this->appid,
			'method' => 'alipay.trade.query',
			'charset' => 'utf-8',
			'sign_type' => 'RSA2',
			'sign' => '',
			'timestamp' => date('Y-m-d H:i:s',  time()),
			'version' => '1.0',
			'biz_content' => json_encode($biz_content),
		];
 
		//组合签名数组
		$signdata = [];
		$signdata['app_id'] = $param['app_id'];
		$signdata['method'] = $param['method'];
		$signdata['charset'] = $param['charset'];
		$signdata['sign_type'] = $param['sign_type'];
		$signdata['timestamp'] = $param['timestamp'];
		$signdata['version'] = $param['version'];
		$signdata['biz_content'] = $param['biz_content'];
 
		//生成签名
		$sign = $this->generateSign($signdata, 'RSA2');
		$param['sign'] = $sign;
		$content = $this->curlPost($this->gateway,$param);
		$alipayData  = json_decode($content, true);	
		//公共响应参数
		$responseData = $alipayData['alipay_trade_query_response'];
		if($responseData['code'] == 10000){
			if($responseData['trade_status'] == $status){
				return true;
			}else {
				return false;
			}
		}else {
			return false;
		}
	}
 
	public function generateSign($params, $signType = "RSA") {
        return $this->sign($this->getSignContent($params), $signType);
    }
 
    public function getSignContent($params) {
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                // 转换成目标字符集
                $v = $this->characet($v, $this->charset);
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        unset ($k, $v);
        return $stringToBeSigned;
    }
 
    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    function characet($data, $targetCharset) {
        if (!empty($data)) {
            $fileType = $this->charset;
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
                //$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
            }
        }
        return $data;
    }
 
 
     /**
	  *
      * 校验$value是否非空
      */
    protected function checkEmpty($value) {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;
        return false;
    }
 
 
    /**
     *
     * 签名函数
     */
    protected function sign($data, $signType = "RSA") {
        $priKey=$this->rsaPrivateKey;
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($priKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        ($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
        if ("RSA2" == $signType) {
			//OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
            openssl_sign($data, $sign, $res, version_compare(PHP_VERSION,'5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256); 
        } else {
            openssl_sign($data, $sign, $res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }
 
    /**
     *
     * 验签函数
     */
    public function rsaCheck($data, $sign,$type = 'RSA'){
    	$public_key = $this->alipayPublicKey;
    	$search = [
            "-----BEGIN PUBLIC KEY-----",
            "-----END PUBLIC KEY-----",
            "\n",
            "\r",
            "\r\n"
        ];
        $public_key=str_replace($search,"",$public_key);
        $public_key=$search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1];
        $res=openssl_get_publickey($public_key);
        if($res)
        {
            if($type == 'RSA'){
                $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res);
            }elseif($type == 'RSA2'){
                $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res,OPENSSL_ALGO_SHA256);
            }
            openssl_free_key($res);
        }else{
            return false;
        }
        return true;
    }
	/*
	 * curl发送post请求,并返回请求头信息
	 * url:       访问路径
	 * postData:  要传递的post数据
	 * refcode:   是否返回请求码
	 * refheader: 是否返回请求头信息
	 * */
	function curlPost($url, $postData, $refcode = false, $refheader = false) {
		$curl = curl_init();
		//设置提交的url
		curl_setopt($curl, CURLOPT_URL, $url);
		//设置获取的信息以文件流的形式返回,而不是直接输出
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		//忽略证书(关闭https验证)
		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
		//设置post方式提交
		curl_setopt($curl, CURLOPT_POST, 1);
		//设置post数据
		$postFields = http_build_query($postData);
		curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields);
		//添加请求头信息
		$headers = addHttpHeader($url);
		curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
		//在尝试连接时等待的秒数
		curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
		//设置超时时间,最大执行时间超时时间(单位:s)
		curl_setopt($curl, CURLOPT_TIMEOUT, 300);
		//是否返回请求头信息(http协议头)
		if ($refheader) {
			curl_setopt($curl, CURLOPT_HEADER, 1);
			//追踪句柄的请求字符串(允许查看请求header)
			curl_setopt($curl, CURLINFO_HEADER_OUT, true);
		} else {
			curl_setopt($curl, CURLOPT_HEADER, 0);
		}
		//执行命令
		$result = curl_exec($curl);
		//转换字符编码
		$result = mb_convert_encoding($result, 'utf-8', 'UTF-8,GBK,GB2312,BIG5');
		//解决返回的json字符串中返回了BOM头的不可见字符(某些编辑器默认会加上BOM头)
		$result = trim($result,chr(239).chr(187).chr(191));
		//获取状态码
		$httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
		//关闭URL请求
		curl_close($curl);
		//是否返回请求码
		if ($refcode) {
			$html = array("httpcode" => $httpcode, "result" => $result);
			return $html;
		} else {
			return $result;
		}
	}
}
?>

2、在app\common目录创建一个Pay.php支付类,代码如下:

<?php
namespace app\common;
use think\Db;
use think\Exception;
/**
 * 支付类
 */
class Pay {
 public $account = array(
            "appid"      => "支付宝的APPID",
            "privateKey" => "商户设置的私钥",
            "publicKey"  => "支付宝自动生成的公钥"
        );
 public function startPay($params){
		//创建支付宝对象
		$alipayobj= new \pay\Alipay($this->account);
		$result=$alipayobj->facepay($params);
		($result['code']==0) && exit('支付通道维护,'.$result['msg']);		
		//接收支付宝返回的订单编号和支付二维码
		$params['out_trade_no'] = $result['data']['out_trade_no'];
		$params['qr_code'] = $result['data']['qr_code'];
		return $this->buildRequestForm(url('@Index/Index/facepay'),$params);
    }

    /**
     * 服务器回调
     */
    public function notify_callback($params){
		//查询数据库订单状态
		$order = OrderModel::where('number', $params['out_trade_no'])->find();
		if(!$order){
			die('不存在编号['.$params['out_trade_no'].']的订单!');
		}
		//防止恶意刷新加钱
		if ($order['status'] == 1) { 
			//直接返回给上游 success 或者 OK 之类的
			exit('success');
		}
		
		//创建支付宝对象
         $alipayobj= new \pay\Alipay($this->account);
		//调用异步通知验证
		$verifyresult=$alipayobj->notify($params);
		if ($verifyresult) {
			//实际付款金额
			$money = (float)$params['total_amount'];
			//这里完成网站的业务逻辑
            exit('success');
		}else{			
			exit('fail'); //返回失败 继续补单
		}
    }
    /**
     * 建立跳转请求表单
     * @param string $url 数据提交跳转到的URL
     * @param array $data 请求参数数组
     * @param string $method 提交方式:post或get 默认post
     * @return string 提交表单的HTML文本
     */
    function buildRequestForm($url, $data, $method = 'post', $button_name = '确定', $show = false) {
        $html = "<form id='requestForm' name='requestForm' action='" . $url . "' method='" . $method . "'>";
        while (list($key, $val) = each($data)) {
            $html.= "<input type='hidden' name='" . $key . "' value='" . $val . "' />";
        }
        $display = $show ? "style='display:block;'" : "style='display:none;'";
        $html.= "<input type='submit' value='" . $button_name . "' " . $display . "></form>";
        $html.= "<script>document.forms['requestForm'].submit();</script>";
        return $html;
    }
	
}
?>

3、controller控制器里发起支付请求

<?php
namespace app\index\controller;
use think\Controller;
use think\Cache;
use think\Session;
use think\Request;
use app\common\Pay;

class Index extends Controller
{ 
    //接口提交
    public function submit() {
        $array=array(
		    "pay_id" => "订单编号", 
		    "money"  => "订单金额",
            "notify_url"  => "异步通知地址",
	    );
        $res  = (new Pay)->startPay($array);
		exit($res);
    }
    //当面付页面
    public function facepay(){
		$data=input('');
		!isset($data['pay_id']) && $this->error('订单编号不能为空');
		empty(trim($data['pay_id'])) && $this->error('订单编号不能为空');
		!isset($data['money']) && $this->error('金额不能为空');
		empty(trim($data['money'])) && $this->error('金额不能为空');
		!isset($data['notify_url']) && $this->error('异步通知地址不能为空');
		empty(trim($data['notify_url'])) && $this->error('异步通知地址不能为空');		
		//生成二维码
		$qrcode='https://my.tv.sohu.com/user/a/wvideo/getQRCode.do?width=200&height=200&text='.trim($data['qr_code']);
		$this->assign('money',trim($data['money']));
		$this->assign('qrcode',$qrcode);
		return view();
	}
}

到这里其实已经差不多了,但还有几个功能需要完成:

  • 显示当面付二维码的页面facepay.html,即facepay()方法的视图。
  • 支付宝当面付是没有同步通知功能的,所以要自己解决,解决思路就是在facepay.html页面使用js定时器,定时查询订单状态,如果订单完成,就跳转到同步通知页面。

最后附上我开发的效果图:

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值