开发前,需要业务部门提供4项内容
apiclient_cert.pem、apiclient_key.pem 这两个ssl验证文件
还有场景编码,需要在后台查看(该批次转账使用的转账场景,可在「商家转账到零钱 - 产品设置」中查看详情,如不填写则使用商家的默认转账场景
如:1001 - 现金营销)
serial_no 这个是证书序列号(获取方式如下图:)
代码如下:
工具类:
<?php
namespace app\common\lib;
/**
* 微信公众号发送红包类
*/
class WechatRed
{
private $sslPath;//API安全证书地址
public function __construct(){
$this->sslPath=dirname(__FILE__).DIRECTORY_SEPARATOR.'pay'.DIRECTORY_SEPARATOR.'wxpay'.DIRECTORY_SEPARATOR.'cert'.DIRECTORY_SEPARATOR;
}
/**post请求 商户转账到零钱
* @param $url
* @param $param
* @return bool|string
*/
public function wx_post($url, $param)
{
$authorization = $this->getV3Sign($url, "POST", $param);
$curl = curl_init();
$headers = [
'Authorization:' . $authorization,
'Accept:application/json',
'Content-Type:application/json;charset=utf-8',
'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
];
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 500);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
curl_setopt($curl, CURLOPT_POST, true);
$res = curl_exec($curl);
curl_close($curl);
return $res;
}
/**
* 微信提现V3签名
* @param $url
* @param $http_method
* @param $body
* @return string
*/
public function getV3Sign($url, $http_method, $body)
{
$nonce = strtoupper($this->createNonceStr(32));
$timestamp = time();
$url_parts = parse_url($url);
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
//拼接参数
$message = $http_method . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$body . "\n";
// 这里需要换成你自己的证书地址
$private_key = $this->getPrivateKey($this->sslPath . 'apiclient_key.pem');
openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption');
// serial_no 这个是证书序列号
$sign = base64_encode($raw_sign);
$token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"', config('global_config.payment_wechat_mchid'), $nonce, $timestamp,config('global_config.payment_serial_no'), $sign);
return $token;
}
/**
* 获取私钥
* @param $filepath
* @return false|resource
*/
public function getPrivateKey($filepath = '')
{
return openssl_get_privatekey(file_get_contents($filepath));
}
}
调用方式如下:
<?php
namespace app\common\model;
class MemberBalance extends \app\common\model\BaseModel
{
protected $error;
/**
* 发起商家转账到零钱
* @param $money 金额
* @param $openid 接收者ID
* @param $nick_name 活动名称
* @param $order_id 订单号
* @return mixed
*/
public function sendBatches($money,$openid,$nick_name,$order_id){
$wechatRed = new \app\common\lib\WechatRed();
$data = array();
$data['appid'] = config('global_config.wechat_appid');//申请商户号的appid(小程序或公众号的appid)
$data['out_batch_no'] = $order_id;//商户订单号 28位
$data['batch_name'] = $nick_name;;//批次名称
$data['batch_remark'] = $nick_name;;//批次备注
$data['total_amount'] = $money*100;//收红包的用户的金额,精确到分
$data['total_num'] = 1;//发送数量
$data['transfer_detail_list'] = [[
"out_detail_no"=>$order_id,
"transfer_amount"=>$money*100,
"transfer_remark"=>$nick_name,
"openid"=>$openid
]];//转账明细
$data['transfer_scene_id'] = "1001";//转账场景ID 现金红包、这个需要注意,要是字符串,不然会“无法将 JSON 输入源“/body/transfer_scene_id”映射到目标字段“转账场景ID”中,此字段需要一个 string 类型的 Json 值”这个错
// 这个场景id还需要和微信商户后台对应(该批次转账使用的转账场景,可在「商家转账到零钱 - 产品设置」中查看详情,如不填写则使用商家的默认转账场景)
$url = "https://api.mch.weixin.qq.com/v3/transfer/batches";//发红包api
$result = $wechatRed->wx_post($url,json_encode($data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
$result = json_decode($result, true);
// 成功返回示例
/**
Array
(
[batch_id] => 131000609078601659665542024101536831369687
[batch_status] => ACCEPTED
[create_time] => 2024-10-15T09:06:23+08:00
[out_batch_no] => 1642002501202410150906229787
)
**/
if (isset($result['batch_id'])) {
return $result;
}else{
$this->error=$result['message'];
return false;
}
}
public function getError(){
return $this->error;
}
}