| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- <?php
- namespace common\components\payment;
- use addons\JoinPay\common\models\JoinpayFastpayOrder;
- use addons\JoinPay\common\models\JoinPayUserBankcard;
- use Yii;
- use Exception;
- use common\components\payment\joinpay\util\{RandomUtil,RequestUtil,AESUtil,ObjectUtil,SignUtil};
- use common\components\payment\joinpay\entity\{Request,SecretKey,Response};
- use common\components\payment\joinpay\exceptions\SDKException;
- use common\models\common\UserBankCard;
- use common\helpers\Url;
- use common\enums\PayTypeEnum;
- use common\enums\StatusEnum;
- use common\models\common\PaymentTrade;
- use common\models\common\PaymentTradeOrder;
- /**
- * 汇聚支付-快捷协议支付
- *
- * Class JoinPayFastPay
- * @package common\components\payment
- */
- class JoinPayFastPay
- {
- public $config = [];
- public $url = 'https://api.joinpay.com/fastpay';
- public $refund_url = 'https://api.joinpay.com/refund';
- public function __construct($config)
- {
- if(stripos($config['joinpay_fast_pay_public_key'],'BEGIN PUBLIC KEY') === false){
- $config['joinpay_fast_pay_public_key'] = "-----BEGIN PUBLIC KEY-----\n".
- wordwrap($config['joinpay_fast_pay_public_key'], 64, "\n", true).
- "\n-----END PUBLIC KEY-----";
- }
- $this->config = $config;
- }
-
- public function getError()
- {
- // 文件services/common/PayService.php $refund_class->getError() 无此方法飘红
- return '';
- }
- /**
- * 快捷支付-发送签约短信
- * @desc 签约第一步
- * @param int $card_id UserBankCard->id
- * @throws Exception
- * @return bool
- */
- public function sendSignSms($card_id)
- {
- $card = JoinPayUserBankcard::findOne(['card_id' => $card_id]);
- if(!$card){
- throw new Exception('银行卡信息不存在');
- }
- $order_id = JoinpayFastPayOrder::createOrder($card->mall_id,$card->user_id,$card_id,0.01);
- $orderInfo = JoinpayFastPayOrder::findOne(['id' => $order_id]);
- // $orderInfo = [];
- $secKey = RandomUtil::randomStr(16);
- $request_data = $this->getData($orderInfo,$card->toArray(),$secKey);
- $request = $this->getRequestObject('fastPay.agreement.signSms',$request_data,$secKey);
- $secretKey = $this->getSecretKeyObject();
- try {
- $response = RequestUtil::doRequest($this->url, $request, $secretKey);
- if ($response->isSuccess()) {
- //受理成功
- $dataArr = json_decode($response->getData(), true);
- if ($dataArr["order_status"] == 'P1000' || $dataArr['order_status'] == 'P3000') {
- //成功
- return true;
- } else {
- \Yii::error('FAIL OR PROCESSING OR UNKNOWN, Response =' . json_encode($dataArr, JSON_UNESCAPED_UNICODE));
- throw new Exception($dataArr['err_msg']);
- }
- } else {
- \Yii::error('受理失败, Response =' . json_encode($response, JSON_UNESCAPED_UNICODE));
- throw new Exception('受理失败');
- }
- }catch (SDKException $e){
- throw new Exception($e->getMsg());
- } catch (Exception $e) {
- throw new Exception($e->getMessage().'请稍后重新发起交易,或联系汇聚客服');
- }
- }
- /**
- * 快捷支付-签约
- * @desc 签约第二步 END
- * @param $card_info
- * @param $sms_code
- * @throws Exception
- * @return bool
- */
- public function sign($card_id,$sms_code)
- {
- $card_info = UserBankCard::findOne([
- 'id' => $card_id,
- 'status' => StatusEnum::ENABLED,
- ]);
- /** @var JoinPayUserBankcard $card_info */
- $joinpay_card_info = JoinPayUserBankcard::findOne([
- 'card_id' => $card_id,
- 'status' => StatusEnum::ENABLED,
- 'is_sign' => 0,
- ]);
- if (empty($card_info) || empty($joinpay_card_info)) throw new \Exception('银行卡不存在');
- /** @var JoinpayFastPayOrder $orderInfo */
- $orderInfo = JoinpayFastpayOrder::find()
- ->where(['bank_card_id' => $card_info->id,'user_id'=>$card_info->user_id])
- ->orderBy('id desc')
- ->one();
- if (empty($orderInfo)) throw new \Exception('签约订单未找到');
- $secKey = RandomUtil::randomStr(16);
- $request_data = [
- 'mch_no_trade' => '',
- 'mch_order_no' => $orderInfo->order_no,
- 'sms_code' => $sms_code
- ];
- $request = $this->getRequestObject("fastPay.agreement.smsSign",$request_data,$secKey);
- $secretKey = $this->getSecretKeyObject();
- try {
- $response = RequestUtil::doRequest($this->url, $request, $secretKey);
- if ($response->isSuccess()) {
- //受理成功
- $dataArr = json_decode($response->getData(), true);
- \Yii::error(' Response data => '.json_encode($dataArr, JSON_UNESCAPED_UNICODE));
- if($dataArr["order_status"] == "P1000" || $dataArr['order_status'] == 'P3000'){
- //成功
- //返回签约ID : sign_no , bank_code,jp_order_no
- $joinpay_card_info->sign_no = $dataArr['sign_no'] ?? '';
- $joinpay_card_info->is_sign = !empty($dataArr['sign_no']) ? 1 : 0;
- $joinpay_card_info->bank_code = $dataArr['bank_code'] ?? '';
- $joinpay_card_info->updated_at = time();
- if(!$joinpay_card_info->save()){
- throw new Exception($joinpay_card_info->getErrorMessage());
- }
- $orderInfo->jp_order_no = $dataArr['jp_order_no'] ?? '';
- $orderInfo->updated_at = time();
- if(!$orderInfo->save()){
- throw new Exception($orderInfo->getErrorMessage());
- }
- return true;
- }else{
- throw new Exception($dataArr['err_msg'] ?? '操作失败');
- }
- }else{
- throw new Exception($response->getBizMsg() ?? '受理失败');
- }
- } catch (Exception $e) {
- throw $e;
- }
- }
- /**
- * 解约
- * @param $card_info
- * @return bool
- * @throws Exception
- */
- public function unsign($card_info)
- {
- $order_id = JoinpayFastPayOrder::createOrder($card_info->mall_id,$card_info->user_id,$card_info->id,0.01,0);
- $orderInfo = JoinpayFastPayOrder::findOne(['id' => $order_id]);
- $secKey = RandomUtil::randomStr(16);
- $request_data = [
- 'mch_order_no' => $orderInfo->order_no,
- 'mch_req_time' => date('Y-m-d H:i:s', $orderInfo->created_at),
- 'sign_no' => AESUtil::encryptECB($card_info->sign_no, $secKey)
- //'bank_card_no' => $card_info->bank_card_no,
- ];
- $request = $this->getRequestObject('fastPay.agreement.unSign',$request_data,$secKey);
- $secretKey = $this->getSecretKeyObject();
- try {
- $response = RequestUtil::doRequest($this->url, $request, $secretKey);
- if ($response->isSuccess()) {
- //受理成功
- $dataArr = json_decode($response->getData(), true);
- \Yii::error(' Response data='.json_encode($dataArr, JSON_UNESCAPED_UNICODE));
- if($dataArr["order_status"] == "P1000" || $dataArr['order_status'] == 'P3000'){
- //成功
- $card_info->sign_no = '';
- $card_info->is_sign = 0;
- $card_info->updated_at = time();
- if(!$card_info->save()){
- throw new Exception($card_info->getErrorMessage());
- }
- $orderInfo->jp_order_no = $dataArr['jp_order_no'] ?? '';
- $orderInfo->updated_at = time();
- if(!$orderInfo->save()){
- throw new Exception($orderInfo->getErrorMessage());
- }
- return true;
- }else{
- throw new Exception('FAIL OR PROCESSING OR UNKNOWN');
- }
- }else{
- throw new Exception($response->getBizMsg() ?? '受理失败');
- }
- } catch (Exception $e) {
- throw $e;
- }
- }
- /**
- * 支付短信接口
- * @param $trade_id 支付流水id
- * @param $card_id 银行卡记录id
- * @throws Exception
- * @return bool
- */
- public function sendPaySms(int $trade_id, $card_info,$mall_id)
- {
- // $card_info = JoinPayUserBankcard::find()->where(['id' => $card_id])->asArray()->one();
- $order_info = PaymentTrade::find()
- ->select('*,out_trade_no as order_no')
- ->where(['id' => $trade_id])
- ->asArray()
- ->one();
- $secKey = RandomUtil::randomStr(16);
- $request_data = $this->getData($order_info,$card_info,$secKey,true);
- $request = $this->getRequestObject("fastPay.agreement.paySms",$request_data,$secKey);
- $secretKey = $this->getSecretKeyObject();
- try {
- $response = RequestUtil::doRequest($this->url, $request, $secretKey);
- if ($response->isSuccess()) {//受理成功
- $dataArr = json_decode($response->getData(), true);
- if($dataArr["order_status"] == "P1000" || $dataArr['order_status'] == 'P3000'){//订单交易成功
- return true;
- }else{
- throw new Exception('FAIL OR PROCESSING OR UNKNOWN');
- }
- }else{
- throw new Exception($response->getBizMsg() ?? '受理失败');
- }
- } catch (Exception $e) {
- throw $e;
- }
- }
- /**
- * 订单支付
- * @param array $order
- * [
- * 'order_no' => '交易流水号'
- * 'sms_code' => '短信验证码'
- * 'created_at' => '交易创建时间戳'
- * ]
- *
- * @throws Exception
- * @return bool
- */
- public function pay($order)
- {
- $secKey = RandomUtil::randomStr(16);
- $request = new Request();
- $request_data = [
- 'mch_no_trade' => '',
- 'mch_order_no' => $order['order_no'],
- 'sms_code' => $order['sms_code'],
- 'mch_req_time' => date('Y-m-d H:i:s', $order['created_at'])
- ];
- $request->setMethod("fastPay.agreement.smsPay");
- $request->setVersion("1.0");
- $request->setMchNo($this->config['joinpay_merchant_no']);
- $request->setSignType("2");
- $request->setRandStr(RandomUtil::randomStr(32));
- $request->setData($request_data);
- $request->setSecKey($secKey);//rsa有效
- $secretKey = $this->getSecretKeyObject();
- try {
- $response = RequestUtil::doRequest($this->url, $request, $secretKey);
- \Yii::error(' fastpay response data => ' . $response->getData());\Yii::getLogger()->flush(true);
- if ($response->isSuccess()) {
- //受理成功
- $dataArr = json_decode($response->getData(), true);
- if($dataArr["order_status"] == "P1000"){
- //订单交易成功
- $trade = PaymentTrade::findOne(['out_trade_no' => $order['order_no']]);
- $trade_order = PaymentTradeOrder::findOne(['trade_id' => $trade->id]);
- // 返回这两个数据给前端展示支付成功页面使用
- return ['amount' => $trade_order->total_fee, 'order_no' => $trade_order->order_no];
- }else{
- throw new Exception('支付异常,请查询是否扣款');
- }
- }else{
- throw new Exception($response->getBizMsg() ?? '受理失败');
- }
- } catch (Exception $e) {
- throw $e;
- }
- }
- /**
- * 组装汇聚请求参数data
- * @param $orderInfo
- * @param $userBankInfo
- * @param $secKey
- * @param bool $is_pay
- * @return array
- */
- private function getData($orderInfo,$userBankInfo,$secKey,$is_pay = false)
- {
- $data = [];
- $data["mch_no_trade"] = "";
- $data["mch_order_no"] = $orderInfo['order_no'];
- $data["order_amount"] = sprintf("%.2f", $orderInfo['pay_fee']);
- //$data["mch_req_time"] = date('Y-m-d H:i:s', time());
- $data["mch_req_time"] = date('Y-m-d H:i:s', $orderInfo['created_at']);
- $data["order_desc"] = $orderInfo['title'];
- $data["id_type"] = "1";
- // $data["callback_url"] = Url::toFront( ['notify/' . PayTypeEnum::action(PayTypeEnum::JOINPAY_FAST_PAY)],TRUE);
- $data["callback_url"] = \Yii::$app->request->hostInfo . '/join-pay/notify/joinpay-fastpay';
- $data["callback_param"] = json_encode(['mall_id' => $userBankInfo['mall_id']]);// 公用回传参数;
- if(!$is_pay){
- $data["payer_name"] = AESUtil::encryptECB($userBankInfo['payer_name'], $secKey);//加密
- $data["id_no"] = AESUtil::encryptECB($userBankInfo['id_no'], $secKey);//加密
- $data["bank_card_no"] = AESUtil::encryptECB($userBankInfo['bank_card_no'], $secKey);//加密
- $data["mobile_no"] = AESUtil::encryptECB($userBankInfo['mobile_no'], $secKey);//加密
- }else{
- //支付
- $data['bank_card_no'] = AESUtil::encryptECB($userBankInfo['bank_card_no'], $secKey);//加密
- }
- //信用卡 需要加入:expire_date,cvv
- //https://www.joinpay.com/open-platform/pages/document.html?apiName=%E5%BF%AB%E6%8D%B7%E5%8D%8F%E8%AE%AE%E6%94%AF%E4%BB%98&id=9
- return $data;
- }
- /**
- * 获取Request 对象
- * @param $method
- * @param $request_data
- * @param $secKey
- * @return Request
- */
- private function getRequestObject($method,$request_data,$secKey)
- {
- $request = new Request();
- $request->setMethod($method);
- $request->setVersion('1.0');
- $request->setMchNo($this->config['joinpay_merchant_no']);
- $request->setSignType('2');
- $request->setRandStr(RandomUtil::randomStr(32));
- $request->setData($request_data);
- $request->setSecKey($secKey);//rsa有效
- return $request;
- }
- /**
- * 获取SecretKey Object
- * @return SecretKey
- */
- private function getSecretKeyObject()
- {
- $secretKey = new SecretKey();
- $secretKey->setReqSignKey($this->config['joinpay_fast_pay_private_key']);//签名:使用商户私钥
- $secretKey->setRespVerifyKey($this->config['joinpay_fast_pay_public_key']);//验签:使用平台公钥
- $secretKey->setSecKeyEncryptKey($this->config['joinpay_fast_pay_public_key']);//sec_key加密:使用平台公钥
- $secretKey->setSecKeyDecryptKey($this->config['joinpay_fast_pay_private_key']);//sec_key解密:使用商户私钥
- return $secretKey;
- }
- /**
- * 回调处理
- * @return bool
- */
- public function notify()
- {
- try{
- $post = Yii::$app->request->post();
- $data = $this->checkNotifyParams($post);
- return [
- 'out_trade_no' => $data['mch_order_no'], //商户订单号
- 'trade_no' => $post['jp_order_no'], //汇聚平台流水号
- 'pay_fee' => $data['order_amount']
- ];
- }catch(Exception $e){
- \Yii::error(__CLASS__ .' method:'.__METHOD__ .' error :'.$e->getMessage());
- return false;
- }
- }
- /**
- * 回调验签
- * @param $data
- * @return bool
- * @throws Exception
- */
- public function signature_verify($data)
- {
- $respJson = json_encode($data,JSON_UNESCAPED_UNICODE);
- $response = new Response();
- try{
- ObjectUtil::fillProperties($response, $respJson);
- }catch (Exception $e){
- throw new Exception(SDKException::BIZ_ERROR, "请求完成,但响应信息转换失败: " . $e->getMessage() . ",respJson = " . $respJson);
- }
- $isOk = false;
- $secretKey = $this->getSecretKeyObject();
- try{
- $signStr = SignUtil::getSortedString($response, false);
- $isOk = SignUtil::verify($signStr, $response->getSign(), $response->getSignType(), $secretKey->getRespVerifyKey());
- }catch (Exception $e){
- throw new Exception(SDKException::BIZ_ERROR, "请求完成,但响应信息验签异常: " . $e->getMessage() . ",respJson = " . $respJson);
- }
- if($isOk !== true){
- throw new Exception(SDKException::BIZ_ERROR, "响应信息验签不通过! respJson = " . $respJson);
- }
- if($data['biz_code'] !== 'JS000000'){
- throw new Exception('error');
- }
- if($data['mch_no'] != $this->config['joinpay_merchant_no']){
- throw new Exception('商户号有误');
- }
- return true;
- }
- /**
- * 校验回调参数
- * @param $res
- * @return bool
- * @throws Exception
- */
- public function checkNotifyParams($res)
- {
- $this->signature_verify($res);
- $data = json_decode($res['data'],true);
- if($data['order_status'] !== 'P1000'){
- //P100:支付成功
- throw new Exception($data['err_msg'] ?? '交易失败');
- }
- $order_no = $data['mch_order_no'];
- $total_fee = (doubleval($data['order_amount']) * 100) . '';
- //订单号查询
- $trade = PaymentTrade::findOne(['trade_no' => $order_no]);
- if (!$trade || $trade->is_pay == 1) {
- throw new Exception('订单不存在');
- }
- $order_amount = (doubleval($trade->pay_fee) * 100) . '';
- //金额是否一致
- if (intval($total_fee) !== intval($order_amount)) {
- throw new Exception('支付金额与订单金额不一致: ' . $order_no);
- }
- return $data;
- }
- /**
- * 退款
- * @param array $info
- * [
- * 'refund_order_no' => $refund_order_no,
- * 'out_trade_no' => $out_trade_no, //trade表交易流水号
- * 'refund_amount' => 1,
- * 'refund_reason' => 1
- * 'trade_created_at' => 1661400214
- * ]
- *
- * @return bool
- * @throws
- */
- public function refund($info)
- {
- $secKey = RandomUtil::randomStr(16);
- $request_data = [
- 'refund_order_no' => $info['refund_order_no'],
- 'refund_amount' => $info['refund_amount'],
- 'refund_reason' => mb_substr($info['refund_reason'],0,60),
- 'org_mch_order_no' => $info['out_trade_no'],
- 'org_mch_req_time' => date('Y-m-d H:i:s',$info['trade_created_at']),
- 'callback_url' => \Yii::getAlias('@apiUrl').'/notify/joinpay-fast-refund'
- ];
- $request = $this->getRequestObject('fastPay.refund',$request_data,$secKey);
- $secretKey = $this->getSecretKeyObject();
- try {
- $response = RequestUtil::doRequest($this->refund_url, $request, $secretKey);
- if ($response->isSuccess()) {
- \Yii::error('joinpay fast refund res:' . $response->getData());
- //受理成功
- $dataArr = json_decode($response->getData(), true);
- if($dataArr["refund_status"] == '101'){
- //退款失败
- throw new Exception('退款失败');
- }
- return true;
- } else {
- \Yii::error('受理失败, Response =' . json_encode($response, JSON_UNESCAPED_UNICODE));
- throw new Exception($response->getBizMsg());
- }
- }catch (SDKException $e){
- throw new Exception($e->getMsg());
- } catch (Exception $e) {
- throw new Exception($e->getMessage());
- }
- }
- }
|