首先吐槽一下
微信真的是越搞越垃圾,文档和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,
],
];
具体下载微信公钥证书的方法
建议还是下载弄一下,不然每次请求都会多一次请求,默认会去微信那边动态获取这个证书
传送门
具体转账代码
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版本并没有直接提供一些查询方法,但是微信所有的方法都封装到了插件里面。
具体文档
由于我们需要去查询"商家付款到零钱"是否成功,所以需要找到这些类
可以去看看具体源码的实现,这里直接列出最终请求的地方
从上述代码中可以看出,如果想通过商家批次单号查询批次单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);
}
}
查询返回
如果想通过其他的方法去查询,在上述获取插件的方法,传入对应的类,调用该类,并传入对应的值即可
如果遇到提示:此IP地址不允许调用该接口
则表示未配置白名单,去配置一下即可
去设置
添加即可,可能会有延迟。