Thinkphp6配置支付宝MD5网页支付和微信支付V2NATIVE扫码支付(未引用第三方依赖,无需使用Composer)

网极科技2个月前代码笔记200

支付宝MD5支付方式简单好用,虽然官方给出了下线通知,但因为用户量巨大,估计也是会长期可用的,但是文档缺失,目前仅有如下文档:
https://opendocs.alipay.com/open/66/103600?pathHash=b76a0c0d
微信支付目前推荐的V3方式依赖证书且过于复杂,V2方式使用更广,虽然是XML提交但是稳定好用,所以本文以V2方式开发。

本文所编写的支付宝和微信支付集成到Thinkphp6.1.4并未引用第三方依赖,无需使用Composer

开发环境 thinkphp6.1.4
php版本8.1.28

支付宝配置

1、配置config/alipay.php相关参数

<?php
return [
    //合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串
    //查看地址:https://b.alipay.com/order/pidAndKey.htm
    'partner' => '',
    // MD5密钥,安全检验码,由数字和字母组成的32位字符串
    //查看地址:https://b.alipay.com/order/pidAndKey.htm
    'key' => '',
    //收款支付宝账号,以2088开头由16位纯数字组成的字符串
    //一般情况下收款账号就是签约账号
    'seller_id' => '',
    'sign_type' => 'MD5',
    'input_charset' => 'utf-8',
    'return_url' => 'https://'.$_SERVER['HTTP_HOST'].'/payment/alipay_return',
    'notify_url' => 'http://'.$_SERVER['HTTP_HOST'].'/payment/alipay_notify',//使用https有不通知的问题,这里建议选用http
];

2、配置app/controller/Payment.php

<?php

namespace app\controller;
//引用方法按需加载
use think\facade\Db;
use think\Response;
use think\facade\Config;
use think\facade\Request;

class Payment    
{
     public function alipay(){
     $alipay_config = Config::get('alipay');// 获取支付宝配置
     $data = $this->request->param();//接收表单提交信息
     $out_trade_no = $data['out_trade_no'];//商户订单号,商户网站订单系统中唯一订单号,必填
       $subject = $data['subject'];//订单名称,必填
        $total_fee = $data['total_fee'];//付款金额,必填
        $body = $data['body'];//商品描述,可空
       //插入数据库 自行修改
       $res = Db::name('order')->insert([
            'out_trade_no' => $out_trade_no,
            'total_fee' => $total_fee,
            'pay_type' => 'alipay',
            'create_time' => date('Y-m-d H:i:s',time()),
            'status' => 0,
        ]);
        if($res){
                // 配置交易参数
                $params = [
                    'service' => 'create_direct_pay_by_user',
                    'partner' => $alipay_config['partner'],
                    '_input_charset' => $alipay_config['input_charset'],
                    'return_url' => $alipay_config['return_url'],
                    'notify_url' => $alipay_config['notify_url'],
                    'out_trade_no' => $out_trade_no,
                    'subject' => $subject,
                    'body' => $body ,
                    'payment_type' => '1',
                    'total_fee' =>  $total_fee,
                    'seller_id' => $alipay_config['seller_id'],
                ];
                // 输出表单并提交,自动跳转到支付宝支付页面
                echo buildRequest($alipay_config, $params);
        }else{
            return json(['code' => 0, 'msg' => '订单创建失败']);
        }
     }

3、配置app/common.php公共方法

/*支付宝相关*/
function md5Sign(string $prestr, string $key): string {
    return md5($prestr . $key);
}
function buildRequest(array $alipay_config, array $params): string {
    // 筛选和排序参数
    $params = paraFilter($params);
    $params = argSort($params);

            
已隐藏部分内容,支付后自动显示
如有疑问请联系QQ:706448591
支付3元查看
return $sHtml; } function createLinkstring(array $para): string { $arg = ""; foreach ($para as $key => $val) { $arg .= $key . "=" . $val . "&"; } // 去掉最后一个&字符 $arg = substr($arg, 0, -1); return $arg; } function paraFilter(array $para): array { $para_filter = []; foreach ($para as $key => $val) { if ($key == "sign" || $key == "sign_type" || $val == "") continue; $para_filter[$key] = $para[$key]; } return $para_filter; } function argSort(array $para): array { ksort($para); reset($para); return $para; }

6、配置支付宝回调方法

回调分 return_url(前台跳转返回) notify_url(后台异步通知)
因为要预防notify不通知,所以这两个地址都需要进行支付成功的判断

//位置 app/controller/Payment.php
 public function alipay_return(){
      //检测登录状态 前台回调跳转要判断用户是否登录有效
      $user = Session::get('user');
      if(!$user){
          //跳转到登录
          return redirect('/login');
      }
    $alipay_config = Config::get('alipay');
    //获取支付宝返回参数
    $data = $_GET;
   // 验签
   if (!$this->verifyAlipayMD5($data, $alipay_config)) {
        return json(['code' => 0, 'msg' => '签名验证失败']);
    }
   // 查找订单
   $order = Db::name('order')->where('out_trade_no', $data['out_trade_no'])->find();
 if (!$order) {
       return json(['code' => 0, 'msg' => '订单不存在']);
   }
        // 检查支付状态
        if ($data['trade_status'] == 'TRADE_SUCCESS' || $data['trade_status'] == 'TRADE_FINISHED') {
            // 处理支付成功后的逻辑
            //自行实现更新订单状态、用户余额等操作
            $balance = $this->update_recharge($order,$data);
            // 查找订单
         $order = Db::name('order')->where('out_trade_no', $data['out_trade_no'])->find();
        }
        View::assign('order',$order);
        //这里我们是前台给用户一个结账单
        View::assign('title','结账单');
        return View::fetch('invoice');
 }


  public function alipay_notify()
{
    //判断是否为post提交
    if($this->request->isPost()){
    // 获取支付宝配置
    $alipay_config = Config::get('alipay');
    // 获取支付宝返回的数据
    $data = $this->request->post();
    // 验签
    if (!$this->verifyAlipayMD5($data, $alipay_config)) {
        return json(['code' => 0, 'msg' => '签名验证失败']);
    }
    // 检查支付状态
    if ($data['trade_status'] == 'TRADE_SUCCESS' || $data['trade_status'] == 'TRADE_FINISHED') {
           // 处理支付成功后的逻辑
        // 查找订单
        $order = Db::name('order')->where('out_trade_no', $data['out_trade_no'])->find();
        if (!$order) {
            return json(['code' => 0, 'msg' => '订单不存在']);
        }
//自行实现更新用户余额 更新用户缓存等操作
        $this->update_recharge($order,$data);
    }
    // 返回成功结果给支付宝
    echo 'success';
}else{
    echo '401';
}
}
private function verifyAlipayMD5($data, $config)
{
            
已隐藏部分内容,支付后自动显示
如有疑问请联系QQ:706448591
支付3元查看
}

5、测试支付宝支付

访问 /payment/alipay?out_trade_no=1024&subject=测试支付&body=支付测试

6、支付宝MD5支付总结

支付宝MD5支付无需加载其他依赖,不需要证书等文件,实现md5加密验签方法即可,简单方便,客户接受度高,用户使用习惯。

微信支付V2配置

1、配置config/wxpay.php相关参数

<?php
return [
   'mch_id' => '',//商户id
    'app_id' => '',//关联公众号的app_id
    'key' => '', // 商户支付密钥
    'notify_url' => 'http://yourdomain.com/notify',
    ];

2、配置app/controller/Payment.php

//位置 app/controller/Payment.php

class Payment
{
     public function wechatpay(){
        $wechatConfig = Config::get('wxpay');
   // 构建支付请求参数
   $params = [
    'appid' => $wechatConfig['app_id'],
    'mch_id' => $wechatConfig['mch_id'],
    'nonce_str' => uniqid(),
    'body' => '商品描述',
    'out_trade_no' => '123',
    'total_fee' => 1, // 单位为分
    'spbill_create_ip' => request()->ip(),
    'notify_url' => $wechatConfig['notify_url'],
    'trade_type' => 'NATIVE',
];
// 生成签名
$params['sign'] = $this->makeSign($params,$wechatConfig['key']);
// 发送请求到微信支付
$response = $this->sendRequest($params);
// 返回二维码链接
return json( $response);
}
protected function makeSign($params,$key)
{
    ksort($params);
    $string = urldecode(http_build_query($params)) . '&key=' . $key;
    return strtoupper(md5($string));
}

private function sendRequest($params, $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder')
{
            
已隐藏部分内容,支付后自动显示
如有疑问请联系QQ:706448591
支付3元查看
} protected function arrayToXml($array) { $xml = '<xml>'; foreach ($array as $key => $val) { if (is_numeric($val)) { $xml .= "<$key>$val</$key>"; } else { $xml .= "<$key><![CDATA[$val]]></$key>"; } } $xml .= '</xml>'; return $xml; }

3、测试微信支付NATIVE

访问 /payment/wechatpay 若返回以下内容则成功

{
  "return_code": "SUCCESS",
  "return_msg": "OK",
  "result_code": "SUCCESS",
  "mch_id": "XXXXXX",
  "appid": "XXXX",
  "nonce_str": "HBW0OOPfB9R9fZfj",
  "sign": "EA76XXX2ADFXXXXXXX8CF7AE",
  "prepay_id": "wx240XXX04XXXXXXb9ad590000",
  "trade_type": "NATIVE",
  "code_url": "weixin://wxpay/bizpayurl?pr=fLxCHbZz3"
}

其中code_url就是微信支付的二维码地址,用前端或者后端生成二维码即可

4、微信支付回调方法

刚才我们看到支付宝的回调分为前端和后端两部分,前端作为给用户展示,后端用于更新订单状态。 而 微信支付前端是扫描二维码支付,所以就需要用微信支付的查询订单接口,在展示二维码后,前端js每秒查询订单状态,成功即给用户提示,然后刷新缓存,代码如下:

//前端查询订单函数,在展示支付二维码后,每秒执行
    function wxpay_query(out_trade_no,wxpay_img){
              $.ajax({
                    type:'POST',
                    url:'/payment/wxpay_query',
                    data:{
                        'out_trade_no':out_trade_no

                    },
                    dataType: "json",
                    success: function(data){
                        if(data.trade_state=='SUCCESS'){
                            $('#wxpay_state').html('支付成功');
                            layer.closeAll(); 
                            layer.msg('充值成功', {icon: 6}); 
                            window.location.reload(); 
                        }else{
                            if(data.attach){
                            $('#wxpay_state').html('支付结果确认中...');

                            }
                            setTimeout(wxpay_query(out_trade_no),10000);
                        }
                    }

              });

        }

查询控制器方法,位置app/controller/Payment.php

public function wxpay_query($outTradeNo)
{
    // 获取微信支付配置
    $wechatConfig = Config::get('wxpay');

    // 构建查单请求参数
    $params = [
        'appid' => $wechatConfig['app_id'],
        'mch_id' => $wechatConfig['mch_id'],
        'out_trade_no' => $outTradeNo,
        'nonce_str' => uniqid(),
    ];

    // 生成签名
    $params['sign'] = $this->makeSign($params, $wechatConfig['key']);

    // 发送请求到微信支付查单接口
    $response = $this->sendRequest($params, 'https://api.mch.weixin.qq.com/pay/orderquery');

    // 将XML响应转换为数组
    $responseArray = $this->xmlToArray($response);

    // 返回查询结果
    return $responseArray;
}

还需要一个后端异步回调方法

public function wechatpayCallback()
{

            
已隐藏部分内容,支付后自动显示
如有疑问请联系QQ:706448591
支付3元查看
} private function verifySign($data, $key) { $sign = $data['sign']; unset($data['sign']);
已隐藏部分内容,支付后自动显示
如有疑问请联系QQ:706448591
支付3元查看
return $signCheck === $sign; } private function returnXml($data) { $xml = "<xml>"; foreach ($data as $key => $value) { $xml .= "<$key><![CDATA[$value]]></$key>"; } $xml .= "</xml>"; return $xml; }

5、微信支付总结

微信支付V2中NATIVE方法实现了下单输出二维码内容,方便使用微信扫描支付,这和支付宝的是截然不同,这种不同也造成了回调方式的不同,支付宝支付成功后会页面跳转,但是微信支付的NATIVE是在自有页面的二维码支付的,所以只能进行查单监控(前端js实现)。异步回调也是必须的,前端回调和后端回调两个同时进行才能确保大大降低掉单的概率。

综上,没有第三方依赖的支付宝MD5,没有用支付证书的微信支付V2的NATIVE扫码支付,适用于thinkphp,也可以拓展到其他php应用。

相关文章

https网页无法加载http的文件,给html头部添加一段代码搞定

https网页无法加载http的文件,给html头部添加一段代码搞定

在强制要求https协议的情况下,一年一签的SSL很多时候忘记续签,导致成为http协议,进而引发文件加载不到的问题 https与http能否共存https地址中,如果加载了http资源,浏...

php使用workerman实现秒极定时任务

php使用workerman实现秒极定时任务

通过宝塔面板的计划任务,我们最多可以将定时任务设置在分钟级别,需要秒级别就不可完成了,所以需要借助其他程序,这里选择workerman.netWorkerman是一款PHP开发的开源高性能的PHP 应...

使用Cloudfare R2云存储替代阿里云腾讯云存储_使用php进行Cloudfare R2存取操作

使用Cloudfare R2云存储替代阿里云腾讯云存储_使用php进行Cloudfare R2存取操作

Cloudfare主要是做全球CDN加速,他的存储R2功能只计算存储和请求费用,不计算流量费用,这点在目前云存储市场上算是独一无二了。比如我们常用的阿里云,他OSS云存储主要构成是流量费用。然后我们看...

【精选】PHP将股票日K线数据转换为半年K线数据

【精选】PHP将股票日K线数据转换为半年K线数据

将股票日K转换为半年K数据,可以用每年的7-1日作为分割点,分为上半年和下半年,开盘价应为分组开始的第一条数据的开盘价,收盘价应为分组数据的最后一条收盘价,最高和最低则为分组中的最高最低,成交量是累加...

宝塔面板Nginx设置任意路径301重定向

宝塔面板Nginx设置任意路径301重定向

接到一个客户的诉求,需要将指定路径进行301重定向,由于宝塔设置重定向只有域名(即根目录),无法对子目录或其他任意路径进行重定向,所以需要手写Nginx的重定向规则。 301重定向和伪静态有...

windows下composer简单使用

windows下composer简单使用

在windows下执行composer,可直接下载composer.phar文件,控制台运行即可此处下载composer.zip然后将该文件composer.phar放置到需要执行composer安装...