支付寶當面付對接

支付寶當面付對接

當面付顧名思義,面對面付款,幫助商家在線下消費場景下實現快速收款;當面付產品支援條碼支付和掃碼支付兩種付款方式。 我們這裡對接的就是掃碼支付 掃碼支付,指用戶打開支付寶錢包中的「掃一掃」功能,掃描商家展示在某收銀場景下的二維碼並進行支付的模式。該模式適用於線下實體店支付、面對面支付等場景。業務流程如下圖所示: 由於當面付的簽約非常簡單,允許個體工商戶/個人商戶簽約。所以該方式也被大量用於線上的掃碼支付,由於該方式違反了支付寶的相關條款,有一定風險,咱作為技術交流,暫且先拋開這個問題。

作為技術對接,即使你沒有簽約當面付產品,也是可以進行開發的。 支付能力直接涉及到交易與資金,為了方便開放者調試支付能力,開放平台已經準備好沙箱環境,包括沙箱環境帳號和沙箱版支付寶錢包,這樣開發者就可以在沙箱環境調試了。點擊了解如何接入沙箱接入沙箱環境 所以我這邊開發使用的是沙箱環境,畢竟裡面好多錢,隨便用。 首先先下載相應的開發語言的sdk 下載:https://docs.open.alipay.com/194/105201/ 掃碼支付文檔:https://docs.open.alipay.com/194/106078/

配置密鑰 為了保證交易雙方(商戶和支付寶)的身份和數據安全,開發者在調用接口前,需要配置雙方密鑰,對交易數據進行雙方校驗。 下載支付寶開放平台開發助手進行密鑰生成。 生成密鑰後,開發者需要在開放平台開發者中心進行密鑰配置,配置完成後可以獲取支付寶公鑰 設計接入 由於我這邊的設計不需要用到輪詢(後面會說),所以沒有加上 以下是我業務中的相關代碼

    public function pay(){

        if (request()->isPost()) {
            
            // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
            // 需保证商户系统端不能重复,建议通过数据库sequence生成,
            $uid = Session::get('sq.uid');
            $outTradeNo = order\_num() . $uid;

            // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
            $subject = '聚合平台用户积分充值';

            // (必填) 订单总金额,单位为元,不能超过1亿元
            // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
            $totalAmount = input('post.pay\_money/f');
            if($totalAmount < 1){
                return \['status' => 1, 'msg' => '最低充值金額1元'\];
            }
            if($totalAmount > 9999999){
                return \['status' => 1, 'msg' => '充值最大金額不能超過9999999元'\];
            }


            // (不推荐使用) 订单可打折金额,可以配合商家平台配置折扣活动,如果订单部分商品参与打折,可以将部分商品总价填写至此字段,默认全部商品可打折
            // 如果该值未传入,但传入了【订单总金额】,【不可打折金额】 则该值默认为【订单总金额】- 【不可打折金额】
            //String discountableAmount = "1.00"; //

            // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段
            // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
            // $undiscountableAmount = "0.01";

            // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
            // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
            //$sellerId = "";

            // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
            $body = "聚合平台用戶積分儲值" . $totalAmount . '元';

            //商户操作员编号,添加此参数可以为商户操作员做销售统计
            // $operatorId = "";

            // (可选) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
            // $storeId = "";

            // 支付宝的店铺编号
            // $alipayStoreId= "";

            // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),系统商开发使用,详情请咨询支付宝技术支持
            // $providerId = ""; //系统商pid,作为系统商返佣数据提取的依据
            // $extendParams = new ExtendParams();
            // $extendParams->setSysServiceProviderId($providerId);
            // $extendParamsArr = $extendParams->getExtendParams();

            // 支付超时,线下扫码交易定义为5分钟
            $timeExpress = "5m";

            // 商品明细列表,需填写购买商品详细信息,
            // $goodsDetailList = array();

            // // 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail
            // $goods1 = new GoodsDetail();
            // $goods1->setGoodsId("apple-01");
            // $goods1->setGoodsName("iphone");
            // $goods1->setPrice(3000);
            // $goods1->setQuantity(1);
            // //得到商品1明细数组
            // $goods1Arr = $goods1->getGoodsDetail();

            // // 继续创建并添加第一条商品信息,用户购买的产品为“xx牙刷”,单价为5.05元,购买了两件
            // $goods2 = new GoodsDetail();
            // $goods2->setGoodsId("apple-02");
            // $goods2->setGoodsName("ipad");
            // $goods2->setPrice(1000);
            // $goods2->setQuantity(1);
            // //得到商品1明细数组
            // $goods2Arr = $goods2->getGoodsDetail();

            // $goodsDetailList = array($goods1Arr,$goods2Arr);

            //第三方应用授权令牌,商户授权系统商开发模式下使用
            $appAuthToken = "";//根据真实值填写

            // 创建请求builder,设置请求参数
            $qrPayRequestBuilder = new AlipayTradePrecreateContentBuilder();
            $qrPayRequestBuilder->setOutTradeNo($outTradeNo);
            $qrPayRequestBuilder->setTotalAmount($totalAmount);
            $qrPayRequestBuilder->setTimeExpress($timeExpress);
            $qrPayRequestBuilder->setSubject($subject);
            $qrPayRequestBuilder->setBody($body);
            // $qrPayRequestBuilder->setUndiscountableAmount($undiscountableAmount);
            // $qrPayRequestBuilder->setExtendParams($extendParamsArr);
            // $qrPayRequestBuilder->setGoodsDetailList($goodsDetailList);
            // $qrPayRequestBuilder->setStoreId($storeId);
            // $qrPayRequestBuilder->setOperatorId($operatorId);
            // $qrPayRequestBuilder->setAlipayStoreId($alipayStoreId);

            $qrPayRequestBuilder->setAppAuthToken($appAuthToken);


            // 调用qrPay方法获取当面付应答
            require ROOT\_PATH.'extend/f2fpay/config/config.php';
            $qrPay = new AlipayTradeService($config);
            $qrPayResult = $qrPay->qrPay($qrPayRequestBuilder);

            //  根据状态值进行业务处理
            switch ($qrPayResult->getTradeStatus()){
                case "SUCCESS":
                    $response = $qrPayResult->getResponse();

                    Db::name('order')
                        ->insert(\[
                            'uid' => $uid,
                            'pay\_id' => $outTradeNo,
                            'money' => $totalAmount,
                            'creat\_time' => time(),
                            'subject' => $subject
                        \]);

                    return \['status' => 0, 'msg' => '支付寶創建訂單二維碼成功!!!"','data' => \[
                        'qr\_code' => $response->qr\_code,
                        'outTradeNo' => $outTradeNo
                    \]\];
                    // $qrcode = $qrPay->create\_erweima($response->qr\_code);
                    // echo $qrcode;
                    // print\_r($response);
                    break;
                case "FAILED":
                    return \['status' => 1, 'msg' => '支付寶創建訂單二維碼失敗!!!"'\];
                    // if(!empty($qrPayResult->getResponse())){
                    //     print\_r($qrPayResult->getResponse());
                    // }
                    break;
                case "UNKNOWN":
                    return \['status' => 1, 'msg' => '系統異常,狀態未知!!!"'\];
                    // echo "系統異常,狀態未知!!!"."<br>--------------------------<br>";
                    // if(!empty($qrPayResult->getResponse())){
                    //     print\_r($qrPayResult->getResponse());
                    // }
                    break;
                default:
                    return \['status' => 1, 'msg' => '不支持的返回狀態,創建訂單二維碼返回異常!!!'\];
                    break;
            }
            return ;
        }


    }

以上就是當面付預下單代碼 關於這個SDK,我非常有必要吐槽一下,哪個傢伙寫的demo,還在PHP例子裡引入了個lotusphp框架,一大堆沒有用的東西,完全沒有考慮我們開發者能不能接受得了。 我也是花了一點時間,把SDK給精簡了一下,只拿出我需要的部分,放入了我自己的框架中,加上了namespace,自動載入。 掃碼支付有一個獨有的功能—-異步通知 這個也正是線上支付最為需要的功能 當收銀台調用預下單請求 API 生成二維碼展示給用戶後,用戶通過手機掃描二維碼進行支付,支付寶會將該筆訂單的變更信息,沿著商戶調用預下單請求時所傳入的異步通知地址 notify_url,通過 POST 請求的形式將支付結果作為參數通知到商戶系統。 記住這個異步通知地址需要在應用那設置一下。

    // 异步回调
    public function notify() {
        if (request()->isPost()) {
            require ROOT\_PATH.'extend/f2fpay/config/config.php';
            $aop                     = new AopClient;
            $aop->alipayrsaPublicKey = $config\['alipay\_public\_key'\];
            $flag                    = $aop->rsaCheckV1($\_POST, null, "RSA2");
            if ($flag) {
                //異步SIGN驗證成功, 可以進行下一步動作。例如驗證訂單金額 然後完成訂單。之類的。。
                //需要驗證的就是 訂單號 與 訂單金額是否一致,驗證成功 就可以對數據庫中的訂單進行操作了。
                //TRADE\_SUCCESS 對於當面付來說,已經到帳了。詳情可以看這裡 https://www.cnblogs.com/tdalcn/p/5956690.html
                if ($\_POST\['trade\_status'\] === "TRADE\_SUCCESS") {
                    //訂單處理模板
                    
                    $res = Db::name('order')
                        ->where('pay\_id', $\_POST\['out\_trade\_no'\])
                        ->where('money', $\_POST\['total\_amount'\])
                        ->where('status', 0)
                        ->find();
                    if($res){
                        Db::name('order')
                            ->where('id',$res\['id'\])
                            ->update(\[
                                'status' => 1,
                                'buyer\_logon\_id' => $\_POST\['buyer\_logon\_id'\],
                                'pay\_time' => $\_POST\['gmt\_payment'\],
                                'pay\_no' => $\_POST\['trade\_no'\]
                            \]);
                        Db::name('user')
                            ->where('uid',$res\['uid'\])
                            ->setInc('integral', floatval($\_POST\['total\_amount'\]) \* 1000);
                    }

                }
            } 
            echo 'success'; //接口必須返回success 不然阿里會一直發送校驗驗證。
        } 
    }

輪詢 其中如果需要頁面支付成功後同步跳轉,則需要添加輪詢,由於當面付是沒有同步通知這個功能,所以需要用到輪詢,並且這個方法也是在當面付的文檔中所提及到的。 以下代碼摘自Bty付費版

public function query()
    {
        if (input('post.no')) {
            $out\_trade\_no = input('post.no');

            $queryContentBuilder = new AlipayTradeQueryContentBuilder();
            $queryContentBuilder->setOutTradeNo($out\_trade\_no);

            $queryResponse = new AlipayTradeService($this->alipay\_config);
            $queryResult   = $queryResponse->queryTradeResult($queryContentBuilder);
            $res\['status'\] = $queryResult->getTradeStatus();
            $res\['buyer'\]  = isset($queryResult->getResponse()->buyer\_logon\_id) ? $queryResult->getResponse()->buyer\_logon\_id : '';
            $res\['amount'\] = $queryResult->getResponse()->buyer\_pay\_amount;

            if ($res\['status'\] == 'SUCCESS') {
                $this->paySuccess('', $out\_trade\_no);
            }

            exit(json\_encode($res));
        } else {
            $this->error('非法請求');
        }
    }

由於我們對接的並不是類似商城一般的系統,所以暫時用不到類似退款這些的複雜操作。 當面付的基礎對接就到此結束。 完結撒花!

Powered by ❤️ with Hugo and Stack Theme.