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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容