|
|
|
@ -5,28 +5,18 @@ namespace app\common\library\payment\gateway\driver; |
|
|
|
|
|
|
|
|
|
use app\common\library\payment\gateway\Driver; |
|
|
|
|
use cores\exception\BaseException; |
|
|
|
|
|
|
|
|
|
# 加载 SDK 初始化文件 |
|
|
|
|
require_once root_path() . "/vendor/huifurepo/dg-php-sdk/BsPaySdk/init.php"; |
|
|
|
|
require_once root_path() . "/vendor/huifurepo/dg-php-sdk/BsPaySdk/request/V2TradePaymentJspayRequest.php"; |
|
|
|
|
require_once root_path() . "/vendor/huifurepo/dg-php-sdk/BsPaySdk/request/V2MerchantBusiOpenRequest.php"; |
|
|
|
|
|
|
|
|
|
use BsPaySdk\core\BsPay; |
|
|
|
|
use BsPaySdk\core\BsPayClient; |
|
|
|
|
use BsPaySdk\request\V2TradePaymentJspayRequest; |
|
|
|
|
use BsPaySdk\request\V2MerchantBusiOpenRequest; |
|
|
|
|
use BsPaySdk\config\MerConfig; |
|
|
|
|
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 下单客户端 |
|
|
|
@ -35,25 +25,28 @@ class Huifu extends Driver |
|
|
|
|
public function setOptions(array $options, string $client): ?Driver |
|
|
|
|
{ |
|
|
|
|
$this->client = $client ?: null; |
|
|
|
|
$this->options = $options; |
|
|
|
|
if ($options['mchType'] === 'provider') { |
|
|
|
|
$config = [ |
|
|
|
|
'sys_id' => $options['provider']['sys_id'], |
|
|
|
|
'product_id' => $options['provider']['product_id'], |
|
|
|
|
'rsa_merch_private_key' => $options['provider']['rsa_merch_private_key'], |
|
|
|
|
'rsa_huifu_public_key' => $options['provider']['rsa_huifu_public_key'], |
|
|
|
|
'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 = [ |
|
|
|
|
'sys_id' => $options['normal']['sys_id'], |
|
|
|
|
'product_id' => $options['normal']['product_id'], |
|
|
|
|
'rsa_merch_private_key' => $options['normal']['rsa_merch_private_key'], |
|
|
|
|
'rsa_huifu_public_key' => $options['normal']['rsa_huifu_public_key'], |
|
|
|
|
'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->merChantKey = $options['normal']['sys_id'].$options['normal']['product_id']; |
|
|
|
|
BsPay::init($config, true, $this->merChantKey); |
|
|
|
|
|
|
|
|
|
$this->config = $config; |
|
|
|
|
return $this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -99,47 +92,87 @@ class Huifu extends Driver |
|
|
|
|
// 发起API调用 微信小程序端 |
|
|
|
|
if ($this->client === ClientEnum::MP_WEIXIN) { |
|
|
|
|
|
|
|
|
|
// 2.组装请求参数 |
|
|
|
|
$request = new V2TradePaymentJspayRequest(); |
|
|
|
|
// 请求日期 |
|
|
|
|
$request->setReqDate(date("Ymd")); |
|
|
|
|
// 请求流水号 |
|
|
|
|
$request->setReqSeqId($outTradeNo); |
|
|
|
|
|
|
|
|
|
$merConfig = BsPay::getConfig($this->merChantKey); |
|
|
|
|
// 商户号 |
|
|
|
|
$request->setHuifuId($merConfig->sys_id); |
|
|
|
|
// 交易类型 |
|
|
|
|
$request->setTradeType("T_MINIAPP"); |
|
|
|
|
// 交易金额 |
|
|
|
|
$request->setTransAmt(sprintf("%.2f", $totalFee)); |
|
|
|
|
// 商品描述 |
|
|
|
|
$request->setGoodsDesc($extra['title'] ?? "商品支付"); |
|
|
|
|
|
|
|
|
|
// 设置非必填字段 |
|
|
|
|
$extendInfoMap = $this->getExtendInfos($extra); |
|
|
|
|
$request->setExtendInfo($extendInfoMap); |
|
|
|
|
|
|
|
|
|
// 3. 发起API调用 |
|
|
|
|
$client = new BsPayClient($this->merChantKey); |
|
|
|
|
$result = $client->postRequest($request); |
|
|
|
|
var_dump($result); |
|
|
|
|
exit(); |
|
|
|
|
Log::append('Alipay-unify', ['client' => $this->client, 'result' => $result]); |
|
|
|
|
if (!$result || $result->isError()) { |
|
|
|
|
//$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; |
|
|
|
|
} |
|
|
|
|
$data = $result->getRspDatas()['data']; |
|
|
|
|
if (isset($data['resp_code']) && $data['resp_code'] == "00000100") { |
|
|
|
|
return $data; |
|
|
|
|
} |
|
|
|
|
$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, 'unify'); |
|
|
|
|
$this->throwError('银联API交易查询失败:' . $e->getMessage(), true, 'tradeQuery'); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 输出错误信息 |
|
|
|
|
* @param string $errMessage 错误信息 |
|
|
|
@ -150,95 +183,11 @@ class Huifu extends Driver |
|
|
|
|
private function throwError(string $errMessage, bool $isLog = false, string $action = '') |
|
|
|
|
{ |
|
|
|
|
$this->error = $errMessage; |
|
|
|
|
$isLog && Log::append("Alipay-{$action}", ['errMessage' => $errMessage]); |
|
|
|
|
$isLog && Log::append("银联-{$action}", ['errMessage' => $errMessage]); |
|
|
|
|
throwError($errMessage); |
|
|
|
|
} |
|
|
|
|
public function getExtendInfos($params) { |
|
|
|
|
// 设置非必填字段 |
|
|
|
|
$extendInfoMap = array(); |
|
|
|
|
// 交易有效期 |
|
|
|
|
$extendInfoMap["time_expire"]= date("YmdHis", time() + 86400); |
|
|
|
|
// 禁用信用卡标记 |
|
|
|
|
$extendInfoMap["limit_pay_type"]= "NO_CREDIT"; |
|
|
|
|
// 是否延迟交易 |
|
|
|
|
$extendInfoMap["delay_acct_flag"]= "N"; |
|
|
|
|
// 渠道号 |
|
|
|
|
$extendInfoMap["channel_no"]= ""; |
|
|
|
|
// 手续费扣款标志 |
|
|
|
|
// $extendInfoMap["fee_flag"]= ""; |
|
|
|
|
// 补贴支付信息 |
|
|
|
|
// $extendInfoMap["combinedpay_data"]= $this->getCombinedpayData(); |
|
|
|
|
// 场景类型 |
|
|
|
|
$extendInfoMap["pay_scene"]= "02"; |
|
|
|
|
// 安全信息 |
|
|
|
|
//$extendInfoMap["risk_check_data"]= $this->getRiskCheckData(); |
|
|
|
|
// 设备信息 |
|
|
|
|
//$extendInfoMap["terminal_device_data"]= $this->getTerminalDeviceData(); |
|
|
|
|
// 分账对象 |
|
|
|
|
//$extendInfoMap["acct_split_bunch"]= $this->getAcctSplitBunch(); |
|
|
|
|
// 传入分帐遇到优惠的处理规则 |
|
|
|
|
$extendInfoMap["term_div_coupon_type"]= "0"; |
|
|
|
|
// 聚合正扫微信拓展参数集合 |
|
|
|
|
$extendInfoMap["wx_data"]= $this->getWxData($params); |
|
|
|
|
// 支付宝扩展参数集合 |
|
|
|
|
//$extendInfoMap["alipay_data"]= $this->getAlipayData(); |
|
|
|
|
// 银联参数集合 |
|
|
|
|
// $extendInfoMap["unionpay_data"]= getUnionpayData(); |
|
|
|
|
// 数字人民币参数集合 |
|
|
|
|
// $extendInfoMap["dc_data"]= getDcData(); |
|
|
|
|
// 商户贴息标记 |
|
|
|
|
$extendInfoMap["fq_mer_discount_flag"]= "N"; |
|
|
|
|
// 异步通知地址 |
|
|
|
|
$extendInfoMap["notify_url"]= $this->notifyUrl(); |
|
|
|
|
// 备注 |
|
|
|
|
$extendInfoMap["remark"] = $params['title'] ?? "商品支付"; |
|
|
|
|
// 账户号 |
|
|
|
|
// $extendInfoMap["acct_id"]= ""; |
|
|
|
|
return $extendInfoMap; |
|
|
|
|
} |
|
|
|
|
public function getWxData($params) { |
|
|
|
|
$dto = array(); |
|
|
|
|
// 子商户公众账号id |
|
|
|
|
$dto["sub_appid"] = "wxa3127ed5c3542a28"; |
|
|
|
|
// 用户标识 |
|
|
|
|
$dto["openid"] = $params['openid']; |
|
|
|
|
// 子商户用户标识 |
|
|
|
|
$dto["sub_openid"] = ""; |
|
|
|
|
// 附加数据 |
|
|
|
|
// $dto["attach"] = ""; |
|
|
|
|
// 商品描述 |
|
|
|
|
// $dto["body"] = ""; |
|
|
|
|
// 商品详情 |
|
|
|
|
// $dto["detail"] = getDetail(); |
|
|
|
|
// 设备号 |
|
|
|
|
// $dto["device_info"] = ""; |
|
|
|
|
// 订单优惠标记 |
|
|
|
|
// $dto["goods_tag"] = ""; |
|
|
|
|
// 实名支付 |
|
|
|
|
// $dto["identity"] = ""; |
|
|
|
|
// 开发票入口开放标识 |
|
|
|
|
// $dto["receipt"] = ""; |
|
|
|
|
// 场景信息 |
|
|
|
|
// $dto["scene_info"] = getSceneInfo(); |
|
|
|
|
// 终端ip |
|
|
|
|
// $dto["spbill_create_ip"] = ""; |
|
|
|
|
// 单品优惠标识 |
|
|
|
|
// $dto["promotion_flag"] = ""; |
|
|
|
|
// 新增商品ID |
|
|
|
|
// $dto["product_id"] = ""; |
|
|
|
|
|
|
|
|
|
return json_encode($dto,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
* 交易查询 (主动查询订单支付状态) |
|
|
|
|
* @param string $outTradeNo 交易订单号 |
|
|
|
|
* @return array|null |
|
|
|
|
* @throws BaseException |
|
|
|
|
*/ |
|
|
|
|
public function tradeQuery(string $outTradeNo): ?array |
|
|
|
|
{ |
|
|
|
|
return $this->getApp()->tradeQuery($outTradeNo); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 支付成功后的异步通知 |
|
|
|
@ -247,11 +196,30 @@ class Huifu extends Driver |
|
|
|
|
*/ |
|
|
|
|
public function notify(): bool |
|
|
|
|
{ |
|
|
|
|
if (!$this->getApp()->notify()) { |
|
|
|
|
$this->setError($this->getApp()->getError()); |
|
|
|
|
return false; |
|
|
|
|
// 接收表单数据 |
|
|
|
|
$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; |
|
|
|
|
} |
|
|
|
|
return 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -264,11 +232,32 @@ class Huifu extends Driver |
|
|
|
|
*/ |
|
|
|
|
public function refund(string $outTradeNo, string $refundAmount, array $extra = []): bool |
|
|
|
|
{ |
|
|
|
|
if (!$this->getApp()->refund($outTradeNo, $refundAmount, $extra)) { |
|
|
|
|
$this->setError($this->getApp()->getError()); |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
if ($data['errCode'] != "SUCCESS") { |
|
|
|
|
$this->throwError($data['errMsg']); |
|
|
|
|
} |
|
|
|
|
// 请求成功 |
|
|
|
|
return true; |
|
|
|
|
} catch (\Throwable $e) { |
|
|
|
|
$this->throwError('银联API退款请求:' . $e->getMessage(), true, 'refund'); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -294,17 +283,19 @@ class Huifu extends Driver |
|
|
|
|
*/ |
|
|
|
|
public function getNotifyParams(): array |
|
|
|
|
{ |
|
|
|
|
return $this->getApp()->getNotifyParams(); |
|
|
|
|
return [ |
|
|
|
|
// 第三方交易流水号 |
|
|
|
|
'tradeNo' => $this->notifyParams['targetOrderId'] |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 返回异步通知结果的输出内容 |
|
|
|
|
* @return string |
|
|
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException |
|
|
|
|
*/ |
|
|
|
|
public function getNotifyResponse(): string |
|
|
|
|
{ |
|
|
|
|
return $this->getApp()->getNotifyResponse(); |
|
|
|
|
return $this->notifyResult ? 'SUCCESS' : 'FAILED'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -314,7 +305,31 @@ class Huifu extends Driver |
|
|
|
|
*/ |
|
|
|
|
public function getUnifyResult(): array |
|
|
|
|
{ |
|
|
|
|
return $this->getApp()->getUnifyResult(); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
* 异步回调地址 |
|
|
|
@ -323,6 +338,6 @@ class Huifu extends Driver |
|
|
|
|
private function notifyUrl(): string |
|
|
|
|
{ |
|
|
|
|
// 例如:https://www.xxxx.com/alipayNotice.php |
|
|
|
|
return base_url() . 'alipayNotice.php'; |
|
|
|
|
return base_url() . 'huifuNotice.php'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|