PHP 支付PC端扫码支付、APP接口调起支付宝支付、微信公众号接入支付宝支付

第一:第三方支付原理

第二:支付接口申请流程

地址:https://docs.open.alipay.com/270/105899/ ;

参考地址:https://blog.csdn.net/november22/article/details/54233269#comments

1. 注册是支付宝商家账号 —— 注册地址:https://memberprod.alipay.com/account/reg/index.htm

2. 注册成功后,找到蚂蚁金服开放平台,点击支付应用

3、创建应用

4、 添加应用中的电脑支付功能

5、开发配置

说明:

(1)其中的支付宝网关地址不用改;

(2)应用网关设置为你网站的域名(如:https://www.xxx.com/),所以这个位置的设置就决定了你的网站首先要上线、然后域           名解析到服务器上才可以申请支付宝的支付接口;

(3)授权回调地址不用设置,一般如果涉及到第三方访问应用的时候才需要设置;

(4)设置应用公钥 —— 获取应用公钥【支付宝公钥】和私钥【商户私钥】的参考文档。

     下载地址:https://docs.open.alipay.com/291/105971

     

下载、解压,然后生成秘钥,注意:文件夹的目录中一定不能带有中文字符吗,不然工具不能使用,这个步骤我就不一步步的说了。,将生成的公钥如下图,配置到查看应用公钥那儿,第一次登陆的应该现实的是配置公钥。点击确定,然后会生成支付宝公钥【这里一定 不要配置错误了,不然后面的程序会跑不起来的】

6、 当配置好后,直接提交审核,之后就等待审核了。
 

7、配置参数

1.  app_id :应用ID,在这个地址中查看:https://openhome.alipay.com/platform/keyManage.htm;

2. merchant_private_key:商户私钥。这个在获取应用公钥和私钥时就已经获取到,公钥上传到支付宝中,
   私钥就在项目中与之匹配验证;

3. notify_url : 异步通知回调地址。支付成功后,支付宝回调服务器的地址;

4. return_url:同步跳转地址。支付成功回调了服务器的地址后,自动跳转到目标地址;

5. charset:编码格式,一般为“UTF-8”;

6. sign_type:签名方式,一般为“RSA2”;

7. gatewayUrl:支付宝网关,https://openapi.alipay.com/gateway.do;

8. alipay_public_key:支付宝公钥,在这个地址查看:https://openhome.alipay.com/platform/keyManage.htm。

第三:PC扫码支付实现【以TP5为例】

1、下载支付宝支付demo,下载地址:https://docs.open.alipay.com/270/106291/

2、解压放入Vendor文件夹下

3、在配置文件中配置支付宝配置参数

【app_id、alipay_public_key、merchant_private_key、charset、sign_type、alipay_public_key与APP支付宝支付的配置参数一致】

'alipay' => [
        //应用ID,您的APPID。
        'app_id' => "",
    
        //商户私钥
        'merchant_private_key' => "",
    
        //异步通知地址
        'notify_url' => \think\Request::instance()->domain()."/order/pay/alipay_notify",
    
        //同步跳转
        'return_url' => \think\Request::instance()->domain()."/order/pay/alipay_notify",
    
        //编码格式
        'charset' => "UTF-8",
    
        //签名方式
        'sign_type'=>"RSA2",
    
        //支付宝网关
        'gatewayUrl' => "https://openapi.alipay.com/gateway.do",
    
        //支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
        'alipay_public_key' => "",
    ],

4、生成支付二维码

 // 支付
    public function orderpay($ordersn)
    {
        $config = Config::get('alipay');
        $order = db('shop_order');
        $where = ['uid'=>$this->uid,'ordersn'=>$ordersn];
        $orderinfo = $order->field('ordersn,pay_price,status')->where($where)->find();
        if(empty($orderinfo)) return $this->error('抱歉,指定订单找不到!');
        if($orderinfo['status']==1) return $this->error('订单已支付成功,请前往订单中心查看!');
        //商户订单号,商户网站订单系统中唯一订单号,必填
        $out_trade_no = trim($orderinfo['ordersn']);

        //订单名称,必填
        $subject = empty($orderinfo['title']) ? '购买雄松智课课程' : $orderinfo;

        //付款金额,必填
        $total_amount = trim($orderinfo['pay_price']);

        //商品描述,可空
        $body = empty($orderinfo['remark']) ? '' : $orderinfo['remark'];
        
        Vendor('alipay.AopSdk');
        Vendor('alipay.pagepay.buildermodel.AlipayTradePagePayContentBuilder');
        Vendor('alipay.pagepay.service.AlipayTradeService');


        // 构造参数
        $payRequestBuilder = new \AlipayTradePagePayContentBuilder();
        $payRequestBuilder->setBody($body);
        $payRequestBuilder->setSubject($subject);
        $payRequestBuilder->setTotalAmount($total_amount);
        $payRequestBuilder->setOutTradeNo($out_trade_no);
        $aop = new \AlipayTradeService($config);

        /**
         * pagePay 电脑网站支付请求
         * @param $builder 业务参数,使用buildmodel中的对象生成。
         * @param $return_url 同步跳转地址,公网可以访问
         * @param $notify_url 异步通知地址,公网可以访问
         * @return $response 支付宝返回的信息
        */
        $response = 
        $aop->pagePay($payRequestBuilder,$config['return_url'],$config['notify_url']);

        //输出二维码
        // var_dump($response);
    }

HTML代码:

<div class="outer-wrap">
			<div class="inner-wrap">
				<p class="order-txt">订单提交成功!订单号:<span id="ordersn">{$ordersn}</span></p>
				<div class="xzyhq-wrap wx_pay" style="display:none">
					<div class="zf-wrap">
						<span class="zf-wrap-txt1">微信支付</span>
						<span class="zf-wrap-txt2">距离二维码过期还剩<i id="count_down">60</i>秒,过期后请刷新页面重新获取二维码。</span>
					</div>
					<div class="syt-content">
						<img src="/static/mba/images/syt_wxzf_ewm.png"/>
					</div>

				</div>
				<div class="xzyhq-wrap">
					<div class="xzyhq-title">选择其他支付方式</div>
					<div class="xzzf-list-wrap">
						<div class="xzzf-list-inner">
							<div class="xzzf-list xzzf-list2" data-flag="wechatpay">
								<i class="xxzf-icon"></i>
								<span>微信支付</span>
							</div>
							<div class="xzzf-list" data-flag="alipay">
								<i class="zfb-icon"></i>
								<span>支付宝支付</span>
							</div>
							<div class="xzzf-list margin-r0" data-flag="bank_cart">
								<i class="yhk-icon"></i>
								<span>银行卡支付</span>
							</div>	
						</div>
					</div>
				</div>
			</div>
		</div>

JavaScript代码:

JavaScript:
$(".xzzf-list").on("click",function(){
	var paytype=$(this).attr('data-flag');
	if (paytype=='wechatpay') {
		$('.wx_pay').css('display','block');
		default_paytype();
	}else if(paytype == 'alipay'){
		$('.wx_pay').css('display','none');
			if(pay_flag){
			   window.clearInterval(paycheck); //销毁定时器
		    }
		var url = "{:url('order/pay/orderpay')}";
		var ordersn = $("#ordersn").html();
		console.log(ordersn);
		url += '?ordersn=' + ordersn;
		window.location.href=url;
	}
    else
    {

    }
});

5、支付宝回调,更新支付状态

public function alipay_notify()
    {
        $arr = Request::instance()->param(); //file_get_contents("php://input");
        empty($arr) ? $arr=$_GET : $arr;
        $config = Config::get('alipay');
        Vendor('alipay.pagepay.service.AlipayTradeService');
        $alipaySevice = new \AlipayTradeService($config);
        $result = $alipaySevice->check($arr);
        /* 实际验证过程建议商户添加以下校验。
        1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
        2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
        3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
        4、验证app_id是否为该商户本身。
        */
        if($result) {//验证成功
            //请在这里加上商户的业务逻辑程序代
            //——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
            //获取支付宝的通知返回参数,可参考技术文档中服务器异步通知参数列表

            //商户订单号
            $out_trade_no = $arr['out_trade_no'];

            //支付宝交易号
            $trade_no = $arr['trade_no'];

            // 交易金额
            $money = $arr['total_amount'];

            $order = db('shop_order');
            $order_info = $order->where("ordersn='{$out_trade_no}'")->find();

            // 判断是否是同一订单
            if($out_trade_no != $order_info['ordersn']){
                //验证失败
                echo "fail";
            }
            if($money == $order_info['pay_price'])
            {
                if($order_info['status'] == 0)
                {
                    //更新支付状态和支付时间
                    $OrderObj = new Order();
                    $result = $OrderObj->UpdateOrder($order_info['ordersn'],1);
                }
                if(empty($out_trade_no)) $this->error('订单状态已改变,请到个人中心查看!');
                $OrderObj = new Order();
                $order = $OrderObj->GetOneOrder($out_trade_no,$this->uid);
                if(empty($order)) $this->error('订单状态已改变,请到个人中心查看!');
                $this->assign('order',$order);
                return view('pay/ordersuccess');
            }else{
                //验证失败
                echo "fail";
            }
            echo "success"; //请不要修改或删除
        }else {
            //验证失败
            echo "fail";
        }
    }

第四:APP实现支付宝支付接口

参考:https://www.jianshu.com/p/178324ca2561

APP官方文档:https://docs.open.alipay.com/204/105465/

1、配置config文件的信息

【app_id、alipay_public_key、merchant_private_key、charset、sign_type、alipay_public_key与PC端扫码支付的配置参数一致】

'appAlipay' => [
        //应用ID,您的APPID。
        'app_id' => "",//  
    
        //商户私钥
        'merchant_private_key' => "",
    
        //支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
        'alipay_public_key' => "",
    
        //异步通知地址【设置该接口无需token验证】
        'notify_url' => \think\Request::instance()- 
                         >domain()."/app/paynotify/alipy_notify",
    
        //同步跳转【设置该接口无需token验证】
        'return_url' => \think\Request::instance()- 
                         >domain()."/app/paynotify/alipy_notify",
    
        //编码格式
        'charset' => "UTF-8",
    
        //签名方式
        'sign_type'=>"RSA2",
        
        //支付宝网关
        'gatewayUrl' => "https://openapi.alipay.com/gateway.do",
    
        //收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
        'seller_id' => '',
        
        //商品信息
        'body'=>"雄松智课",
        
        //商品信息
        'subject'=>"智课方案",
        
        'format' =>'json',
    ],

2、商户APP端请求商户服务器接口,提交订单数据。

3、商户服务器端接收数据,然后对数据进行签名,返回请求参数到商户APP端

//引入Alipay的SDK
vendor('alipay/aop/AopClient');
vendor('alipay/aop/request/AlipayTradeAppPayRequest');


    /**
     * 支付宝支付[添加订单],商户服务器端接收数据,对数据进行签名,返回请求参数到商户APP端
     */
    public function aliBuyPlans()
    {
        $rule = [
            'orderSn'    => 'require',
            'plans'      => 'require',
            'payPrice'   => 'require|float|egt:0.01',
        ];
        $msg = [
            'orderSn.require'   => '订单号不能为空',
            'plans.require'     => '请选择购买方案信息',
            'payPrice.require'  => '请填写正确的金额',
            'payPrice.float'    => '请填写正确的金额',
        ];
         
        //验证
        $data = $this->params;
        $validate = new Validate($rule, $msg);
        !$validate->check($data) and $this->returnError($validate->getError());
        //购买方案
        $uid = $this->user['id'];
        $data['payPrice'] = number_format($data['payPrice'],2);
        $plans  = json_decode(htmlspecialchars_decode($this->params['plans']), true);
        if (!is_array($plans)) $this->returnError('方案信息格式错误');
        
        // 启动事务
        Db::startTrans();
        
        $title = '推荐方案';
        $body = config('appAlipay.body');
        $subject = config('appAlipay.subject');
        $orderModel = new OrderModel();
        $orderInfo = $orderModel->checkOrdsn($data['orderSn'], $uid);
        // 该订单号不存在
        if (empty($orderInfo))
        {
            $orderid = (int)$orderModel->addOrder($uid, $data['orderSn'], $data['payPrice'], 1, $title,0);
            $flag = $orderModel->addOrderGoods($uid, $orderid, $plans);
            if ($flag)
            {
                // 提交事务
                Db::commit();
                $wxData['orderId'] = $orderid;
                $wxData['orderSn'] = $data['orderSn'];
                //数据进行签名,返回请求参数到商户APP端
                $orderString = $this->alipay($body, $subject, $data['orderSn'], $data['payPrice']);
                $wxData['orderString'] = $orderString;
                $this->returnSuccess('添加订单成功',$wxData);
            }
            else
            {
                // 回滚事务
                Db::rollback();
                $this->returnError('添加订单失败');
            }
        }
        else
        {
            $wxData['orderId'] = $orderInfo['id'];
            $wxData['orderSn'] = $orderInfo['ordersn'];
            //数据进行签名,返回请求参数到商户APP端
            $orderString = $this->alipay($body, $subject, $data['orderSn'], $data['payPrice']);
            $wxData['orderString'] = $orderString;
            $this->returnSuccess('添加订单成功',$wxData);
        }
    }



    /**
     * 对数据进行签名,返回请求参数到商户APP端
     * @param string $body 商品描述
     * @param string $subject 商品名称
     * @param string $outTradeNo 交易订单号
     * @param float $totalAmount 支付金额
     */
    protected function alipay($body, $subject, $outTradeNo, $totalAmount)
    {
        $aop = new \AopClient();
        //正式环境网关 https://openapi.alipay.com/gateway.do
        $aop->gatewayUrl = config('appAlipay.gatewayUrl');//沙箱环境网关
        
        $aop->format = "json";
        $aop->charset = "UTF-8";
        $aop->signType = "RSA2";
        $aop->appId = config('appAlipay.app_id');
        $aop->rsaPrivateKey = config('appAlipay.merchant_private_key');//rsaPrivateKey私钥
        $aop->alipayrsaPublicKey = config('appAlipay.alipay_public_key');//alipayrsaPublicKey请填写支付宝公钥
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
        $request = new \AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数,这里只需要传入业务参数
       $bizcontent =  "{\"body\":\"{$body}\","
                    . "\"subject\": \"{$subject}\","
                    . "\"out_trade_no\": \"{$outTradeNo}\","
                    . "\"timeout_express\": \"30m\","
                    . "\"total_amount\": \"{$totalAmount}\","
                    . "\"product_code\":\"QUICK_MSECURITY_PAY\""
                    . "}";
        $request->setNotifyUrl(config('appAlipay.notify_url'));//异步通知地址
        $request->setBizContent($bizcontent);
        //这里和普通的接口调用不同,使用的是sdkExecute,sdkExecute()方法,作用生成签名,
        $response = $aop->sdkExecute($request);
        
        //htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义,
        //实际打印到日志以及http传输不会有这个问题
        //就是orderString 可以直接给客户端请求,无需再做处理。
        //$response =  json_encode(array("res"=>htmlspecialchars($response)));
         
        return $response;
    }

 说明:sdkExecute()方法,作用生成签名,详细步骤如下:

      将请求参数组装分下列3步,以最后第三步获取到的请求为准。

1)将请求参数的键按字典排序,然后按照key=value&key=value方式拼接,得到未签名原始字符串如下:

app_id=2015052600090779&biz_content={"timeout_express":"30m","product_code":"QUICK_MSECURITY_PAY","total_amount":"0.01","subject":"1","body":"我是测试数据","out_trade_no":"IQJZSRC1YMQB5HU"}&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http://domain.merchant.com/payment_notify&sign_type=RSA2&timestamp=2016-08-25 20:26:31&version=1.0

2)再对原始字符串进行签名

app_id=2015052600090779&biz_content={"timeout_express":"30m","product_code":"QUICK_MSECURITY_PAY","total_amount":"0.01","subject":"1","body":"我是测试数据","out_trade_no":"IQJZSRC1YMQB5HU"}&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http://domain.merchant.com/payment_notify&sign_type=RSA2&timestamp=2016-08-25 20:26:31&version=1.0&sign=cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj+y48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp/M45w1ZsDOiduBbduGfRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ77Lo5J0PpUUWwyQGt0M4cj8g=

3)最后对请求字符串的所有一级value(biz_content作为一个value)进行encode,编码格式按请求串中的charset为准,没传charset按UTF-8处理,获得最终的请求字符串:

app_id=2015052600090779&biz_content=%7B%22timeout_express%22%3A%2230m%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22out_trade_no%22%3A%22IQJZSRC1YMQB5HU%22%7D&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fdomain.merchant.com%2Fpayment_notify&sign_type=RSA2&timestamp=2016-08-25%2020%3A26%3A31&version=1.0&sign=cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj%2By48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp%2FM45w1ZsDOiduBbduGfRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ77Lo5J0PpUUWwyQGt0M4cj8g%3D

4、商户APP接收从商户服务器端返回的请求参数,然后调起支付宝支付面板。

若用户支付成功,支付宝会同步给商户APP端返回一个支付结果。相应地,支付宝也会通过异步通知给商户服务器端返回一个支付结果。

注意:由于同步通知和异步通知都可以作为支付完成的凭证,且异步通知支付宝一定会确保发送给商户服务端。为了简化集成流程,商户可以将同步结果仅仅作为一个支付结束的通知(忽略执行校验),实际支付是否成功,完全依赖服务端异步通知。

5、服务端异步通知处理机制(支付宝主动发起通知,该方式才会被启用)

官方接口文档:https://docs.open.alipay.com/204/105301/

注意点:

1)必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;

2)支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:$_POST[‘out_trade_no’];

3)程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);

4)当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。

/**
     * 支付异步通知
     */
    public function notify()
    {
        $aop = new \AopClient;
        $aop->alipayrsaPublicKey = config('alipay.alipay_public_key');
        $flag = $aop->rsaCheckV1($_POST, NULL, "RSA2");
        
        if($flag)
        {
            //校验通知数据的正确性
            $out_trade_no = $_POST['out_trade_no'];  //商户订单号
            $trade_no = $_POST['trade_no'];          //支付宝交易号
            $trade_status = $_POST['trade_status'];  //交易状态
            $total_amount = $_POST['total_amount'];  //订单的实际金额
            $app_id = $_POST['app_id'];
            //验证app_id是否为该商户本身
            if($app_id != $this->config['app_id'])
            {
                echo 'fail';
            }
            
            //只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
            if ($trade_status != 'TRADE_FINISHED' && $trade_status != 'TRADE_SUCCESS')
            {
                echo 'fail';
            }
            
            //校验订单的正确性
            if(!empty($out_trade_no))
            {
            
               //1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号;
              
               //2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额);
            
               //3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)。
            
               //上述1、2、3有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
            
               //校验成功后在response中返回success,校验失败返回failure
            
           }
            
           echo 'fail';
        }
        echo 'fail';//验证签名失败
    }


6、当商户APP端接收到支付宝的同步返回结果为成功时,商户APP端再请求商户服务器端API,判断订单最终支付结果,并做出最终响应。


第五:微信公众号接入支付宝支付

地址:https://www.cnblogs.com/luokakale/p/8029614.html

猜你喜欢

转载自blog.csdn.net/a898712940/article/details/89632906