You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
12 KiB
345 lines
12 KiB
<?php
|
|
declare (strict_types=1);
|
|
|
|
namespace app\common\library\payment\gateway\driver;
|
|
|
|
use app\common\library\payment\gateway\Driver;
|
|
use cores\exception\BaseException;
|
|
use app\common\library\Log;
|
|
use app\common\enum\Client as ClientEnum;
|
|
/**
|
|
* 全民付移动支付驱动
|
|
* Class Wechat
|
|
* @package app\common\library\payment\gateway\driver
|
|
*/
|
|
class Huifu extends Driver
|
|
{
|
|
private $domain = "https://api-mop.chinaums.com";
|
|
private $result;
|
|
/**
|
|
* 设置支付宝配置信息(全局只需设置一次)
|
|
* @param array $options 支付宝配置信息
|
|
* @param string $client 下单客户端
|
|
* @return Driver|null
|
|
*/
|
|
public function setOptions(array $options, string $client): ?Driver
|
|
{
|
|
$this->client = $client ?: null;
|
|
if ($options['mchType'] === 'provider') {
|
|
$config = [
|
|
'appid' => $options['provider']['appid'],
|
|
'appkey' => $options['provider']['appkey'],
|
|
'msgSrcId' => $options['provider']['msgSrcId'],
|
|
'msgSrc' => $options['provider']['msgSrc'],
|
|
'md5Secret' => $options['provider']['md5Secret'],
|
|
'mid' => $options['provider']['mid'],
|
|
'tid' => $options['provider']['tid'],
|
|
];
|
|
} else {
|
|
$config = [
|
|
'appid' => $options['normal']['appid'],
|
|
'appkey' => $options['normal']['appkey'],
|
|
'msgSrcId' => $options['normal']['msgSrcId'],
|
|
'msgSrc' => $options['normal']['msgSrc'],
|
|
'md5Secret' => $options['normal']['md5Secret'],
|
|
'mid' => $options['normal']['mid'],
|
|
'tid' => $options['normal']['tid'],
|
|
];
|
|
}
|
|
$this->config = $config;
|
|
return $this;
|
|
}
|
|
|
|
/* 统一下单API
|
|
* @param string $outTradeNo 交易订单号
|
|
* @param string $totalFee 实际付款金额
|
|
* @param array $extra 附加的数据 (需要携带openid)
|
|
* @return bool
|
|
* @throws BaseException
|
|
*/
|
|
public function addReceiver(string $type, string $account, string $name, string $relation_type = "HEADQUARTER", string $custom_relation = ""): bool
|
|
{
|
|
|
|
return true;
|
|
}
|
|
/**
|
|
* 统一下单API
|
|
* @param string $outTradeNo 交易订单号
|
|
* @param string $totalFee 实际付款金额
|
|
* @param array $extra 附加的数据 (需要携带openid)
|
|
* @return bool
|
|
* @throws BaseException
|
|
*/
|
|
public function profitsharing(string $transaction_id, string $out_order_no, array $receivers): bool
|
|
{
|
|
return true;
|
|
}
|
|
public function profitsharingQuery(string $out_order_no, string $transaction_id): ?array{
|
|
return [];
|
|
}
|
|
/**
|
|
* 统一下单API
|
|
* @param string $outTradeNo 交易订单号
|
|
* @param string $totalFee 实际付款金额
|
|
* @param array $extra 附加的数据 (需要携带openid)
|
|
* @return bool
|
|
* @throws BaseException
|
|
*/
|
|
public function unify(string $outTradeNo, string $totalFee, array $extra = []): bool
|
|
{
|
|
try {
|
|
$result = null;
|
|
// 发起API调用 微信小程序端
|
|
if ($this->client === ClientEnum::MP_WEIXIN) {
|
|
|
|
//$outTradeNo = date("YmdHis").mt_rand(1000,9999);
|
|
$order_url = $this->domain . "/v1/netpay/wx/mini-pre-order";
|
|
//$order_url = $this->domain . "/v1/netpay/wx/unified-order";
|
|
$params = [
|
|
"requestTimestamp" => date("Y-m-d H:i:s"),
|
|
"merOrderId" => $this->config['msgSrcId'].$outTradeNo,
|
|
"mid" => $this->config['mid'],
|
|
"tid" => $this->config['tid'],
|
|
"subAppId" => $this->config['appid'],
|
|
"subOpenId" => $extra['openid'],
|
|
"tradeType" => "MINI",
|
|
"notifyUrl" => $this->notifyUrl(),
|
|
"orderDesc" => "商品支付",
|
|
"totalAmount" => bcmul($totalFee, "100"),
|
|
"divisionFlag" => false,//分账标记
|
|
//"platformAmount" => false,//平台商户分 账金额
|
|
// "subOrders" => [
|
|
// [
|
|
// "mid" => "111",
|
|
// "totalAmount" => 1,
|
|
// ]
|
|
|
|
// ],
|
|
];
|
|
$authorization = $this->getAuthorization($params);
|
|
$headers = ["Authorization:".$authorization];
|
|
|
|
$data = httpRequest($order_url, "POST", json_encode($params), $headers, false);
|
|
if ($data['errCode'] != "SUCCESS") {
|
|
Log::append('银联-unify', ['client' => $this->client, 'result' => $data]);
|
|
return false;
|
|
}
|
|
$this->result = $data;
|
|
return true;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$this->throwError('银联API下单失败:' . $e->getMessage(), true, 'unify');
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* 交易查询 (主动查询订单支付状态)
|
|
* @param string $outTradeNo 交易订单号
|
|
* @return array|null
|
|
* @throws BaseException
|
|
*/
|
|
public function tradeQuery(string $outTradeNo): ?array
|
|
{
|
|
try {
|
|
$tradeQuery_url = $this->domain . "/v1/netpay/wx/mini-pre-query";
|
|
$params = [
|
|
"requestTimestamp" => date("Y-m-d H:i:s"),
|
|
"merOrderId" => $outTradeNo,//date("YmdHis").mt_rand(1000,9999),
|
|
"mid" => $this->config['mid'],
|
|
"tid" => $this->config['tid'],
|
|
];
|
|
$authorization = $this->getAuthorization($params);
|
|
$headers = ["Authorization:".$authorization];
|
|
|
|
$data = httpRequest($tradeQuery_url, "POST", json_encode($params), $headers, false);
|
|
// echo "<pre>";
|
|
// print_r($data);
|
|
// exit();
|
|
if ($data['errCode'] != "SUCCESS") {
|
|
Log::append('huifu-tradeQuery', ['client' => $this->client, 'result' => $data]);
|
|
return false;
|
|
}
|
|
return [
|
|
// 支付状态: true成功 false失败
|
|
'paySuccess' => isset($data['status']) && $data['status'] === 'TRADE_SUCCESS',
|
|
// 第三方交易流水号
|
|
'tradeNo' => $data['targetOrderId'] ?? ""
|
|
];
|
|
return $data;
|
|
} catch (\Throwable $e) {
|
|
$this->throwError('银联API交易查询失败:' . $e->getMessage(), true, 'tradeQuery');
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* 输出错误信息
|
|
* @param string $errMessage 错误信息
|
|
* @param bool $isLog 是否记录日志
|
|
* @param string $action 当前的操作
|
|
* @throws BaseException
|
|
*/
|
|
private function throwError(string $errMessage, bool $isLog = false, string $action = '')
|
|
{
|
|
$this->error = $errMessage;
|
|
$isLog && Log::append("银联-{$action}", ['errMessage' => $errMessage]);
|
|
throwError($errMessage);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 支付成功后的异步通知
|
|
* @return bool
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
|
|
*/
|
|
public function notify(): bool
|
|
{
|
|
// 接收表单数据
|
|
$this->notifyParams = request()->filter([])->post();
|
|
|
|
$notifyParams = $this->notifyParams;
|
|
$sign = $notifyParams['sign'];
|
|
unset($notifyParams['sign']);
|
|
ksort($notifyParams);
|
|
$str = http_build_query($notifyParams);
|
|
$verifyNotify = false;
|
|
if ($sign != md5($str)) {
|
|
$verifyNotify = true;
|
|
}
|
|
|
|
// 判断交易单状态必须是支付成功
|
|
$this->notifyResult = $verifyNotify && $this->notifyParams['status'] === 'TRADE_SUCCESS';
|
|
// 记录日志
|
|
Log::append('银联-notify', [
|
|
'params' => $this->notifyParams,
|
|
'verifyNotify' => $verifyNotify,
|
|
'response' => $this->getNotifyResponse(),
|
|
'result' => $this->notifyResult,
|
|
'message' => '银联异步回调验证' . ($this->notifyResult ? '成功' : '失败')
|
|
]);
|
|
return $this->notifyResult;
|
|
}
|
|
|
|
/**
|
|
* 微信支付退款API
|
|
* @param string $outTradeNo 第三方交易单号
|
|
* @param string $refundAmount 退款金额
|
|
* @param array $extra 附加数据 (需要携带订单付款总金额)
|
|
* @return bool
|
|
* @throws BaseException
|
|
*/
|
|
public function refund(string $outTradeNo, string $refundAmount, array $extra = []): bool
|
|
{
|
|
|
|
try {
|
|
$refund_url = $this->domain . "/v1/netpay/refund";
|
|
$params = [
|
|
"requestTimestamp" => date("Y-m-d H:i:s"),
|
|
"merOrderId" => $outTradeNo,
|
|
"mid" => $this->config['mid'],
|
|
"tid" => $this->config['tid'],
|
|
"targetOrderId" => $extra['trade_no'],
|
|
"refundAmount" => bcmul($refundAmount, "100"),
|
|
"refundOrderId" => $this->config['msgSrcId'].date("YmdHis").mt_rand(1000, 9999),
|
|
"refundDesc" => "订单退款",
|
|
];
|
|
$authorization = $this->getAuthorization($params);
|
|
$headers = ["Authorization:".$authorization];
|
|
|
|
$data = httpRequest($refund_url, "POST", json_encode($params), $headers, false);
|
|
//var_dump($data);
|
|
|
|
if ($data['errCode'] != "SUCCESS") {
|
|
$this->throwError($data['errMsg']);
|
|
}
|
|
// 请求成功
|
|
return true;
|
|
} catch (\Throwable $e) {
|
|
$this->throwError('银联API退款请求:' . $e->getMessage(), true, 'refund');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 商家转账到零钱API
|
|
* @param string $outTradeNo 交易订单号
|
|
* @param string $totalFee 实际付款金额
|
|
* @param array $extra 附加的数据 (需要携带openid、desc)
|
|
* @return bool
|
|
* @throws BaseException
|
|
*/
|
|
public function transfers(string $outTradeNo, string $totalFee, array $extra = []): bool
|
|
{
|
|
if (!$this->getApp()->transfers($outTradeNo, $totalFee, $extra)) {
|
|
$this->setError($this->getApp()->getError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 获取异步回调的请求参数
|
|
* @return array
|
|
*/
|
|
public function getNotifyParams(): array
|
|
{
|
|
return [
|
|
// 第三方交易流水号
|
|
'tradeNo' => $this->notifyParams['targetOrderId']
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 返回异步通知结果的输出内容
|
|
* @return string
|
|
*/
|
|
public function getNotifyResponse(): string
|
|
{
|
|
return $this->notifyResult ? 'SUCCESS' : 'FAILED';
|
|
}
|
|
|
|
/**
|
|
* 返回统一下单API的结果 (用于前端)
|
|
* @return array
|
|
* @throws BaseException
|
|
*/
|
|
public function getUnifyResult(): array
|
|
{
|
|
if (empty($this->result)) {
|
|
$this->throwError('当前没有unify结果', true, 'getUnifyResult');
|
|
}
|
|
$this->result['out_trade_no'] = $this->result['merOrderId'];//str_replace($this->config['msgSrcId'], "", $this->result['merOrderId']);
|
|
return $this->result;
|
|
}
|
|
/**
|
|
* 生成授权码
|
|
* [getAuthorization description]
|
|
* @param [type] $data [description]
|
|
* @return [type] [description]
|
|
*/
|
|
private function getAuthorization($data){
|
|
$appid = $this->config['appid'];
|
|
$appkey = $this->config['appkey'];
|
|
|
|
$timestamp = date("YmdHis",time());
|
|
$nonce = md5(uniqid((string)microtime(true),true));
|
|
$body = json_encode($data);
|
|
//echo $body;
|
|
$str = bin2hex(hash('sha256', $body, true));
|
|
$signature = base64_encode(hash_hmac('sha256', "$appid$timestamp$nonce$str", $appkey, true));
|
|
$authorization = 'OPEN-BODY-SIG AppId="'.$appid.'", Timestamp="'.$timestamp.'", Nonce="'.$nonce.'", Signature="'.$signature.'"';
|
|
|
|
return $authorization;
|
|
}
|
|
/**
|
|
* 异步回调地址
|
|
* @return string
|
|
*/
|
|
private function notifyUrl(): string
|
|
{
|
|
// 例如:https://www.xxxx.com/alipayNotice.php
|
|
return base_url() . 'huifuNotice.php';
|
|
}
|
|
}
|
|
|