<?php

use app\common\model\WxSubMember;
use think\Controller;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;

class WechatSub 
{

    /** 准备部分
     * 1.文档(v3分账) https://pay.weixin.qq.com/docs/merchant/products/profit-sharing/apilist.html
     * 2.商户号 munch_id
     * 3.商户私钥--merchantPrivateKey--商户平台开可以下载
     * 4.商户证书序列号--merchantCertSerialNo--商户平台可以下载
     * 5.微信支付平台证书--自己在本地生成--具体步骤见其他控制器
     * 6. sdk-- composer require wechatpay/wechatpay
     * 7. 支付订单的是否需要传分账标识profit_sharing--true/false
     */


    /**
     * @return \WeChatPay\BuilderChainable
     */
    public function getInstance(): \WeChatPay\BuilderChainable
    {
        $merchantId = '商户号';
        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = '商户私钥的文件路径';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        // 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = '证书序列号';

        // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
        $platformCertificateFilePath = '自己生成的微信平台证书的路径';
        $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,
            ],
        ]);
        // 加密敏感信息
        $encryptor = static function (string $msg) use ($platformPublicKeyInstance): string {
            return \WeChatPay\Crypto\Rsa::encrypt($msg, $platformPublicKeyInstance);
        };
        return $instance;
    }


    /**
     * 特别注意
     * 1.微信分账的订单在下单支付的时候 需要把订单的分账标识打开,在实际业务中可根据自身情况灵活控制
     * 2.微信没有查询分账接收方这个接口,需要自己建表把分账接收方存起来
     * 3.微信的分账产品开通容易但是提升额度比较困难,最大支持30的比例,30的意思是100元中最多有30元可以分出去,不是每个人最多30,若需要提高比例则要向微信提出书面申请
     */


    private function getEncrypt($str)
    {
        //$str是待加密字符串
        $public_key_path = $this->public_path() . 'static/cert/3.pem';
        $public_key = file_get_contents($public_key_path);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            throw new Exception('encrypt failed');
        }
        return $sign;
    }

    /**
     *  添加分账接收方
     */
    public function addWxMember($user)
    {
        // $user数组
        $instance = $this->getInstance();
        $resp = $instance->chain('v3/profitsharing/receivers/add')->post(
            [
                'debug' => false,
                'json' => [
                    'appid' => 'wx7ac5a73893c2c6b8',
                    'type' => 'PERSONAL_OPENID',
                    'account' => $user['account'],
                    //'name' => $this->getEncrypt('name'), //如果给商户分账 这个name字段必传
                    'relation_type' => 'USER'
                ]
            ]
        // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
        );
        $response = json_decode($resp->getBody()->getContents(), true);
        /**
         * 返回数据示例
         *  "type" : "MERCHANT_ID",
         * "account" : "example_account",
         * "name" : "example_name",
         * "relation_type" : "SERVICE_PROVIDER",
         * "custom_relation" : "example_custom_relation"
         */
        if ($response) {
            // wx没有查询分账接收方这一接口,所以微信分账添加或删除接收方都需要自己存到数据库--(微信分账接收方表)
            $data = [];
            $data['account'] = $user['account'];
            $data['type'] = 'PERSONAL_OPENID';
            $data['createtime'] = time();
            WxSubMember::create($data);
            return true;
        } else {
            return false;
        }

        //halt(json_decode($resp->getBody()->getContents(), true), PHP_EOL);
        //return $response;
    }

    /**
     *  删除分账接收方
     */
    public function deleteWxMember($user)
    {
        // 商户号
        $instance = $this->getInstance();
        // 发送请求 auth证书
        $resp = $instance->chain('v3/profitsharing/receivers/delete')->post(
            [
                'debug' => false,
                'json' => [
                    'appid' => $user['appid'],
                    'type' => $user['type'],
                    'account' => $user['openid'],
                    //'name' => $encryptor('a'),//如果是给商户进行分账时会用到这个
                ],
            ]
        // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
        );
        $response = json_decode($resp->getBody()->getContents(), true);
        if ($response) {
            WxSubMember::where('id', '=', $user['id'])->delete();
            return true;
        } else {
            return false;
        }
    }

    /**
     *  分账
     */
    public function subWx($orderInfo)
    {
        // 商户号
        $instance = $this->getInstance();
        //组装分账接收方数据
        $data = [];
        //业务逻辑部分,如果是多人请用二维数组
        $aa = [
            'type' => 'PERSONAL_OPENID',
            'account' => '填写分账接收方的openid',
            'amount' => 1,//单位是分
            'description' => '完成首单返佣'
        ];
        $data[] = $aa;
        try {
            $resp = $instance->chain('v3/profitsharing/orders')->post(
                [
                    'debug' => false,
                    'json' => [
                        'appid' => 'wx7ac5a73893c2c6b8',
                        'transaction_id' => $orderInfo->wx_order_num,//微信订单号(微信官方生成的订单号)
                        'out_order_no' => '分账请求订单号',//内部分账单号,自己生成,用这个订单号查询分账是否完成
                        'receivers' => $data,
                        'unfreeze_unsplit' => true //如果为false,该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。
                    ]
                ] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
            );
        } catch (\Throwable $e) {
            halt($e->getMessage());
        }
        $result = json_decode($resp->getBody()->getContents(), true);
        //halt(json_decode($resp->getBody()->getContents(), true), PHP_EOL);
        if ($result) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 查询分账结果
     */
    public function getWxSubData($data)
    {
        //todo 待测
        $instance = $this->getInstance();
        $response = $instance->chain('v3/transactions/split/query')->get([
            'transaction_id' => $data['transaction_id'],//支付订单号
        ]);
        $result = json_decode($response->getBody(), true);
        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
            // 输出分账结果
            echo "分账结果查询成功,分账单号:{$result['split_id']},分账金额:{$result['split_amount']}";
        } else {
            // 输出错误信息
            echo "分账结果查询失败,错误代码:{$result['return_code']},错误信息:{$result['return_msg']}";
        }
    }
}

最后检测日期:2023/10/09