Thinkphp6微信App支付APIV3对接流程_thinkphp微信支付v3_qq_41755278的博客-程序员秘密

技术标签: PHP  微信pay  

  1. 安装微信支付sdk我这里使用的是wechatpay-php。
    直接使用命令:composer require wechatpay/wechatpay
    安装php微信支付sdk
  2. 生成平台证书,这里取参考微信支付github文档说明
    下载证书工具
    在这里插入图片描述
    生成平台证书
    生成文件
  3. 创建唤起微信支付数据以api接口方式返回给app端。
    public function wechatArousePay()
    {
    
        $order_id     = $this->request->post('order_id/d', null);
        $order_number = $this->request->post('order_number/s', null);
        if (!$order_id and !$order_number) {
    
            return self::fail('订单ID或订单号必须存在其中一个');
        }
        $order_map = array(
            ['uid', '=', $this->userinfo['account']],
            ['pay_state', '<>', 20]
        );
        if ($order_id) {
    //订单ID
            $order_map[] = ['order_id', '=', $order_id];
        }
        if ($order_number) {
    //订单号
            $order_map[] = ['order_number', '=', $order_number];
        }
        $order_info = Order::where($order_map)->find();
        if (!$order_info) {
    
            return self::fail('该项目不存在或已支付');
        }
        if ($order_info['total_price'] <= 0) {
    
            return self::fail('订单支付金额不合法!');
        }
        $PaymentAmount = bcmul($order_info['total_price'], 100, 0);
        // 商户号,假定为`1000100`
        $merchantId = config('wechatpay.mch_id');
        // 商户私钥,文件路径假定为 `/path/to/merchant/apiclient_key.pem`
        // 这里是微信商户端生成的私钥将整个文件放在wechatpay文件夹下
        $merchantPrivateKeyFilePath = public_path() . 'wechatpay/apiclient_key.pem';
        // 加载商户私钥
        $merchantPrivateKeyInstance = PemUtil::loadPrivateKey($merchantPrivateKeyFilePath);
        // 商户证书,文件路径假定为 `/path/to/merchant/apiclient_cert.pem`
        // 这里是微信商户端生成的公钥将整个文件放在wechatpay文件夹下
        $merchantCertificateFilePath = public_path() . 'wechatpay/apiclient_cert.pem';
        // 加载商户证书
        $merchantCertificateInstance = PemUtil::loadCertificate($merchantCertificateFilePath);
        // 解析商户证书序列号
        $merchantCertificateSerial = PemUtil::parseCertificateSerialNo($merchantCertificateInstance);
        // 平台证书,可由下载器 `./bin/CertificateDownloader.php` 生成并假定保存为 `/path/to/wechatpay/cert.pem`
        $platformCertificateFilePath = public_path() . 'wechatpay/cert.pem';
        // 加载平台证书
        $platformCertificateInstance = PemUtil::loadCertificate($platformCertificateFilePath);
        // 解析平台证书序列号
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateInstance);
        // 工厂方法构造一个实例
        $instance = Builder::factory([
            'mchid'      => $merchantId,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformCertificateInstance,
            ],
        ]);
        try {
    
            $resp        = $instance
                ->v3->pay->transactions->app
                ->post(['json' => [
                    'appid'        => config('wechatpay.wchat_app_id'),
                    'mchid'        => $merchantId,
                    'description'  => $order_info['remark'],
                    'out_trade_no' => $order_info['order_number'],
                    'notify_url'   => config('wechatpay.notify_url') . '/client/xxxx.ClientPay/weiChatNotify',
                    'amount'       => [
                        'total'    => (int)$PaymentAmount,
                        'currency' => 'CNY'
                    ],
                ]]);
            $result_code = $resp->getStatusCode();
            if ($result_code == 200) {
    
                $result_data         = json_decode($resp->getBody(), true);
                $arouse_data         = [
                    'appid'     => config('wechatpay.wchat_app_id'),
                    'partnerid' => $merchantId,
                    'prepayid'  => $result_data['prepay_id'],
                    'package'   => "Sign=WXPay",
                    'noncestr'  => MD5($order_info['order_number']),
                    'timestamp' => get_now_time(),
                ];
                $arouse_data['sign'] = Rsa::sign(
                    Formatter::joinedByLineFeed(...array_values($arouse_data)),
                    $merchantPrivateKeyInstance
                );
                return self::successful('success', $arouse_data);
            }
        } catch (\Exception $exception) {
    
            // 进行错误处理
            if ($exception instanceof \GuzzleHttp\Exception\RequestException && $exception->hasResponse()) {
    
                $r = $exception->getResponse();
                return self::result($r->getStatusCode(), $r->getReasonPhrase());
            }
        }
    }
  1. ApiPost模拟数据。
    在这里插入图片描述
  2. 数据回调。
    public function weiChatNotify()
    {
    
        //接收返回参数json数据转数组
        $get_content = json_decode(file_get_contents('php://input'), true);
        //将接收到的数据存入文件中
        $file_directory3 = public_path() . '/wei_chat_notify.txt';
        file_put_contents($file_directory3, file_get_contents('php://input'));
        //将接收到的数据解密
        $result_content = AesGcm::decrypt($get_content['resource']['ciphertext'], config('wechatpay.apiv3'), $get_content['resource']['nonce'], $get_content['resource']['associated_data']);
        //将json数据转数组
        $content = json_decode($result_content, true);
        //验签订单
        $result_data = $this->authWeiChatOrder($content);
        if (!$result_data) {
    
            exit('FAIL');
        }
        $Order = new Order();
        $Order::startTrans();
        $order_info   = $result_data['order'];//订单信息
        $payment_info = $result_data['body'];//订单支付信息
        try {
    
            $chang_data               = array(
                'pay_state'      => 20,//支付状态
                'pay_style'      => 20,//支付方式
                'pay_time'       => strtotime($payment_info['time_end']),//支付完成时间
                'payment_price'  => bcdiv($payment_info['total_fee'], 100, 2),//支付金额
                'transaction_id' => $payment_info['transaction_id'],//微信支付订单号
                'openid'         => $payment_info['openid'],//微信用户openid
                'notify_json'    => $payment_info
            );
            $chang_data['need_price'] = bcsub($order_info['total_price'], $chang_data['payment_price'], 2);
            $result                   = $Order->where([['order_id', '=', $order_info['order_id']]])->update($chang_data);
            if ($result) {
    
                $Order::commitTrans();
                exit("SUCCESS");
            }
            throw new Exception('操作失败');
        } catch (\Exception $exception) {
    
            $Order::rollbackTrans();
            exit("FAIL");
        }
    }
  1. 订单校验。
    public function authWeiChatOrder(array $arr)
    {
    
        if (!$arr) {
    
            return null;
        }
        $order_info = Order::where([['order_number', '=', $arr['out_trade_no']]])->find();
        if (!$order_info) {
    
            return null;
        }
        $OrderQueryUrl  = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $OrderQueryBody = array_filter([
            'appid'          => config('wechatpay.wchat_app_id'),//String(32) # 必填 # 微信分配的小程序ID',
            'mch_id'         => config('wechatpay.mch_id'),//String(32) # 必填 # 微信支付分配的商户号',
            'transaction_id' => $arr['transaction_id'], // String(32) # 与 out_trade_no 二选一 # 微信的订单号,优先使用',
            'out_trade_no'   => $arr['out_trade_no'],//String(32)#与transaction_id二选一#商户系统内部订单号详见商户订单号',
            'nonce_str'      => MD5($arr['out_trade_no']), // String(32) # 必填 # 随机字符串,不长于32位。推荐随机数生成算法',
            'sign_type'      => 'MD5', // String(32) # 选填 # 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5',
        ]);
        // 生成验证签名
        $OrderQueryBody['sign'] = $this->genSign($OrderQueryBody, config('wechatpay.key'));
        //将数组转xml数据
        $xmlContext = $this->ArrayToXml($OrderQueryBody);
        //post发送请求
        $request = $this->ConnCurlSend($OrderQueryUrl, '', $xmlContext, 'POST');
        //将xml数据转数组
        $reqContext = $this->XmlToArray($request);
        if ($reqContext['return_code'] == 'SUCCESS' && $reqContext['result_code'] == 'SUCCESS' && $reqContext['return_msg'] == 'OK') {
    
            return [
                'order' => $order_info,
                'body'  => $reqContext,
            ];
        }
        return null;
    }
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41755278/article/details/119764358

智能推荐

20190119——Java从入门到精通 第十八章 多线程_宫城诗的博客-程序员秘密

如果一次只完成一件事情,就会很容易实现,但现实生活中很多事情都是同时进行的,所以在java中为了模拟这种状态,引入了线程机制,简单的说,当程序同时完成多件事情,就是所谓的多线程程序,多线程应用相当广泛,使用多线程可以创建窗口程序,网络程序等。线程简介世间万物都可以同时完成很多工作,例如,人体可以同时进行呼吸、血液循环、思考问题等活动。用户既可以使用计算机听歌,也可以使用它打印文件,而这些活动完...

《The Wiley Handbook of Human Computer Interaction》翻译(冰山一角)_brown和cairns将沉浸感简单的定义为“参与游戏的程度”。_KeddyWell的博客-程序员秘密

一、翻译原文定位:Part VII Interaction,A Quick Look at Game Engagement Theories 部分二、翻译原文链接(英文):三、翻译(中文):主题3 :认知这个主题从认知的角度描述了游戏参与。心流理论研究者(2012年的考克斯、凯恩斯、沙阿和卡罗尔;2004年的卢卡斯和雪莉;2011年的威布尔和维斯马斯)将心流定义为认知方面的任务体验(...

Ubuntu 16.04开发CUDA程序入门(一)_ubuntu16.04 cuda编程_海燕呐哈哈哈哈哈哈的博客-程序员秘密

以在Ubuntu16.04下开发CUDA程序实现两个一维数组相加的简单实例,展示了在Ubuntu系统下开发CUDA程序的基本过程。

linux sysctl 内存,linux的sysctl.conf参数介绍_Damon DanceForMe的博客-程序员秘密

abi.vsyscall32 = 1debug.exception-trace = 1debug.kprobes-optimization = 1dev.cdrom.autoclose = 1dev.cdrom.autoeject = 0dev.cdrom.check_media = 0dev.cdrom.debug = 0dev.cdrom.info = CD-ROM information, ...

spring源码执行步骤__Ray_Lee_的博客-程序员秘密

BeanFactory factory = new XmlBeanFactory();factory.getBean();个人总结的上面两行代码执行的步骤图。参考了《Spring源码深度解析》。自己理解的,如有错误,欢迎指出。

cmath常用函数_cmath包括哪些函数_真的胜哥的博客-程序员秘密

原文:http://blog.sina.com.cn/s/blog_68e462650100l3zy.html1.绝对值函数int abs(int i) 返回整型参数i的绝对值long labs(long n) 返回长整型参数n的绝对值double fabs(double x) 返回双精度参数x的绝对值2.指数函数double pow(double x,double y) ...

随便推点

HDU2059 龟兔赛跑 (DP)_2059 龟兔赛跑_一百个Chocolate的博客-程序员秘密

Description据说在很久很久以前,可怜的兔子经历了人生中最大的打击——赛跑输给乌龟后,心中郁闷,发誓要报仇雪恨,于是躲进了杭州下沙某农业园卧薪尝胆潜心修炼,终于练成了绝技,能够毫不休息得以恒定的速度(VR m/s)一直跑。兔子一直想找机会好好得教训一下乌龟,以雪前耻。最近正值HDU举办50周年校庆,社会各大名流齐聚下沙,兔子也趁此机会向乌龟发起挑战。虽然乌龟深知获胜希望不大,不过迫于...

Listary一款不只是程序员需要的软件_vr7jj的博客-程序员秘密

当我们使用电脑试图去打开某个文件的时候,你总会先想这个文件在哪个盘在哪个目录下。然后双击一层层的进直到找到目标文件。这样频繁的文件夹点击将会极大的制约你的效率。Listary就能帮你解决上述问题,当然它的强大之处不仅限于此。

Python-求字典中的最值_已知有一个包含一些同学成绩的字典_不想敲代码的小孩的博客-程序员秘密

已知一个包含一些同学成绩的字典,现在需要计算所有成绩的最高分、最低分、平均分,并查找所有最高分的同学。例如 scores={“zhang San”:45,“Wang Wu”:40,“Zhou liu”:96,“Zhao Qi”:65,“Sun Ba”:90……}代码:scores={“zhang San”:45,“Wang Wu”:40,“Zhou liu”:96,“Zhao Qi”:65,...

关于spring resttemplate超时设置_张大仙是个妖怪的博客-程序员秘密

Spring org.springframework.web.client.RestTemplate 使用 org.springframework.http.client.SimpleClientHttpRequestFactory建立 java.net.HttpURLConnection后者采用 HttpURLConnection 的默认超时配置HttpURLConnection 超时属...

python并发编程之多线程理论部分_weixin_30736301的博客-程序员秘密

一 什么是线程    在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程  线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线 流水线的工作需要电源,电源就相当于cpu  所以,进程只是用来把资源集中到一起(进程只是一个资源单...

推荐文章

热门文章

相关标签