Selaa lähdekoodia

feat(payment): integrate Lakala payment provider with WeChat OpenID support

- Add TransactionService facade for Lakala SDK integration
- Implement payment parameter creation with WeChat OpenID
- Update Lakala configuration to include sub_appid field
- Modify order confirmation page to handle WeChat login and OpenID retrieval
- Add WeChat API endpoint for user session management
- Refactor payment provider to use transactional database operations
- Improve error handling in transaction service with proper exception throwing
- Update frontend to pass OpenID during Lakala payment processing
- Add input field configuration for Lakala sub merchant app ID
- Fix variable naming inconsistencies in test files
runphp 4 kuukautta sitten
vanhempi
sitoutus
409c37a571

+ 15 - 0
config.php

@@ -220,6 +220,21 @@ return json_decode(<<<'JSON'
     "_fc_drag_tag": "input",
     "display": true,
     "hidden": false
+   },
+   {
+   "type": "input",
+    "title": "子商户公众账号ID(sub_appid)",
+    "field": "sub_appid",
+    "value":"wx1308d811d01639eb",
+    "props": {
+      "placeholder": "请输入子商户公众账号ID"
+    },
+    "$required": true,
+    "_fc_id": "id_F0j5md1fz7stawc",
+    "name": "ref_Ffipmd1fz7staxc",
+    "_fc_drag_tag": "input",
+    "display": true,
+    "hidden": false
    },
        {
     "type": "input",

+ 1 - 0
src/Config.php

@@ -14,6 +14,7 @@ use SixShop\System\Trait\ConfigTrait;
  * @property string $complete_notify_url 收货确认通知地址
  * @property string $environment 环境
  * @property string $org_code 机构代码
+ * @property string $sub_appid 子商户公众账号ID
  * @property string $receiver_agreement_file 默认合作协议文件
  */
 class Config

+ 16 - 0
src/Facade/TransactionService.php

@@ -0,0 +1,16 @@
+<?php
+declare(strict_types=1);
+namespace SixShop\Lakala\Facade;
+
+use think\Facade;
+
+/**
+ * @mixin \SixShop\Lakala\Service\TransactionService
+ */
+class TransactionService extends Facade
+{
+    protected static function getFacadeClass()
+    {
+        return \SixShop\Lakala\Service\TransactionService::class;
+    }
+}

+ 5 - 1
src/Hook/LakalaHook.php

@@ -6,6 +6,7 @@ use app\model\User;
 use SixShop\Core\Attribute\Hook;
 use SixShop\Payment\Event\GatheringPaymentEvent;
 use SixShop\Payment\PaymentInfo;
+use SixShop\Wechat\Facade\WechatUser;
 
 class LakalaHook
 {
@@ -18,7 +19,10 @@ class LakalaHook
         return new PaymentInfo(
             'lakala',
             '拉卡拉',
-            '拉卡拉'
+            '拉卡拉',
+            params: [
+                'openid' => WechatUser::openid($event->request->userID),
+            ]
         );
     }
 }

+ 77 - 1
src/PaymentProvider.php

@@ -3,19 +3,66 @@ declare(strict_types=1);
 
 namespace SixShop\Lakala;
 
+use SixShop\Lakala\Dto\LocationInfo;
+use SixShop\Lakala\Facade\TransactionService;
+use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaAccBusiFields;
+use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaDetail;
+use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaGoodsDetail;
 use SixShop\Payment\Contracts\PaymentNotifyResult;
 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\Enum\PaymentBizEnum;
+use SixShop\Payment\Enum\PaymentStatusEnum;
 
 class PaymentProvider implements PaymentProviderInterface
 {
+    public function __construct(
+        private readonly Config $config,
+        private readonly ExtensionPaymentEntity $extensionPaymentEntity
+    )
+    {
+    }
+
     #[\Override] public function create(array $order, PaymentBizEnum $bizType): PaymentResponse
     {
-        throw new \Exception('Not implemented');
+        $payment = $this->extensionPaymentEntity->where([
+            'order_id' => $order['id'],
+            'pay_type' => Extension::EXTENSION_ID,
+            'biz_type' => $bizType,
+        ])->findOrEmpty();
+        if (!$payment->isEmpty()) {
+            // todo 判断订单是否支付成功
+            // 支付时间结束关闭订单
+            // 订单未支付可重新支付
+            // 交易已关闭请重新下单
+            //订单已付款请勿重复操作
+            // 订单已退款请重新下单
+            //订单已撤销请重新下单
+            // 订单支付中请稍后再试
+            // 订单支付失败请重新下单
+            throw new  \RuntimeException('开发测试中,请稍后再试');
+        }
+        $payment->transaction(function () use ($bizType, $order, $payment) {
+            $payment->save([
+                'user_id' => $order['user_id'],
+                'order_id' => $order['id'],
+                'order_sn' => $order['order_sn'],
+                'biz_type' => $bizType,
+                'pay_type' => Extension::EXTENSION_ID,
+                'amount' => $order['pay_amount'],
+                'status' => PaymentStatusEnum::PENDING
+            ]);
+            $expireDuration = 15; // 分钟
+            $payment->expire_time = time() + $expireDuration * 60;
+            $payment->payment_param = $this->createPaymentParam($order, $payment, $expireDuration);
+            $payment->transaction_id = $payment->payment_param['transactionID'];
+            $payment->save();
+        });
+        return new PaymentResponse(orderNo: $payment->out_trade_no, type: self::PAYMENT_TYPE, raw: $payment->toArray());
     }
 
     #[\Override] public function notify(array $request): PaymentNotifyResult
@@ -37,4 +84,33 @@ class PaymentProvider implements PaymentProviderInterface
     {
         throw new \Exception('Not implemented');
     }
+
+    private function createPaymentParam(array $order, ExtensionPaymentEntity $payment, int $expireDuration): array
+    {
+        $accBusiFields = new TradePreorderWechaAccBusiFields();
+        $accBusiFields->setTimeoutExpress($expireDuration);
+        $accBusiFields->setSubAppid($this->config->sub_appid);
+        $accBusiFields->setUserId($order['params']['openid']);
+        $detail = new TradePreorderWechaDetail();
+        $goodsDetailList = [];
+        foreach ($order['order_goods'] as $goods) {
+            $goodsDetail = new TradePreorderWechaGoodsDetail();
+            $goodsDetail->setGoodsId($goods['goods_id']);
+            $goodsDetail->setQuantity($goods['num']);
+            $goodsDetail->setPrice(round($goods['price'] * 100, 0));
+            $goodsDetail->setGoodsName($goods['goods_name']);
+            $goodsDetailList[] = $goodsDetail;
+        }
+        $detail->setGoodsDetail($goodsDetailList);
+        $accBusiFields->setDetail($detail);
+        TransactionService::preOrder(
+            outTradeNo: $payment['out_trade_no'],
+            totalAmount: $payment['amount'],
+            locationInfo: new LocationInfo(requestIP: $order['params']['ip']),
+            subject: '订单:' . $order['order_sn'],
+            settleType: '1', // 拉卡拉分账
+            remark: $order['description'],
+            accBusiFields: $accBusiFields,
+        );
+    }
 }

+ 11 - 19
src/Service/TransactionService.php

@@ -11,6 +11,7 @@ use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaGoodsDetail;
 use SixShop\Lakala\OpenAPISDK\V3\Model\TransPreorderRequest;
 use SixShop\Lakala\Config;
 use SixShop\Lakala\Dto\LocationInfo;
+use function SixShop\Core\throw_logic_exception;
 
 /**
  * TransactionService
@@ -51,7 +52,7 @@ class TransactionService
         string $settleType = '',
         string $remark = '',
         TradeAccBusiFields $accBusiFields = null,
-    )
+    ):object
     {
         $request = new TransPreorderRequest();
 
@@ -74,24 +75,15 @@ class TransactionService
         $request->setAccBusiFields($accBusiFields);
         $request->setCompleteNotifyUrl($this->config->complete_notify_url);
 
-        try {
-            $response = $this->transPreorderApi->transPreorder($request);
-            if ($response->getRespData()) {
-                print_r($response->getRespData());
-                print_r($response->getAccRespFields());
-            }
-            else {
-                print_r($response);
-            }
-            echo $response->getCode();
-
-            # 响应头信息
-            print_r($response->getHeaders());
-
-            # 响应原文
-            echo $response->getOriginalText();
-        } catch (\Lakala\OpenAPISDK\V3\ApiException $e) {
-            echo $e->getMessage();
+        $response = $this->transPreorderApi->transPreorder($request);
+        if ($response->getCode() == 'BBS00000') {
+            return $response->getRespData();
+        } else {
+            throw_logic_exception(
+                msg:$response->getMsg(),
+                status: $response->getCode(),
+                data: $response->getRespData(),
+            );
         }
     }
 }

+ 7 - 7
tests/Service/TransactionServiceTest.php

@@ -35,13 +35,13 @@ class TransactionServiceTest extends TestCase
         $detail = new TradePreorderWechaDetail();
         $detail->setCostPrice($totalAmount);
         $detail->setReceiptId('');
-        $goods_detail = new TradePreorderWechaGoodsDetail();
-        $goods_detail->setGoodsId('3452234');
-        $goods_detail->setWxpayGoodsId('');
-        $goods_detail->setGoodsName('');
-        $goods_detail->setQuantity(1);
-        $goods_detail->setPrice($totalAmount);
-        $detail->setGoodsDetail([$goods_detail]);
+        $goodsDetail = new TradePreorderWechaGoodsDetail();
+        $goodsDetail->setGoodsId('3452234');
+        $goodsDetail->setWxpayGoodsId('');
+        $goodsDetail->setGoodsName('');
+        $goodsDetail->setQuantity(1);
+        $goodsDetail->setPrice($totalAmount);
+        $detail->setGoodsDetail([$goodsDetail]);
         $accBusiFields->setDetail($detail);
         $this->transactionService->preOrder(
             outTradeNo: generate_number(NumberBizEnum::ORDER_PAY),