소스 검색

feat(payment): 添加统一的退款接口,实现余额退款

runphp 6 달 전
부모
커밋
cdc18d9b13

+ 50 - 0
database/migrations/20250905165126_extension_refund.php

@@ -0,0 +1,50 @@
+<?php
+
+use think\migration\db\Column;
+use think\migration\Migrator;
+
+class ExtensionRefund extends Migrator
+{
+    /**
+     * Change Method.
+     *
+     * Write your reversible migrations using this method.
+     *
+     * More information on writing migrations is available here:
+     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
+     *
+     * The following commands can be used in this method and Phinx will
+     * automatically reverse them when rolling back:
+     *
+     *    createTable
+     *    renameTable
+     *    addColumn
+     *    renameColumn
+     *    addIndex
+     *    addForeignKey
+     *
+     * Remember to call "create()" or "update()" and NOT "save()" when working
+     * with the Table class.
+     */
+    public function change(): void
+    {
+        $this->table('extension_refund')
+            ->setComment('支付退款记录表')
+            ->addColumn(Column::integer('payment_id')->setComment('关联支付记录ID'))
+            ->addColumn(Column::char('order_sn', 20)->setComment('支付订单编号'))
+            ->addColumn(Column::char('out_refund_no', 20)->setComment('商户退款单号'))
+            ->addColumn(Column::string('reason'))->setComment('退款原因')
+            ->addColumn(Column::decimal('amount', 10, 2)->setComment('退款金额(元)'))
+            ->addColumn(Column::tinyInteger('status')->setComment('退款状态:0-待退款/1-退款中/2-成功/3-失败'))
+            ->addColumn(Column::char('refund_id', 32)->setComment('三方退款唯一订单号'))
+            ->addColumn(Column::integer('success_time')->setComment('退款成功时间'))
+            ->addColumn(Column::json('refund_param')->setComment('退款参数'))
+            ->addColumn(Column::json('refund_result')->setComment('查询退款结果信息'))
+            ->addColumn(Column::string('status_desc')->setComment('退款状态说明'))
+            ->addTimestamps()
+            ->addIndex(['refund_id'])
+            ->addIndex(['out_refund_no'], ['unique' => true])
+            ->addIndex(['order_sn'])
+            ->create();
+    }
+}

+ 3 - 2
src/Contracts/PaymentProviderInterface.php

@@ -32,11 +32,12 @@ interface PaymentProviderInterface
 
     /**
      * 申请退款
-     * @param array $refund 退款信息(如原订单号、退款金额、原因等)
+     * @param int $recordID 支付记录ID
+     * @param PaymentRefundRequest $param 退款参数
      * @return PaymentRefundResult
      * @throws PaymentException
      */
-    public function refund(array $refund): PaymentRefundResult;
+    public function refund(int $recordID, PaymentRefundRequest $param): PaymentRefundResult;
 
     /**
      * 查询退款状态

+ 25 - 0
src/Contracts/PaymentRefundRequest.php

@@ -0,0 +1,25 @@
+<?php
+declare(strict_types=1);
+namespace SixShop\Payment\Contracts;
+
+readonly class PaymentRefundRequest
+{
+    public function __construct(private  float $amount, private string $reason, private array $raw = [])
+    {
+    }
+
+    public function getAmount(): float
+    {
+        return $this->amount;
+    }
+
+    public function getReason(): string
+    {
+        return $this->reason;
+    }
+
+    public function getRaw(): array
+    {
+        return $this->raw;
+    }
+}

+ 9 - 8
src/Contracts/PaymentRefundResult.php

@@ -3,15 +3,16 @@ declare(strict_types=1);
 
 namespace SixShop\Payment\Contracts;
 
-class PaymentRefundResult
+use SixShop\Payment\Model\ExtensionRefundModel;
+
+readonly class PaymentRefundResult
 {
-    public function __construct(
-        public string $refundNo,
-        public string $orderNo,
-        public float  $refundAmount,
-        public string $status,
-        public array  $raw
-    )
+    public function __construct(private ExtensionRefundModel $refund)
+    {
+    }
+
+    public function getRefund(): ExtensionRefundModel
     {
+        return $this->refund;
     }
 }

+ 10 - 0
src/Entity/ExtensionRefundEntity.php

@@ -0,0 +1,10 @@
+<?php
+declare(strict_types=1);
+namespace SixShop\Payment\Entity;
+
+use SixShop\Core\Entity\BaseEntity;
+
+class ExtensionRefundEntity extends BaseEntity
+{
+
+}

+ 3 - 1
src/Enum/NumberBizEnum.php

@@ -6,15 +6,17 @@ namespace SixShop\Payment\Enum;
 
 enum NumberBizEnum: int
 {
-    // 1 订单号 2 订单支付
+    // 1 订单号 2 订单支付 3 订单退款
     case ORDER_NO = 1;
     case ORDER_PAY = 2;
+    case ORDER_REFUND = 3;
 
     public function toString(): string
     {
         return match ($this) {
             self::ORDER_NO => '订单号',
             self::ORDER_PAY => '订单支付',
+            self::ORDER_REFUND => '订单退款',
         };
     }
 

+ 22 - 0
src/Enum/RefundStatusEnum.php

@@ -0,0 +1,22 @@
+<?php
+declare(strict_types=1);
+namespace SixShop\Payment\Enum;
+
+enum RefundStatusEnum:int
+{
+    case PENDING = 0;
+    case REFUNDING = 1;
+    case SUCCESS = 2;
+    case FAIL = 3;
+
+    public function toString(): string
+    {
+        return match ($this) {
+            self::PENDING => '待退款',
+            self::REFUNDING => '退款中',
+            self::SUCCESS => '成功',
+            self::FAIL => '失败',
+            default => '未知',
+        };
+    }
+}

+ 17 - 0
src/Event/RefundSuccessEvent.php

@@ -0,0 +1,17 @@
+<?php
+declare(strict_types=1);
+namespace SixShop\Payment\Event;
+
+use SixShop\Payment\Model\ExtensionRefundModel;
+
+readonly class RefundSuccessEvent
+{
+    public function __construct(private ExtensionRefundModel $refund)
+    {
+    }
+
+    public function getRefund(): ExtensionRefundModel
+    {
+        return $this->refund;
+    }
+}

+ 2 - 2
src/Model/ExtensionPaymentModel.php

@@ -46,9 +46,9 @@ class ExtensionPaymentModel extends Model
 
     protected function getOptions(): array
     {
-        return array_merge(parent::getOptions(),[
+        return [
             'insert' => ['out_trade_no'],
             'readonly' => ['order_id', 'order_sn', 'out_trade_no', 'biz_type', 'pay_type', 'amount'],
-        ]);
+        ];
     }
 }

+ 30 - 0
src/Model/ExtensionRefundModel.php

@@ -0,0 +1,30 @@
+<?php
+declare(strict_types=1);
+
+namespace SixShop\Payment\Model;
+
+use SixShop\Payment\Enum\NumberBizEnum;
+use SixShop\Payment\Enum\RefundStatusEnum;
+use think\Model;
+
+class ExtensionRefundModel extends Model
+{
+    protected function setOutRefundNoAttr($value, $data): string
+    {
+        return generate_number(NumberBizEnum::ORDER_REFUND);
+    }
+
+    protected function getOptions(): array
+    {
+        return [
+            'name' => 'extension_refund',
+            'type' => [
+                'status' => RefundStatusEnum::class,
+                'refund_param' => 'json',
+                'refund_result' => 'json',
+            ],
+            'insert' => ['out_refund_no'],
+            'readonly' => ['payment_id', 'order_sn', 'out_refund_no', 'reason', 'amount'],
+        ];
+    }
+}

+ 22 - 8
src/PaymentManager.php

@@ -2,23 +2,25 @@
 declare(strict_types=1);
 namespace SixShop\Payment;
 
+use SixShop\Payment\Contracts\PaymentProviderInterface;
 use SixShop\Payment\Contracts\PaymentQueryResult;
+use SixShop\Payment\Contracts\PaymentRefundRequest;
+use SixShop\Payment\Contracts\PaymentRefundResult;
 use SixShop\Payment\Contracts\PaymentResponse;
 use SixShop\Payment\Entity\ExtensionPaymentEntity;
+use SixShop\Payment\Entity\ExtensionRefundEntity;
 use SixShop\Payment\Enum\PaymentBizEnum;
 use SixShop\Payment\Event\BeforePayEvent;
 use SixShop\Payment\Event\GatheringPaymentEvent;
 use SixShop\System\ExtensionManager;
 use SixShop\System\Model\ExtensionModel;
 use think\facade\Event;
-use think\Request;
 
 readonly class PaymentManager
 {
 
     public function __construct(
         private ExtensionManager $extensionManager,
-        private ExtensionPaymentEntity $extensionPaymentEntity,
     )
     {
     }
@@ -45,22 +47,27 @@ readonly class PaymentManager
     /**
      *  创建支付订单
      */
-    public function create($paymentID,  array $order, PaymentBizEnum $bizType = PaymentBizEnum::ORDER_PAY):  PaymentResponse
+    public function create(string $paymentID,  array $order, PaymentBizEnum $bizType = PaymentBizEnum::ORDER_PAY):  PaymentResponse
     {
-        $extension = $this->extensionManager->getExtension($paymentID);
         Event::trigger(new BeforePayEvent($order, $paymentID, $bizType));
-        return $extension->getPaymentProvider()->create($order, $bizType);
+        return $this->getPaymentProvider($paymentID)->create($order, $bizType);
     }
     
     /**
      * 查询支付订单
      */
-    public function query($paymentID, $recordID): PaymentQueryResult
+    public function query(string $paymentID, $recordID): PaymentQueryResult
     {
-        $extension = $this->extensionManager->getExtension($paymentID);
-        return $extension->getPaymentProvider()->query($recordID);
+        return $this->getPaymentProvider($paymentID)->query($recordID);
     }
 
+    /**
+     * 退款
+     */
+    public function refund(string $paymentID, int $recordID, PaymentRefundRequest $param): PaymentRefundResult
+    {
+        return $this->getPaymentProvider($paymentID)->refund($recordID, $param);
+    }
     /**
      * 获取指定支付方式
      */
@@ -88,4 +95,11 @@ readonly class PaymentManager
         // todo
         return true;
     }
+
+    private function getPaymentProvider(string $paymentID): PaymentProviderInterface
+    {
+        $extension = $this->extensionManager->getExtension($paymentID);
+        return $extension->getPaymentProvider();
+    }
+
 }