From 515771739f94bd4d34d2db71fc7f3432a45f4dad Mon Sep 17 00:00:00 2001 From: elf <360197197@qq.com> Date: Sun, 28 May 2023 23:59:11 +0800 Subject: [PATCH] xg --- app/Command/JinlingCommand.php | 52 +- app/Controller/Payment/NotifyController.php | 154 ++++- app/Controller/Payment/PayController.php | 107 +++- app/Exception/Handler/AppExceptionHandler.php | 7 + app/Helper/Efps/AbstractApi.php | 3 +- app/Helper/Efps/Api.php | 163 +++++- app/Helper/Efps/Result.php | 30 +- app/Helper/Efps/Signer.php | 5 +- app/Helper/RedisKey.php | 4 + app/Middleware/RequestLogMiddleware.php | 67 +++ app/Model/Order.php | 1 + app/Model/RefundOrder.php | 16 + app/Model/RequestLog.php | 10 + app/Model/User.php | 10 + app/Request/ApiRequest.php | 4 +- app/Request/RefundQueryRequest.php | 13 + app/Request/RefundRequest.php | 13 + app/Request/RegisterRequest.php | 4 +- app/Service/PaymentService.php | 179 +----- config/routes.php | 12 +- payment.sql | 86 +++ test.php | 527 ++++++++++++++++++ 22 files changed, 1260 insertions(+), 207 deletions(-) create mode 100644 app/Middleware/RequestLogMiddleware.php create mode 100644 app/Model/RefundOrder.php create mode 100644 app/Model/RequestLog.php create mode 100644 app/Model/User.php create mode 100644 app/Request/RefundQueryRequest.php create mode 100644 app/Request/RefundRequest.php create mode 100644 payment.sql create mode 100644 test.php diff --git a/app/Command/JinlingCommand.php b/app/Command/JinlingCommand.php index 8c7fa5f..d845424 100644 --- a/app/Command/JinlingCommand.php +++ b/app/Command/JinlingCommand.php @@ -6,6 +6,8 @@ namespace App\Command; use App\Helper\Efps\Api; use App\Helper\Signer; +use App\Helper\StringHelper; +use App\Request\RegisterRequest; use App\Service\AppService; use App\Service\MerchantService; use App\Service\PaymentService; @@ -40,8 +42,40 @@ class JinlingCommand extends HyperfCommand public function handle(): void { - /*Api::register([ - 'merId' => "100", // 562238003185933 + + $ret = Api::certifiedPayment([ + 'outTradeNo' => time() . rand(1000, 9999), + 'userName' => 'abc', + 'phoneNum' => '18760419185', + 'bankCardNo' => '6214835911385365', + 'certificatesNo' => '350824199001105476', + 'payAmount' => 100, + ]); + var_dump($ret); + return; + /*$ret = Api::unifiedPayment(); + var_dump($ret); + return;*/ + /*$params = [ + 'app_id' => '202304270000004', + 'timestamp' => time(), + 'nonce_str' => StringHelper::getRandomString(32), + 'data' => json_encode([ + 'test' => 1, + ]), + ]; + $sign = \App\Helper\Platform\Signer::sign($params, 'lSHKbuFngCXHN8Ue1s8QHAAzPvOL3u9O'); + $params['sign'] = $sign; + var_dump($params); + $request = new RegisterRequest($params); + $params = $request->getData(); + var_dump($params); + return;*/ + /*$ret = Api::unifiedPayment(); + var_dump($ret); + return;*/ + $ret = Api::register([ + 'merId' => "T100", // 562238003185933 'backUrl' => 'http://www.baidu.com', 'certificateName' => '测试', 'lawyerCertType' => 0, @@ -52,16 +86,18 @@ class JinlingCommand extends HyperfCommand 'contactPhone' => '18888888888', 'accountType' => 3, 'canDownType' => '01', - ]);*/ - Api::bindCard([ - 'memberId' => '562238003185933', + ]); + var_dump($ret); + return; + $ret = Api::bindCard([ 'mchtOrderNo' => time() . rand(1000, 9999), - 'userName' => '测试', - 'phoneNum' => '18888888888', + 'userName' => '廖金灵', + 'phoneNum' => '18760419185', 'bankCardNo' => '6214835911385365', 'bankCardType' => 'debit', - 'certificatesNo' => '430481198104234557', + 'certificatesNo' => '350824199001105476', ]); + var_dump($ret); return; echo \App\Helper\Efps\Signer::sign('sss'); $sm3 = new \OneSm\Sm3(); diff --git a/app/Controller/Payment/NotifyController.php b/app/Controller/Payment/NotifyController.php index 6655f79..94cf45f 100644 --- a/app/Controller/Payment/NotifyController.php +++ b/app/Controller/Payment/NotifyController.php @@ -4,26 +4,164 @@ declare(strict_types=1); namespace App\Controller\Payment; -use App\Service\PaymentService; +use App\Helper\Efps\Signer; +use App\Helper\Log; +use App\Helper\Platform\Notification; +use App\Model\App; +use App\Model\Order; +use App\Model\RefundOrder; use Hyperf\HttpServer\Contract\RequestInterface; class NotifyController extends AbstractController { - private PaymentService $paymentService; - public function __construct(PaymentService $paymentService) + public function __construct() { - $this->paymentService = $paymentService; } - public function index(RequestInterface $request) + public function payment(RequestInterface $request) { $params = $request->all(); - return $this->paymentService->notify($params); + Log::info('paymentNotifyToOut params:', $params); + $sign = $request->getHeader('x-efps-sign'); + $data = json_encode($params); + if (!Signer::verify($data, $sign)) { + Log::info('paymentNotifyToOut data: ' . $data . ' sign: ' . $sign); + return [ + 'returnCode' => '0001', + 'returnMsg' => '签名验证失败' + ]; + } + $outOrderNo = $params['outTradeNo'] ?: ''; + $order = Order::where('out_order_no', $outOrderNo)->first(); + if (!$order) { + Log::info('paymentNotifyToOut: ' . '订单号不存在[' . $outOrderNo . ']'); + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + $app = App::where('app_id', $order->app_id)->first(); + if (!$app) { + Log::info('paymentNotifyToOut: ' . 'APP不存在[' . $outOrderNo . '][' . $order->app_id . ']'); + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + if ($order->status != Order::STATUS_WAIT_PAY) { + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + + if ($params['payState'] === '00') { + $order->status = Order::STATUS_PAYED; + $order->fee = $params['procedureFee'] ?: 0; + $order->pay_order_no = $params['transactionNo'] ?: ''; + $order->channel_order_no = $params['channelOrder'] ?: ''; + $order->payed_at = date('Y-m-d H:i:s'); + $order->save(); + } elseif ($params['payState'] === '01') { + $order->status = Order::STATUS_FAILED; + $order->error_code = '01'; + $order->error_msg = '处理失败'; + $order->payed_at = date('Y-m-d H:i:s'); + $order->save(); + } + + $result = $this->notify($order->notify_url, $app, [ + 'outTradeNo' => $params['outTradeNo'] ?: '', + 'transactionNo' => $params['transactionNo'] ?: '', + 'payState' => $params['payState'], + 'amount' => $params['amount'] ?: 0, + ]); + Log::info('paymentNotifyToOut result: ' . '订单[' . $outOrderNo . '][' . $$result . ']'); + + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; } - public function efps(RequestInterface $request) { + public function refund(RequestInterface $request) + { $params = $request->all(); - return $this->paymentService->efpsNotify($params); + Log::info('refundNotifyToOut params:', $params); + $sign = $request->getHeader('x-efps-sign'); + $data = json_encode($params); + if (!Signer::verify($data, $sign)) { + Log::info('refundNotifyToOut data: ' . $data . ' sign: ' . $sign); + return [ + 'returnCode' => '0001', + 'returnMsg' => '签名验证失败' + ]; + } + $outRefundNo = $params['outRefundNo'] ?: ''; + $order = RefundOrder::where('out_refund_order_no', $outRefundNo)->first(); + if (!$order) { + Log::info('refundNotifyToOut: ' . '订单号不存在[' . $outRefundNo . ']'); + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + $app = App::where('app_id', $order->app_id)->first(); + if (!$app) { + Log::info('refundNotifyToOut: ' . 'APP不存在[' . $outRefundNo . '][' . $order->app_id . ']'); + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + if ($order->status != RefundOrder::STATUS_APPLY_SUCCESS) { + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + if ($params['payState'] === '00') { + $order->status = RefundOrder::STATUS_REFUND_SUCCESS; + $order->fee = $params['procedureFee'] ?: 0; + $order->refunded_at = date('Y-m-d H:i:s'); + $order->save(); + } elseif ($params['payState'] === '01') { + $order->status = RefundOrder::STATUS_REFUND_FAILED; + $order->error_code = '01'; + $order->error_msg = '处理失败'; + $order->refunded_at = date('Y-m-d H:i:s'); + $order->save(); + } + + $result = $this->notify($order->notify_url, $app, [ + 'outRefundNo' => $params['outRefundNo'] ?: '', + 'transactionNo' => $params['transactionNo'] ?: '', + 'payState' => $params['payState'], + 'amount' => $params['amount'] ?: 0, + 'refundAmount' => $params['refundAmount'] ?: 0, + ]); + Log::info('paymentNotifyToOut result: ' . '订单[' . $outRefundNo . '][' . $$result . ']'); + return [ + 'returnCode' => '0000', + 'returnMsg' => '处理成功' + ]; + } + + protected function notify($url, $app, $data) { + $params = [ + 'app_id' => $app->app_id, + 'nonce_str' => StringHelper::getRandomString(32), + 'timestamp' => time(), + ]; + $params['data'] = json_encode($data); + $params['sign'] = PlatformSigner::sign($params, $app->app_key); + + Log::info('notifyToOut params:', $params, 'platform'); + + Log::info('notifyToOut url:' . $url, [], 'platform'); + $result = Notification::post($url, $params); + Log::info('notifyToOut response:' . $result, [], 'platform'); + return $result; } } diff --git a/app/Controller/Payment/PayController.php b/app/Controller/Payment/PayController.php index 08bb9aa..8528e0c 100644 --- a/app/Controller/Payment/PayController.php +++ b/app/Controller/Payment/PayController.php @@ -6,11 +6,16 @@ namespace App\Controller\Payment; use App\Exception\BusinessException; use App\Helper\Efps\Api; +use App\Model\Order; +use App\Model\RefundOrder; +use App\Model\User; use App\Request\BindCardConfirmRequest; use App\Request\BindCardRequest; use App\Request\JsapiPayRequest; use App\Request\ProtocolPayConfirmRequest; use App\Request\ProtocolPayPreRequest; +use App\Request\RefundQueryRequest; +use App\Request\RefundRequest; use App\Request\RegisterRequest; use App\Request\UnBindCardRequest; use Hyperf\HttpServer\Contract\RequestInterface; @@ -25,23 +30,26 @@ class PayController extends AbstractController $this->paymentService = $paymentService; } - public function jsapi(RequestInterface $request) - { - $form = new JsapiPayRequest($request->all()); - $order = $this->paymentService->jsapiPay($form->getApp(), $form->getData()); - return $this->success([ - 'pay_url' => $order->pay_url, - 'order_no' => $order->order_no, - ]); - } - public function register(RequestInterface $request) { $req = new RegisterRequest($request->all()); - $result = Api::register($req->getData()); + $app = $req->getApp(); + $data = $req->getData(); + $result = Api::register($data); if (!$result->isSuccess()) { throw new BusinessException($result->getMessage()); } + if ($result->get('auditState') == 1) { + $user = new User(); + $user->app_id = $app->app_id; + $user->member_id = $result->get('memberId'); + $user->merId = $data['merId'] ?: ''; + $user->card_no = $data['lawyerCertNo'] ?: ''; + $user->card_valid_to = $data['certificateTo'] ?: ''; + $user->mobile = $data['contactPhone'] ?: ''; + $user->save(); + } + return $this->success($result->getData()); } @@ -78,20 +86,95 @@ class PayController extends AbstractController public function protocolPayPreRequest(RequestInterface $request) { $req = new ProtocolPayPreRequest($request->all()); - $result = Api::protocolPayPre($req->getData()); + $app = $req->getApp(); + $data = $req->getData(); + $result = Api::protocolPayPre($data); + + $order = $this->paymentService->createOrder($app, $data); + if (!$result->isSuccess()) { + $order->status = Order::STATUS_APPLY_FAIL; + $order->save(); throw new BusinessException($result->getMessage()); } + $order->token = $result->get('token', ''); + $order->protocol = $result->get('protocol', ''); + $order->status = Order::STATUS_WAIT_PAY; + $order->save(); return $this->success($result->getData()); } public function protocolPayConfirm(RequestInterface $request) { $req = new ProtocolPayConfirmRequest($request->all()); + $app = $req->getApp(); + $data = $req->getData(); + $order = Order::where('app_id', $app->app_id) + ->where('token', $data['token']) + ->where('protocol', $data['protocol']) + ->first(); + if (!$order) { + throw new BusinessException('订单不存在'); + } $result = Api::protocolPayConfirm($req->getData()); if (!$result->isSuccess()) { + $order->status = Order::STATUS_FAILED; + $order->error_code = $result->getCode(); + $order->error_msg = $result->getMessage(); + $order->payed_at = date('Y-m-d H:i:s'); + $order->save(); + throw new BusinessException($result->getMessage()); + } + + // 00 01 03 + $this->paymentService->handlePayResult($result, $order); + return $this->success($result->getData()); + } + + public function refund(RequestInterface $request) { + $req = new RefundRequest($request->all()); + $app = $req->getApp(); + $data = $req->getData(); + + $refundOrder = new RefundOrder(); + $refundOrder->app_id = $app->app_id; + $refundOrder->out_order_no = $data['outTradeNo'] ?: ''; + $refundOrder->out_refund_order_no = $data['outRefundNo'] ?: ''; + $refundOrder->order_amount = $data['amount'] ?: ''; + $refundOrder->refund_amount = $data['refundAmount'] ?: ''; + $refundOrder->remark = $data['remark'] ?: ''; + $refundOrder->notify_url = $data['notifyUrl'] ?: ''; + + $result = Api::refund($data); + if (!$result->isSuccess()) { + $refundOrder->status = RefundOrder::STATUS_APPLY_FAILED; + $refundOrder->error_code = $result->getCode(); + $refundOrder->error_msg = $result->getMessage(); + $refundOrder->save(); throw new BusinessException($result->getMessage()); } + $refundOrder->status = RefundOrder::STATUS_APPLY_SUCCESS; + $refundOrder->save(); + return $this->success($result->getData()); } + + public function refundQuery(RequestInterface $request) { + $req = new RefundQueryRequest($request->all()); + $data = $req->getData(); + $result = Api::refundQuery($data); + if (!$result->isSuccess()) { + throw new BusinessException($result->getMessage()); + } + + return $this->success( + $result->getData([ + 'outRefundNo', + 'transactionNo', + 'amount', + 'refundAmount', + 'refundState' + ]) + ); + } } diff --git a/app/Exception/Handler/AppExceptionHandler.php b/app/Exception/Handler/AppExceptionHandler.php index 86506c2..fb926e1 100644 --- a/app/Exception/Handler/AppExceptionHandler.php +++ b/app/Exception/Handler/AppExceptionHandler.php @@ -7,6 +7,7 @@ namespace App\Exception\Handler; use App\Constants\ResultCode; use App\Exception\BasicException; use App\Helper\Result; +use Hyperf\Context\Context; use Hyperf\Contract\StdoutLoggerInterface; use Hyperf\ExceptionHandler\ExceptionHandler; use Hyperf\HttpMessage\Stream\SwooleStream; @@ -54,6 +55,12 @@ class AppExceptionHandler extends ExceptionHandler $this->logger->error($throwable->getTraceAsString()); } $result = new Result($code, $message, $data); + $requestLog = Context::get('requestLog'); + if ($requestLog) { + $requestLog->response_data = json_encode($result->toArray(), JSON_UNESCAPED_UNICODE); + $requestLog->response_time = date('Y-m-d H:i:s'); + $requestLog->save(); + } return $response ->withAddedHeader('content-type', 'application/json; charset=utf-8') ->withStatus($httpCode) diff --git a/app/Helper/Efps/AbstractApi.php b/app/Helper/Efps/AbstractApi.php index a03d850..9728a89 100644 --- a/app/Helper/Efps/AbstractApi.php +++ b/app/Helper/Efps/AbstractApi.php @@ -12,7 +12,7 @@ abstract class AbstractApi { protected static $client; - protected static $env = 'dev'; + protected static $env = 'prod'; public static function getConfig($key) { @@ -23,6 +23,7 @@ abstract class AbstractApi public static function request($uri, $params, $sign) { try { Log::info('url:' . $uri, [], 'efps'); + Log::info('headers:', self::getXEfpsHeaders($sign), 'efps'); $response = self::getClient()->post($uri, [ 'json' => $params, 'headers' => self::getXEfpsHeaders($sign), diff --git a/app/Helper/Efps/Api.php b/app/Helper/Efps/Api.php index 233dd7d..fbea378 100644 --- a/app/Helper/Efps/Api.php +++ b/app/Helper/Efps/Api.php @@ -7,6 +7,50 @@ use App\Helper\StringHelper; class Api extends AbstractApi { + public static function apply() + { + $paper = [ + "certificateName" => "李四", + "contactPhone" => "13531231222", + "email" => "test1@test.cn", + "lawyerCertNo" => "430481198104234557", + "lawyerCertType" => "0", + "merchantType" => "3", + "openBank" => "中国银行", + "openingLicenseAccountPhoto" => "https://www.epaylinks.cn/www/wimages/epl_logo.png", + "settleAccount" => "李四", + "settleAccountNo" => "6214830201234567", + "settleAccountType" => "2", + "settleTarget" => "2" + ]; + $business = [ + [ + "businessCode" => "WITHDRAW_TO_SETTMENT_DEBIT", + "creditcardsEnabled" => 0, + "refundEnabled" => 1, + "refundFeePer" => 0, + "refundFeeRate" => 0, + "settleCycle" => "D+0", + "stage" => [ + [ + "amountFrom" => 0, + "feePer" => 50 + ] + ] + ] + ]; + $params = [ + 'acqSpId' => self::getConfig('customerCode'), + 'merchantName' => "测试商户20211202", + 'acceptOrder' => 0, + 'openAccount' => 1, + 'paper' => json_encode($paper, JSON_UNESCAPED_UNICODE), + 'business' => $business + ]; + $sign = Signer::sign(json_encode($params)); + return self::request($uri, $params, $sign); + } + public static function register($params) { $merId = $params['merId'] ?? ''; @@ -43,7 +87,7 @@ class Api extends AbstractApi public static function bindCard($params) { - $memberId = $params['memberId'] ?? ''; + $memberId = self::getConfig('customerCode'); $mchtOrderNo = $params['mchtOrderNo'] ?? StringHelper::generateOrderNo(); $userName = $params['userName'] ?? ''; $phoneNum = $params['phoneNum'] ?? ''; @@ -55,7 +99,7 @@ class Api extends AbstractApi $uri = '/api/txs/protocol/bindCard'; $params = [ - 'version' => '3.0', + 'version' => '2.0', 'customerCode' => self::getConfig('customerCode'), 'mchtOrderNo' => $mchtOrderNo, 'memberId' => $memberId, @@ -67,6 +111,19 @@ class Api extends AbstractApi 'certificatesNo' => Signer::publicEncrypt($certificatesNo), 'nonceStr' => StringHelper::getRandomString(32), ]; + /*$params = [ + 'version' => '2.0', + 'customerCode' => self::getConfig('customerCode'), + 'mchtOrderNo' => $mchtOrderNo, + 'memberId' => $memberId, + 'userName' => $userName, + 'phoneNum' => $phoneNum, + 'bankCardNo' => $bankCardNo, + 'bankCardType' => $bankCardType, + 'certificatesType' => '01', + 'certificatesNo' => $certificatesNo, + 'nonceStr' => StringHelper::getRandomString(32), + ];*/ if ($bankCardType == 'credit') { $params['expired'] = Signer::publicEncrypt($expired); $params['cvn'] = Signer::publicEncrypt($cvn); @@ -93,6 +150,39 @@ class Api extends AbstractApi return self::request($uri, $params, $sign); } + public static function certifiedPayment($params) + { + $outTradeNo = $params['outTradeNo'] ?? StringHelper::generateOrderNo(); + $userName = $params['userName'] ?? ''; + $phoneNum = $params['phoneNum'] ?? ''; + $bankCardNo = $params['bankCardNo'] ?? ''; + $certificatesNo = $params['certificatesNo'] ?? ''; + $payAmount = $params['payAmount'] ?? 0; + $payCurrency = 'CNY'; + $orderInfo = []; + $orderInfo['Id'] = $outTradeNo; + $orderInfo['businessType'] = '130001'; + $orderInfo['goodsList'] = [['name' => 'pay', 'number' => 'one', 'amount' => $payAmount]]; + + $uri = '/api/txs/pay/CertifiedPayment'; + $params = [ + 'version' => '3.0', + 'customerCode' => self::getConfig('customerCode'), + 'outTradeNo' => $outTradeNo, + 'userName' => Signer::publicEncrypt($userName), + 'certificatesType' => '01', + 'certificatesNo' => Signer::publicEncrypt($certificatesNo), + 'phoneNum' => Signer::publicEncrypt($phoneNum), + 'bankCardNo' => Signer::publicEncrypt($bankCardNo), + 'payAmount' => $payAmount, + 'payCurrency' => $payCurrency, + 'orderInfo' => $orderInfo, + 'transactionStartTime' => date('YmdHis'), + 'nonceStr' => StringHelper::getRandomString(32), + ]; + $sign = Signer::sign(json_encode($params)); + return self::request($uri, $params, $sign); + } public static function unBindCard($params) { $protocol = $params['protocol'] ?? ''; @@ -157,4 +247,73 @@ class Api extends AbstractApi $sign = Signer::sign(json_encode($params)); return self::request($uri, $params, $sign); } + + public static function refund($params) + { + $uri = '/api/txs/pay/Refund/V2'; + $outRefundNo = $params['outRefundNo'] ?? ''; + $notifyUrl = $params['notifyUrl'] ?? ''; + $outTradeNo = $params['outTradeNo'] ?? ''; + $refundAmount = $params['refundAmount'] ?? 0; + $amount = $params['amount'] ?? 0; + $remark = $params['remark'] ?? ''; + + $params = [ + // 'version' => '3.0', + 'customerCode' => self::getConfig('customerCode'), + 'outRefundNo' => $outRefundNo, + 'outTradeNo' => $outTradeNo, + 'refundAmount' => $refundAmount, + 'amount' => $amount, + 'remark' => $remark, + 'notifyUrl' => $notifyUrl, + 'nonceStr' => StringHelper::getRandomString(32), + ]; + $sign = Signer::sign(json_encode($params)); + return self::request($uri, $params, $sign); + } + + public static function refundQuery($params) + { + $uri = '/api/txs/custPay/refundQuery'; + $outRefundNo = $params['outRefundNo'] ?? ''; + + $params = [ + // 'version' => '3.0', + 'customerCode' => self::getConfig('customerCode'), + 'outRefundNo' => $outRefundNo, + 'nonceStr' => StringHelper::getRandomString(32), + ]; + $sign = Signer::sign(json_encode($params)); + return self::request($uri, $params, $sign); + } + + public static function unifiedPayment() + { + $uri = '/api/txs/pay/UnifiedPayment'; + $orderNo = "123456".date('YmdHis'); + $client_ip = "127.0.0.1"; + + $orderInfo = []; + $orderInfo['Id'] = $orderNo; + $orderInfo['businessType'] = '130001'; + $orderInfo['goodsList'] = [['name'=>'pay', 'number'=>'one', 'amount'=>1]]; + + $params = [ + 'outTradeNo' => $orderNo, + 'customerCode' => self::getConfig('customerCode'), + 'clientIp' => $client_ip, + 'orderInfo' => $orderInfo, + 'payCurrency' => 'CNY', + 'payAmount' => 10, + 'payCurrency' => 'CNY', + 'notifyUrl' => 'http://www.baidu.com', + 'redirectUrl' => 'http://www.baidu.com', + 'transactionStartTime' =>date('YmdHis'), + 'nonceStr' => 'pay'.rand(100,999), + 'version' => '3.0' + ]; + $sign = Signer::sign(json_encode($params)); + return self::request($uri, $params, $sign); + } } \ No newline at end of file diff --git a/app/Helper/Efps/Result.php b/app/Helper/Efps/Result.php index b0861dd..6208471 100644 --- a/app/Helper/Efps/Result.php +++ b/app/Helper/Efps/Result.php @@ -18,9 +18,14 @@ class Result return $this->data['returnCode'] == '0000'; } - public function get($key) + public function get($key, $default = null) { - return $this->data[$key] ?? null; + return $this->data[$key] ?? $default; + } + + public function getCode() + { + return $this->data['returnCode']; } public function getMessage() @@ -28,13 +33,20 @@ class Result return $this->data['returnMsg']; } - public function getData() + public function getData(array $fields = null) { - $data = $this->data; - unset($data['returnCode']); - unset($data['returnMsg']); - unset($data['nonceStr']); - unset($data['customerCode']); - return $data; + $returnData = []; + if (is_array($fields)) { + foreach ($fields as $field) { + $returnData[$field] = $this->data[$field] ?? null; + } + } else { + $returnData = $this->data; + } + unset($returnData['returnCode']); + unset($returnData['returnMsg']); + unset($returnData['nonceStr']); + unset($returnData['customerCode']); + return $returnData; } } \ No newline at end of file diff --git a/app/Helper/Efps/Signer.php b/app/Helper/Efps/Signer.php index 98024ec..40cec9f 100644 --- a/app/Helper/Efps/Signer.php +++ b/app/Helper/Efps/Signer.php @@ -7,7 +7,7 @@ use App\Helper\StringHelper; class Signer { - protected static $env = 'dev'; + protected static $env = 'prod'; private static function getConfig($key) { @@ -61,9 +61,8 @@ class Signer if (empty($res)) { throw new BusinessException('RSA公钥错误, 请检查公钥文件格式是否正确'); } - $crypttext = ""; - openssl_public_encrypt($data,$crypttext, $res ); + openssl_public_encrypt($data,$crypttext, $res); openssl_free_key($res); return(base64_encode($crypttext)); diff --git a/app/Helper/RedisKey.php b/app/Helper/RedisKey.php index 332a702..27c82e4 100644 --- a/app/Helper/RedisKey.php +++ b/app/Helper/RedisKey.php @@ -13,4 +13,8 @@ class RedisKey public static function getGenerateOrderNoKey($timestamp) { return 'gennerate_order_no:' . date('YmdHi', $timestamp); } + + public static function getGenerateRequestIdKey($timestamp) { + return 'gennerate_request_id:' . date('YmdHi', $timestamp); + } } \ No newline at end of file diff --git a/app/Middleware/RequestLogMiddleware.php b/app/Middleware/RequestLogMiddleware.php new file mode 100644 index 0000000..878b303 --- /dev/null +++ b/app/Middleware/RequestLogMiddleware.php @@ -0,0 +1,67 @@ +container = $container; + $this->response = $response; + $this->request = $request; + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $params = $this->request->all(); + $appId = $params['app_id'] ?? 0; + $requestLog = new RequestLog(); + $requestLog->app_id = $appId; + $requestLog->request_id = $this->generateOrderNo(); + $requestLog->request_uri = $this->request->getRequestUri(); + $requestLog->request_data = json_encode($params, JSON_UNESCAPED_UNICODE); + $requestLog->request_time = date('Y-m-d H:i:s'); + $requestLog->save(); + + $ctxResponse = Context::get(ResponseInterface::class); + $ctxResponse = $ctxResponse->withHeader('X-Request-Id', $requestLog->request_id); + Context::set(ResponseInterface::class, $ctxResponse); + + Context::set('requestLog', $requestLog); + + $response = $handler->handle($request); + $requestLog->response_data = (string)$response->getBody(); + $requestLog->response_time = date('Y-m-d H:i:s'); + return $response; + } + private function generateOrderNo() { + $now = time(); + $key = RedisKey::getGenerateRequestIdKey($now); + $incrId = Redis::incr($key); + $incrId = '' . $incrId; + Redis::expire($key, 5*60); + $padLength = 8 - strlen($incrId); + $incrId = str_pad($incrId, $padLength, '0', STR_PAD_LEFT); + return date('YmdHis', $now) . $incrId; + } +} diff --git a/app/Model/Order.php b/app/Model/Order.php index 7f19715..583e36b 100644 --- a/app/Model/Order.php +++ b/app/Model/Order.php @@ -8,6 +8,7 @@ class Order extends Model { public const STATUS_PREPARE = 1; public const STATUS_WAIT_PAY = 2; + public const STATUS_APPLY_FAIL = 3; public const STATUS_PAYED = 6; public const STATUS_FAILED = 9; diff --git a/app/Model/RefundOrder.php b/app/Model/RefundOrder.php new file mode 100644 index 0000000..35b2afb --- /dev/null +++ b/app/Model/RefundOrder.php @@ -0,0 +1,16 @@ +app->app_key)) { throw new BusinessException('验签错误'); } diff --git a/app/Request/RefundQueryRequest.php b/app/Request/RefundQueryRequest.php new file mode 100644 index 0000000..6bcf8ae --- /dev/null +++ b/app/Request/RefundQueryRequest.php @@ -0,0 +1,13 @@ +merchant_id = $app->merchant_id; $order->app_id = $app->app_id; - $order->order_no = $this->generateOrderNo(); - $order->out_order_no = $params['out_order_no']; - $order->order_name = $params['order_name']; - $order->amount = $params['amount']; - $order->currency = $params['currency']; - $order->redirect_url = $params['redirect_url']; - $order->notify_url = $params['notify_url']; - $order->direct_pay = $params['direct_pay'] ?? 0; - $order->show_pc_pay_url = $params['show_pc_pay_url'] ?? 0; - $order->o_number = $params['o_number'] ?? ''; - $order->pos_no = $params['pos_no'] ?? ''; - $order->status = Order::STATUS_PREPARE; - $order->save(); + $order->out_order_no = $params['outTradeNo'] ?: ''; + $order->amount = $params['payAmount'] ?: 0; + $order->notify_url = $params['notifyUrl'] ?: ''; + $order->order_info = json_encode($params['orderInfo'] ?: [], JSON_UNESCAPED_UNICODE); return $order; } + public function handlePayResult(Result $result, Order $order) { + if ($result->get('payState') === '00') { + $order->status = Order::STATUS_PAYED; + $order->fee = $result->get('procedureFee', 0); + $order->pay_order_no = $result->get('transactionNo', ''); + $order->channel_order_no = $result->get('channelOrder', ''); + $order->payed_at = date('Y-m-d H:i:s'); + $order->save(); + } elseif ($result->get('payState') === '01') { + $order->status = Order::STATUS_FAILED; + $order->error_code = '01'; + $order->error_msg = '支付失败'; + $order->payed_at = date('Y-m-d H:i:s'); + $order->save(); + } + } + private function generateOrderNo() { $now = time(); $key = RedisKey::getGenerateOrderNoKey($now); @@ -49,139 +49,4 @@ class PaymentService extends AbstractService $incrId = str_pad($incrId, $padLength, '0', STR_PAD_LEFT); return date('YmdHis', $now) . $incrId; } - - public function updateOrderResult(Order $order, array $result) - { - if ($result['is_success']) { - $order->pay_order_no = $result['pay_order_no']; - $order->pay_url = $result['pay_url']; - $order->status = Order::STATUS_WAIT_PAY; - } else { - $order->error_code = $result['error_code']; - $order->error_msg = $result['error_msg']; - $order->status = Order::STATUS_FAILED; - } - $order->save(); - } - - public function jsapiPay(App $app, array $params) { - $order = Order::where('app_id', $app->app_id)->where('out_order_no', $params['out_order_no'])->first(); - if ($order) { - throw new BusinessException('订单重复'); - } - $order = $this->createOrder($app, $params); - $result = Api::getExchangeRate('AUD', 'CNY'); - if (!$result->isSuccess()) { - throw new BusinessException('获取汇率接口失败'); - } - $rate = $result->get('rate'); - $result = Api::makeJSAPIOrder( - $order->order_name, - $order->order_no, - 'AUD', - intval($order->amount / $rate), - 'http://146.70.113.165:9501/payment/notify', - 'http://146.70.113.165:9501/payment/page?order_no=' . $order->order_no - ); - $this->updateOrderResult($order, $result->toArray()); - return $order; - } - - public function notify($params) { - Log::info('notify:', $params, 'omipay'); - if (!Signer::verify($params)) { - return 'SIGN FAIL'; - } - if ($params['return_code'] != 'SUCCESS') { - return 'STATUS FAIL'; - } - $params = [ - 'order_no' => $params['out_order_no'], - 'cny_amount' => $params['cny_amount'], - 'exchange_rage' => $params['exchange_rage'], - ]; - return $this->handleNotify($params); - } - - public function efpsNotify($params) { - Log::info('notify:', $params, 'omipay'); - if (!Signer::verify($params)) { - return 'SIGN FAIL'; - } - if ($params['return_code'] != 'SUCCESS') { - return 'STATUS FAIL'; - } - $params = [ - 'order_no' => $params['outTradeNo'], - - ]; - return $this->handleNotify($params); - } - - private function handleNotify($params) { - $order = Order::where('order_no', $params['order_no'] ?: '') - ->where('status', Order::STATUS_WAIT_PAY) - ->first(); - if (!$order) { - return 'ORDER FAIL'; - } - $app = App::where('app_id', $order->app_id)->first(); - $order->status = Order::STATUS_PAYED; - $order->payed_at = date('Y-m-d H:i:s'); - $order->exchange_rate = $params['exchange_rate'] ?: 1; - $order->cny_amount = $params['cny_amount'] ?: $order->amount; - if (!$order->save()) { - return 'NOTIFY FAIL'; - } - - Log::info('notifyToOut url:' . $order->notify_url, [], 'omipay'); - $result = Notification::post($order->notify_url, $this->buildNotifyParams($order, $app)); - Log::info('notifyToOut response:' . $result, [], 'omipay'); - if ($result != 'SUCCESS') { - return 'NOTIFY FAIL'; - } - return 'SUCCESS'; - } - - protected function buildNotifyParams(Order $order, App $app) { - $params = [ - 'app_id' => $order->app_id, - 'nonce_str' => StringHelper::getRandomString(32), - 'timestamp' => time(), - ]; - $bizData = [ - 'code' => 'SUCCESS', - 'order_no' => $order->order_no, - 'out_order_no' => $order->out_order_no, - 'currency' => $order->currency, - 'total_amount' => $order->amount, - 'order_time' => date('YmdHis', $order->created_at->timestamp), - 'pay_time' => date('YmdHis', strtotime($order->payed_at)), - 'exchange_rate' => $order->exchange_rate, - 'cny_amount' => $order->cny_amount, - ]; - $params['data'] = json_encode($bizData); - $params['sign'] = PlatformSigner::sign($params, $app->app_key); - - Log::info('notifyToOut params:', $params, 'omipay'); - - return $params; - } - - public function unifiedPay(App $app, array $params) { - $order = Order::where('app_id', $app->app_id)->where('out_order_no', $params['out_order_no'])->first(); - if ($order) { - throw new BusinessException('订单重复'); - } - $order = $this->createOrder($app, $params); - $result = \App\Helper\Efps\Api::unifiedPayment( - $order->order_no, - $order->order_name, - $order->amount, - 'http://146.70.113.165:9501/payment/notify', - 'http://146.70.113.165:9501/payment/page?order_no=' . $order->order_no - ); - $this->updateOrderResult($order, $result->toArray()); - return $order; - } } diff --git a/config/routes.php b/config/routes.php index ce01004..0ce107b 100644 --- a/config/routes.php +++ b/config/routes.php @@ -3,7 +3,6 @@ declare(strict_types=1); use App\Controller\Payment\NotifyController; -use App\Controller\Payment\PageController; use App\Controller\Payment\PayController; use Hyperf\HttpServer\Router\Router; @@ -12,13 +11,18 @@ Router::get('/favicon.ico', function () { }); Router::addGroup('/payment',function () { - Router::post('/jsapi', [PayController::class, 'jsapi']); Router::post('/register', [PayController::class, 'register']); + Router::post('/refund', [PayController::class, 'refund']); Router::post('/bind-card', [PayController::class, 'bindCard']); Router::post('/bind-card-confirm', [PayController::class, 'bindCardConfirm']); Router::post('/un-bind-card', [PayController::class, 'unBindCard']); Router::post('/protocol-pay-pre-request', [PayController::class, 'protocolPayPreRequest']); Router::post('/protocol-pay-confirm', [PayController::class, 'protocolPayConfirm']); - Router::get('/page', [PageController::class, 'index']); - Router::addRoute(['GET', 'POST'], '/notify', [NotifyController::class, 'index']); + Router::post('/refund-query', [PayController::class, 'refundQuery']); +}, ['middleware' => [\App\Middleware\RequestLogMiddleware::class]]); + +Router::addGroup('/payment',function () { + Router::addRoute(['GET', 'POST'], '/notify', [NotifyController::class, 'payment']); + Router::addRoute(['GET', 'POST'], '/notify', [NotifyController::class, 'refund']); }); + diff --git a/payment.sql b/payment.sql new file mode 100644 index 0000000..3091bd4 --- /dev/null +++ b/payment.sql @@ -0,0 +1,86 @@ +CREATE TABLE `request_logs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `request_id` varchar(22) not null COMMENT '请求ID', + `app_id` varchar(16) NOT NULL COMMENT '应用ID', + `request_uri` varchar(255) not null COMMENT '请求URI', + `request_data` text default NULL COMMENT '请求内容', + `response_data` text default NULL COMMENT '响应内容', + `request_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `response_time` datetime default NULL, + `third_request_data` text default NULL COMMENT '第三方请求内容', + `third_response_data` text default NULL COMMENT '第三方响应内容', + `third_request_time` datetime default NULL, + `third_response_time` datetime default NULL, + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY `idx_appid` (`app_id`) USING BTREE, + KEY `idx_createdat` (`created_at`) USING BTREE, + UNIQUE KEY `udx_requestid` (`request_id`) USING BTREE, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; + +CREATE TABLE `users` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `request_id` varchar(22) not null COMMENT '请求ID', + `app_id` varchar(16) NOT NULL COMMENT '应用ID', + `member_id` varchar(20) not null default '', + `merId` varchar(50) not null default '', + `card_no` varchar(32) not null default '', + `mobile` varchar(15) not null default '', + `card_valid_to` varchar(10) default NULL COMMENT '身份证有效期', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY `idx_appid` (`app_id`) USING BTREE, + KEY `idx_memberid` (`member_id`) USING BTREE, + KEY `idx_createdat` (`created_at`) USING BTREE, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; + +CREATE TABLE `orders` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `app_id` varchar(16) NOT NULL COMMENT '应用ID', + `order_info` text default null COMMENT '商品信息', + `out_order_no` varchar(32) NOT NULL COMMENT '外部订单号', + `pay_order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '支付订单号', + `channel_order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '渠道上游订单号', + `amount` int NOT NULL DEFAULT '0' COMMENT '支付金额', + `fee` int NOT NULL DEFAULT '0' COMMENT '手续费', + `redirect_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '跳转地址', + `notify_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '通知地址', + `pay_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '支付地址', + `token` varchar(32) NOT NULL DEFAULT '', + `protocol` varchar(16) NOT NULL DEFAULT '', + `payed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '支付时间', + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态', + `error_code` varchar(32) NOT NULL DEFAULT '' COMMENT '错误码', + `error_msg` varchar(255) NOT NULL DEFAULT '' COMMENT '错误信息', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY `idx_outorderno` (`out_order_no`) USING BTREE, + KEY `idx_token` (`token`) USING BTREE, + KEY `idx_protocol` (`protocol`) USING BTREE, + KEY `idx_createdat` (`created_at`) USING BTREE, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='订单表'; + +CREATE TABLE `refund_orders` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `app_id` varchar(16) NOT NULL COMMENT '应用ID', + `out_order_no` varchar(32) NOT NULL COMMENT '外部订单号', + `out_refund_order_no` varchar(32) NOT NULL COMMENT '外部退款订单号', + `order_amount` int NOT NULL DEFAULT '0' COMMENT '订单金额', + `refund_amount` int NOT NULL DEFAULT '0' COMMENT '退款金额', + `fee` int NOT NULL DEFAULT '0' COMMENT '手续费', + `notify_url` varchar(1024) NOT NULL DEFAULT '' COMMENT '通知地址', + `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', + `refunded_at` datetime default NULL COMMENT '退款时间', + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态', + `error_code` varchar(32) NOT NULL DEFAULT '' COMMENT '错误码', + `error_msg` varchar(255) NOT NULL DEFAULT '' COMMENT '错误信息', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY `idx_refundorderno` (`refund_order_no`) USING BTREE, + KEY `idx_createdat` (`created_at`) USING BTREE, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='退款订单表'; \ No newline at end of file diff --git a/test.php b/test.php new file mode 100644 index 0000000..68ccd90 --- /dev/null +++ b/test.php @@ -0,0 +1,527 @@ + '562265003122220', + 'customer_code' => '562276004021027', + 'notify_url' => 'http://www.baidu.com', + 'return_url' => 'http://www.baidu.com' + ); + + + public function check() { + if (!$this->config['customer_code'] ) { + E("支付设置有误!"); + } + return true; + } + + // 测试主扫 + public function buildRequestForm() { + $orderNo = "123456".date('YmdHis'); + + echo 'orderNo: '.$orderNo; + echo PHP_EOL; + $client_ip = "127.0.0.1"; + if (getenv('HTTP_CLIENT_IP')) { + $client_ip = getenv('HTTP_CLIENT_IP'); + } elseif (getenv('HTTP_X_FORWARDED_FOR')) { + $client_ip = getenv('HTTP_X_FORWARDED_FOR'); + } elseif (getenv('REMOTE_ADDR')) { + $client_ip = getenv('REMOTE_ADDR'); + } else { + $client_ip = $_SERVER['REMOTE_ADDR']; + } + + $orderInfo=array(); + $orderInfo['Id'] = $orderNo; + $orderInfo['businessType'] = '130001'; + $orderInfo['goodsList'] = array(array('name'=>'pay','number'=>'one','amount'=>1)); + + $param = array( + 'outTradeNo' => $orderNo, + 'customerCode' => $this->config['customer_code'], + 'clientIp' => $client_ip, + 'orderInfo' => $orderInfo, + 'payMethod' => 7, + 'payAmount' => 10, + 'payCurrency' => 'CNY', + 'channelType' =>'02', + 'notifyUrl' =>$this->config['notify_url'], + 'redirectUrl' =>$this->config['return_url'], + 'transactionStartTime' =>date('YmdHis'), + 'nonceStr' => 'pay'.rand(100,999), + 'version' => '3.0' + ); + $sign = $this->sign(json_encode($param)); + + echo 'request: '.json_encode($param); + echo PHP_EOL .'sign: '.$sign; + + $request = $this->http_post_json($this->gateway,json_encode($param),$sign); + if($request && $request[0] == 200){ + echo PHP_EOL . 'response: '; + return $request[1]; + + }else{ + print_r($request); + exit; + } + exit; + } + + + // 统一下单 + public function buildRequestForm1() { + $orderNo = "123456".date('YmdHis'); + + echo 'orderNo: '.$orderNo; + echo PHP_EOL; + $client_ip = "127.0.0.1"; + if (getenv('HTTP_CLIENT_IP')) { + $client_ip = getenv('HTTP_CLIENT_IP'); + } elseif (getenv('HTTP_X_FORWARDED_FOR')) { + $client_ip = getenv('HTTP_X_FORWARDED_FOR'); + } elseif (getenv('REMOTE_ADDR')) { + $client_ip = getenv('REMOTE_ADDR'); + } else { + $client_ip = $_SERVER['REMOTE_ADDR']; + } + + $orderInfo = array(); + $orderInfo['Id'] = $orderNo; + $orderInfo['businessType'] = '130001'; + $orderInfo['goodsList'] = array(array('name'=>'pay','number'=>'one','amount'=>1)); + + $param = array( + 'outTradeNo' => $orderNo, + 'customerCode' => $this->config['customer_code'], + 'clientIp' => $client_ip, + 'orderInfo' => $orderInfo, + 'payCurrency' => 'CNY', + + // 'payMethod' => 7, + + 'payAmount' => 10, + 'payCurrency' => 'CNY', + // 'channelType' =>'02', + 'notifyUrl' =>$this->config['notify_url'], + 'redirectUrl' =>$this->config['return_url'], + 'transactionStartTime' =>date('YmdHis'), + 'nonceStr' => 'pay'.rand(100,999), + 'version' => '3.0' + ); + $sign = $this->sign(json_encode($param)); + + echo 'request: '.json_encode($param); + echo PHP_EOL .'sign: '.$sign; + + $request = $this->http_post_json($this->gateway1, json_encode($param),$sign); + if($request && $request[0] == 200){ + echo PHP_EOL . 'response: '; + return $request[1]; + + }else{ + print_r($request); + exit; + } + exit; + } + + + // 统一下单 + public function buildRequestForm2() { + $orderNo = "123456".date('YmdHis'); + + echo 'orderNo: '.$orderNo; + echo PHP_EOL; + $client_ip = "127.0.0.1"; + if (getenv('HTTP_CLIENT_IP')) { + $client_ip = getenv('HTTP_CLIENT_IP'); + } elseif (getenv('HTTP_X_FORWARDED_FOR')) { + $client_ip = getenv('HTTP_X_FORWARDED_FOR'); + } elseif (getenv('REMOTE_ADDR')) { + $client_ip = getenv('REMOTE_ADDR'); + } else { + $client_ip = $_SERVER['REMOTE_ADDR']; + } + + $orderInfo = array(); + $orderInfo['Id'] = $orderNo; + $orderInfo['businessType'] = '130001'; + $orderInfo['goodsList'] = array(array('name'=>'pay','number'=>'one','amount'=>1)); + + $param = array( + 'outTradeNo' => $orderNo, + 'customerCode' => $this->config['customer_code'], + 'clientIp' => $client_ip, + 'orderInfo' => $orderInfo, + 'payCurrency' => 'CNY', + 'payAmount' => 10, + 'payCurrency' => 'CNY', + 'bankCardType' =>'debit', + 'notifyUrl' =>$this->config['notify_url'], + 'frontUrl' =>$this->config['return_url'], + 'transactionStartTime' =>date('YmdHis'), + 'nonceStr' => 'pay'.rand(100,999), + 'version' => '3.0' + ); + $sign = $this->sign(json_encode($param)); + + echo 'request: '.json_encode($param); + echo PHP_EOL .'sign: '.$sign; + + $request = $this->http_post_json($this->gateway2, json_encode($param),$sign); + if($request && $request[0] == 200){ + echo PHP_EOL . 'response: '; + return $request[1]; + + }else{ + print_r($request); + exit; + } + exit; + } + + //测试单笔提现 + public function withDraw() { + $orderNo = "tx123".date('YmdHis'); + + echo '订单号:'.$orderNo; + echo '
'; + $param = array( + 'outTradeNo' => $orderNo, + 'customerCode' => $this->config['customer_code'], + 'amount' => 10, + 'bankUserName' =>$this->public_encrypt('张三'), + 'bankCardNo' => $this->public_encrypt('6214858888883338'), + 'bankName' => '招商银行', + 'bankAccountType' =>'2', + 'payCurrency' => 'CNY', + 'notifyUrl' =>$this->config['notify_url'], + 'nonceStr' => 'pay'.rand(100,999), + ); + $sign = $this->sign(json_encode($param)); + echo 'request: '.json_encode($param); + echo '
sign: '.$sign; + $request = $this->http_post_json($this->withdrawalToCard,json_encode($param),$sign); + if($request && $request[0] == 200){ + echo '
'.'response: '; + return $request[1]; + }else{ + print_r($request); + exit; + } + exit; + + } + + public function bindCard() { + $orderNo = "tx123".date('YmdHis'); + + echo '订单号:'.$orderNo; + echo '
'; + $param = array( + 'version' => '3.0', + 'mchtOrderNo' => time() . rand(1000, 9999), + 'customerCode' => $this->config['customer_code'], + 'memberId' => $this->config['customer_code'], + 'userName' => $this->public_encrypt('zs'), + 'phoneNum' => $this->public_encrypt('18760419185'), + 'bankCardNo' => $this->public_encrypt('6214835911385365'), + 'bankCardType' => 'debit', + 'certificatesType' => '01', + 'certificatesNo' => $this->public_encrypt('350824199001105476'), + 'nonceStr' => 'pay'.rand(100,999), + ); + $sign = $this->sign(json_encode($param)); + echo 'request: '.json_encode($param); + echo '
sign: '.$sign; + $request = $this->http_post_json($this->bindCardUrl,json_encode($param),$sign); + if($request && $request[0] == 200){ + echo '
'.'response: '; + return $request[1]; + }else{ + print_r($request); + exit; + } + exit; + + } + + + //进件 + //发起进件 + public function apply(){ + $paper = '{"certificateName":"李四","contactPhone":"13531231222","email":"test1@test.cn","lawyerCertNo":"430481198104234557","lawyerCertType":"0","merchantType":"3","openBank":"中国银行","openingLicenseAccountPhoto":"https://www.epaylinks.cn/www/wimages/epl_logo.png","settleAccount":"李四","settleAccountNo":"6214830201234567","settleAccountType":"2","settleTarget":"2"}'; + $business = array( + array( + "businessCode"=>"WITHDRAW_TO_SETTMENT_DEBIT", + "creditcardsEnabled"=>0, + "refundEnabled"=>1, + "refundFeePer"=>0, + "refundFeeRate"=>0, + "settleCycle"=>"D+0", + "stage"=>array( + array( + "amountFrom"=>0, + "feePer"=>50 + ) + ) + ) + ); + $param =array( + 'acqSpId' => $this->config['customer_code'], + 'merchantName' => "测试商户20211202", + 'acceptOrder' => 0, + 'openAccount' => 1, + 'paper' => $paper, + 'business' =>$business + ); + $sign = $this->sign(json_encode($param)); + + + echo json_encode($param); + + $res = $this->http_post_json($this->apply_url,json_encode($param),$sign); + var_dump($res); + die; + } + + + public function generateSign($params) { + return $this->sign($this->getSignContent($params)); + } + + public function rsaSign($params) { + return $this->sign($this->getSignContent($params)); + } + + protected function getSignContent($params) { + ksort($params); + + $stringToBeSigned = ""; + $i = 0; + foreach ($params as $k => $v) { + + if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) { + // 转换成目标字符集 + $v = $this->characet($v, $this->charset); + + if ($i == 0) { + $stringToBeSigned .= "$k" . "=" . "$v"; + } else { + $stringToBeSigned .= "&" . "$k" . "=" . "$v"; + } + + $i++; + } + } + + unset ($k, $v); + + return $stringToBeSigned; + } + + protected function sign($data) { + + $certs = array(); + openssl_pkcs12_read(file_get_contents($this->rsaPrivateKeyFilePath), $certs, $this->password); //其中password为你的证书密码 + + ($certs) or die('请检查RSA私钥配置'); + + openssl_sign($data, $sign, $certs['pkey'],OPENSSL_ALGO_SHA256); + + $sign = base64_encode($sign); + return $sign; + } + + /** + * 校验$value是否非空 + * if not set ,return true; + * if is null , return true; + **/ + protected function checkEmpty($value) { + if (!isset($value)) + return true; + if ($value === null) + return true; + if (trim($value) === "") + return true; + + return false; + } + + + public function rsaCheckV2($params, $rsaPublicKeyFilePath,$sign) { + //$sign = $params['sign']; + //$params['sign'] = null; + + return $this->verify($params, $sign, $rsaPublicKeyFilePath); + } + + //使用易票联公钥验签 //返回的验签字段有中文需要加JSON_UNESCAPED_UNICODE才能验签通过 + //$data2 = json_encode($data, JSON_UNESCAPED_UNICODE); + function verify($data, $sign, $rsaPublicKeyFilePath) { + + //读取公钥文件 + $pubKey = file_get_contents($this->publicKeyFilePath); + + $res = openssl_get_publickey($pubKey); + + ($res) or die('RSA公钥错误。请检查公钥文件格式是否正确'); + //调用openssl内置方法验签,返回bool值 + + $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256); + + if(!$this->checkEmpty($this->publicKeyFilePath)) { + //释放资源 + openssl_free_key($res); + } + + return $result; + } + + //使用易票联公钥加密 + function public_encrypt($data) + { + return $data; + //读取公钥文件 + $pubKey = file_get_contents($this->publicKeyFilePath); + + $res = openssl_get_publickey($pubKey); + + ($res) or die('RSA公钥错误。请检查公钥文件格式是否正确'); + + $crypttext = ""; + + openssl_public_encrypt($data,$crypttext, $res); + + + if(!$this->checkEmpty($this->publicKeyFilePath)) { + //释放资源 + openssl_free_key($res); + } + + return(base64_encode($crypttext)); + + } + + /** + * 转换字符集编码 + * @param $data + * @param $targetCharset + * @return string + */ + function characet($data, $targetCharset) { + + + if (!empty($data)) { + $fileType = $this->charset; + if (strcasecmp($fileType, $targetCharset) != 0) { + + $data = mb_convert_encoding($data, $targetCharset); + // $data = iconv($fileType, $targetCharset.'//IGNORE', $data); + } + } + + + return $data; + } + + protected function getParam($para) { + $arg = ""; + while (list ($key, $val) = each($para)) { + $arg.=$key . "=" . $val . "&"; + } + //去掉最后一个&字符 + $arg = substr($arg, 0, -1); + return $arg; + } + + /** + * 获取远程服务器ATN结果,验证返回URL + * @param $notify_id + * @return + * 验证结果集: + * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 + * true 返回正确信息 + * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟 + */ + protected function getResponse2($Params) { + $veryfy_url = $this->gateway . "?" . $Params; + $responseTxt = $this->fsockOpen($veryfy_url); + return $responseTxt; + } + + protected function http_post_json($url, $jsonStr,$sign) + { + $ch = curl_init(); + $headers = array( + 'Content-Type: application/json; charset=utf-8', + 'Content-Length: ' . strlen($jsonStr), + 'x-efps-sign-no:'.$this->sign_no, + 'x-efps-sign-type:SHA256withRSA', + 'x-efps-sign:'.$sign, + 'x-efps-timestamp:'.date('YmdHis'), + ); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过检查 + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 跳过检查 + //curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $response = curl_exec($ch); + + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + return array($httpCode, $response); + } + + +} + + +$efalipay = new Efalipay(); +echo $efalipay->bindCard();