当面付(対面支払い)は、その名の通り対面での支払いを指し、店舗がオフラインの消費シーンで迅速な集金を可能にするものです。当面付製品は、バーコード支払いとQRコード決済の2種類の支払い方法をサポートしています。 ここで連携するのはQRコード決済です。 QRコード決済とは、ユーザーがAlipayウォレットの「スキャン」機能を開き、店舗がレジのシーンで提示するQRコードをスキャンして支払いを行うモードを指します。このモードは、オフラインの実店舗での支払い、対面支払いなどのシナリオに適しています。ビジネスフローは以下の図の通りです。

当面付の契約は非常に簡単で、個人事業主/個人商店の契約が許可されています。そのため、この方法はオンラインのQRコード決済にも大量に利用されていますが、Alipayの関連規約に違反するため、一定のリスクがあります。技術交流として、この問題は一旦置いておきましょう。

技術連携としては、当面付製品を契約していなくても開発を進めることができます。 支払い機能は取引と資金に直接関わるため、開発者が支払い機能をデバッグしやすいように、オープンプラットフォームはサンドボックス環境(サンドボックス環境アカウントとサンドボックス版Alipayウォレットを含む)を用意しています。これにより、開発者はサンドボックス環境でデバッグできます。サンドボックスへの接続方法 サンドボックス環境への接続 をクリックして詳細を確認してください。

そのため、私の開発ではサンドボックス環境を使用しています。何しろ中にはたくさんのお金があるので、自由に使えますからね。
まず、対応する開発言語のSDKをダウンロードします。ダウンロード:https://docs.open.alipay.com/194/105201/ QRコード決済ドキュメント:https://docs.open.alipay.com/194/106078/

キーの設定 取引当事者(加盟店とAlipay)の身元とデータセキュリティを確保するため、開発者はインターフェースを呼び出す前に、双方のキーを設定し、取引データの双方検証を行う必要があります。 Alipayオープンプラットフォーム開発アシスタント をダウンロードしてキーを生成してください。 キー生成後、開発者はオープンプラットフォーム開発者センターでキー設定を行う必要があります。設定完了後、Alipay公開鍵を取得できます。

設計と連携 私の設計ではポーリング(後述)を使用する必要がないため、追加していません。 以下は私の業務における関連コードです。

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については、どうしても文句を言いたいです。誰が書いたデモなのか、PHPの例にlotusphpフレームワークを導入していて、大量の不要なものが含まれており、私たち開発者が受け入れられるかどうかを全く考慮していません。 私も少し時間をかけてSDKを簡素化し、必要な部分だけを取り出して自分のフレームワークに組み込み、namespaceとオートロードを追加しました。

QRコード決済には独自の機能があります—-非同期通知です。 これはオンライン決済で最も必要とされる機能でもあります。 レジが事前注文リクエストAPIを呼び出してQRコードを生成しユーザーに表示した後、ユーザーが携帯電話でQRコードをスキャンして支払いを行うと、Alipayは当該注文の変更情報を、加盟店が事前注文リクエストを呼び出した際に渡した非同期通知アドレス 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('非法请求');
    }
}

私たちが連携しているのは一般的なECサイトのようなシステムではないため、現時点では返金のような複雑な操作は必要ありません。

当面付の基本的な連携はこれで終了です。 完了!お疲れ様でした!

著作権表示

著者: MoeJue

リンク: https://ja.moejue.cn/posts/107/

ライセンス: クリエイティブ・コモンズ表示-非営利-継承4.0国際ライセンス

この作品は、クリエイティブ・コモンズ表示-非営利-継承4.0国際ライセンスに基づいてライセンスされています。

検索を開始

キーワードを入力して記事を検索

↑↓
ESC
⌘K ショートカット