Hello! 欢迎来到阿珏酱のBlog!

支付宝当面付对接


avatar
阿珏 2020-04-04 3.21k

当面付顾名思义,面对面付款,帮助商家在线下消费场景下实现快速收款;当面付产品支持条码支付和扫码支付两种付款方式。
我们这里对接的就是扫码支付
扫码支付,指用户打开支付宝钱包中的“扫一扫”功能,扫描商家展示在某收银场景下的二维码并进行支付的模式。该模式适用于线下实体店支付、面对面支付等场景。业务流程如下图所示:

由于当面付的签约非常简单,允许个体工商户/个人商户签约。所以该方式也被大量用于线上的扫码支付,由于该方式违反了支付宝的相关条款,有一定风险,咱作为技术交流,暂且先抛开这个问题。

作为技术对接,即使你没有签约当面付产品,也是可以进行开发的。
支付能力直接涉及到交易与资金,为了方便开放者调试支付能力,开放平台已经准备好沙箱环境,包括沙箱环境账号和沙箱版支付宝钱包,这样开发者就可以在沙箱环境调试了。点击了解如何接入沙箱接入沙箱环境

所以我这边开发使用的是沙箱环境,毕竟里面好多钱,随便用。

首先先下载相应的开发语言的sdk 下载:https://docs.open.alipay.com/194/105201/
扫码支付文档:https://docs.open.alipay.com/194/106078/

配置密钥
为了保证交易双方(商户和支付宝)的身份和数据安全,开发者在调用接口前,需要配置双方密钥,对交易数据进行双方校验。
下载支付宝开放平台开发助手进行密钥生成。
生成密钥后,开发者需要在开放平台开发者中心进行密钥配置,配置完成后可以获取支付宝公钥

设计接入
由于我这边的设计不需要用到轮询(后面会说),所以没有加上
以下是我业务中的相关代码

复制代码
  1. public function pay(){
  2.  
  3. if (request()->isPost()) {
  4. // (必填) 商户网站订单系统中唯一订单号,64个字符以内,只能包含字母、数字、下划线,
  5. // 需保证商户系统端不能重复,建议通过数据库sequence生成,
  6. $uid = Session::get('sq.uid');
  7. $outTradeNo = order_num() . $uid;
  8.  
  9. // (必填) 订单标题,粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
  10. $subject = '聚合平台用户积分充值';
  11.  
  12. // (必填) 订单总金额,单位为元,不能超过1亿元
  13. // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
  14. $totalAmount = input('post.pay_money/f');
  15. if($totalAmount < 1){
  16. return ['status' => 1, 'msg' => '最低充值金额1元'];
  17. }
  18. if($totalAmount > 9999999){
  19. return ['status' => 1, 'msg' => '充值最大金额不能超过9999999元'];
  20. }
  21.  
  22.  
  23. // (不推荐使用) 订单可打折金额,可以配合商家平台配置折扣活动,如果订单部分商品参与打折,可以将部分商品总价填写至此字段,默认全部商品可打折
  24. // 如果该值未传入,但传入了【订单总金额】,【不可打折金额】 则该值默认为【订单总金额】- 【不可打折金额】
  25. //String discountableAmount = "1.00"; //
  26.  
  27. // (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段
  28. // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
  29. // $undiscountableAmount = "0.01";
  30.  
  31. // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)
  32. // 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
  33. //$sellerId = "";
  34.  
  35. // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
  36. $body = "聚合平台用户积分充值" . $totalAmount . '元';
  37.  
  38. //商户操作员编号,添加此参数可以为商户操作员做销售统计
  39. // $operatorId = "";
  40.  
  41. // (可选) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
  42. // $storeId = "";
  43.  
  44. // 支付宝的店铺编号
  45. // $alipayStoreId= "";
  46.  
  47. // 业务扩展参数,目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法),系统商开发使用,详情请咨询支付宝技术支持
  48. // $providerId = ""; //系统商pid,作为系统商返佣数据提取的依据
  49. // $extendParams = new ExtendParams();
  50. // $extendParams->setSysServiceProviderId($providerId);
  51. // $extendParamsArr = $extendParams->getExtendParams();
  52.  
  53. // 支付超时,线下扫码交易定义为5分钟
  54. $timeExpress = "5m";
  55.  
  56. // 商品明细列表,需填写购买商品详细信息,
  57. // $goodsDetailList = array();
  58.  
  59. // // 创建一个商品信息,参数含义分别为商品id(使用国标)、名称、单价(单位为分)、数量,如果需要添加商品类别,详见GoodsDetail
  60. // $goods1 = new GoodsDetail();
  61. // $goods1->setGoodsId("apple-01");
  62. // $goods1->setGoodsName("iphone");
  63. // $goods1->setPrice(3000);
  64. // $goods1->setQuantity(1);
  65. // //得到商品1明细数组
  66. // $goods1Arr = $goods1->getGoodsDetail();
  67.  
  68. // // 继续创建并添加第一条商品信息,用户购买的产品为“xx牙刷”,单价为5.05元,购买了两件
  69. // $goods2 = new GoodsDetail();
  70. // $goods2->setGoodsId("apple-02");
  71. // $goods2->setGoodsName("ipad");
  72. // $goods2->setPrice(1000);
  73. // $goods2->setQuantity(1);
  74. // //得到商品1明细数组
  75. // $goods2Arr = $goods2->getGoodsDetail();
  76.  
  77. // $goodsDetailList = array($goods1Arr,$goods2Arr);
  78.  
  79. //第三方应用授权令牌,商户授权系统商开发模式下使用
  80. $appAuthToken = "";//根据真实值填写
  81.  
  82. // 创建请求builder,设置请求参数
  83. $qrPayRequestBuilder = new AlipayTradePrecreateContentBuilder();
  84. $qrPayRequestBuilder->setOutTradeNo($outTradeNo);
  85. $qrPayRequestBuilder->setTotalAmount($totalAmount);
  86. $qrPayRequestBuilder->setTimeExpress($timeExpress);
  87. $qrPayRequestBuilder->setSubject($subject);
  88. $qrPayRequestBuilder->setBody($body);
  89. // $qrPayRequestBuilder->setUndiscountableAmount($undiscountableAmount);
  90. // $qrPayRequestBuilder->setExtendParams($extendParamsArr);
  91. // $qrPayRequestBuilder->setGoodsDetailList($goodsDetailList);
  92. // $qrPayRequestBuilder->setStoreId($storeId);
  93. // $qrPayRequestBuilder->setOperatorId($operatorId);
  94. // $qrPayRequestBuilder->setAlipayStoreId($alipayStoreId);
  95.  
  96. $qrPayRequestBuilder->setAppAuthToken($appAuthToken);
  97.  
  98.  
  99. // 调用qrPay方法获取当面付应答
  100. require ROOT_PATH.'extend/f2fpay/config/config.php';
  101. $qrPay = new AlipayTradeService($config);
  102. $qrPayResult = $qrPay->qrPay($qrPayRequestBuilder);
  103.  
  104. // 根据状态值进行业务处理
  105. switch ($qrPayResult->getTradeStatus()){
  106. case "SUCCESS":
  107. $response = $qrPayResult->getResponse();
  108.  
  109. Db::name('order')
  110. ->insert([
  111. 'uid' => $uid,
  112. 'pay_id' => $outTradeNo,
  113. 'money' => $totalAmount,
  114. 'creat_time' => time(),
  115. 'subject' => $subject
  116. ]);
  117.  
  118. return ['status' => 0, 'msg' => '支付宝创建订单二维码成功!!!"','data' => [
  119. 'qr_code' => $response->qr_code,
  120. 'outTradeNo' => $outTradeNo
  121. ]];
  122. // $qrcode = $qrPay->create_erweima($response->qr_code);
  123. // echo $qrcode;
  124. // print_r($response);
  125. break;
  126. case "FAILED":
  127. return ['status' => 1, 'msg' => '支付宝创建订单二维码失败!!!"'];
  128. // if(!empty($qrPayResult->getResponse())){
  129. // print_r($qrPayResult->getResponse());
  130. // }
  131. break;
  132. case "UNKNOWN":
  133. return ['status' => 1, 'msg' => '系统异常,状态未知!!!"'];
  134. // echo "系统异常,状态未知!!!"."<br>--------------------------<br>";
  135. // if(!empty($qrPayResult->getResponse())){
  136. // print_r($qrPayResult->getResponse());
  137. // }
  138. break;
  139. default:
  140. return ['status' => 1, 'msg' => '不支持的返回状态,创建订单二维码返回异常!!!'];
  141. break;
  142. }
  143. return ;
  144. }
  145.  
  146.  
  147. }

以上就是当面付预下单代码
关于这个SDK,我非常有必要吐槽一下,哪个货写的demo,还在PHP例子里引入了个lotusphp框架,一大堆没有用的东西,完全没有考虑我们开发者能不能接受的了。
我也是花了点时间,把SDK给精简了一下,只拿出我需要的部分,放入了我自己的框架中,加上了namespace,自动加载。

扫码支付有一个独有的功能—-异步通知
这个也正是线上支付最为需要的功能
当收银台调用预下单请求 API 生成二维码展示给用户后,用户通过手机扫描二维码进行支付,支付宝会将该笔订单的变更信息,沿着商户调用预下单请求时所传入的异步通知地址 notify_url,通过 POST 请求的形式将支付结果作为参数通知到商户系统。

记住这个异步通知地址需要在应用那设置一下。

复制代码
  1. // 异步回调
  2. public function notify() {
  3. if (request()->isPost()) {
  4. require ROOT_PATH.'extend/f2fpay/config/config.php';
  5. $aop = new AopClient;
  6. $aop->alipayrsaPublicKey = $config['alipay_public_key'];
  7. $flag = $aop->rsaCheckV1($_POST, null, "RSA2");
  8. if ($flag) {
  9. //异步SIGN验证成功, 可以进行下一步动作。例如验证订单金额 然后完成订单。之类的。。
  10. //需要验证的就是 订单号 与 订单金额是否一致,验证成功 就可以对数据库中的订单进行操作了。
  11. //TRADE_SUCCESS 对于当面付来说,已经到账了。详情可以看这里 https://www.cnblogs.com/tdalcn/p/5956690.html
  12. if ($_POST['trade_status'] === "TRADE_SUCCESS") {
  13. //订单处理模板
  14. $res = Db::name('order')
  15. ->where('pay_id', $_POST['out_trade_no'])
  16. ->where('money', $_POST['total_amount'])
  17. ->where('status', 0)
  18. ->find();
  19. if($res){
  20. Db::name('order')
  21. ->where('id',$res['id'])
  22. ->update([
  23. 'status' => 1,
  24. 'buyer_logon_id' => $_POST['buyer_logon_id'],
  25. 'pay_time' => $_POST['gmt_payment'],
  26. 'pay_no' => $_POST['trade_no']
  27. ]);
  28. Db::name('user')
  29. ->where('uid',$res['uid'])
  30. ->setInc('integral', floatval($_POST['total_amount']) * 1000);
  31. }
  32.  
  33. }
  34. }
  35. echo 'success'; //接口必须返回success 不然阿里会一直发送校验验证。
  36. }
  37. }

轮询
其中如果需要页面支付成功后同步跳转,则需要添加轮询,由于当面付是没有同步通知这个功能,所以需要用到轮询,并且这个方法也是在当面付的文档中所提及到的。
以下代码摘自Bty付费版

复制代码
  1. public function query()
  2. {
  3. if (input('post.no')) {
  4. $out_trade_no = input('post.no');
  5.  
  6. $queryContentBuilder = new AlipayTradeQueryContentBuilder();
  7. $queryContentBuilder->setOutTradeNo($out_trade_no);
  8.  
  9. $queryResponse = new AlipayTradeService($this->alipay_config);
  10. $queryResult = $queryResponse->queryTradeResult($queryContentBuilder);
  11. $res['status'] = $queryResult->getTradeStatus();
  12. $res['buyer'] = isset($queryResult->getResponse()->buyer_logon_id) ? $queryResult->getResponse()->buyer_logon_id : '';
  13. $res['amount'] = $queryResult->getResponse()->buyer_pay_amount;
  14.  
  15. if ($res['status'] == 'SUCCESS') {
  16. $this->paySuccess('', $out_trade_no);
  17. }
  18.  
  19. exit(json_encode($res));
  20. } else {
  21. $this->error('非法请求');
  22. }
  23. }

由于我们对接的并不是类似商城一般的系统,所以暂时用不到类似退款这些的复杂操作。

当面付的基础对接就到此结束。
完结撒花!

  • avatar
    游客

    可以可以

  • avatar
    游客

    博主大大着实是厉害啊,偷偷薅羊毛。。

  • avatar
    游客

    。。。。。个人就能用几十天。

  • avatar
    游客

    当面付就是额度太小了。

  • avatar
    游客

    这个输入qq可以自动填写怎么搞,好6啊aru_17

    • avatar
      博主

      @ 北挽_ @北挽_:这个现在不是很场常的见吗,就那样那样写

      • avatar
        游客

        @ 阿珏 @阿珏:我的也搞上了,感觉好好玩,谢谢博主!aru_17

  • avatar
    游客

    前排围观aru_8

  • avatar
    游客

    完结撒花aru_8

发表评论
OωO表情

相关阅读

恰饭

欢迎阅读『支付宝当面付对接 (゜-゜)つロ 干杯~』