Php 对接微信V3业务"商家转账到零钱"功能

首先吐槽一下

微信真的是越搞越垃圾,文档和shit一样,直接看不懂,微信的包也是,稍微一点没注意,就直接蹦,一点面子都不给,哈哈哈哈。。。
因为目前项目涉及到一个分销的功能,首先就想到了微信的"企业付款到零钱功能",然后去申请,商户后台看到的只有"商家付款到零钱",心里想着也是一样的,反正都是打款,应该和以前大差不差的,结果,"商家付款到零钱"这个功能前前后后提交申请10多次,一直没过,人工随时排队,又解决不了问题,每次说反馈上去,然后3天后,发条短信给你,让你"以实际审核为准",麻辣隔壁,服了,好消息是:经过长达半年的抗战(2022-11 ~ 2023-05) 月,我的功能终于下来。

步入正题

首先我选择的composer包是yansongda/pay ~3.2.0 (本来想沿用easywechant,奈何V3版本的功能需要php8.0以上,所以放弃了)

composer require yansongda/pay:~3.2.0 -vvv

创建一个config配置文件(生成的文件 pay.php,位于:项目根目录/config/pay.php)

php artisan vendor:publish --provider="Yansongda\LaravelPay\PayServiceProvider" --tag=laravel-pay

文件大致内容


declare(strict_types=1);

use Yansongda\Pay\Pay;

return [
    // 支付宝支付相关配置
    'alipay' => [
        'default' => [
            // 必填-支付宝分配的 app_id
            'app_id' => '',
            // 必填-应用私钥 字符串或路径
            'app_secret_cert' => '',
            // 必填-应用公钥证书 路径
            'app_public_cert_path' => '',
            // 必填-支付宝公钥证书 路径
            'alipay_public_cert_path' => '',
            // 必填-支付宝根证书 路径
            'alipay_root_cert_path' => '',
            'return_url' => '',
            'notify_url' => '',
            // 选填-服务商模式下的服务商 id,当 mode 为 Pay::MODE_SERVICE 时使用该参数
            'service_provider_id' => '',
            // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SANDBOX, MODE_SERVICE
            'mode' => Pay::MODE_NORMAL,
        ],
    ],
    //   微信支付相关配置
    'wechat' => [
        'default' => [
            // 必填-商户号,服务商模式下为服务商商户号
            'mch_id' => '',
            // 必填-商户秘钥
            'mch_secret_key' => '',
            // 必填-商户私钥 字符串或路径
            'mch_secret_cert' => '',
            // 必填-商户公钥证书路径
            'mch_public_cert_path' => '',
            // 必填
            'notify_url' => '',
            // 选填-公众号 的 app_id
            'mp_app_id' => '',
            // 选填-小程序 的 app_id
            'mini_app_id' => '',
            // 选填-app 的 app_id
            'app_id' => '',
            // 选填-合单 app_id
            'combine_app_id' => '',
            // 选填-合单商户号
            'combine_mch_id' => '',
            // 选填-服务商模式下,子公众号 的 app_id
            'sub_mp_app_id' => '',
            // 选填-服务商模式下,子 app 的 app_id
            'sub_app_id' => '',
            // 选填-服务商模式下,子小程序 的 app_id
            'sub_mini_app_id' => '',
            // 选填-服务商模式下,子商户id
            'sub_mch_id' => '',
            // 选填-微信公钥证书路径, optional,强烈建议 php-fpm 模式下配置此参数
            'wechat_public_cert_path' => [
                '45F59D4DABF31918AFCEC556D5D2C6E376675D57' => __DIR__.'/Cert/wechatPublicKey.crt',
            ],
            // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE
            'mode' => Pay::MODE_NORMAL,
        ],
    ],
    // 银联支付相关配置
    'unipay' => [
        'default' => [
            // 必填-商户号
            'mch_id' => '',
            // 必填-商户公私钥
            'mch_cert_path' => '',
            // 必填-商户公私钥密码
            'mch_cert_password' => '000000',
            // 必填-银联公钥证书路径
            'unipay_public_cert_path' => '',
            // 必填
            'return_url' => '',
            // 必填
            'notify_url' => '',
        ],
    ],
    // GuzzleHttp相关配置
    'http' => [ // optional
        'timeout' => 5.0, // 请求超时时间
        'connect_timeout' => 5.0,  // 连接超时时间
        // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
    ],
    // optional,默认 warning;日志路径为:sys_get_temp_dir().'/logs/yansongda.pay.log'
    'logger' => [
        'enable' => false,  // 开启日志或者关闭  true or false
        'file' => null,   // 日志保存路径 默认路径:sys_get_temp_dir().'/logs/yansongda.pay.log'
        'level' => 'debug', // 日志级别 Debug、Info、Notice、Warning、Error、Critical、Alert、Emergency
        'type' => 'single', // optional, 可选 daily.
        'max_file' => 30, // 留存多少天内的日志,默认30天,根据实际需要可以更改
    ],
];

具体内容可以看官方文档
传送门

转账到零钱的一些配置和代码

由于我只需要微信的功能,所以我精简了一下
<?php

declare(strict_types=1);

use Yansongda\Pay\Pay;

return [
    'wechat' => [
        'default' => [
            // 必填-商户号,服务商模式下为服务商商户号
            'mch_id' => '',
            // 必填-商户秘钥
            'mch_secret_key' => '',
            // 必填-商户私钥 字符串或路径,文件名:apiclient_key.pem 的文件
            'mch_secret_cert' => '',
            // 必填-商户公钥证书路径,文件名:apiclient_cert.pem的文件
            'mch_public_cert_path' => '',
            // 必填
            'notify_url' => '', // 回调地址
            // 选填-小程序 的 app_id
            'mini_app_id' => '',
            // 选填-微信公钥证书路径, optional,强烈建议 php-fpm 模式下配置此参数
            // 这个参数需要去下载证书,具体下载方法在下面贴出来
            'wechat_public_cert_path' => [],
            // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE
            'mode' => Pay::MODE_NORMAL,
        ],
    ],
    'http' => [ // optional
        'timeout' => 5.0,
        'connect_timeout' => 5.0,
        // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
    ],
    // optional,默认 warning;
    'logger' => [
        'enable' => true,
        'file' => str_replace('\\', '/', storage_path()).'/logs/transfer/transferLog.log', // 日志我是放在laravel默认的日志目录下
        'level' => 'warning',
        'type' => 'daily', // daily 按天生成日志,生成日志格式 transferLog-2023-05-15.log
        'max_file' => 180,
    ],
];
具体下载微信公钥证书的方法

建议还是下载弄一下,不然每次请求都会多一次请求,默认会去微信那边动态获取这个证书
传送门

image.png

具体转账代码

use Yansongda\Pay\Pay;

class TestController {

  public function transfer() {
          $result = Pay::wechat(config('pay'))->transfer([
              'appid' => wxxxxxxx, // 微信小程序的app_id
              'out_batch_no' => (string)time(), // 商家批次单号
              'batch_name' => '提现',    // 该笔批量转账的名称
              'batch_remark' => '商家付款到零钱',  // 转账说明
              'total_amount' => 0.1 * 100,  // 转账金额,单位:分 
              'total_num' => 1,   // 转账总笔数
              'transfer_detail_list' => [   
                [
                    'out_detail_no' => (string)(time() + 5),  // 商家明细单号
                    'transfer_amount' => 0.1 * 100,  // 转账金额
                    'transfer_remark' => '商家付款到零钱',  // 单条转账备注(微信用户会收到该备注)
                    'openid' => 'ozxxxxxxxxxx', //  转账用户的 openid
                ],
            ],
        ]);
        // 返回结果
        return response()->json($result);
   }
}

返回参数:

{
    "batch_id": "1318888888888888888888888888888888888888888",
    "create_time": "2023-05-15T15:19:09+08:00",
    "out_batch_no": "1684135149"
}

至此,商家转账到零钱成功

由于这个API不具备回调功能所以想知道转账的状态只有2种办法

1.微信商户后台直接去查询(不建议,麻烦)

2.通过api查询

由于yansongda V3版本并没有直接提供一些查询方法,但是微信所有的方法都封装到了插件里面。

具体文档

传送门

image.png

由于我们需要去查询"商家付款到零钱"是否成功,所以需要找到这些类

image.png

可以去看看具体源码的实现,这里直接列出最终请求的地方

image.png

从上述代码中可以看出,如果想通过商家批次单号查询批次单QueryOutBatchNoPlugin查询订单,需要传入两个参数,其他具体的可选参数可以通过"微信官网文档"查看

具体代码

class TestController {
  public function queryOutBatch() {
        $pay = Pay::wechat(config('pay'));
        //   获取插件
        $plugins = $pay->mergeCommonPlugins([QueryOutBatchNoPlugin::class]);
        // need_query_detail:【是否查询转账明细单】 true-是;false-否,默认否。商户可选择是否查询指定状态的转账明细单,当转账批次单状态为“FINISHED”(已完成)时,才会返回满足条件的转账明细单
        $result = $pay->pay($plugins ,['out_batch_no' => '1684122786','need_query_detail' => false]);
        return response()->json($result);
  }
}

查询返回

image.png

如果想通过其他的方法去查询,在上述获取插件的方法,传入对应的类,调用该类,并传入对应的值即可

如果遇到提示:此IP地址不允许调用该接口

则表示未配置白名单,去配置一下即可


image.png

去设置


image.png

添加即可,可能会有延迟。


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容