您的当前位置:首页>全部文章>文章详情

【PHP】微信支付v3的jsapi接口接入thinkphp6完整流程

CrazyPanda发表于:2023-12-01 20:37:03浏览:781次TAG:

        相信,写过微信支付接口的程序员,都会骂一句,什么垃圾文档。惠州网站建设今天给个完整的解决案例。哎,绕来绕去,把你绕坑里。我也是不知道掉了多少坑才写出这个避坑文档。目的是想让自己记住thinkphp6在接入微信支付v3时候jsapi的时候,不要在掉一次坑。因为,官网文档的说明内容真的让人无语。都严重怀疑,他不想让人成功接入他们支付一样。

        下面说下我们怎么掉坑和出坑的。

        1,打开 http://image.51bj.top/blog/2024/10/chapter2_1.shtml  阅读里面的相关设置信息,配置好你的商户号i信息那些。这个配置就不多说了,根据流程操作就好了。

        2,有了相关信息之后,一般,我们这里要求,A要有证书文件,这里文件有三个,可以看下截图。

          1.png

        这三个文件中,我们这里测试和使用的时候,目前就只用到 apiclient_key.pem,其他文件,我这里暂时没用到,可能技术不够吧。但是目前我就只用到这个文件,其他三个文件是放在一起的。放到你的站点中。这个路径要你的程序能访问到。

        B,获取微信商户ID,公众号的ID,公众号的密钥,微信支付api v3的密钥。

        C,平台证书,这个也是巨坑之一,不认证看文档,你都不知道要用来干啥。


        3,开发之前,我们先疏通下微信支付的流程。


        你晕不晕,我不清楚,反正我是挺晕的。说了半天,没告诉你,什么时候要用上面提到的哪个文件。用哪个接口去申请流程。

        这个时候,我们想到的是他们提供的接口。好家伙,不提还好。一提,把几火。

        先看他们的demo吧。下面是demo下载地址。

        http://image.51bj.top/blog/2024/10/wechatpay6_0.shtml

1.png

        我们是php的,所以选择这个 wechatpay-php就可以了,后面的哪个东西,你不要管。wechatpay-guzzle-middleware这个不知道是哪个专家非得跟他放一起,还不写清楚是干啥的。其实这wechatpay-guzzle-middleware就是一个将php扩展。用来做网页请求的。这里,你就把他当成html里面的ajax这样的东西就好了。放在一起,只能让人掉坑里。

        然后,我们看看代码。继续掉坑。后面,我们在整理出自己的流程图和使用哪个接口。

        下载demo的时候,他提供composer的方法,我们就用这个方法。其他方法,可能会缺少依赖。

1.png

        

    composer require wechatpay/wechatpay


        这个时候,https://github.com/wechatpay-apiv3/wechatpay-php  ,我们要从这个文档里面拿到的代码,基本就是可以用的。其他百度回来的代码,就各种问题。不过这里的代码不是直接用的,要各种修改。这里拿最原本的代码来说吧。直接上代码。



        首先,我们新建一个叫 wxPay.php的文件,然后,第一段代码加入进去。我们上代码吧。

1.png

        因为我们当初是放在index这个文件下面的,所以就没改成wxPay了,将就着用吧。然后下面将他们的代码加入进来。

        

public function wxpay(){
    // 商户号
    $merchantId = '190000****';

    // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
    $merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
    $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

    // 「商户API证书」的「证书序列号」
    $merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';

    // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
    $platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
    $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

    // 从「微信支付平台证书」中获取「证书序列号」
    $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

    // 构造一个 APIv3 客户端实例
    $instance = Builder::factory([
        'mchid'      => $merchantId,
        'serial'     => $merchantCertificateSerial,
        'privateKey' => $merchantPrivateKeyInstance,
        'certs'      => [
            $platformCertificateSerial => $platformPublicKeyInstance,
        ],
    ]);

    // 发送请求
    $resp = $instance->chain('v3/certificates')->get(
        ['debug' => true] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
    );
    echo $resp->getBody(), PHP_EOL;
}


        用这个作为 第一个输入的地方,因为 ['debug' => true] ,所以,有错误的话是可以直接显示出来的。

        上面的参数按照提示补充进去就可以了。

        这里有个坑,上面的两个证书,直接用我们的下载到的商户证书就可以了,不要用平台证书。

        还有个就是 file:///这个地方是3个/,为什么呢?file://算是php的读取格式吧,跟ftp://一个道理。然后,第三个/ 是文件路径。

        

然后,根据他们的文档,下面的是native的支付,其实,是什么支付,主要就是看传什么参数,这里,我们调整下,改成jsapi。

        

try {
    $resp = $instance
    ->chain('v3/pay/transactions/jsapi')
    ->post(['json' => [
        'mchid'        => '1900006XXX',
        'out_trade_no' => 'native12177525012014070332333',
        'appid'        => 'wxdace645e0bc2cXXX',
        'description'  => 'Image形象店-深圳腾大-QQ公仔',
        'notify_url'   => 'https://weixin.qq.com/',
        'amount'       => [
            'total'    => 1,
            'currency' => 'CNY'
        ],
    ]]);

    echo$resp->getStatusCode(), PHP_EOL;
    echo$resp->getBody(), PHP_EOL;
} catch (\Exception$e) {
    // 进行错误处理
    echo$e->getMessage(), PHP_EOL;
    if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        echo$r->getStatusCode() .' '.$r->getReasonPhrase(), PHP_EOL;
        echo$r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
    }
    echo$e->getTraceAsString(), PHP_EOL;
}

        到这里,我们就可以获得$resp,这个东西有什么用呢?这个东西就是下面用来给微信支付接口发送数据的东东,这里暂时叫他一个实例后的对象。然后,我们继续往下看。

        到这里,我们的预支付就完成了,返回的$resp->getBody()里面就带有prepay_id,这个参数就是唤醒微信支付的参数。好了,到这里,哪个官方的说明文档又开始犯二了。到这里,他就不告诉你下面要怎么办。如果你以前有过微信开发经验的话,可能知道要怎么办。但是,像我这种刚接触微信支付的新手来说,就懵懵懂懂了。不知道怎么搞了。

        这个时候,我们回去看文档。https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml,这个地方,有一个叫唤醒jsapi支付的。好吧,我们接着弄。那具体怎么弄呢?

        文档,我们就不说了,大家自己看吧,这里,我们拿出这个代码来给大家展示下。

        

function onBridgeReady() {
        WeixinJSBridge.invoke('getBrandWCPayRequest', {
            "appId": "wx2421b1c4370ec43b",     //公众号ID,由商户传入    
            "timeStamp": "1395712654",     //时间戳,自1970年以来的秒数    
            "nonceStr": "e61463f8efa94090b1f366cccfbbb444",      //随机串    
            "package": "prepay_id=up_wx21201855730335ac86f8c43d1889123400",
            "signType": "RSA",     //微信签名方式:    
            "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg=="//微信签名
        },
        function(res) {
            if (res.err_msg =="get_brand_wcpay_request:ok") {
                // 使用以上方式判断前端返回,微信团队郑重提示:
                //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
            }
        });
    }
    if (typeof WeixinJSBridge=="undefined") {
        if (document.addEventListener) {
            document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
        } elseif (document.attachEvent) {
            document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
            document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
        }
    } else {
        onBridgeReady();
    }

        是不是有点蒙,刚开的时候,我还以为是个php函数,后来在微信支付文档里面看到说WeixinJSBridge 是微信内置浏览器的对象,其他浏览器没有。所以嘛,简单来说,这个就是客户端的东西。既然是js调用的,我们就在获得预支付结果的pid哪里,将这个唤醒微信支付的js输出到微信就好了。好了,到这里,神奇的事情发生了。微信支付,成功唤醒了。

        到这里,是不是很觉得云里雾里呢?是的,没看到代码之前,都是蒙的。文档的最后,我们会把完整的tp6写的微信支付v3的代码,显示出来。

        到这里,微信支付v3的tp6开发,支付部分就完成了。但是回调还没写。回调又是怎么样的呢?

        先留个悬念。我们这里先解决下微信支付思路的问题。

        

        目前我们这里之说,jsapi的微信支付问题。其他的可能参数不同,流程可能有些差异。

        A,使用微信登录获得openid,这个一般微信登录的时候,都会有返回,这个是用来验证微信登录是否成功的重要参数,也是微信支付的时候,判断是哪个账号支付的。

        B,使用商户号,商户证书,openid这写参数,生成$resp对象,这个就是一个用php来模拟浏览器访问微信接口的东西,返回出来的一个对象。这个对象可以用来预支付。

        C,预支付之后,我们要用预支付返回的prepay_id,和商户证书,和其他参数生成一个签名,这个签名成功之后,我们在调用WeixinJSBridge这个,唤醒微信支付。

        好吧,这个就是微信支付从下单到唤醒的流程。

        下面,我们给出这个部分的代码。

         获得openid的代码


<?php
    $url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx5f7e1393xxxxxx&redirect_uri=http%3A%2F%2Fwww.xxxx.cn%2Fapi.php%2Fajax%2Fhuidiaourl&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
    header("Location:".$url);

    //这里的重点是  回调的url    redirect_uri=http%3A%2F%2Fwww.xxxx.cn%2Fapi.php%2Fajax%2Fhuidiaourl

    function huidiaourl(){
        $post=Request::param();
       // dump($post);
        if(is_array($post)){
            $url2="https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx5f7e1393932628df&secret=821bf3340ad1eace4c585b6cb5e754a5&code=".$post["code"]."&grant_type=authorization_code";
            $str=get_urls($url2);
            $arr=json_decode($str,true);
           
            $arr['openid'];   //这个$arr数组就含有openid,需要的时候,大家可以自己打印出来看看
        }
    }
?>

        获得openid之后,下面给出完整的调用代码。

        

<?php
publicfunctionwxpay(){
    $configdb=Db::name("config");
    $configdata=$configdb->where("name in ('web_keywords','web_name','web_logo','ico_log','web_basehost','web_description','web_attr_15','web_attr_16','web_attr_17','web_attr_18','web_attr_19','web_attr_20','web_attr_21')")->order("id desc")->column('*','id');
    $data_users=Db::name("users")->where("open_id",Session::get("wxcode"))->find();
     $post=Request::post();//接收本站提交的参数
     $get=Request::get();//接收微信回调过来的信息
    $yicun=false;

    $openid=Session::get("openid");
    if(!$openid){
        echo"openid错误...";
        exit();
    }
    $payorder=Db::name("payorder")->where([["openid","=",$openid],["paystatus","=",0]])->order("id desc")->find();
    if($payorder){
        if(time()-$payorder['addtime']<=60 ){
            $out_trade_no=$payorder["out_trade_no"];
            $yicun=true;
        }else{
            $out_trade_no='dahonghu'.time().rand(1000,9999);
        }
    }else{
        $out_trade_no='dahonghu'.time().rand(1000,9999);
    }
    // 设置参数
   
    // 商户号
    $merchantId = '16422XXXXX';    
    // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
    $merchantPrivateKeyFilePath = 'file:///apiclient_key.pem';
    $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);    
    // 「商户API证书」的「证书序列号」
    $merchantCertificateSerial = '48185fb88454XXXXXXXXXXXXXX';    
    // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
    $platformCertificateFilePath = 'file:///wechatpay.pem';
    $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);    
    // 从「微信支付平台证书」中获取「证书序列号」
    $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
   
    // 构造一个 APIv3 客户端实例
    $instance = Builder::factory([
        'mchid'      => $merchantId,
        'serial'     => $merchantCertificateSerial,
        'privateKey' => $merchantPrivateKeyInstance,
        'certs'      => [
            $platformCertificateSerial => $platformPublicKeyInstance,
        ],
    ]);    
    // 发送请求
    $resp = $instance->chain('v3/certificates')->get(
        ['debug' => false] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
    );
   
    try {    
        $zhenjia=intval(round($post["tprice"]*100));    
        $resp = $instance
        ->chain('v3/pay/transactions/jsapi')
        ->post(['json' => [
            'mchid'        => '1642XXXXXXX',
            'out_trade_no' => $out_trade_no,
            'appid'        => 'wx5f7XXXXXX',
            'description'  => 'vip年卡',
            'notify_url'   => 'http://www.dahXXXXX.cn/index/wx3_notify',
            'amount'       => [
                'total'    => $zhenjia,
                'currency' => 'CNY'
            ],
            'payer'        =>[
                "openid"=>$openid
                 ]
        ]]);
       
        $inarr=[
                "out_trade_no"=>$out_trade_no,
                "description"=>'vip年卡',
                "openid"=>$openid,
                "addtime"=>time(),
                "days"=>365,
                "jine"=>floatval($zhenjia/100),
                "paytype"=>"wxpay"
            ];
        if($yicun){
            $input_payorder=Db::name("payorder")->where("out_trade_no",$out_trade_no)->save($inarr);
        }else{
            $input_payorder=Db::name("payorder")->save($inarr);  
        }    
        $prepay_id_arr=json_decode($resp->getBody(),true);
        $prepay_id="prepay_id=".$prepay_id_arr["prepay_id"];
        $sn=self::signname($prepay_id);
        $sn_obj=json_decode($sn);
        echo'<script>function onBridgeReady() {
                WeixinJSBridge.invoke("getBrandWCPayRequest",'.$sn.',
                function(res) {
                    if (res.err_msg == "get_brand_wcpay_request:ok") {
                        // 使用以上方式判断前端返回,微信团队郑重提示:
                        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                        //alert(res.err_msg);
                 nbsp;       location.href="/index.php";
                    }
                });
            }
            if (typeof WeixinJSBridge == "undefined") {
                if (document.addEventListener) {
                    document.addEventListener("WeixinJSBridgeReady", onBridgeReady, false);
                } else if (document.attachEvent) {
                    document.attachEvent("WeixinJSBridgeReady", onBridgeReady);
                    document.attachEvent("onWeixinJSBridgeReady", onBridgeReady);
                }
            } else {
                onBridgeReady();
            }</script>
             ';
    } catch (\Exception$e) {
        // 进行错误处理
        echo$e->getMessage(), PHP_EOL;
        if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
            $r = $e->getResponse();
            echo$r->getStatusCode() .' '.$r->getReasonPhrase(), PHP_EOL;
            echo$r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
        }
        echo$e->getTraceAsString(), PHP_EOL;
    }
}

           这里还要给一个加密的函数。

        

publicfunctionsignname($package){
        $merchantPrivateKeyFilePath = 'file:///zs/apiclient_key.pem';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);
        $params = [
            'appId'     => 'wx5f7e13xxxxxx',
            'timeStamp' => (string)Formatter::timestamp(),
            'nonceStr'  => Formatter::nonce(),
            'package'   => $package,
        ];
        $params += ['paySign' => Rsa::sign(
            Formatter::joinedByLineFeed(...array_values($params)),
            $merchantPrivateKeyInstance
        ), 'signType' => 'RSA'];
        returnjson_encode($params);
    }

            到这里,微信从下单到预支付到拉起微信支付都完成了。

        以上案例由惠州网站建设原创发布,如需要使用相关代码,请备注来源。

猜你喜欢

【PHP】intervention/image设置文字竖排显示
在使用 intervention/image 库时,要让文字竖排显示,可以通过设置文字的样式和使用 rotate 方法将文字旋转90度来实现。以下是一个示例代码:use&nbsp;Intervention\Image\ImageManagerStatic&nbsp;as&nbsp;Image; &nbsp; //&nbsp;初始化ImageManager $imageManager&nbsp;=&nbsp;new&nbsp;Image(); &nbsp; //&nbsp;加载背景图片 $back
发表于:2024-04-08 浏览:321 TAG:
【PHP】php中设计模式有哪些
php中设计模式有单例模式、工厂模式、抽象工厂模式、观察者模式、适配器模式、策略模式、装饰器模式、迭代器模式等等。详细介绍:1、单例模式,用于确保一个类只有一个实例,并提供一个全局访问点,可以使用静态变量和静态方法来实现单例模式;2、工厂模式,用于创建对象,而不需要直接调用构造函数,可以使用工厂类来创建对象,并隐藏对象的创建逻辑;3、抽象工厂模式,用于创建一系列相关的对象等等。本教程操作系统:windows10系统、PHP8.1.3版本、Dell G3电脑。PHP是一种广泛使用的编程语
发表于:2023-12-06 浏览:313 TAG:
【PHP】TP使用Intervention\Image在图片上绘制矩形、文字
1. 在图片上绘制矩形use&nbsp;Intervention\Image\ImageManagerStatic&nbsp;as&nbsp;Image; &nbsp; public&nbsp;function&nbsp;drawRectangle() { &nbsp;&nbsp;&nbsp;&nbsp;$image&nbsp;=&nbsp;Image::make(&#39;path/to/your/image.jpg&#39;);&nbsp;//&nbsp;替换为你的图片路径 &nbsp; &amp;
发表于:2024-04-12 浏览:364 TAG:
【PHP】thinkphp5支付宝服务商手机网站支付(新版sdk)
&nbsp;public&nbsp;function&nbsp;pay()&nbsp;{ &nbsp;Vendor(&#39;alipay.wappay.service.AlipayTradeService&#39;); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Vendor(&#39;alipay.wappay.buildermodel.AlipayTradeWapPayContentBuilder&#39;); &nbsp;&nbsp;&amp;
发表于:2023-12-27 浏览:320 TAG:
【PHP】thinkphp5.1+workman+jsonRpc
1.下载jsonRpc包放到vendor目录下 &nbsp;2.启动文件 &nbsp;3.启动效果:启动命令php allserver.php start &nbsp;4.RpcClient Rpc客户端+RpcClient Rpc服务端 访问:http://localhost/product/public/index/RpcTest/rpctest 报错:stream_socket_client(): unable to connect to tcp://127.0.0.1:2015 (由于目标计算机积极拒绝,无法连接。
发表于:2024-05-27 浏览:333 TAG:
【PHP】php8和php7哪个好
PHP8相较于PHP7在性能、新特性和语法改进、类型系统、错误处理和扩展等方面都有一些优势和改进。然而,选择使用哪个版本要根据具体的需求和项目情况来决定。详细介绍:1、性能提升,PHP8引入了Just-in-Time(JIT)编译器,可以提高代码的执行速度;2、新特性和语法改进,PHP8支持命名参数和可选参数的声明,使得函数调用更加灵活;引入了匿名类、属性的类型声明等等。【程序员必备开发工具推荐】Apifox一款免费API管理工具Apifox = Postman + Swagger +
发表于:2023-12-04 浏览:598 TAG:
【PHP】php常用的第三方类库有哪些
hp常用的第三方类库有laravel、symfony、guzzle、phpunit、monolog、swift mailer、phpexcel、carbon、doctrine、phpmailer等。详细介绍:1、laravel是一个流行的php框架,提供了丰富的功能和工具,用于快速构建web应用程序,它包含了许多常用的类库,例如路由、数据库访问、模板引擎、身份验证等等。本教程操作系统:windows10系统、PHP 8.1.3版本、DELL G3电脑。在PHP开发中,有许多常用的第三方类库可以
发表于:2024-03-17 浏览:285 TAG:
【PHP】什么是微服务架构
随着互联网的不断发展,越来越多的网站和应用程序应运而生。而对于开发者来说,如何快速高效地构建应用程序,是一个重要的挑战。其中,微服务架构已经成为了一个越来越受欢迎的解决方案。而php作为一种最受欢迎的web开发语言之一,也已经成为了很多开发者在构建微服务架构时的首选语言。本文将为大家介绍PHP如何应用于微服务架构,帮助大家更好地理解微服务架构以及如何使用PHP构建高效的微服务应用程序。什么是微服务架构?微服务架构(Microservices Architecture)是一种构建分布式应用程序的软
发表于:2024-05-23 浏览:288 TAG:
【PHP】Laravel的生命周期面试
在Laravel框架中,生命周期(Lifecycle)指的是从请求到达应用到应用响应请求的一系列过程。这个过程包括了从路由解析到最终视图渲染或响应发送的一系列中间件、控制器逻辑和事件处理。理解Laravel的生命周期对于开发高质量、可维护的应用至关重要。下面是一些关键的环节和概念,可以帮助你准备Laravel生命周期的面试:
发表于:2025-03-20 浏览:44 TAG: #php #laravel
【PHP】php 实现SHA256WithRSA
SHA256WithRSA 是一种常用的数字签名算法,可以通过 PHP 的 OpenSSL 扩展来实现。以下是一个简单的示例代码://&nbsp;加载私钥文件 $private_key&nbsp;=&nbsp;openssl_pkey_get_private(file_get_contents(&#39;private.key&#39;)); //&nbsp;加载公钥文件 $public_key&nbsp;=&nbsp;openssl_pkey_get_public(file_get_cont
发表于:2024-01-31 浏览:455 TAG: