tp3.2 企业转账到零钱
- 不同于企业付款到零钱,转账功能更丰富化,支持批量付款,付款可以手机上再确定下
- php5.6 7.2均可
- action
$res = D('WxTransToPerson')->tixian($trade_no,$openid,(int)$flag['withdraw_money']);
if($res['num'] <= 0){
$maindb->rollback();
$this->json->err('微信打款出错-' . $res['desc']);
}
- WxTransToPersonModel
<?php
namespace Common\Model;
use Think\Model;
use Vendor\Func\Http;
/**
* 小程序之企业转账到零钱
*/
class WxTransToPersonModel extends Model
{
//其中 wx_ciphertext 方法是获取平台证书和平台证书序列号使用,获取平台证书需要安装php7.1以上
/***
* out_trade_no 订单号
* money 支付金额,单位:分
*/
public function tixian($out_trade_no,$openid,$money){
setlog([$out_trade_no,$openid,$money],[],'','trans_start.log');
if(!$out_trade_no || !$openid){
$result_arr = [
'num' => -999,
'desc' => '缺少参数',
];
return $result_arr;
}
if($money < 30){
$result_arr = [
'num' => -99,
'desc' => '最低0.3元',
];
return $result_arr;
}
if(10000 < $money){
$result_arr = [
'num' => -939,
'desc' => '最多一次100元',
];
return $result_arr;
}
//查询用户授权记录
Vendor('wxpay3.WxPayJsApiPay');
$input = new \JsApiPay();
$url='https://api.mch.weixin.qq.com/v3/transfer/batches';
$body['appid'] = C('WX_APP_ID');
$body['out_batch_no'] = 'Y' . $out_trade_no;// 批次单号由单个单号演变而来
$body['batch_name'] = '用户提现';
$body['batch_remark'] = '提现到账';
$body['total_amount'] = $money;
$body['total_num'] = 1;
//订单风险金
$detail = [
'out_detail_no' => $out_trade_no,
'transfer_amount' => $money,
'transfer_remark' => '用户提现到账',
'openid' => $openid,
//'user_name' => $input->getEncrypt($user_name), //noted zb敏感信息加密
];
$body['transfer_detail_list'][] = $detail;
$responseData = $input->Unite($url,'POST',json_encode($body));
$res = json_decode($responseData,true);
setlog([$out_trade_no,$openid,$money],$res,'','trans.log');
if($res['httpCode'] != 200){
$result_arr = [
'num' => -299,
'desc' => '发生错误-' . $res['message'],
];
return $result_arr;
}
$result_arr = [
'num' => 1,
'desc' => '处理成功-',
'batch_id' => $res['batch_id'],
'out_batch_no' => $res['out_batch_no'],
];
return $result_arr;
}
/*
*array(3) {
["code"] => string(10) "NOT_ENOUGH"
["message"] => string(30) "账户余额不足,请充值"
["httpCode"] => int(403)
}
*
array(4) {
["batch_id"] => string(40) "1030000005401361716282022060500845809117"// 微信明细单号
["create_time"] => string(25) "2022-06-05T12:54:50+08:00"
["out_batch_no"] => string(15) "Y20220605125450" // 微信批次单号
["httpCode"] => int(200)
}
https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml
*/
}
- WxPayJsApiPay.php
<?php
class JsApiPay
{
public $data = null;
public $serial_no = '****************************************'; //商户证书序列号 和 平台证书序列号 是两回事---由函数根据参数抓取
public $mchid = '*****'; //商户号
public $mch_key_v3 = '*******'; //v3秘钥
public function Unite($url,$http_method='GET',$body){
$Authorization = $this->getSign($url,$http_method,$body);
return $this->curl_request($url, $body, $http_method, $Authorization);
}
public function JsapiSign($prepay_id,$appid,$timestamp,$nonce){
$mch_private_key = file_get_contents('./apiclient_key.pem'); //获取密钥文件内容
$message = $appid."\n".
$timestamp."\n".
$nonce."\n".
$prepay_id."\n";
openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
return $sign;
}
public function getSign($url,$http_method='GET',$body){
$serial_no=$this->serial_no;
$merchant_id=$this->mchid;
$timestamp=time();
$nonce=$this->getNonceStr();
$mch_private_key = file_get_contents('./apiclient_key.pem'); //获取密钥文件内容
$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";
openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
$schema = 'WECHATPAY2-SHA256-RSA2048';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$merchant_id, $nonce, $timestamp, $serial_no, $sign);
return $schema.' '.$token;
}
/*验证签名*/
public function verifysign($serial_no,$TIMESTAMP,$NONCE,$BODY,$SIGNATURE){
$SIGNATURE = base64_decode($SIGNATURE);
$message = $TIMESTAMP."\n".
$NONCE."\n".
$BODY."\n";
$mch_private_key = $this->wx_ciphertext($serial_no);
$pubkeyid = openssl_pkey_get_public($mch_private_key);
$ok = openssl_verify($message, $SIGNATURE, $pubkeyid,OPENSSL_ALGO_SHA256);
openssl_free_key($pubkeyid);
if($ok == 1){
return true;
}else{
return false;
}
}
/*获取微信支付平台证书
serial_no:平台证书序列号
*/
public function wx_ciphertext(){
$url = 'https://api.mch.weixin.qq.com/v3/certificates';
$certificates = $this->Unite($url,'GET','');
$certificates = json_decode($certificates,true);
// foreach ($certificates['data'] as $key => $value) {
// if($value['serial_no']==$this->serial_no){
// $associatedData=$value['encrypt_certificate']['associated_data'];
// $nonceStr=$value['encrypt_certificate']['nonce'];
// $ciphertext=$value['encrypt_certificate']['ciphertext'];
// break;
// }
// }
$data = $certificates['data'][0]['encrypt_certificate'];
$associatedData = $data['associated_data'];
$nonceStr = $data['nonce'];
$ciphertext = $data['ciphertext'];
$AesUtil = new \AesUtil($this->mch_key_v3);
return $AesUtil->decryptToString($associatedData, $nonceStr, $ciphertext); //获取平台证书。复制到文本里改为pem后缀
}
/**
* 产生随机字符串,不长于32位
* @param int $length
* @return 产生的随机字符串
*/
public static function getNonceStr($length = 32)
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++ ) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**获取平台证书序列号**/
public static function getCertificate($filepath) {
$cert = openssl_x509_read(file_get_contents($filepath));
$cert_data = openssl_x509_parse($cert);
dump($cert_data);
array_walk($cert_data, 'print_element');
// Free the resource
openssl_x509_free($cert);
dump($cert);
// return openssl_x509_read(file_get_contents($filepath));
}
/**
* @Description: curl请求
* @Author: Yang
* @param $url
* @param null $data
* @param string $method
* @param array $header
* @param bool $https
* @param int $timeout
* @return mixed
*/
function curl_request($url,$data=null,$method='get',$Authorization,$https=true,$timeout = 5){
$header = array(
'Content-Type: application/json',
'Accept: application/json',
'Wechatpay-Serial:******',//平台序列号
'Authorization: '.$Authorization
);
$user_agent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36";
$method = strtoupper($method);
$ch = curl_init();//初始化
curl_setopt($ch, CURLOPT_URL, $url);//访问的URL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//只获取页面内容,但不输出
if($https){
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);//https请求 不验证证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//https请求 不验证HOST
}
if ($method != "GET") {
if($method == 'POST'){
curl_setopt($ch, CURLOPT_POST, true);//请求方式为post请求
}
if ($method == 'PUT' || strtoupper($method) == 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); //设置请求方式
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);//请求数据
}
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头
curl_setopt($ch, CURLOPT_USERAGENT,$user_agent);
//curl_setopt($ch, CURLOPT_HEADER, false);//设置不需要头信息
$result = curl_exec($ch);//执行请求
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
if($result){
// file_put_contents('./text.txt', '请求返回:'.$result.PHP_EOL, FILE_APPEND);
$result=json_decode($result,true);
$result['httpCode']=$httpCode;
$result = json_encode($result,JSON_UNESCAPED_UNICODE);
} else {
$result = json_encode(['httpCode'=>$httpCode]);
}
curl_close($ch);//关闭curl,释放资源
return $result;
}
/**
* 敏感信息加密算法
* @param $str
* @return string
*/
public function getEncrypt($str)
{
//$str是待加密字符串
$public_key = file_get_contents('../vendor/wxpay3/cacert/pingtai_cert.pem'); //此处证书文件为平台证书
$encrypted = '';
openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING);//这里 类型要设置为OPENSSL_PKCS1_OAEP_PADDING
//base64编码
$sign = base64_encode($encrypted);
return $sign;
}
}
class AesUtil{
/**
* AES key
* @var string
*/
private $aesKey;
const KEY_LENGTH_BYTE = 32;
const AUTH_TAG_LENGTH_BYTE = 16;
/**
* Constructor
*/
public function __construct($aesKey)
{
if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
}
$this->aesKey = $aesKey;
}
/**
* Decrypt AEAD_AES_256_GCM ciphertext
*
* @param string $associatedData AES GCM additional authentication data
* @param string $nonceStr AES GCM nonce
* @param string $ciphertext AES GCM cipher text
*
* @return string|bool Decrypted string on success or FALSE on failure
*/
public function decryptToString($associatedData, $nonceStr, $ciphertext)
{
$ciphertext = \base64_decode($ciphertext);
if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
return false;
}
// ext-sodium (default installed on >= PHP 7.2)
if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
}
// ext-libsodium (need install libsodium-php 1.x via pecl)
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
}
// openssl (PHP >= 7.1 support AEAD)
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
$authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr,$authTag, $associatedData);
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}
}
相信坚持的力量,日复一日的习惯.