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.
268 lines
7.9 KiB
268 lines
7.9 KiB
<?php
|
|
/**
|
|
* Created by PhpStorm.
|
|
* User: travis
|
|
* Date: 2019/12/12
|
|
* Time: 6:20
|
|
*/
|
|
|
|
namespace api\logic;
|
|
|
|
use backend\modules\shop\models\ars\PaymentLog;
|
|
use backend\modules\shop\models\ars\WxPayConfig;
|
|
use Yii;
|
|
use EasyWeChat\Factory;
|
|
use yii\db\Exception;
|
|
use yii\helpers\Json;
|
|
use yii\httpclient\Client;
|
|
use yii\web\BadRequestHttpException;
|
|
use yii\base\BaseObject;
|
|
|
|
class WxPaymentLogic extends BaseObject
|
|
{
|
|
/*支付类型*/
|
|
const PAY_TYPE_WEB = 1;
|
|
const PAY_TYPE_MINI_PROGRAM = 2;
|
|
/*发起支付方式*/
|
|
const TRADE_TYPE_JS_API = 'JSAPI';
|
|
|
|
public $appId;
|
|
public $mchId;
|
|
public $key;
|
|
public $certPath;
|
|
public $keyPath;
|
|
public $notifyUrl;
|
|
public $tradeType;
|
|
public $payType;
|
|
public $app;
|
|
public $order;
|
|
|
|
|
|
/**
|
|
* @param $payType
|
|
* @throws BadRequestHttpException
|
|
* @throws Exception
|
|
* 微信统一下单
|
|
*/
|
|
public function wxPayment($payType)
|
|
{
|
|
$this->payType = $payType;
|
|
$unifyParams = $this->applyPaymentData();
|
|
$this->unify($unifyParams);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return array
|
|
* @throws BadRequestHttpException
|
|
* @throws Exception
|
|
* 生成支付参数
|
|
*/
|
|
private function applyPaymentData()
|
|
{
|
|
$orderId = Yii::$app->request->getBodyParam('order_id');/*int 商品id*/
|
|
$paymentAmount = Yii::$app->request->getBodyParam('payment_amount');/*int 商品id*/
|
|
$notifyUrl = Yii::$app->request->getBodyParam('notify_url');/*int 商品id*/
|
|
if (empty($orderId) || empty($paymentAmount) || empty($notifyUrl)) {
|
|
throw new BadRequestHttpException(Helper::REQUEST_BAD_PARAMS);
|
|
}
|
|
|
|
$this->tradeType = self::TRADE_TYPE_JS_API;
|
|
$this->savePaymentLog($orderId, $paymentAmount, $notifyUrl);
|
|
|
|
$params = [
|
|
'body' => '订单支付',
|
|
'out_trade_no' => $orderId,
|
|
'total_fee' => round($paymentAmount * 100),
|
|
'openid' => Yii::$app->user->identity->wx_openid,
|
|
];
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* @param string $orderId
|
|
* @param float $paymentAmount
|
|
* @param string $notifyUrl
|
|
* @throws Exception
|
|
* 保存支付信息
|
|
*/
|
|
private function savePaymentLog($orderId, $paymentAmount, $notifyUrl)
|
|
{
|
|
$paymentLog = PaymentLog::findOne(['order_id' => $this->order->order_sn]);
|
|
if (!$paymentLog) {
|
|
$paymentLog = new PaymentLog();
|
|
}
|
|
$paymentLog->order_id = $orderId;
|
|
$paymentLog->payment_amount = $paymentAmount;
|
|
$paymentLog->notify_url = $notifyUrl;
|
|
$paymentLog->type = $this->payType;
|
|
$paymentLog->status = PaymentLog::PAYMENT_STATUS_WAITING;
|
|
if (!$paymentLog->save()) {
|
|
throw new Exception(Helper::errorMessageStr($paymentLog->errors));
|
|
}
|
|
}
|
|
|
|
private function getPaymentApp()
|
|
{
|
|
$this->initObject();
|
|
$config = [
|
|
'app_id' => $this->appId,
|
|
'mch_id' => $this->mchId,
|
|
'key' => $this->key,
|
|
'cert_path' => $this->certPath,
|
|
'key_path' => $this->keyPath,
|
|
'notify_url' => $this->notifyUrl,
|
|
'trade_type' => $this->tradeType,
|
|
'sandbox' => true, // 设置为 false 或注释则关闭沙箱模式
|
|
];
|
|
$this->app = Factory::payment($config);
|
|
// 判断当前是否为沙箱模式:
|
|
$this->app->inSandbox();
|
|
}
|
|
|
|
/**
|
|
* @var WxPayConfig $wxPayConfig
|
|
*/
|
|
private function initObject()
|
|
{
|
|
$path = Yii::getAlias('@backend');
|
|
$wxPayConfig = WxPayConfig::find()->one();
|
|
// $wxConfig = WxConfig::find()->one();
|
|
// switch ($this->payType) {
|
|
// case self::PAY_TYPE_WEB:
|
|
// $this->appId = trim($wxConfig->wx_appId);
|
|
// break;
|
|
// case self::PAY_TYPE_MINI_PROGRAM:
|
|
// $this->appId = trim($wxConfig->mini_program_appId);
|
|
// break;
|
|
// }
|
|
|
|
$this->mchId = $wxPayConfig->mch_id;
|
|
$this->certPath = trim($path . $wxPayConfig->cert_path);
|
|
$this->keyPath = trim($path . $wxPayConfig->key_path);
|
|
$this->notifyUrl = Yii::$app->request->hostInfo . '/wx-payment/notify';
|
|
}
|
|
|
|
/**
|
|
* @param $unifyParams
|
|
* @return mixed
|
|
* 统一下单
|
|
*/
|
|
private function unify($unifyParams)
|
|
{
|
|
$this->getPaymentApp();
|
|
return $this->app->order->unify($unifyParams);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return array|bool
|
|
* @throws BadRequestHttpException
|
|
* @throws \yii\base\InvalidConfigException
|
|
* @throws \yii\httpclient\Exception
|
|
* 微信支付回调
|
|
*/
|
|
public function notify()
|
|
{
|
|
$notifyData = Json::decode(Json::encode(simplexml_load_string(Yii::$app->request->getRawBody(), 'SimpleXMLElement', LIBXML_NOCDATA)));
|
|
Yii::info($notifyData, "notify");
|
|
if (!$this->checkSign($notifyData)) {
|
|
throw new BadRequestHttpException(Helper::REQUEST_BAD_PARAMS);
|
|
}
|
|
$tra = Yii::$app->db->beginTransaction('SERIALIZABLE');
|
|
try {
|
|
if ($notifyData->result_code != 'SUCCESS' || $notifyData->return_code != 'SUCCESS') {
|
|
throw new BadRequestHttpException('result_code or return_code is false');
|
|
}
|
|
$paymentLog = PaymentLog::findOne(['order_id' => $notifyData->out_trade_no]);
|
|
$this->notifyUrl = Yii::$app->request->hostInfo . $paymentLog->notify_url;
|
|
$paymentLog->mch_id = $notifyData->mch_id;
|
|
$paymentLog->wx_refund_id = $notifyData->transaction_id; //交易号
|
|
$paymentLog->status = PaymentLog::PAYMENT_STATUS_SUCCESS;
|
|
if (!$paymentLog->save()) {
|
|
throw new Exception(Helper::errorMessageStr($paymentLog->errors));
|
|
}
|
|
if (!$tra->commit()) {
|
|
throw new Exception('保存数据失败');
|
|
}
|
|
/*转发回调信息*/
|
|
$this->forwardNotify($notifyData, true);
|
|
|
|
return ['return_code' => 'SUCCESS', 'return_msg' => 'OK'];//回传成功信息到微信服务器
|
|
} catch (Exception $e) {
|
|
$tra->rollBack();
|
|
$this->forwardNotify($notifyData, false);
|
|
Yii::info($e->getMessage(), 'notify');
|
|
return false;
|
|
} catch (BadRequestHttpException $e) {
|
|
Yii::info($e->getMessage(), 'notify');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $notifyData
|
|
* @param $status
|
|
* @return bool
|
|
* @throws \yii\base\InvalidConfigException
|
|
* @throws \yii\httpclient\Exception
|
|
* 转发异步回调信息
|
|
*/
|
|
private function forwardNotify($notifyData, $status)
|
|
{
|
|
$notify = [
|
|
'notify' => [
|
|
'status' => $status,
|
|
'notify' => $notifyData
|
|
]
|
|
];
|
|
$client = new Client();
|
|
$response = $client->createRequest()
|
|
->setMethod('POST')
|
|
->setUrl($this->notifyUrl)
|
|
->addHeaders(['content-type' => 'application/json'])
|
|
->setContent(Json::encode($notify))
|
|
->send();
|
|
if ($response->isOk) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $data
|
|
* @return bool
|
|
* 支付成功回调验证签名和支付金额
|
|
*/
|
|
public function checkSign($data)
|
|
{
|
|
$this->initObject();
|
|
$notifySign = $data['sign'];
|
|
unset($data['sign']);
|
|
$sign = $this->_sign($data);
|
|
if ($notifySign == $sign) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $arr
|
|
* @return string
|
|
* 微信签名方法
|
|
*/
|
|
private function _sign($arr)
|
|
{
|
|
$arr = array_filter($arr);
|
|
ksort($arr);
|
|
$arr['key'] = $this->key;
|
|
$queryString = http_build_query($arr);
|
|
$queryString = urldecode($queryString);
|
|
return strtoupper(md5($queryString));
|
|
}
|
|
|
|
|
|
}
|