Browse Source

feat: 退款功能

wechat_public_accounts
ww519441258 5 years ago
parent
commit
aad1a772b3
  1. 11
      api/config/main.php
  2. 25
      api/controllers/WxPaymentController.php
  3. 156
      api/logic/WxPaymentLogic.php
  4. 29
      backend/modules/shop/migrations/m191217_033724_add_column_payment_at_wx_payment_id_in_payment_log.php
  5. 28
      backend/modules/shop/migrations/m191217_034337_alter_column_order_id_in_payment_log_refund_log.php
  6. 12
      backend/modules/shop/models/ars/PaymentLog.php

11
api/config/main.php

@ -38,6 +38,16 @@ return [
'prefix' => function ($message) {
}
],
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
'categories' => ['refund_log'],
'logFile' => '@app/runtime/logs/refund_log.log',
'logVars' => [],
'exportInterval' => 1,
'prefix' => function ($message) {
}
],
],
],
'user' => [
@ -96,6 +106,7 @@ return [
'extraPatterns' => [
'GET web' => 'web',
'POST notify' => 'notify',
'POST refund' => 'refund',
'POST test' => 'test',
]
],

25
api/controllers/WxPaymentController.php

@ -73,4 +73,29 @@ class WxPaymentController extends CommonController
$data = Yii::$app->request->getBodyParam('notify');/*int 商品id*/
return $data;
}
/**
* @return bool
* @throws BadRequestHttpException
* @throws \yii\db\Exception
* @throws \yii\web\NotFoundHttpException
* 申请退款
*/
public function actionApplyRefund()
{
return $this->object->applyRefund();
}
/**
* @return mixed
* @throws BadRequestHttpException
* @throws \yii\db\Exception
* @throws \yii\web\NotFoundHttpException
* 退款
*/
public function actionRefund()
{
return $this->object->refund();
}
}

156
api/logic/WxPaymentLogic.php

@ -8,7 +8,9 @@
namespace api\logic;
use backend\modules\shop\models\ars\Order;
use backend\modules\shop\models\ars\PaymentLog;
use backend\modules\shop\models\ars\RefundLog;
use backend\modules\shop\models\ars\WxPayConfig;
use Yii;
use EasyWeChat\Factory;
@ -17,12 +19,21 @@ use yii\helpers\Json;
use yii\httpclient\Client;
use yii\web\BadRequestHttpException;
use yii\base\BaseObject;
use yii\web\NotFoundHttpException;
class WxPaymentLogic extends BaseObject
{
/*支付类型*/
const PAY_TYPE_WEB = 1;
const PAY_TYPE_MINI_PROGRAM = 2;
/*支付状态*/
const STATUS_PAYMENT_WAITING = 0;
const STATUS_PAYMENT_SUCCESS = 1;
/*退款状态*/
const STATUS_REFUND_WAIT = 0; //退款待审核
const STATUS_REFUND_CONFIRM = 1; //退款待确认
const STATUS_REFUND_SUCCESS = 2; //退款成功
const STATUS_REFUND_PORTION = 3; //部分退款
/*发起支付方式*/
const TRADE_TYPE_JS_API = 'JSAPI';
@ -36,10 +47,12 @@ class WxPaymentLogic extends BaseObject
public $payType;
public $app;
public $order;
public $viewAction = 'view';
/**
* @param $payType
* @return mixed
* @throws BadRequestHttpException
* @throws Exception
* 微信统一下单
@ -48,10 +61,9 @@ class WxPaymentLogic extends BaseObject
{
$this->payType = $payType;
$unifyParams = $this->applyPaymentData();
$this->unify($unifyParams);
return $this->unify($unifyParams);
}
/**
* @return array
* @throws BadRequestHttpException
@ -96,13 +108,13 @@ class WxPaymentLogic extends BaseObject
$paymentLog->payment_amount = $paymentAmount;
$paymentLog->notify_url = $notifyUrl;
$paymentLog->type = $this->payType;
$paymentLog->status = PaymentLog::PAYMENT_STATUS_WAITING;
$paymentLog->status = self::STATUS_PAYMENT_WAITING;
if (!$paymentLog->save()) {
throw new Exception(Helper::errorMessageStr($paymentLog->errors));
}
}
private function getPaymentApp()
private function getApp()
{
$this->initObject();
$config = [
@ -113,11 +125,11 @@ class WxPaymentLogic extends BaseObject
'key_path' => $this->keyPath,
'notify_url' => $this->notifyUrl,
'trade_type' => $this->tradeType,
'sandbox' => true, // 设置为 false 或注释则关闭沙箱模式
// 'sandbox' => true, // 设置为 false 或注释则关闭沙箱模式
];
$this->app = Factory::payment($config);
// 判断当前是否为沙箱模式:
$this->app->inSandbox();
// $this->app->inSandbox();
}
/**
@ -127,16 +139,16 @@ class WxPaymentLogic extends BaseObject
{
$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;
// }
switch ($this->payType) {
case self::PAY_TYPE_WEB:
// $wxConfig = WxConfig::find()->one();
// $this->appId = trim($wxConfig->appid);
break;
case self::PAY_TYPE_MINI_PROGRAM:
// $miniProgramConfig = MiniProgramConfig::find()->one();
// $this->appId = trim($miniProgramConfig->appid);
break;
}
$this->mchId = $wxPayConfig->mch_id;
$this->certPath = trim($path . $wxPayConfig->cert_path);
$this->keyPath = trim($path . $wxPayConfig->key_path);
@ -150,7 +162,7 @@ class WxPaymentLogic extends BaseObject
*/
private function unify($unifyParams)
{
$this->getPaymentApp();
$this->getApp();
return $this->app->order->unify($unifyParams);
}
@ -174,14 +186,17 @@ class WxPaymentLogic extends BaseObject
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;
$paymentLog->wx_payment_id = $notifyData->transaction_id; //交易号
$paymentLog->status = self::STATUS_PAYMENT_SUCCESS;
$paymentLog->payment_at = time();
if (!$paymentLog->save()) {
throw new Exception(Helper::errorMessageStr($paymentLog->errors));
}
if (!$tra->commit()) {
throw new Exception('保存数据失败');
}
@ -195,13 +210,16 @@ class WxPaymentLogic extends BaseObject
Yii::info($e->getMessage(), 'notify');
return false;
} catch (BadRequestHttpException $e) {
$tra->rollBack();
$this->forwardNotify($notifyData, false);
Yii::info($e->getMessage(), 'notify');
return false;
}
}
/**
* @param $notifyData
* @param $notifyData $tra->rollBack();
* @param $status
* @return bool
* @throws \yii\base\InvalidConfigException
@ -230,6 +248,104 @@ class WxPaymentLogic extends BaseObject
}
}
/**
* @return bool
* @throws BadRequestHttpException
* @throws Exception
* @throws NotFoundHttpException
* 申请退款
*/
public function applyRefund()
{
$orderId = Yii::$app->request->getBodyParam('order_id');
$refundId = Yii::$app->request->getBodyParam('wx_refund_id');
$refundAmount = Yii::$app->request->getBodyParam('refund_amount');
$refundAccount = Yii::$app->request->getBodyParam('refund_account');
$reason = Yii::$app->request->getBodyParam('reason');
if (empty($orderId) || empty($refundId) || empty($refundAmount) || empty($reason)) {
throw new BadRequestHttpException(Helper::REQUEST_BAD_PARAMS);
}
$paymentLog = PaymentLog::findOne(['order_id' => $orderId]);
if (empty($paymentLog)) {
throw new NotFoundHttpException('订单支付信息未找到');
}
if (RefundLog::findOne(['order_id' => $orderId, 'status' => self::STATUS_REFUND_WAIT])) {
throw new BadRequestHttpException('此订单存在等待审核的退款申请');
}
$refundedAmount = RefundLog::find()
->where(['order_id' => $orderId, 'status' => self::STATUS_PAYMENT_SUCCESS])
->sum('refund_amount') ?? 0;
$refundLog = new RefundLog();
$refundLog->order_id = $orderId;
$refundLog->wx_refund_id = Helper::timeRandomNum(3, 'P');
$refundLog->reason = $reason;
$refundLog->order_amount = $paymentLog->payment_amount;
$refundLog->refund_amount = $refundAmount;
$refundLog->refunded_amount = $refundedAmount;
$refundLog->type = $paymentLog->type;
$refundLog->status = self::STATUS_REFUND_WAIT;
$refundLog->refund_account = $refundAccount;
$refundLog->applyed_at = time();
if (!$refundLog->save()) {
throw new Exception(Helper::errorMessageStr($refundLog->errors));
}
return true;
}
/**
* @return mixed
* @throws BadRequestHttpException
* @throws Exception
* @throws NotFoundHttpException
* 退款
*/
public function refund()
{
$orderId = Yii::$app->request->getbodyParam('order_id');
if (empty($orderId)) {
throw new BadRequestHttpException(Helper::REQUEST_BAD_PARAMS);
}
$paymentLog = PaymentLog::findOne(['order_id' => $orderId]);
if (empty($paymentLog)) {
throw new NotFoundHttpException('订单支付信息未找到');
}
$refundLog = RefundLog::findOne(['order_id' => $orderId, 'status' => self::STATUS_REFUND_WAIT]);
if (!$refundLog) {
throw new NotFoundHttpException('订单退款信息未找到');
}
/*参数分别为:微信订单号、商户退款单号、订单金额、退款金额、其他参数*/
$this->getApp();
$config = ['refund_desc' => '退款'];
$result = $this->app->refund->byTransactionId(
$paymentLog->wx_refund_id,
$refundLog->wx_refund_id,
round($paymentLog->payment_amount * 100),
round($refundLog->refund_amount * 100),
$config
);
Yii::info($result, 'refund_log');
if ($result['return_code'] == 'FAIL' || $result['return_msg'] != 'OK' || $result['result_code'] == 'FAIL') {
throw new BadRequestHttpException($result['return_msg']);
}
if ($result['err_code_des']) {
throw new BadRequestHttpException($result['err_code_des']);
}
$paymentLog->status = $refundLog->refund_amount < $paymentLog->payment_amount ? self::STATUS_REFUND_SUCCESS : self::STATUS_REFUND_PORTION;
if (!$paymentLog->save()) {
throw new Exception(Helper::errorMessageStr($paymentLog->errors));
}
$refundLog->status = $refundLog->refund_amount < $paymentLog->payment_amount ? self::STATUS_REFUND_SUCCESS : self::STATUS_REFUND_PORTION;
$refundLog->finished_at = time();
if (!$refundLog->save()) {
throw new Exception(Helper::errorMessageStr($refundLog->errors));
}
return $result;
}
/**
* @param $data

29
backend/modules/shop/migrations/m191217_033724_add_column_payment_at_wx_payment_id_in_payment_log.php

@ -0,0 +1,29 @@
<?php
use yii\db\Migration;
/**
* Class m191217_033724_add_column_payment_at_wx_payment_id_in_payment_log
*/
class m191217_033724_add_column_payment_at_wx_payment_id_in_payment_log extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('ats_payment_log', 'wx_payment_id', $this->string(64)->after('order_id')->comment('微信支付交易单号'));
$this->addColumn('ats_payment_log', 'payment_at', $this->integer()->comment('支付时间'));
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropColumn('ats_payment_log', 'wx_payment_id');
$this->dropColumn('ats_payment_log', 'payment_at');
return true;
}
}

28
backend/modules/shop/migrations/m191217_034337_alter_column_order_id_in_payment_log_refund_log.php

@ -0,0 +1,28 @@
<?php
use yii\db\Migration;
/**
* Class m191217_034337_alter_column_order_id_in_payment_log_refund_log
*/
class m191217_034337_alter_column_order_id_in_payment_log_refund_log extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->alterColumn('ats_payment_log', 'order_id', $this->string()->comment('订单号'));
$this->alterColumn('ats_refund_log', 'order_id', $this->string()->comment('订单号'));
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
return true;
}
}

12
backend/modules/shop/models/ars/PaymentLog.php

@ -10,6 +10,7 @@ use yii\behaviors\TimestampBehavior;
*
* @property int $id
* @property int $order_id 订单id
* @property string $wx_payment_id 微信退款单号
* @property string $wx_refund_id 微信退款单号
* @property string $mch_id 商户号
* @property int $order_amount 订单金额
@ -20,12 +21,10 @@ use yii\behaviors\TimestampBehavior;
* @property string $refund_account 退款账户
* @property int $updated_at 更新时间
* @property int $created_at 创建时间
* @property int $payment_at 支付时间
*/
class PaymentLog extends \yii\db\ActiveRecord
{
const PAYMENT_STATUS_WAITING = 0;
const PAYMENT_STATUS_SUCCESS = 1;
/**
* {@inheritdoc}
*/
@ -40,9 +39,9 @@ class PaymentLog extends \yii\db\ActiveRecord
public function rules()
{
return [
[['order_id', 'order_amount', 'payment_amount', 'type', 'status'], 'integer'],
[['wx_refund_id', 'mch_id', 'refund_account'], 'string', 'max' => 64],
[['notify_url'], 'string', 'max' => 255],
[['order_amount', 'payment_amount', 'type', 'status', 'payment_at'], 'integer'],
[['wx_payment_id', 'wx_refund_id', 'mch_id', 'refund_account'], 'string', 'max' => 64],
[['order_id', 'notify_url'], 'string', 'max' => 255],
];
}
@ -54,6 +53,7 @@ class PaymentLog extends \yii\db\ActiveRecord
return [
'id' => 'id',
'order_id' => '订单id',
'wx_payment_id' => '微信支付单号',
'wx_refund_id' => '微信退款单号',
'mch_id' => '商户号',
'order_amount' => '订单金额',

Loading…
Cancel
Save