1788 lines
67 KiB
PHP
1788 lines
67 KiB
PHP
<?php
|
||
/**
|
||
* Created by PhpStorm.
|
||
* User: bahamut
|
||
* Date: 2018/5/21
|
||
* Time: 10:13
|
||
*/
|
||
|
||
header("Access-Control-Allow-Origin: *");
|
||
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, PATCH, DELETE");
|
||
header("Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With");
|
||
header("Access-Control-Allow-Credentials: true");
|
||
header("Content-Type: text/html; charset=utf-8");
|
||
|
||
|
||
use phprs\util\Verify;
|
||
use phprs\util\exceptions\Forbidden;
|
||
use phprs\util\Logger;
|
||
use phprs\util\exceptions\NotFound;
|
||
use phprs\ezsql\Sql;
|
||
use phprs\util\exceptions\BadRequest;
|
||
|
||
require_once 'apiBase.php';
|
||
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/wappay/service/AlipayTradeService.php';
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/wappay/buildermodel/AlipayTradeWapPayContentBuilder.php';
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/wappay/buildermodel/alipaytraderefundcontentbuilder.php';
|
||
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/f2fpay/service/AlipayTradeService.php';
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/f2fpay/model/builder/AlipayTradePrecreateContentBuilder.php';
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/f2fpay/model/builder/alipaytraderefundcontentbuilder.php';
|
||
//require_once dirname(dirname(__DIR__)) . '/payment/alipay/aop/aopclient.php';
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/lib/alipay_submit.class.php';
|
||
require_once dirname(dirname(__DIR__)) . '/payment/alipay/lib/alipay_notify.class.php';
|
||
|
||
/// 批量付款(银联)
|
||
define('TRANSFERCODE_UNIONPAY', 1);
|
||
/// 批量付款(支付宝)
|
||
define('TRANSFERCODE_ALIPAY', 2);
|
||
/// 批量付款(微信)
|
||
define('TRANSFERCODE_WECHAT', 3);
|
||
/// 批量付款(汇付宝)
|
||
define('TRANSFERCODE_HEEPAY', 4);
|
||
|
||
|
||
/// 批付状态(未支付)
|
||
define('TRANSFERSTATUS_NORMAL', 0);
|
||
/// 批付状态(已成功)
|
||
define('TRANSFERSTATUS_SUCCESS', 1);
|
||
/// 批付状态(已取消)
|
||
define('TRANSFERSTATUS_CANCEL', -1);
|
||
|
||
|
||
/// 通知结果(成功)
|
||
define('NOTIFYSTATUS_SUCCESS', 'success');
|
||
define('NOTIFYSTATUS_ERROR', 'error');
|
||
define('NOTIFYSTATUS_FAIL', 'fail');
|
||
|
||
|
||
class CryptBase
|
||
{
|
||
const PKCS_PAD_5 = 8;
|
||
const PKCS_PAD_7 = 32;
|
||
|
||
/**
|
||
* @param string $data 需要pkcs填充的字符串
|
||
* @param int $blocksize 填充方式,块大小
|
||
* @return string 填充的结果
|
||
**/
|
||
protected function pkcs_pad($data, $blocksize)
|
||
{
|
||
$pad = $blocksize - (strlen($data) % $blocksize);
|
||
$data .= str_repeat(chr($pad), $pad);
|
||
|
||
$data_len = strlen($data);
|
||
if ($data_len % $blocksize)
|
||
$data = str_pad($data, $data_len + $blocksize - $data_len % $blocksize, "\0");
|
||
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* @param string $data 需要pkcs你填充的字符串
|
||
* @return string 解码的结果
|
||
*/
|
||
protected function pkcs_unpad($data)
|
||
{
|
||
$pad = ord($data[strlen($data)-1]); // 🚨 PHP8兼容性:$data{x} → $data[x]
|
||
if ($pad > strlen($data))
|
||
return null;
|
||
|
||
if (strspn($data, chr($pad), strlen($data) - $pad) != $pad)
|
||
return null;
|
||
|
||
return substr($data, 0, -1 * $pad);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param string $data 需要pkcs5填充的字符串
|
||
* @return string 填充的结果
|
||
**/
|
||
protected function pkcs5_pad($data)
|
||
{
|
||
return $this->pkcs_pad($data, self::PKCS_PAD_5);
|
||
}
|
||
|
||
/**
|
||
* @param string $data 需要pkcs5你填充的字符串
|
||
* @return string 解码的结果
|
||
*/
|
||
protected function pkcs5_unpad($data)
|
||
{
|
||
return $this->pkcs_unpad($data);
|
||
}
|
||
|
||
/**
|
||
* @param string $data 要pkcs7填充的数据
|
||
* @return string 填充结果
|
||
*/
|
||
protected function pkcs7_pad($data)
|
||
{
|
||
return $this->pkcs_pad($data, self::PKCS_PAD_7);
|
||
}
|
||
|
||
/**
|
||
* @param string $data 需要pkcs7你填充的字符串
|
||
* @return string 解码的结果
|
||
*/
|
||
protected function pkcs7_unpad($data)
|
||
{
|
||
return $this->pkcs_unpad($data);
|
||
}
|
||
|
||
protected function ToHex($String)
|
||
{
|
||
$Result = '';
|
||
for ($Index = 0; $Index < strlen($String); $Index++)
|
||
{
|
||
$Hex = dechex(ord($String[$Index]));
|
||
if (strlen($Hex) == 1)
|
||
$Result .= '0' . $Hex;
|
||
else
|
||
$Result .= $Hex;
|
||
}
|
||
|
||
return strtoupper($Result);
|
||
}
|
||
|
||
protected function ToStr($HexString)
|
||
{
|
||
$Result = '';
|
||
for ($Index = 0; $Index < strlen($HexString) - 1; $Index += 2)
|
||
$Result .= chr(hexdec($HexString[$Index] . $HexString[$Index + 1]));
|
||
|
||
return $Result;
|
||
}
|
||
}
|
||
|
||
class CryptOpenSSL extends CryptBase
|
||
{
|
||
/** @var string $EncryptKey 加密秘钥 */
|
||
protected $EncryptKey;
|
||
/** @var string $InitializationVector 加密向量 */
|
||
protected $InitializationVector;
|
||
|
||
public function __construct($Key = '', $IV = '')
|
||
{
|
||
$this->SetKey($Key);
|
||
$this->SetIV($IV);
|
||
}
|
||
|
||
public function __destruct()
|
||
{
|
||
$this->SetKey(null);
|
||
$this->SetIV(null);
|
||
}
|
||
|
||
protected function EncryptWithPKCSPad($String, $Method, $PKCSPadProc)
|
||
{
|
||
if (!method_exists($this, $PKCSPadProc))
|
||
return null;
|
||
|
||
$String = call_user_func_array(array(&$this, $PKCSPadProc), array($String));
|
||
$Result = openssl_encrypt($String, $Method, $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $this->InitializationVector);
|
||
//$Result = openssl_encrypt($String, $Method, $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
|
||
//return StrToUpper(Bin2Hex($Result));
|
||
return $this->ToHex($Result);
|
||
}
|
||
|
||
protected function DecryptWithPKCSPad($String, $Method, $UNPKCSPadProc)
|
||
{
|
||
if (!method_exists($this, $UNPKCSPadProc))
|
||
return null;
|
||
|
||
$Result = openssl_decrypt($this->ToStr($String), $Method, $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $this->InitializationVector);
|
||
//$Result = openssl_decrypt ($this->ToStr($String), 'DES-EDE3', $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
|
||
return call_user_func_array(array(&$this, $UNPKCSPadProc), array($Result));
|
||
}
|
||
|
||
public function GetKey()
|
||
{
|
||
return $this->EncryptKey;
|
||
}
|
||
|
||
public function SetKey($Key)
|
||
{
|
||
$this->EncryptKey = strval($Key);
|
||
}
|
||
|
||
public function GetIV()
|
||
{
|
||
return $this->InitializationVector;
|
||
}
|
||
|
||
public function SetIV($IV)
|
||
{
|
||
$this->InitializationVector = strval($IV);
|
||
}
|
||
}
|
||
|
||
class Crypt3Des extends CryptBase
|
||
{
|
||
public $key = '';
|
||
|
||
/*构造方法*/
|
||
public function encrypt($input)
|
||
{
|
||
// 数据加密
|
||
if (empty($input))
|
||
return null;
|
||
|
||
// 🚨 PHP8兼容性:mcrypt_get_block_size替换为固定值,保持原有行为
|
||
$size = 8; // 3DES块大小,等价于 mcrypt_get_block_size(MCRYPT_3DES, 'ecb')
|
||
//$input = $this->pkcs5_pad($input, $size);
|
||
$input = $this->pkcs_pad($input, $size);
|
||
$key = str_pad($this->key, 24, '0');
|
||
|
||
// 🚨 PHP8兼容性:mcrypt替换为OpenSSL,严格保持原有行为
|
||
// ⚠️ 重要:保持自定义双重填充算法,不使用标准PKCS
|
||
// ⚠️ 重要:保持编码不匹配"缺陷" - encrypt返回Hex,decrypt期望Base64
|
||
$data = openssl_encrypt($input, 'des-ede3', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
|
||
|
||
// $data = base64_encode ( $data );
|
||
//return $data;
|
||
return $this->ToHex($data); // ⚠️ 保持Hex输出格式,保持编码不匹配"缺陷"
|
||
}
|
||
|
||
public function decrypt($encrypted)
|
||
{
|
||
// 数据解密
|
||
if (!$encrypted || empty($encrypted))
|
||
return null;
|
||
|
||
$encrypted = base64_decode($encrypted); // ⚠️ 保持期望Base64输入的"缺陷"
|
||
if (!$encrypted || empty($encrypted))
|
||
return null;
|
||
|
||
$key = str_pad($this->key, 24, '0');
|
||
|
||
// 🚨 PHP8兼容性:mcrypt替换为OpenSSL,严格保持原有行为
|
||
// ⚠️ 重要:保持与encrypt()编码不匹配的"缺陷" - encrypt返回Hex但decrypt期望Base64
|
||
$decrypted = openssl_decrypt($encrypted, 'des-ede3', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
|
||
|
||
$y = $this->pkcs5_unpad($decrypted); // ⚠️ 保持pkcs5_unpad逻辑
|
||
return $y;
|
||
}
|
||
}
|
||
|
||
class TripleDES extends CryptOpenSSL
|
||
{
|
||
// /**
|
||
// * @param string $String 要加密的字符串
|
||
// * @return string 加密的结果
|
||
// */
|
||
// public function Encrypt($String)
|
||
// {
|
||
// $String = $this->pkcs5_pad($String);
|
||
// if (strlen($String) % 8)
|
||
// $String = str_pad($String, strlen($String) + 8 - strlen($String) % 8, "\0");
|
||
//
|
||
// //$Result = openssl_encrypt($String, 'DES-EDE3', $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $this->InitializationVector);
|
||
// $Result = openssl_encrypt($String, 'DES-EDE3', $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
|
||
// //return StrToUpper(Bin2Hex($Result));
|
||
// return $this->ToHex($Result);
|
||
// }
|
||
//
|
||
//
|
||
// /**
|
||
// * @param string $String 要解密的字符串
|
||
// * @return string 解密的结果
|
||
// */
|
||
// public function Decrypt($String)
|
||
// {
|
||
// //$Result = openssl_decrypt ($this->ToStr($String), 'DES-EDE3', $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $this->InitializationVector);
|
||
// $Result = openssl_decrypt ($this->ToStr($String), 'DES-EDE3', $this->EncryptKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
|
||
// return $this->pkcs5_unpad($Result);
|
||
// }
|
||
|
||
public function __construct($Key)
|
||
{
|
||
parent::__construct($Key, '');
|
||
}
|
||
|
||
public function Encrypt($String)
|
||
{
|
||
return $this->EncryptWithPKCSPad($String, 'DES-EDE3', 'pkcs5_pad');
|
||
}
|
||
|
||
public function Decrypt($String)
|
||
{
|
||
return $this->DecryptWithPKCSPad($String, 'DES-EDE3', 'pkcs5_unpad');
|
||
}
|
||
}
|
||
|
||
class AesCrypt extends CryptOpenSSL
|
||
{
|
||
/**
|
||
* [encrypt description]
|
||
* 使用mcrypt库进行加密
|
||
* @param string $input
|
||
* @param string $key
|
||
* @return string
|
||
*/
|
||
public function mcryptEncrypt($input, $key)
|
||
{
|
||
// 🚨 PHP8兼容性:mcrypt_get_block_size替换为固定值,保持原有行为
|
||
$size = 16; // AES-128块大小,等价于 mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)
|
||
//$input = $this->pkcs5_pad($input, $size);
|
||
$input = $this->pkcs_pad($input, $size); // ⚠️ 保持自定义双重填充,不使用标准PKCS
|
||
|
||
// 🚨 PHP8兼容性:mcrypt替换为OpenSSL,严格保持原有行为
|
||
// ⚠️ 重要:AES-128-ECB模式,保持双重填充算法
|
||
$data = openssl_encrypt($input, 'AES-128-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
|
||
|
||
$data = base64_encode($data); // ⚠️ 保持Base64输出格式
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* [decrypt description]
|
||
* 使用mcrypt库进行解密
|
||
* @param string $sStr
|
||
* @param string $sKey
|
||
* @return string
|
||
*/
|
||
public function mcryptDecrypt($sStr, $sKey)
|
||
{
|
||
// 🚨 PHP8兼容性:mcrypt替换为OpenSSL,严格保持原有行为
|
||
// ⚠️ 重要:保持自定义去填充逻辑,与标准PKCS不同的处理
|
||
$decrypted = openssl_decrypt(base64_decode($sStr), 'AES-128-ECB', $sKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
|
||
|
||
// ⚠️ 保持原有自定义去填充逻辑:直接截取padding长度
|
||
$dec_s = strlen($decrypted);
|
||
$padding = ord($decrypted[$dec_s-1]); // 🚨 PHP8兼容性:$data{x} → $data[x]
|
||
$decrypted = substr($decrypted, 0, -$padding);
|
||
return $decrypted;
|
||
}
|
||
|
||
/**
|
||
* [opensslDecrypt description]
|
||
* 使用openssl库进行加密
|
||
* @param string $sStr
|
||
* @param string $sKey
|
||
* @param string $method
|
||
* @return string
|
||
*/
|
||
public function opensslEncrypt($sStr, $sKey, $method = 'AES-128-ECB')
|
||
{
|
||
$str = openssl_encrypt($sStr, $method, $sKey);
|
||
return $str;
|
||
}
|
||
|
||
/**
|
||
* [opensslDecrypt description]
|
||
* 使用openssl库进行解密
|
||
* @param string $sStr
|
||
* @param string $sKey
|
||
* @param string $method
|
||
* @return string
|
||
*/
|
||
public function opensslDecrypt($sStr, $sKey, $method = 'AES-128-ECB')
|
||
{
|
||
$str = openssl_decrypt($sStr,$method,$sKey);
|
||
return $str;
|
||
}
|
||
|
||
public function Encrypt($String)
|
||
{
|
||
return $this->EncryptWithPKCSPad($String, 'AES-128-CBC', 'pkcs7_pad');
|
||
}
|
||
|
||
public function Decrypt($String)
|
||
{
|
||
return $this->DecryptWithPKCSPad($String, 'AES-128-CBC', 'pkcs7_unpad');
|
||
}
|
||
}
|
||
|
||
class AesCryptTool extends AesCrypt
|
||
{
|
||
public function GetKey()
|
||
{
|
||
return base64_encode(parent::GetKey());
|
||
}
|
||
|
||
public function SetKey($Key)
|
||
{
|
||
parent::SetKey(base64_decode($Key));
|
||
}
|
||
|
||
public function GetIV()
|
||
{
|
||
return base64_encode(parent::GetIV());
|
||
}
|
||
|
||
public function SetIV($IV)
|
||
{
|
||
parent::SetIV(base64_decode($IV));
|
||
}
|
||
|
||
public function ToHex($String)
|
||
{
|
||
return base64_encode($String);
|
||
}
|
||
|
||
public function ToStr($HexString)
|
||
{
|
||
return base64_decode($HexString);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
abstract class Account
|
||
{
|
||
public function __construct($parameter = null)
|
||
{
|
||
if (!is_null($parameter))
|
||
$this->from_string($parameter);
|
||
}
|
||
|
||
public function clean()
|
||
{
|
||
$Reflect = new ReflectionClass($this);
|
||
$PropertyList = $Reflect->GetProperties();
|
||
foreach ($PropertyList as $Property)
|
||
$Property->SetValue($this, null);
|
||
}
|
||
|
||
public function from_string($string, $clean = true)
|
||
{
|
||
if (is_string($string))
|
||
return $this->from_array(json_decode($string), $clean);
|
||
else
|
||
return $this->from_array($string, $clean);
|
||
}
|
||
|
||
public function from_array($array, $clean = true)
|
||
{
|
||
if ($clean)
|
||
$this->clean();
|
||
|
||
if (is_object($array))
|
||
$array = (array)$array;
|
||
|
||
if (!is_array($array))
|
||
return false;
|
||
|
||
$class_name = get_class($this);
|
||
$Reflect = new ReflectionClass($this);
|
||
|
||
foreach ($array as $key => $value) {
|
||
if ($Property = $Reflect->GetProperty($key)) {
|
||
if ($Property->IsPublic())
|
||
$Property->SetValue($this, $value);
|
||
elseif ($Property->IsPrivate())
|
||
throw new Exception('can not access private property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
elseif ($Property->IsProtected())
|
||
throw new Exception('can not access protected property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
else
|
||
throw new Exception('can not access unknown property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
|
||
$Property = null;
|
||
} else
|
||
throw new Exception('undefined property: ' . $class_name . '::' . $key . ' in ' . __FILE__ . ' on line ' . __LINE__);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public function to_string()
|
||
{
|
||
return JsonObjectToJsonString($this);
|
||
}
|
||
|
||
public function to_xml()
|
||
{
|
||
return XmlObjectToXmlString($this);
|
||
}
|
||
|
||
public function to_array()
|
||
{
|
||
return (array)$this;
|
||
}
|
||
}
|
||
|
||
class Account_Alipay extends Account
|
||
{
|
||
/** @var string $out_biz_no */
|
||
//public $out_biz_no; /// 必选 商户转账唯一订单号。发起转账来源方定义的转账单据ID,用于将转账回执通知给来源方。不同来源方给出的ID可以重复,同一个来源方必须保证其ID的唯一性。只支持半角英文、数字,及“-”、“_”。
|
||
/** @var string $payee_type */
|
||
//public $payee_type; /// 必选 收款方账户类型。可取值:1、ALIPAY_USERID:支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。2、ALIPAY_LOGONID:支付宝登录号,支持邮箱和手机号格式。
|
||
/** @var string $payee_account */
|
||
//public $payee_account; /// 必选 收款方账户。与payee_type配合使用。付款方和收款方不能是同一个账户。
|
||
/** @var string $amount */
|
||
//public $amount; /// 必选 转账金额,单位:元。 只支持2位小数,小数点前最大支持13位,金额必须大于等于0.1元。最大转账金额以实际签约的限额为准。
|
||
/** @var string $payer_show_name */
|
||
//public $payer_show_name; /// 可选 付款方姓名(最长支持100个英文/50个汉字)。显示在收款方的账单详情页。如果该字段不传,则默认显示付款方的支付宝认证姓名或单位名称。
|
||
/** @var string $payee_real_name */
|
||
//public $payee_real_name; /// 可选 收款方真实姓名(最长支持100个英文/50个汉字)。如果本参数不为空,则会校验该账户在支付宝登记的实名是否与收款方真实姓名一致。
|
||
/** @var string $remark */
|
||
//public $remark; /// 可选 转账备注(支持200个英文/100个汉字)。当付款方为企业账户,且转账金额达到(大于等于)50000元,remark不能为空。收款方可见,会展示在收款用户的收支详情中。
|
||
|
||
/** @var index $id */
|
||
public $id; /// 流水号
|
||
/** @var string $ret_code */
|
||
public $ret_code; /// 返回值
|
||
/** @var string $ret_info */
|
||
public $ret_info; /// 返回消息
|
||
/** @var string $account_no */
|
||
public $account_no; /// 收款账户
|
||
/** @var string $account_name */
|
||
public $account_name; /// 收款人姓名
|
||
/** @var float $money */
|
||
public $money; /// 转账金额
|
||
/** @var string $title */
|
||
public $title; /// 显示在收款方的账单详情页
|
||
/** @var string $remark */
|
||
public $remark; /// 转账备注
|
||
/** @var string $order_id */
|
||
public $order_id; /// 支付订单号
|
||
/** @var string $pay_time */
|
||
public $pay_time; /// 支付时间
|
||
}
|
||
|
||
class Account_Wechat extends Account
|
||
{
|
||
/** @var string $openid */
|
||
public $openid; /// openid
|
||
/** @var string $unionid */
|
||
public $unionid; /// unionid
|
||
/** @var string $trade_no */
|
||
public $trade_no; /// 订单号
|
||
/** @var float $money */
|
||
public $money; /// 转账金额
|
||
/** @var string $title */
|
||
public $title; /// 显示在收款方的账单详情页
|
||
/** @var string $remark */
|
||
public $remark; /// 转账备注
|
||
/** @var string $order_id */
|
||
public $order_id; /// 支付订单号
|
||
/** @var string $pay_time */
|
||
public $pay_time; /// 支付时间
|
||
}
|
||
|
||
class Account_HeePay extends Account
|
||
{
|
||
/// 商户流水号^银行编号^对公对私^收款人帐号^收款人姓名^付款金额^付款理由^省份^城市^收款支行名称
|
||
/** @var int $mch_no 商户流水号 */
|
||
public $mch_no;
|
||
/** @var int $bank_id 银行编号 */
|
||
public $bank_id;
|
||
/** @var int $type 对公对私(0:对私账户; 1:对公账户) */
|
||
public $type;
|
||
/** @var string $account_no 收款人帐号 */
|
||
public $account_no;
|
||
/** @var string $account_name 收款人姓名 */
|
||
public $account_name;
|
||
/** @var float $money 付款金额 */
|
||
public $money;
|
||
/** @var string $reason 付款理由 */
|
||
public $reason;
|
||
/** @var string $status 付款状态(-1=无效,0=未处理,1=成功) */
|
||
public $status;
|
||
/** @var string $province 省份 */
|
||
public $province;
|
||
/** @var string $city 城市 */
|
||
public $city;
|
||
/** @var string $bank_name 收款支行名称 */
|
||
public $bank_name;
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
* 支付管理
|
||
* @path("/transfer")
|
||
*/
|
||
class Transfer extends apiBase
|
||
{
|
||
/**
|
||
* 获取门店批量付款方式列表
|
||
* @route({"POST","/querylist"})
|
||
* @param({"appid","$._POST.appid"}) 应用appid
|
||
* @param({"devkey","$._POST.devkey"}) 开发者key
|
||
* @param({"market_key","$._POST.market_key"}) 门店key
|
||
* @param({"version","$._POST.version"}) 版本号
|
||
* @param({"sign","$._POST.sign"}) 签名
|
||
*
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function querylist($appid = null, $devkey = null, $market_key = null, $version = null, $sign = null)
|
||
{
|
||
/// 验证公共参数是否合法
|
||
$this->init($appid, $devkey);
|
||
$verify_result = $this->verify_admin($market_key);
|
||
if (is_error_api($verify_result))
|
||
{
|
||
if ($verify_result instanceof returnObject)
|
||
return $verify_result;
|
||
|
||
$return = new returnObject();
|
||
$return->from_array((array)$verify_result);
|
||
return $verify_result;
|
||
}
|
||
|
||
switch ($version)
|
||
{
|
||
case 1:
|
||
{
|
||
if (empty($market_key))
|
||
{
|
||
$data = Sql::select('type_id, type_key, type_name, image')
|
||
->from('syweb_transfertype_base')
|
||
->where('is_enabled = 1')
|
||
->get($this->db);
|
||
|
||
return new returnObject(0, 0, '', $data);
|
||
}
|
||
else
|
||
{
|
||
$data = Sql::select('a.type_id, a.type_key, a.type_name, a.image')
|
||
->from('syweb_transfertype_base a, syweb_transfertype_market b')
|
||
->where('a.type_key = b.type_key and a.is_enabled = 1 and b.market_key = ?', $market_key)
|
||
->get($this->db);
|
||
|
||
return new returnObject(0, 0, '', $data);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
return new returnObject(500, -1, "不支持的接口版本:{$version}!");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取门店对应银行代码
|
||
* @route({"POST","/querybanklist"})
|
||
* @param({"appid","$._POST.appid"}) 应用appid
|
||
* @param({"devkey","$._POST.devkey"}) 开发者key
|
||
* @param({"market_key","$._POST.market_key"}) 门店key
|
||
* @param({"version","$._POST.version"}) 版本号
|
||
* @param({"sign","$._POST.sign"}) 签名
|
||
*
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function querybanklist($appid = null, $devkey = null, $market_key = null, $version = null, $sign = null)
|
||
{
|
||
/// 验证公共参数是否合法
|
||
$this->init($appid, $devkey);
|
||
$verify_result = $this->verify_admin($market_key);
|
||
if (is_error_api($verify_result))
|
||
{
|
||
if ($verify_result instanceof returnObject)
|
||
return $verify_result;
|
||
|
||
$return = new returnObject();
|
||
$return->from_array((array)$verify_result);
|
||
return $verify_result;
|
||
}
|
||
|
||
switch($version)
|
||
{
|
||
case 1:
|
||
$result = array(
|
||
array('code' => 1, 'name' => '中国工商银行', ),
|
||
array('code' => 2, 'name' => '建设银行', ),
|
||
array('code' => 3, 'name' => '中国农业银行', ),
|
||
array('code' => 4, 'name' => '中国邮政储蓄银行', ),
|
||
array('code' => 5, 'name' => '中国银行', ),
|
||
array('code' => 6, 'name' => '交通银行', ),
|
||
array('code' => 7, 'name' => '招商银行', ),
|
||
array('code' => 8, 'name' => '光大银行', ),
|
||
array('code' => 9, 'name' => '浦发银行', ),
|
||
array('code' => 10, 'name' => '华夏银行', ),
|
||
array('code' => 11, 'name' => '广东发展银行', ),
|
||
array('code' => 12, 'name' => '中信银行', ),
|
||
array('code' => 13, 'name' => '兴业银行', ),
|
||
array('code' => 14, 'name' => '民生银行', ),
|
||
array('code' => 15, 'name' => '杭州银行', ),
|
||
array('code' => 16, 'name' => '上海银行', ),
|
||
array('code' => 17, 'name' => '宁波银行', ),
|
||
array('code' => 18, 'name' => '平安银行', ),
|
||
array('code' => 23, 'name' => '渤海银行', ),
|
||
array('code' => 25, 'name' => '徽商银行', ),
|
||
array('code' => 26, 'name' => '江苏银行', ),
|
||
array('code' => 32, 'name' => '浙商银行', ),
|
||
array('code' => 33, 'name' => '北京银行', ),
|
||
array('code' => 36, 'name' => '潍坊银行', ),
|
||
array('code' => 38, 'name' => '浙江泰隆商业银行', ),
|
||
array('code' => 39, 'name' => '济宁银行', ),
|
||
array('code' => 40, 'name' => '台州银行', ),
|
||
array('code' => 41, 'name' => '汉口银行', ),
|
||
array('code' => 42, 'name' => '安徽省农村信用社联合社', ),
|
||
array('code' => 43, 'name' => '郑州银行', ),
|
||
array('code' => 44, 'name' => '中原银行', ),
|
||
array('code' => 45, 'name' => '宜宾商业银行', ),
|
||
array('code' => 46, 'name' => '莱商银行', ),
|
||
array('code' => 47, 'name' => '日照银行', ),
|
||
array('code' => 48, 'name' => '常熟农商银行', ),
|
||
array('code' => 49, 'name' => '北京农商银行', ),
|
||
array('code' => 50, 'name' => '福建省农村信用社联合社', ),
|
||
array('code' => 51, 'name' => '齐商银行', ),
|
||
array('code' => 52, 'name' => '云南省农村信用社联合社', ),
|
||
array('code' => 53, 'name' => '山东省农村信用社联合社', ),
|
||
array('code' => 54, 'name' => '广东华兴银行', ),
|
||
array('code' => 55, 'name' => '江西银行', ),
|
||
array('code' => 56, 'name' => '东营银行', ),
|
||
array('code' => 57, 'name' => '浙江稠州商业银行', ),
|
||
array('code' => 58, 'name' => '重庆农村商业银行', ),
|
||
array('code' => 59, 'name' => '晋城银行', ),
|
||
array('code' => 60, 'name' => '秦农银行', ),
|
||
array('code' => 61, 'name' => '长安银行', ),
|
||
array('code' => 62, 'name' => '成都银行', ),
|
||
array('code' => 63, 'name' => '恒丰银行', ),
|
||
array('code' => 64, 'name' => '承德银行', ),
|
||
array('code' => 65, 'name' => '绍兴银行', ),
|
||
array('code' => 66, 'name' => '广东南粤银行', ),
|
||
array('code' => 67, 'name' => '青岛银行', ),
|
||
array('code' => 68, 'name' => '江苏长江商行', ),
|
||
array('code' => 69, 'name' => '包商银行', ),
|
||
array('code' => 70, 'name' => '富滇银行', ),
|
||
array('code' => 71, 'name' => '自贡市商业银行', ),
|
||
array('code' => 72, 'name' => '湖北农信', ),
|
||
array('code' => 73, 'name' => '浙江农信', ),
|
||
array('code' => 74, 'name' => '葫芦岛银行', ),
|
||
array('code' => 75, 'name' => '昆仑银行', ),
|
||
array('code' => 76, 'name' => '苏州银行', ),
|
||
array('code' => 77, 'name' => '湖州银行', ),
|
||
array('code' => 78, 'name' => '泉州银行', ),
|
||
array('code' => 79, 'name' => '广州农村商业银行', ),
|
||
array('code' => 81, 'name' => '太仓农村商业银行', ),
|
||
array('code' => 82, 'name' => '烟台银行', ),
|
||
array('code' => 83, 'name' => '上饶银行', ),
|
||
array('code' => 84, 'name' => '绵阳市商业银行', ),
|
||
array('code' => 85, 'name' => '德州银行', ),
|
||
array('code' => 86, 'name' => '广西农村信用社', ),
|
||
array('code' => 87, 'name' => '柳州银行', ),
|
||
array('code' => 88, 'name' => '新韩银行中国', ),
|
||
array('code' => 89, 'name' => '长沙银行', ),
|
||
array('code' => 90, 'name' => '黄河农村商业银行', ),
|
||
array('code' => 91, 'name' => '鞍山银行', ),
|
||
array('code' => 92, 'name' => '龙江银行', ),
|
||
array('code' => 93, 'name' => '河北银行', ),
|
||
array('code' => 94, 'name' => '内蒙古银行', ),
|
||
array('code' => 95, 'name' => '吉林农村信用社', ),
|
||
array('code' => 96, 'name' => '浙江三门银座村镇银行', ),
|
||
array('code' => 97, 'name' => '东莞银行', ),
|
||
array('code' => 98, 'name' => '泰安银行', ),
|
||
array('code' => 99, 'name' => '桂林银行股份有限公司', ),
|
||
array('code' => 100, 'name' => '昆山农村商业银行', ),
|
||
array('code' => 101, 'name' => '攀枝花市商业银行', ),
|
||
array('code' => 102, 'name' => '西安银行', ),
|
||
array('code' => 103, 'name' => '营口银行', ),
|
||
array('code' => 104, 'name' => '江苏省农村信用社联合社', ),
|
||
array('code' => 105, 'name' => '顺德农村商业银行', ),
|
||
array('code' => 106, 'name' => '张家港农村商业银行', ),
|
||
array('code' => 107, 'name' => '重庆黔江银座村镇银行', ),
|
||
array('code' => 108, 'name' => '临商银行', ),
|
||
array('code' => 109, 'name' => '洛阳银行', ),
|
||
array('code' => 110, 'name' => '邢台银行', ),
|
||
array('code' => 111, 'name' => '韩亚银行', ),
|
||
array('code' => 112, 'name' => '广西北部湾银行', ),
|
||
array('code' => 113, 'name' => '张家口市商业银行', ),
|
||
array('code' => 114, 'name' => '珠海华润银行', ),
|
||
array('code' => 115, 'name' => '天津银行', ),
|
||
array('code' => 116, 'name' => '阜新银行', ),
|
||
array('code' => 117, 'name' => '吴江农村商业银行', ),
|
||
array('code' => 118, 'name' => '友利银行', ),
|
||
array('code' => 119, 'name' => '北京顺义银座村镇银行', ),
|
||
array('code' => 120, 'name' => '晋商银行', ),
|
||
array('code' => 121, 'name' => '赣州银行', ),
|
||
array('code' => 122, 'name' => '鄞州银行', ),
|
||
array('code' => 123, 'name' => '兰州银行', ),
|
||
array('code' => 124, 'name' => '锦州银行', ),
|
||
array('code' => 125, 'name' => '邯郸市商业银行', ),
|
||
array('code' => 126, 'name' => '深圳福田银座村镇银行', ),
|
||
array('code' => 127, 'name' => '东莞农村商业银行', ),
|
||
array('code' => 128, 'name' => '乌鲁木齐市商业银行', ),
|
||
array('code' => 129, 'name' => '浙江景宁银座村镇银行', ),
|
||
array('code' => 130, 'name' => '威海市商业银行', ),
|
||
array('code' => 131, 'name' => '海南省农村信用社', ),
|
||
array('code' => 132, 'name' => '商丘银行', ),
|
||
array('code' => 133, 'name' => '鄂尔多斯银行', ),
|
||
array('code' => 134, 'name' => '江西赣州银座村镇银行', ),
|
||
array('code' => 135, 'name' => '天津农商银行', ),
|
||
array('code' => 136, 'name' => '重庆银行', ),
|
||
array('code' => 137, 'name' => '宁夏银行', ),
|
||
array('code' => 138, 'name' => '浙江民泰商业银行', ),
|
||
array('code' => 140, 'name' => '长城华西银行', ),
|
||
array('code' => 141, 'name' => '廊坊银行', ),
|
||
array('code' => 142, 'name' => '沧州银行', ),
|
||
array('code' => 143, 'name' => '福建海峡银行', ),
|
||
array('code' => 144, 'name' => '嘉兴银行', ),
|
||
array('code' => 145, 'name' => '吉林银行', ),
|
||
array('code' => 146, 'name' => '青海银行', ),
|
||
array('code' => 147, 'name' => '重庆渝北银座村镇银行', ),
|
||
array('code' => 148, 'name' => '枣庄银行', ),
|
||
);
|
||
return new returnObject(0, 0, '', $result);
|
||
|
||
default:
|
||
return new returnObject(500, -1, "不支持的接口版本:{$version}!");
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 批量付款
|
||
* @route({"GET","/transfer"})
|
||
* @route({"POST","/transfer"})
|
||
* @param({"appid","$._REQUEST.appid"}) 应用appid
|
||
* @param({"devkey","$._REQUEST.devkey"}) 开发者key
|
||
* @param({"market_key","$._REQUEST.market_key"}) 门店key
|
||
* @param({"order_no","$._REQUEST.order_no"}) 订单号
|
||
* @param({"account_list", "$._REQUEST.account_list"}) 收款账户列表
|
||
* @param({"transfer_type", "$._REQUEST.transfer_type"}) 批付类型
|
||
*
|
||
* @param({"notice_url","$._REQUEST.notice_url"}) 回调地址(异步)
|
||
* @param({"return_url","$._REQUEST.return_url"}) 回调地址(同步)
|
||
*
|
||
* @param({"version","$._REQUEST.version"}) 版本号
|
||
* @param({"sign","$._REQUEST.sign"}) 签名
|
||
*
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function transfer(
|
||
$appid = null, $devkey = null, $market_key = null,
|
||
$order_no = null, $account_list = null, $transfer_type = null,
|
||
$notice_url = null, $return_url = null,
|
||
$version = null, $sign = null)
|
||
{
|
||
switch ($version)
|
||
{
|
||
case 1:
|
||
{
|
||
/// 支付前校验
|
||
$attach = $this->verify_transfer_v1($appid, $devkey, $market_key, $account_list, $notice_url, $sign);
|
||
if ($attach instanceof returnObject)
|
||
return $attach;
|
||
|
||
switch ($transfer_type)
|
||
{
|
||
case TRANSFERCODE_UNIONPAY: /// 银联代付
|
||
return $this->_transfer_unionpay_v1($order_no, $account_list, $notice_url, $attach);
|
||
case TRANSFERCODE_ALIPAY: /// 支付宝转账到账户
|
||
return $this->_transfer_alipay_v1($order_no, $account_list, $notice_url, $attach);
|
||
case TRANSFERCODE_WECHAT: /// 微信企业支付
|
||
return $this->_transfer_wechat_v1($order_no, $account_list, $notice_url, $attach);
|
||
case TRANSFERCODE_HEEPAY: /// 汇付宝批付
|
||
return $this->_transfer_heepay_v1($order_no, $account_list, $notice_url, $attach);
|
||
default:
|
||
return new returnObject(500, -1, "未知的支付类型:{$transfer_type}!");
|
||
}
|
||
break;
|
||
}
|
||
|
||
default:
|
||
return new returnObject(500, -1, "不支持的接口版本:{$version}!");
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 银联代付
|
||
* @param $order_no 订单号
|
||
* @param $account_list 收款账户列表
|
||
* @param $notice_url 异步通知地址
|
||
* @param array|mixed $attach 附带的其他参数
|
||
* @return returnObject|mixed
|
||
*/
|
||
private function _transfer_unionpay_v1($order_no, $account_list, $notice_url, $attach)
|
||
{
|
||
return new returnObject(1, -1, '还没有实现该功能, 请关注平台后续更新!', null);
|
||
}
|
||
|
||
/**
|
||
* @note 支付宝转账到账户
|
||
* @param $order_no 订单号
|
||
* @param $account_list 收款账户列表{"account_no":"收款账户","account_name":"收款人姓名","money":"转账金额","title":"显示在收款方的账单详情页","remark":"转账备注"}
|
||
* @param $notice_url 异步通知地址
|
||
* @param array|mixed $attach 附带的其他参数
|
||
* @return returnObject|mixed
|
||
*/
|
||
private function _transfer_alipay_v1($order_no, $account_list, $notice_url, $attach)
|
||
{
|
||
//return new returnObject(1, -1, '还没有实现该功能, 请关注平台后续更新!', null);
|
||
$alipay_pid = $this->marketInfo['alipay_pid']; /// 支付宝pid
|
||
$alipay_key = $this->marketInfo['alipay_key']; /// 支付宝安全检验码
|
||
$alipay_account_no = $this->marketInfo['alipay_account_no']; /// 支付宝账号
|
||
$alipay_account_name = $this->marketInfo['alipay_account_name']; /// 支付宝账户名
|
||
|
||
if (is_string($account_list))
|
||
$account_list = JsonStringToJsonObject($account_list);
|
||
if (!is_array($account_list))
|
||
$account_list = array($account_list);
|
||
|
||
$account = new Account_Alipay();
|
||
$batch_fee = 0; /// 总金额
|
||
$batch_num = count($account_list); /// 总笔数
|
||
$detail_data = array();
|
||
$index = 0;
|
||
$acc_list = array();
|
||
foreach ($account_list as $item)
|
||
{
|
||
$account->from_string($item);
|
||
$account->id = ++$index;
|
||
$account->ret_code = 0;
|
||
$account->ret_info = 'wait';
|
||
|
||
$batch_fee += $account->money; /// 金额
|
||
array_push($detail_data, "{$account->id}^{$account->account_no}^{$account->account_name}^{$account->money}^{$account->remark}");
|
||
array_push($acc_list, $account->to_array());
|
||
}
|
||
|
||
$transfer = $this->saved_transfer_info_v1($order_no, $acc_list, $notice_url, TRANSFERCODE_ALIPAY, $attach);
|
||
if (!$transfer)
|
||
return new returnObject(1, 500, '写入支付信息失败, 请联系管理员或稍候再试!', null);
|
||
|
||
$data = array(
|
||
'service' => 'batch_trans_notify',
|
||
'partner' => $alipay_pid,
|
||
'notify_url' => $this->getFullUrl('/api/transfer/notify/alipay'), /// 通知地址
|
||
'email' => $alipay_account_no, /// 付款方账号
|
||
'account_name' => $alipay_account_name, /// 付款方账户名 个人支付宝账号是真实姓名公司支付宝账号是公司名称
|
||
'pay_date' => date('Ymd'), /// 付款当天日期 格式:年[4位]月[2位]日[2位],如:20100801
|
||
'batch_no' => sprintf('%s%08d', date('YmdHis'), $transfer['id']), /// 批次号 格式:当天日期[8位]+序列号[3至16位],如:201008010000001
|
||
'batch_fee' => $batch_fee, /// 付款总金额 即参数detail_data的值中所有金额的总和
|
||
'batch_num' => $batch_num, /// 付款笔数 即参数detail_data的值中,“|”字符出现的数量加1,最大支持1000笔(即“|”字符出现的数量999个)
|
||
'detail_data' => implode('|', $detail_data), /// 付款详细数据 格式:流水号1^收款方帐号1^真实姓名^付款金额1^备注说明1|流水号2^收款方帐号2^真实姓名^付款金额2^备注说明2....
|
||
'_input_charset' => trim(strtolower('utf-8')), /// 字符编码格式 目前支持 gbk 或 utf-8
|
||
);
|
||
|
||
/// 建立请求
|
||
$alipaySubmit = new AlipaySubmit(array(
|
||
'partner' => $alipay_pid, /// pid
|
||
'md5_key' => $alipay_key, /// 支付宝安全检验码
|
||
'sign_type' => 'MD5', /// 签名方式
|
||
'input_charset' => 'utf-8', /// 字符编码格式 目前支持 gbk 或 utf-8
|
||
));
|
||
$html_text = $alipaySubmit->buildRequestForm($data, 'get', '确认');
|
||
|
||
die($html_text);
|
||
// $alipay_appid = $this->marketInfo['alipay_appid']; /// 支付宝appid
|
||
//
|
||
// $aop = new AopClient();
|
||
// $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
|
||
// $aop->appId = $alipay_appid; /// 支付宝分配给开发者的应用ID
|
||
// ///$aop->rsaPrivateKey = '请填写开发者私钥去头去尾去回车,一行字符串'; /// 证书私钥
|
||
// $aop->rsaPrivateKeyFilePath = dirname(dirname(__DIR__)) . '/payment/alipay/key/' . $alipay_appid . '/rsa_private_key.pem'; /// 商户私钥文件名
|
||
// ///$aop->alipayrsaPublicKey = '请填写支付宝公钥,一行字符串'; /// 证书公钥
|
||
// $aop->alipayPublicKey = dirname(dirname(__DIR__)) . '/payment/alipay/key/' . $alipay_appid . '/alipay_rsa_public_key.pem'; /// 支付宝公钥文件名
|
||
// $aop->apiVersion = '1.0'; /// 版本
|
||
// $aop->signType = 'RSA2'; /// 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
|
||
// $aop->postCharset = 'GBK'; /// 请求使用的编码格式,如utf-8,gbk,gb2312等
|
||
// $aop->format = 'json'; /// 仅支持JSON
|
||
//
|
||
// if (is_string($account_list))
|
||
// $account_list = JsonStringToJsonObject($account_list);
|
||
//
|
||
// $account = new Account_Alipay();
|
||
// $list = array();
|
||
// $success = 0;
|
||
// $failed = 0;
|
||
// foreach ($account_list as $item)
|
||
// {
|
||
// $account->from_string($item);
|
||
// $biz_content = array(
|
||
// 'out_biz_no' => md5(date('YmdHis') . $alipay_appid . random(8, 1)), /// 必选 商户转账唯一订单号。发起转账来源方定义的转账单据ID,用于将转账回执通知给来源方。不同来源方给出的ID可以重复,同一个来源方必须保证其ID的唯一性。只支持半角英文、数字,及“-”、“_”。
|
||
// 'payee_type' => 'ALIPAY_LOGONID', /// 必选 收款方账户类型。可取值:1、ALIPAY_USERID:支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。2、ALIPAY_LOGONID:支付宝登录号,支持邮箱和手机号格式。
|
||
// 'payee_account' => $account->account_no, /// 必选 收款方账户。与payee_type配合使用。付款方和收款方不能是同一个账户。
|
||
// 'amount' => $account->money, /// 必选 转账金额,单位:元。 只支持2位小数,小数点前最大支持13位,金额必须大于等于0.1元。最大转账金额以实际签约的限额为准。
|
||
// 'payer_show_name' => $account->title, /// 可选 付款方姓名(最长支持100个英文/50个汉字)。显示在收款方的账单详情页。如果该字段不传,则默认显示付款方的支付宝认证姓名或单位名称。
|
||
// 'payee_real_name' => $account->account_name, /// 可选 收款方真实姓名(最长支持100个英文/50个汉字)。如果本参数不为空,则会校验该账户在支付宝登记的实名是否与收款方真实姓名一致。
|
||
// 'remark' => $account->remark, /// 可选 转账备注(支持200个英文/100个汉字)。当付款方为企业账户,且转账金额达到(大于等于)50000元,remark不能为空。收款方可见,会展示在收款用户的收支详情中。
|
||
// );
|
||
//
|
||
// $request = new AlipayFundTransToaccountTransferRequest();
|
||
// $request->setBizContent(JsonObjectToJsonString($biz_content));
|
||
// $result = $aop->execute($request);
|
||
// $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
|
||
// $resultCode = $result->$responseNode->code;
|
||
// if (!empty($resultCode) && $resultCode == 10000) {
|
||
// //echo "成功";
|
||
// $account->ret_code = 0;
|
||
// $account->ret_info = NOTIFYSTATUS_SUCCESS;
|
||
// $account->order_id = $result->$responseNode->order_id;
|
||
// $account->pay_time = $result->$responseNode->pay_date;
|
||
// $success++;
|
||
// } else {
|
||
// //echo "失败";
|
||
// $account->ret_code = $result->$responseNode->code;
|
||
// $account->ret_info = $result->$responseNode->msg;
|
||
// $failed++;
|
||
// }
|
||
//
|
||
// array_push($list, $account);
|
||
// }
|
||
//
|
||
// Sql::update('syweb_core_transferinfo')
|
||
// ->setArgs(array('status' => TRANSFERSTATUS_SUCCESS, 'account_list' => JsonObjectToJsonString($list), ))
|
||
// ->whereArgs(array('id' => $transfer['id']))
|
||
// ->exec($this->db);
|
||
//
|
||
// /// 发送回调通知
|
||
// $this->send_order_notice();
|
||
// return new returnObject(0, 0, "批量支付完成!成功{$success}笔,失败{$failed}笔!");
|
||
}
|
||
|
||
/**
|
||
* 作用:使用证书,以post方式提交xml到对应的接口url
|
||
*/
|
||
function curl_post_ssl($url, $vars, $second=30)
|
||
{
|
||
$ch = curl_init();
|
||
//超时时间
|
||
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
|
||
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
|
||
|
||
curl_setopt($ch,CURLOPT_URL,$url);
|
||
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
|
||
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);
|
||
|
||
//以下两种方式需选择一种
|
||
/******* 此处必须为文件服务器根目录绝对路径 不可使用变量代替*********/
|
||
curl_setopt($ch,CURLOPT_SSLCERT,"/home/lizi/addons/grow/template/mobile/cash/apiclient_cert.pem");
|
||
curl_setopt($ch,CURLOPT_SSLKEY,"/home/lizi/addons/grow/template/mobile/cash/apiclient_key.pem");
|
||
|
||
|
||
curl_setopt($ch,CURLOPT_POST, 1);
|
||
curl_setopt($ch,CURLOPT_POSTFIELDS,$vars);
|
||
|
||
$data = curl_exec($ch);
|
||
if ($data) {
|
||
curl_close($ch);
|
||
return $data;
|
||
} else {
|
||
$error = curl_errno($ch);
|
||
echo "call faild, errorCode:$error\n";
|
||
curl_close($ch);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @note 微信企业付款
|
||
* @param $order_no 订单号
|
||
* @param $account_list 收款账户列表 {"openid":"收款openid","unionid":"收款unionid","trade_no":"订单号","money":"转账金额","title":"显示在收款方的账单详情页","remark":"转账备注"}
|
||
* @param $notice_url 异步通知地址
|
||
* @param array|mixed $attach 附带的其他参数
|
||
* @return returnObject|mixed
|
||
*/
|
||
private function _transfer_wechat_v1($order_no, $account_list, $notice_url, $attach)
|
||
{
|
||
//return new returnObject(1, -1, '还没有实现该功能, 请关注平台后续更新!', null);
|
||
$weixin_appid = $this->marketInfo['weixin_appid']; /// 微信appid
|
||
$weixin_secret_appid = $this->marketInfo['weixin_secret_appid']; /// 微信secretid
|
||
$weixin_mchid = $this->marketInfo['weixin_mchid']; /// 微信商户号
|
||
$weixin_paykey = $this->marketInfo['weixin_paykey']; /// 微信支付key
|
||
|
||
if (is_string($account_list))
|
||
$account_list = JsonStringToJsonObject($account_list);
|
||
if (!is_array($account_list))
|
||
$account_list = array($account_list);
|
||
|
||
$account = new Account_Wechat();
|
||
$index = 0;
|
||
$acc_list = [];
|
||
foreach ($account_list as $item)
|
||
{
|
||
$account->from_string($item);
|
||
$account->id = ++$index;
|
||
$account->ret_code = 0;
|
||
$account->ret_info = 'wait';
|
||
array_push($acc_list, $account->to_array());
|
||
}
|
||
$transfer = $this->saved_transfer_info_v1($order_no, $acc_list, $notice_url, TRANSFERCODE_WECHAT, $attach);
|
||
if (!$transfer)
|
||
return new returnObject(1, 500, '写入支付信息失败, 请联系管理员或稍候再试!', null);
|
||
|
||
/// 微信付款到个人的接口
|
||
$url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
|
||
for ($index = 0; $index < count($acc_list); $index++)
|
||
{
|
||
$acc_list[$index]['order_id'] = md5(time() . rand(100000, 999999)); /// 商户订单号
|
||
|
||
$data = [
|
||
'mch_appid' => $weixin_appid, /// 公众账号appid
|
||
'mchid' => $weixin_mchid, /// 商户号 微信支付平台账号
|
||
///'device_info' => '', /// 设备号
|
||
'nonce_str' => md5(time() . rand(1000, 9999)), /// 随机字符串
|
||
'partner_trade_no' => $acc_list[$index]['order_id'], /// 商户订单号
|
||
'openid' => $acc_list[$index]['openid'], /// 商户appid下,某用户的openid
|
||
'check_name' => 'NO_CHECK', /// 校验用户姓名选项 NO_CHECK:不校验真实姓名;FORCE_CHECK:强校验真实姓名
|
||
///'re_user_name' => '', /// 收款用户真实姓名。如果check_name设置为FORCE_CHECK,则必填用户真实姓名
|
||
'amount' => intval(floatval($acc_list[$index]['money']) * 100), /// 企业付款金额,单位为分
|
||
'desc' => $acc_list[$index]['title'], /// 企业付款操作说明信息。必填。
|
||
'spbill_create_ip' => GetClientAddress(), /// Ip地址,该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP。
|
||
];
|
||
/// 签名
|
||
$data['sign'] = mb_strtoupper(SignParameter($data, $weixin_paykey), USEDCHARSET);
|
||
/// 转成xml字符串
|
||
$xml = XmlObjectToXmlString($data);
|
||
|
||
|
||
/// 发送请求
|
||
$ch = curl_init();
|
||
/// 超时时间
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||
|
||
curl_setopt($ch, CURLOPT_URL, $url);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
|
||
//以下两种方式需选择一种
|
||
/******* 此处必须为文件服务器根目录绝对路径 不可使用变量代替*********/
|
||
curl_setopt($ch, CURLOPT_SSLCERT, sprintf(dirname(dirname(__DIR__)) . '\\payment\\wechat\\cert\\%s\\apiclient_cert.pem', $weixin_mchid));
|
||
curl_setopt($ch, CURLOPT_SSLKEY, sprintf(dirname(dirname(__DIR__)) . '\\payment\\wechat\\cert\\%s\\apiclient_key.pem', $weixin_mchid));
|
||
|
||
curl_setopt($ch, CURLOPT_POST, 1);
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
|
||
|
||
$result = curl_exec($ch);
|
||
if ($result) {
|
||
curl_close($ch);
|
||
} else {
|
||
$errno = curl_errno($ch);
|
||
$errmsg = curl_error($ch);
|
||
curl_close($ch);
|
||
|
||
return new returnObject(1, $errno, $errmsg);
|
||
}
|
||
|
||
$obj = XmlStringToXmlObject($result);
|
||
|
||
if (0 != strcasecmp(strval(@$obj->return_code), 'SUCCESS')) {
|
||
$acc_list[$index]['ret_code'] = strval(@$obj->return_code);
|
||
$acc_list[$index]['ret_info'] = empty(strval(@$obj->return_msg)) ? '企业付款请求失败' : strval(@$obj->return_msg);
|
||
} elseif (0 != strcasecmp(strval(@$obj->result_code), 'SUCCESS')) {
|
||
$acc_list[$index]['ret_code'] = strval(@$obj->result_code);
|
||
$acc_list[$index]['ret_info'] = empty(strval(@$obj->err_code_des)) ? '企业付款请求失败' : strval(@$obj->err_code_des);
|
||
} else {
|
||
$acc_list[$index]['transaction_id'] = strval(@$obj->payment_no); /// 微信的订单号
|
||
$acc_list[$index]['pay_time'] = strval(@$obj->payment_time); /// 支付时间
|
||
$acc_list[$index]['ret_code'] = 1;
|
||
$acc_list[$index]['ret_info'] = 'success';
|
||
}
|
||
}
|
||
|
||
Sql::update('syweb_core_transferinfo')
|
||
->setArgs(array(
|
||
'status' => TRANSFERSTATUS_SUCCESS,
|
||
'account_list' => JsonObjectToJsonString($acc_list),
|
||
'transfer_time' => time(),
|
||
))
|
||
->whereArgs(array('id' => $transfer['id']))
|
||
->exec($this->db);
|
||
return new returnObject(0, 0, '批量付款成功!', $acc_list);
|
||
}
|
||
|
||
/**
|
||
* @note 汇付宝批量付款
|
||
* @param $order_no 订单号
|
||
* @param $account_list 收款账户列表{"mch_no":"商户流水号","bank_id":"银行编号","type":"对公对私","account_no":"收款人帐号","account_name":"收款人姓名","money":"付款金额","reason":"付款理由","province":"省份","city":"城市","bank_name":"收款支行名称"}
|
||
* @param $notice_url 异步通知地址
|
||
* @param array|mixed $attach 附带的其他参数
|
||
* @return returnObject|mixed
|
||
*/
|
||
private function _transfer_heepay_v1($order_no, $account_list, $notice_url, $attach)
|
||
{
|
||
if (is_string($account_list))
|
||
$account_list = JsonStringToJsonObject($account_list);
|
||
if (!is_array($account_list))
|
||
$account_list = array($account_list);
|
||
|
||
$total_money = 0;
|
||
$detail_list = array();
|
||
$account = new Account_HeePay();
|
||
$index = 0;
|
||
$acc_list = array();
|
||
foreach ($account_list as $item)
|
||
{
|
||
$index++;
|
||
$account->from_string($item);
|
||
|
||
/// 转账总金额
|
||
$total_money += $account->money;
|
||
/// 流水号
|
||
if (empty($account->mch_no)) $account->mch_no = $index;
|
||
/// 收款格式:商户流水号^银行编号^对公对私^收款人帐号^收款人姓名^付款金额^付款理由^省份^城市^收款支行名称
|
||
array_push($detail_list, "{$account->mch_no}^{$account->bank_id}^{$account->type}^{$account->account_no}^{$account->account_name}^{$account->money}^{$account->reason}^{$account->province}^{$account->city}^{$account->bank_name}");
|
||
array_push($acc_list, $account);
|
||
}
|
||
|
||
$transfer = $this->saved_transfer_info_v1($order_no, $acc_list, $notice_url, TRANSFERCODE_HEEPAY, $attach);
|
||
if (!$transfer)
|
||
return new returnObject(1, 500, '写入支付信息失败, 请联系管理员或稍候再试!', null);
|
||
|
||
$url_small = 'https://Pay.heepay.com/API/PayTransit/PayTransferWithSmallAll.aspx'; /// 小额正式交易地址
|
||
$url_large = 'https://Pay.heepay.com/API/PayTransit/PayTransferWithLargeWork.aspx'; /// 大额正式交易地址
|
||
|
||
$transfer_key = $this->marketInfo['heepay_transferkey']; /// 转账key
|
||
$detail_data_key = $this->marketInfo['heepay_3deskey']; /// 账户数据的3des加密key
|
||
|
||
$version = 3; /// 当前接口版本号3
|
||
$agent_id = $this->marketInfo['heepay_mchid']; /// 商户内码,例如1664502
|
||
$batch_no = $transfer['out_trade_no']; /// 批量付款订单号(要保证唯一)。最大长度50个字符,最小长度10个字符
|
||
$batch_amt = $total_money; /// 付款总金额不可为空,单位:元,小数点后保留两位。
|
||
$batch_num = count($account_list); /// 该次付款总笔数,付给多少人的数目,“单笔数据集”里面的数据总笔数
|
||
$detail_data = implode('|', $detail_list); /// 批付到银行帐户格式:“商户流水号^银行编号^对公对私^收款人帐号^收款人姓名^付款金额^付款理由^省份^城市^收款支行名称”来组织数据,每条整数据间用“竖线”符号分隔,商户流水号长度最长20字符。省市格式请严格按照4省市查询接口中的说明填写。注:整理好信息之后,需要对此数据进行3DES加密,具体加密方式参考:接口规则-参数规定10 3DES加密机制,银行编号和对公对私对应数值请在 接口规则-参数规定8和9
|
||
$notify_url = $this->getFullUrl('/api/transfer/notify/heepay'); /// 支付后返回的商户处理页面,URL参数是以http://或https://开头的完整URL地址(后台处理),提交的url地址必须外网能访问到,否则无法通知商户。
|
||
$ext_param1 = $transfer['id']; /// 商户自定义原样返回,最大长度50个字符
|
||
//$sign = ''; /// MD5签名结果
|
||
|
||
/// 组织签名
|
||
$sign = "agent_id={$agent_id}&batch_amt={$batch_amt}&batch_no={$batch_no}&batch_num={$batch_num}&detail_data={$detail_data}&ext_param1={$ext_param1}&key={$transfer_key}¬ify_url={$notify_url}&version={$version}";
|
||
/// 获取sign密钥
|
||
$sign = md5(strtolower($sign));
|
||
|
||
/// 初始化一个对象
|
||
if (function_exists('openssl_encrypt'))
|
||
$des = new TripleDES($detail_data_key);
|
||
else
|
||
{
|
||
$des = new Crypt3Des();
|
||
$des->key = $detail_data_key;
|
||
}
|
||
|
||
$detail_data = iconv("utf-8", "gbk//IGNORE", $detail_data);
|
||
$detail_data_des = $des->Encrypt($detail_data);
|
||
|
||
$url = $total_money < 50000 ? $url_small : $url_large;
|
||
$data = array(
|
||
'agent_id' => $agent_id,
|
||
'batch_amt' => $batch_amt,
|
||
'batch_no' => $batch_no,
|
||
'batch_num' => $batch_num,
|
||
'detail_data' => $detail_data_des,
|
||
'ext_param1' => $ext_param1,
|
||
'notify_url' => $notify_url,
|
||
'sign' => $sign,
|
||
'version' => $version,
|
||
);
|
||
|
||
$result = Characet(SendPost($url, $data));
|
||
$result = XmlStringToXmlObject($result);
|
||
if (is_object($result))
|
||
{
|
||
$result = (array)$result;
|
||
|
||
if ('0000' == $result['ret_code'])
|
||
return new returnObject(0, 0, '批量付款成功!', null);
|
||
else
|
||
return new returnObject(1, $result['ret_code'], $result['ret_msg'], null);
|
||
}
|
||
else
|
||
return new returnObject(1, 500, $result, null);
|
||
}
|
||
|
||
|
||
/**
|
||
* 批量付款回调通知
|
||
* @route({"GET","/notify/unionapy"})
|
||
* @route({"POST","/notify/unionapy"})
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function notify_unionpay()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 批量付款回调通知
|
||
* @route({"GET","/notify/alipay"})
|
||
* @route({"POST","/notify/alipay"})
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function notify_alipay()
|
||
{
|
||
try {
|
||
/// notify_time 通知的发送时间。 格式yyyy-MM-dd HH:mm:ss。
|
||
/// notify_type 通知的类型。 batch_trans_notify
|
||
/// notify_id 支付宝通知校验ID,商户可以用这个流水号询问支付宝该条通知的合法性。
|
||
/// sign_type 签名方式 DSA、RSA、MD5三个值可选,必须大写。
|
||
/// sign 签名 请参见本文档“附录:签名与验签”。
|
||
/// batch_no 转账批次号。
|
||
/// pay_user_id 付款的支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。
|
||
/// pay_user_name 付款账号姓名。
|
||
/// pay_account_no 付款账号。
|
||
/// success_details 批量付款中成功付款的信息。格式为:流水号^收款方账号^收款账号姓名^付款金额^成功标识(S)^成功原因(null)^支付宝内部流水号^完成时间。每条记录以“|”间隔。
|
||
/// fail_details 批量付款中未成功付款的信息。格式为:流水号^收款方账号^收款账号姓名^付款金额^失败标识(F)^失败原因^支付宝内部流水号^完成时间。每条记录以“|”间隔。
|
||
|
||
$id = @$_REQUEST['batch_no'];
|
||
if (empty($id))
|
||
die (NOTIFYSTATUS_FAIL);
|
||
|
||
$id = mb_substr($id, 14, mb_strlen($id, USEDCHARSET) - 14, USEDCHARSET);
|
||
if (empty($id))
|
||
die (NOTIFYSTATUS_FAIL);
|
||
$id = intval($id);
|
||
|
||
$transfer = Sql::select('a.id, a.app_key, a.dev_key, a.market_key, b.alipay_pid, b.alipay_key, b.signkey, a.account_list, a.notice_url, a.return_url, a.status, a.notify_status, a.version')
|
||
->from('syweb_core_transferinfo a, syweb_market b')
|
||
->where('a.market_key = b.market_key and a.id = ?', $id)
|
||
->get($this->db, null);
|
||
if (empty($transfer))
|
||
//return new returnObject(1, 10003, '找不到对应的支付信息。');
|
||
die(NOTIFYSTATUS_FAIL);
|
||
else
|
||
$transfer = $transfer[0];
|
||
|
||
$alipay_pid = $transfer['alipay_pid']; /// 支付宝pid
|
||
$alipay_key = $transfer['alipay_key']; /// 支付宝安全检验码
|
||
$version = $transfer['version']; /// 版本号
|
||
switch($version) {
|
||
case 1:
|
||
/// 计算得出通知验证结果
|
||
$notify = new AlipayNotify(array(
|
||
'partner' => $alipay_pid, /// pid
|
||
'md5_key' => $alipay_key, /// 支付宝安全检验码
|
||
'sign_type' => 'MD5', /// 签名方式
|
||
'input_charset' => 'utf-8', /// 字符编码格式 目前支持 gbk 或 utf-8
|
||
));
|
||
|
||
|
||
if ($notify->verifyNotify()) {
|
||
/// 验证成功
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
/// 请在这里加上商户的业务逻辑程序代
|
||
/// ——请根据您的业务逻辑来编写程序(以下代码仅作参考)——
|
||
/// 获取支付宝的通知返回参数,可参考技术文档中服务器异步通知参数列表
|
||
/// 批量付款数据中转账成功的详细信息
|
||
$succ_details = $_REQUEST['success_details'];
|
||
/// 批量付款数据中转账失败的详细信息
|
||
$fail_details = $_REQUEST['fail_details'];
|
||
///
|
||
$account_list = json_decode($transfer['account_list']); /// 保存的收款账户列表
|
||
|
||
/// 处理成功的记录
|
||
$succ_list = explode('|', $succ_details);
|
||
foreach($succ_list as $succ_item) {
|
||
/// 流水号^收款方账号^收款账号姓名^付款金额^成功标识(S)^成功原因(null)^支付宝内部流水号^完成时间
|
||
$acc = explode('^', $succ_item);
|
||
|
||
/** @var Account_Alipay $account */
|
||
foreach ($account_list as $account) {
|
||
if ($account->id == $acc[0]) {
|
||
$account->ret_code = 1;
|
||
$account->ret_info = NOTIFYSTATUS_SUCCESS;
|
||
$account->order_id = $acc[6];
|
||
$account->pay_time = $acc[7];
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 处理失败的记录
|
||
$fail_list = explode('|', $fail_details);
|
||
foreach($fail_list as $fail_item) {
|
||
/// 流水号^收款方账号^收款账号姓名^付款金额^失败标识(F)^失败原因^支付宝内部流水号^完成时间
|
||
$acc = explode('^', $fail_item);
|
||
|
||
/** @var Account_Alipay $account */
|
||
foreach ($account_list as $account) {
|
||
if ($account->id == $acc[0]) {
|
||
$account->ret_code = -1;
|
||
$account->ret_info = $acc[5];
|
||
$account->order_id = $acc[6];
|
||
$account->pay_time = $acc[7];
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
Sql::update('syweb_core_transferinfo')
|
||
->setArgs(array(
|
||
'status' => TRANSFERSTATUS_SUCCESS,
|
||
'account_list' => JsonObjectToJsonString($account_list),
|
||
'transaction_id' => @$_REQUEST['batch_no'],
|
||
'transfer_time' => time(),
|
||
))
|
||
->whereArgs(array('id' => $id))
|
||
->exec($this->db);
|
||
|
||
/// 发送回调通知
|
||
$this->send_order_notice();
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
die(NOTIFYSTATUS_SUCCESS);
|
||
} else {
|
||
/// 验证失败
|
||
die(NOTIFYSTATUS_FAIL);
|
||
}
|
||
|
||
default:
|
||
///return new returnObject(500, -1, "不支持的接口版本:{$version}!");
|
||
die(NOTIFYSTATUS_FAIL);
|
||
}
|
||
} catch (Exception $e) {
|
||
die(NOTIFYSTATUS_FAIL);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 批量付款回调通知
|
||
* @route({"GET","/notify/wechat"})
|
||
* @route({"POST","/notify/wechat"})
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function notify_wechat()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 批量付款回调通知
|
||
* @route({"GET","/notify/heepay"})
|
||
* @route({"POST","/notify/heepay"})
|
||
* @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden",{"error":"Forbidden"}}) cookie不可用
|
||
* @return string|returnObject|mixed
|
||
*/
|
||
public function notify_heepay()
|
||
{
|
||
try {
|
||
$ret_code = $_REQUEST['ret_code']; /// 返回码值0000 表示查询成功,其他详见附录
|
||
$ret_msg = $_REQUEST['ret_msg']; /// 返回码信息提示
|
||
$agent_id = $_REQUEST['agent_id']; /// 商户编号 如1234567
|
||
$hy_bill_no = $_REQUEST['hy_bill_no']; /// 汇付宝交易号(订单号)
|
||
$status = $_REQUEST['status']; /// -1=无效,0=未处理,1=成功
|
||
$batch_no = $_REQUEST['batch_no']; /// 商户系统内部的订单号
|
||
$batch_amt = $_REQUEST['batch_amt']; /// 成功付款金额
|
||
$batch_num = $_REQUEST['batch_num']; /// 成功付款数量
|
||
$detail_data = $_REQUEST['detail_data']; /// 付款明细,单笔数据集里面按照“商户流水号^收款人帐号^收款人姓名^付款金额^付款状态”来组织数据,每条整数据间用“竖线”符号分隔,付款状态S表示付款成功,状态F代表失败
|
||
$ext_param1 = intval($_REQUEST['ext_param1']); /// 商家数据包,原样返回
|
||
$sign = $_REQUEST['sign']; /// MD5签名结果
|
||
|
||
$transfer = Sql::select('a.id, a.app_key, a.dev_key, a.market_key, b.heepay_transferkey, b.heepay_3deskey, b.signkey, a.account_list, a.notice_url, a.return_url, a.status, a.notify_status, a.version')
|
||
->from('syweb_core_transferinfo a, syweb_market b')
|
||
->where('a.market_key = b.market_key and a.id = ?', $ext_param1)
|
||
->get($this->db, null);
|
||
if (empty($transfer))
|
||
//return new returnObject(1, 10003, '找不到对应的支付信息。');
|
||
die(NOTIFYSTATUS_FAIL);
|
||
else
|
||
$transfer = $transfer[0];
|
||
|
||
$version = intval($transfer['version']); /// 版本号
|
||
$transfer_key = $transfer['heepay_transferkey']; /// 批付秘钥
|
||
//$detail_data_key = $transfer['heepay_3deskey']; /// 账户数据的3des加密key
|
||
$acc_list = json_decode($transfer['account_list']); /// 保存的收款账户列表
|
||
//$notice_url = $transfer['notice_url']; /// 异步回调地址
|
||
//$return_url = $transfer['return_url']; /// 同步回调地址
|
||
//$transfer_status = $transfer['status']; /// 批付状态
|
||
//$notify_status = $transfer['notify_status']; /// 通知状态
|
||
switch ($version) {
|
||
case 1:
|
||
/// 组织签名
|
||
$str = "ret_code={$ret_code}&ret_msg={$ret_msg}&agent_id={$agent_id}&hy_bill_no={$hy_bill_no}&status={$status}&batch_no={$batch_no}&batch_amt={$batch_amt}&batch_num={$batch_num}&detail_data={$detail_data}&ext_param1={$ext_param1}&key={$transfer_key}";
|
||
/// 获取sign密钥
|
||
$md5 = md5(strtolower($str));
|
||
if ($sign != $md5)
|
||
//return new returnObject(1, 10004, '签名错误。');
|
||
die(NOTIFYSTATUS_FAIL);
|
||
|
||
if ('0000' != $ret_code)
|
||
return new returnObject(1, $ret_code, $ret_msg);
|
||
|
||
/// 处理收款账户信息
|
||
if (!empty($detail_data)) {
|
||
$account_list = array();
|
||
$account = new Account_HeePay();
|
||
foreach ((array)explode('|', $detail_data) as $item) {
|
||
/// 商户流水号^收款人帐号^收款人姓名^付款金额^付款状态
|
||
$account->from_string($item);
|
||
array_push($account_list, $account);
|
||
|
||
/** @var Account_HeePay $acc */
|
||
foreach ($acc_list as $acc) {
|
||
if ($acc->mch_no == $account->mch_no) {
|
||
$acc->status = $account->status;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// 改变订单状态
|
||
if (1 == $status) /// 成功
|
||
{
|
||
Sql::update('syweb_core_transferinfo')
|
||
->setArgs(array(
|
||
'status' => TRANSFERSTATUS_SUCCESS,
|
||
'account_list' => JsonObjectToJsonString($acc_list),
|
||
'transaction_id' => $hy_bill_no,
|
||
'transfer_time' => time(),
|
||
))
|
||
->whereArgs(array('id' => $ext_param1))
|
||
->exec($this->db);
|
||
} elseif (0 == $status) { /// 未处理
|
||
Sql::update('syweb_core_transferinfo')
|
||
->setArgs(array(
|
||
'status' => TRANSFERSTATUS_NORMAL,
|
||
'account_list' => JsonObjectToJsonString($acc_list),
|
||
'transaction_id' => $hy_bill_no,
|
||
'transfer_time' => time(),
|
||
))
|
||
->whereArgs(array('id' => $ext_param1))
|
||
->exec($this->db);
|
||
} elseif (-1 == $status) { /// 无效
|
||
Sql::update('syweb_core_transferinfo')
|
||
->setArgs(array(
|
||
'status' => TRANSFERSTATUS_CANCEL,
|
||
'account_list' => JsonObjectToJsonString($acc_list),
|
||
'transaction_id' => $hy_bill_no,
|
||
'transfer_time' => time(),
|
||
))
|
||
->whereArgs(array('id' => $ext_param1))
|
||
->exec($this->db);
|
||
}
|
||
|
||
/// 发送回调通知
|
||
$this->send_order_notice();
|
||
|
||
return new returnObject(0, 0, '批量支付成功!');
|
||
default:
|
||
return new returnObject(500, -1, "不支持的接口版本:{$version}!");
|
||
}
|
||
|
||
} catch(Exception $e) {
|
||
return new returnObject(1, $e->getCode(), $e->getMessage());
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 批付前校验
|
||
* @param string $appid 应用id
|
||
* @param string $devkey 开发者key
|
||
* @param string $market_key 门店key
|
||
* @param string $account_list 收款账户列表
|
||
* @param string $notice_url 异步通知地址
|
||
* @param string $sign 签名
|
||
* @return array|returnObject
|
||
*/
|
||
private function verify_transfer_v1($appid, $devkey, $market_key, $account_list, $notice_url, $sign)
|
||
{
|
||
/// 验证公共参数是否合法
|
||
parent::init($appid, $devkey);
|
||
$verify_result = parent::verify_admin($market_key);
|
||
if (!is_error_api($verify_result))
|
||
{
|
||
/// 校验签名
|
||
$param = $_REQUEST;
|
||
if (isset($param['sign']))
|
||
{
|
||
$raw_sign = $param['sign'];
|
||
unset($param['sign']);
|
||
}
|
||
else
|
||
$raw_sign = $sign;
|
||
$own_sign = SignParameter($param, $this->marketInfo['signkey']);
|
||
|
||
if ($own_sign != $raw_sign)
|
||
return new returnObject(500, -1, '签名错误', null);
|
||
|
||
if (empty($account_list))
|
||
return new returnObject(500, 500, '请指定收款账户!', null);
|
||
|
||
//if (empty($notice_url))
|
||
// return new returnObject(500, 500, '请指定一个支付回调通知页面!', null);
|
||
/*
|
||
$referer_url = $_SERVER['HTTP_REFERER']; /// 当前调用退款的域名
|
||
$local_url = $this->getLocaleUrl(); /// 本地域名
|
||
|
||
/// 转账安全域名
|
||
$transfer_safe_domain = json_decode($this->marketInfo['transfer_safe_domain']);
|
||
$referer_paths = parse_url($referer_url);
|
||
$local_paths = parse_url($local_url);
|
||
|
||
if (!empty($referer_paths) && count($referer_paths) > 0)
|
||
{
|
||
$referer_domain = $referer_paths['host'];
|
||
$local_domain = $local_paths['host'];
|
||
|
||
if (!in_array($referer_domain, $transfer_safe_domain) && $local_domain != $referer_domain)
|
||
return new returnObject(500, 13006, '不是安全的域名。', $referer_domain);
|
||
}
|
||
*/
|
||
/// 获取附加的参数
|
||
$attach = GetAttachParameters(array('appid', 'devkey', 'market_key', 'account_list', 'notice_url', 'return_url', 'transfer_type', 'version', 'sign', ));
|
||
$attach['transfer_time'] = time();
|
||
|
||
return $attach;
|
||
}
|
||
elseif ($verify_result instanceof returnObject)
|
||
{
|
||
return $verify_result;
|
||
}
|
||
else
|
||
{
|
||
$return = new returnObject();
|
||
$return->from_array((array)$verify_result);
|
||
return $verify_result;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 保存转账信息
|
||
* @param string $order_no 订单号
|
||
* @param string|mixed $account_list 收款账户列表
|
||
* @param string $notice_url 异步回调地址
|
||
// * @param string $return_url 同步回调地址
|
||
* @param int $type 支付方式
|
||
* @param array $attach 支付附加参数
|
||
* @return mixed
|
||
**/
|
||
private function saved_transfer_info_v1($order_no, $account_list, $notice_url, $type, $attach)
|
||
{
|
||
$info = Sql::select('a.status')
|
||
->from('syweb_core_transferinfo a')
|
||
->where('app_key = ? and market_key = ? and order_no = ?', $this->appid, $this->market_key, $order_no)
|
||
->get($this->db, null);
|
||
if (!empty($info))
|
||
$info = $info[0];
|
||
|
||
if (!empty($info) && is_numeric($info['status']))
|
||
{
|
||
switch ($info['status'])
|
||
{
|
||
case TRANSFERSTATUS_SUCCESS:
|
||
echo '订单已成功处理!';
|
||
return false;
|
||
|
||
case TRANSFERSTATUS_CANCEL:
|
||
echo '订单已被撤销!';
|
||
return false;
|
||
|
||
case TRANSFERSTATUS_NORMAL:
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (empty($account_list))
|
||
return null;
|
||
|
||
if (is_array($account_list) || is_object($account_list))
|
||
$account_list = JsonObjectToJsonString($account_list);
|
||
|
||
if (is_array($attach) || is_object($attach))
|
||
$attach = JsonObjectToJsonString($attach);
|
||
|
||
$record = array(
|
||
'app_key' => $this->appid, /// 应用key
|
||
'dev_key' => $this->devkey, /// 开发者key
|
||
'market_key' => $this->market_key, /// 门店key
|
||
'account_list' => $account_list, /// 收款账户列表
|
||
'notice_url' => $notice_url, /// 异步回调地址
|
||
'return_url' => '', /// 同步回调地址
|
||
'type' => $type, /// 支付方式
|
||
'order_no' => $order_no, /// 订单号
|
||
'out_trade_no' => md5(date('YmdHis') . '_' . $this->appid . '_' . $this->market_key . '_' . random(8, 1)), /// 支付唯一ID
|
||
'transaction_id' => null, /// 三方订单号
|
||
'status' => TRANSFERSTATUS_NORMAL, /// 状态
|
||
'attach' => $attach, /// 附加信息
|
||
'create_time' => time(), /// 支付创建时间戳
|
||
'notify_status' => 0, /// 回调状态
|
||
'notify_count' => 0, /// 回调次数
|
||
'version' => 1, /// 版本号
|
||
);
|
||
|
||
$this->db->beginTransaction();
|
||
try {
|
||
$id = Sql::insertInto('syweb_core_transferinfo')->values($record)->exec($this->db)->lastInsertId();
|
||
$this->db->commit();
|
||
|
||
$record['id'] = $id;
|
||
return $record;
|
||
} catch (Exception $Exception) {
|
||
$this->db->rollBack();
|
||
echo $Exception->getMessage();
|
||
return null;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @note 批量通知所有没有成功发送回调通知的记录
|
||
* @return returnObject
|
||
*/
|
||
private function send_order_notice()
|
||
{
|
||
$transfer = Sql::select('a.*, b.signkey')
|
||
->from('syweb_core_transferinfo a, syweb_market b')
|
||
->where('a.market_key = b.market_key and a.status = 1 and a.notify_status != 1 and a.notify_count < 10')
|
||
->get($this->db, null);
|
||
if (empty($transfer))
|
||
return true;
|
||
|
||
$succ_list = array();
|
||
$fail_list = array();
|
||
foreach ($transfer as $record)
|
||
{
|
||
$data = array(
|
||
'account_list' => $record['account_list'],
|
||
'order_no' => $record['order_no'],
|
||
'out_trade_no' => $record['out_trade_no'],
|
||
'transaction_id' => $record['transaction_id'],
|
||
'attach' => $record['attach'],
|
||
'version' => $record['version'],
|
||
);
|
||
|
||
$data['sign'] = SignParameter($data, $record['signkey']);
|
||
if (!empty($record['notice_url']))
|
||
$result = SendPost($record['notice_url'], $data);
|
||
else
|
||
$result = NOTIFYSTATUS_SUCCESS;
|
||
if (strcasecmp($result, NOTIFYSTATUS_SUCCESS) == 0)
|
||
array_push($succ_list, $record['id']);
|
||
else
|
||
array_push($fail_list, $record['id']);
|
||
}
|
||
|
||
$time = time();
|
||
|
||
if (!empty($succ_list) && count($succ_list) > 0)
|
||
{
|
||
if (!empty($succ_list = implode(',', $succ_list)))
|
||
$this->db->exec(/** @lang text */"update syweb_core_transferinfo set notify_status = 1, notify_count = notify_count + 1, notify_time = {$time} where id in ({$succ_list})");
|
||
}
|
||
|
||
if (!empty($fail_list) && count($fail_list) > 0)
|
||
{
|
||
if (!empty($fail_list = implode(',', $fail_list)))
|
||
$this->db->exec(/** @lang text */"update syweb_core_transferinfo set notify_status = -1, notify_count = notify_count + 1, notify_time = {$time} where id in ({$fail_list})");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* @property({"default":"@db"})
|
||
* @var PDO
|
||
*/
|
||
public $db;
|
||
}
|
||
|
||
|