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

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


キーの設定
取引当事者(加盟店と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とオートロードを追加しました。

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サイトのようなシステムではないため、現時点では返金のような複雑な操作は必要ありません。
