Laravel 集成支付宝电脑网站支付

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jiongxian1/article/details/88808768

前期准备:

1.下载SDK,里面还有Demo,可以参照Demo里面的内容快速接入

支付宝电脑网站支付SDK&Demo

2.把下载的zip文件解压,放到项目目录里,这里作者放在app文件夹里,方便查看

3.因为Laravel框架的原因,文件的入口在/public/index.php这个文件这里,所以所有需要require的文件的路径都要在相应的文件里修改一下,这里作者的路劲修改后举例为:

require_once '../app/AliPay/config.php';

为了方便使用,对部分类文件使用命名空间,这样使用起来目标明确,虽然会额外费点功夫,但作者觉得思路最重要。

因为是支付这块的内容,然后为了区别微信支付还有网银支付,所以命名空间定为:

namespace APP\Pay\Alipay;

4.为了方便日后管理,把一些相关的类文件集中起来放置在一个文件夹里,文件夹名为 qy ,意为为企业支付建立的这个文件夹

qy文件夹内有2个目录:

buildermodel : 存放构造供接口使用的builder

service : 存放相关的接口实现类

这里对部分需要引入命名空间的文件列举一下(目前只做了支付,退款还没做,所以退款涉及的类文件不做举例)

//支付宝电脑网站支付(alipay.trade.page.pay)接口业务参数封装
/app/Alipay/qy/buildermodel/AlipayTradePagePayContentBuilder.php
//支付宝电脑网站支付查询接口(alipay.trade.query)接口业务参数封装
/app/Alipay/qy/buildermodel/AlipayTradeQueryContentBuilder.php

//支付宝电脑网站支付接口实现类
/app/Alipay/qy/service/AlipayTradeServicePC.php

这里特别要注意,在引入命名空间后,要执行一下composer的自动加载类命令(应该是这个意思)

首先,进入项目的根目录,然后执行下面的命令

composer dump-autoload

这个命令很重要,不然上面引入命名空间的类文件无法被识别

作者就在这里吃了大亏,说到底是对这个框架不熟悉

5.配置支付宝接口需要的基础信息,在/app/Alipay/config.php这个文件里设置,主要为以下几个变量

//应用ID,您的APPID。
'app_    id' => "",

//收款支付宝账号
'seller_id' => "",
		
//支付宝公钥
'alipay_public_key' => "",

//商户私钥
'merchant_private_key' => "",

//异步通知地址,只有扫码支付预下单可用
'notify_url' =>     'http://'.$_SERVER['HTTP_HOST']."/Notify/AlipayCallBack",

//同步通知地址,个人认为相当于一个跳转回原页面的中间页,好像是可以不设置,会自动跳回员阿里的下单页面
'return_url' => 'http://'.$_SERVER['HTTP_HOST']."/user/check",

开始调用接口

6.前端 - 传递下单的金额,调用后端下单接口,即第7步的接口

//充值按钮绑定事件
$("#btnRecharge").click(function(){
	$_money = $("input#money");
	var money = $_money.val();
	var min = "1";
	var msg = "";
	if(money==""){
		msg = "请填写充值金额";
	}
	if(parseFloat(money)<parseFloat(min)){
		msg = "充值金额不能小于充值最小金额,请重新填写";
	}
	if(msg!=""){
		$_money.val(money).focus();
		alert(msg);
		return false;
	}
	var send = {_token:$('meta[name="csrf-token"]').attr('content'),money:money};
	$.ajax({
		url : "/user/AlipayOrder",
		type : "POST",
		datatype : "json",
		data : send,
		success : function(json){
                        //下单成功,直接输出下单结果
			document.write(json);
		},
		error : function(xhr){
			//...
		}
	});
});

7.后端 - 生成支付订单,打印(返回)下单结果(不知道这样子描述是否有误。。)


use APP\Pay\Alipay\AlipayTradeServicePC;
use APP\Pay\Alipay\AlipayTradePagePayContentBuilder;
use APP\Pay\Alipay\AlipayTradeQueryContentBuilder;
use APP\Pay\Alipay\Tool;

public function AlipayOrder(Request $request){
	//检查参数是否正确
	if(!$request->has('money') || floatval($request->money)<floatval(config('recharge.minRecharge'))){
		$this->jsonError('参数错误');
		return false;
	}
	//付款金额,必填
	$money = $request->money;
	$totalAmount = 0.01;
	require_once '../app/AliPay/config.php';
	
	//商户订单号,商户网站订单系统中唯一订单号,必填
        //这里自己按需要写个方法生成,方便多个地方调用
	$outTradeNo = $this->getOutTradeNo(16);
	//订单名称,必填
	$subject = $this->getSubject();
	//商品描述,可空
	$body = $this->getBody();
	//最晚付款时间,逾期将关闭交易
	$timeExpress = "5m";
	$selleId = $config['seller_id'];
	//构造参数
	$paybuilder = new AlipayTradePagePayContentBuilder();
	$paybuilder->setBody($body);
	$paybuilder->setSubject($subject);
	$paybuilder->setTotalAmount($totalAmount);
	$paybuilder->setOutTradeNo($outTradeNo);
	$paybuilder->setTimeExpress($timeExpress);

	$aop = new AlipayTradeServicePC($config);

	/**
	 * pagePay 电脑网站支付请求
	 * @param $builder 业务参数,使用buildmodel中的对象生成。
	 * @param $return_url 同步跳转地址,公网可以访问
	 * @param $notify_url 异步通知地址,公网可以访问
	 * @return $response 支付宝返回的信息
	*/
	$response = $aop->pagePay($paybuilder,$config['return_url'],$config['notify_url']);
	
	//预生成充值订单 - PC端
	$remarks = json_encode(Array('app_id'=>Config('recharge.alipayPartner'), 'seller_id'=>$selleId));
	$this->addChargeRecord($money, "alipay", $outTradeNo, 0, $remarks);
	
	//输出表单
	var_dump($response);
}

8.后端 - 支付结果异步通知接口 

use APP\Pay\Alipay\AlipayTradeServicePC;

public function AliPayCallbackHandler(Request $request){
	require_once '../app/AliPay/config.php';

	$arr=$request->all();
	$alipaySevice = new AlipayTradeServicePC($config); 
	//$alipaySevice->writeLog(var_export($_POST,true));
	$result = $alipaySevice->check($arr);

	if($result) {
		//商户订单号
		$out_trade_no = $arr['out_trade_no'];
		//支付宝交易号
		$trade_no = $arr['trade_no'];
		//交易状态
		$trade_status = $arr['trade_status'];
		$buyer_id = $arr['buyer_id'];
		$seller_id = $arr['seller_id'];
		$app_id = $arr['app_id'];
		$total_amount = $arr['total_amount'];

		if($arr['trade_status'] == 'TRADE_FINISHED') {
			//注意:
			//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
			$this->UserCharge($total_amount,'alipay',$out_trade_no, $buyer_id, 2, $seller_id, $app_id);
		}
		else if ($arr['trade_status'] == 'TRADE_SUCCESS') {
			//注意:
			//付款完成后,支付宝系统发送该交易状态通知
			$this->UserCharge($total_amount,'alipay',$out_trade_no, $buyer_id, 1, $seller_id, $app_id);
		}
		echo "success";	//请不要修改或删除
	}else {
		//验证失败
		echo "fail";
	}
}

/**
 * [会员充值回调处理,调用UserPayController写好的回调处理接口,与查询接口共用]
 * @param  int $money    [订单金额]
 * @param  int $payType [订单类型]
 * @param  int $outTradeNo [商户订单号]
 * @param  int $payAccount [支付账号]
 * @param  int $status [支付状态]
 */
function UserCharge($money, $payType, $outTradeNo, $payAccount, $status, $seller_id, $app_id){
	$UserPayController = new UserPayController();
	$UserPayController->UserCharge($money, $payType, $outTradeNo, $payAccount, $status, $seller_id, $app_id);
}


9.支付接口、查询接口对应的回调处理

/**
 * [会员充值回调处理]
 * @param  int $money    [订单金额]
 * @param  int $payType [订单类型 weixin|alipay]
 * @param  int $outTradeNo [商户订单号]
 * @param  int $payAccount [支付账号]
 * @param  int $status [支付状态]
 * @param  int $appID [商务ID]
 * @param  int $smId [收款支付宝账号对应的支付宝唯一用户号/微信支付分配的商户号]
 */
function UserCharge($money, $payType, $outTradeNo, $payAccount, $status, $smId, $appID=null){
	//支付类型 判断设置 日志类型
	$payLogType = "";
	if($payType=="alipay") $payLogType='Alipay';
	else if($payType=="weixin") $payLogType='WechatPay';
        //防止异步支付通知接口与查询接口同时调用回调处理,使用事务,通知在事务内使用lockForUpdate阻止同时读取修改用户的信息
	DB::beginTransaction();
	$Function= new Function();
        //按照商务订单号获取预充值订单
	$record = $Function->getUserChargeRecordByOutTradeNo($outTradeNo);
	$record = json_decode(json_encode($record),true);
	$remarks = json_decode($record['remarks'],true);
	if($record['status']!="0"){
		//已经处理过充值回调,直接返回true
		//防止多次处理
		return true;
	}
	
	/* -------------------- 检验 开始 -------------------- */
	$msg = "";
	$checkResult=1;
	//检验1:out_trade_no 商务订单号
	if($outTradeNo != $record['trade_no']){
		$msg = "商务订单号参数错误";
		$checkResult = 0;
	}
	//检验2:total_amount/total_fee 实际金额
	if($checkResult==1 && floatval($money) != floatval($record['money'])){
		$msg = "订单金额参数错误";
		$checkResult = 0;
	}
	//检验3:app_id/appid 商务号ID
	//支付宝查询订单时未返回这个参数,跳过这个检查
	if($checkResult==1 && $appID != null){
		if($payType == "alipay" && $appID != $remarks['app_id']){
			$msg = "支付宝分配给开发者的应用ID参数错误";
			$checkResult = 0;
		} else if($payType == "weixin" && $appID != $remarks['appid']){
			$msg = "微信支付分配的公众账号ID参数错误";
			$checkResult = 0;
		}
	}
	//检验4:seller_id/mch_id
	if($checkResult==1 && $payType == "alipay" && $smId != $remarks['seller_id']){
		$msg = "收款支付宝账号参数错误";
		$checkResult = 0;
	} else if($checkResult==1 && $payType == "weixin" && $smId != $remarks['mch_id']){
		$msg = "商户号参数错误";
		$checkResult = 0;
	}
	if($checkResult == 0){
		Log::error("用户充值失败,out_trade_no为{$outTradeNo},原因为{$msg}");
		$this->writePayLog("用户充值失败,out_trade_no为{$outTradeNo},原因为{$msg}", $payLogType);
        //更新用户预充值记录信息
		$res = ...;
		if(!$res){
			DB::rollback();
			Log::error("用户充值失败,更新充值表失,out_trade_no为{$outTradeNo},原因为{$msg}");
			$this->writePayLog("用户充值失败,更新充值表失败,out_trade_no为{$outTradeNo},原因为{$msg}", $payLogType);
		} else{
			DB::commit();
			Log::info("用户充值失败,更新充值表成功,out_trade_no为{$outTradeNo},原因为{$msg}");
			$this->writePayLog("用户充值失败,更新充值表成功,out_trade_no为{$outTradeNo},原因为{$msg}", $payLogType);
		}
		return Array('result'=>0,'msg'=>$msg);
	}
	/* -------------------- 检验 结束 -------------------- */
	
	//检验通过,开始处理业务流程
	//更新充值表
        ...
	//更新用户余额
	...
	DB::commit();
}

10.实现查询接口,作者在第4步设置的同步通知地址里使用。该地址会传递商务订单号(out_trade_no),使用该参数进行查询轮询

前端  - 调用查询接口

$(function(){
	//支付宝轮询充值结果
	function queryOrderAliPay() {
		var out_trade_no = URL.getUrlParam('out_trade_no');
		if(out_trade_no==null) return false;
		load();
		//设置每隔500毫秒执行一次load() 方法  轮询订单
		var myIntval = setInterval(function () {
			load()
		}, 500);

		function load() {
			$.ajax({
				type: "POST",
				url: "{{ route( 'filling.queryOrderAliPay')}}",
				dataType: "json",
				data: {'_token': '{{csrf_token()}}',out_trade_no:out_trade_no},
				success: function (json) {
					if(json.trade_status  == 'TRADE_SUCCESS' || json.trade_status  == 'TRADE_FINISHED'){
						setTimeout(function(){
							window.location.href="..."; //充值成功,跳转回充值页面
							clearInterval(myIntval);
						},500);
					}
				}
			});
		}
	}
	queryOrderAliPay();
});

后端 - 实现查询功能

//查询支付宝订单
public function queryOrderAliPay(Request $request){
	require_once '../app/AliPay/config.php';
	
	//商户订单号,商户网站订单系统中唯一订单号
	$out_trade_no = trim($request->out_trade_no);
	//构造参数
	$builder = new AlipayTradeQueryContentBuilder();
	$builder->setOutTradeNo($out_trade_no);

	$aop = new AlipayTradeServicePC($config);
	//调用支付宝查询接口
	$response = $aop->Query($builder);
	$response = json_decode(json_encode($aop->query($builder)), true);
	
	if(array_key_exists("code", $response) && $response['code']==10000){
		if($response['trade_status']=="TRADE_SUCCESS" || $response['trade_status']=="TRADE_FINISHED"){
			DB::beginTransaction();
			$outTradeNo = $response['out_trade_no'];
			$record = ...; //按商务订单号获取预生成的订单信息
			$record = json_decode(json_encode($record),true);
			if($record['status']==="0"){
				Log::info(" 充值后轮询更新用户信息 ");
				$status = $response['trade_status']=="TRADE_SUCCESS" ? 1 : ($response['trade_status']=="TRADE_FINISHED" ? 2 : 0);
				$this->UserCharge(floatval($response['total_amount']),'alipay',$outTradeNo,$response['buyer_user_id'],$status,$config['seller_id'],$config['app_id']);
			}
			DB::commit();
			
			return json_encode([
				'return_code'=>$response["code"],
				'trade_status'=>$response["trade_status"]
			],true);
		}
	}
}

后续有时间再做完善这篇文章

猜你喜欢

转载自blog.csdn.net/jiongxian1/article/details/88808768
今日推荐