Browse Source

feat(credit-payment): 完成支付页面额度支付集成

1. 在额度模块中添加 `/api/credit-balance/me` 路由
   - 从上下文中获取当前用户ID,无需前端传递
   - 修复路由顺序问题,确保 `/me` 在 `/:userId` 之前注册
   - 添加集成测试验证功能

2. 更新小程序支付页面支持额度支付
   - 添加支付方式选择组件(微信支付 vs 额度支付)
   - 实现额度查询逻辑,使用新的 `/me` 路由
   - 添加额度支付处理函数 `handleCreditPayment`
   - 额度为0或不足时禁用额度支付选项
   - 保持与微信支付选项的并行工作

3. 修复依赖问题
   - 更新 server 包和订单模块的 package.json,添加额度模块依赖
   - 运行 pnpm install 安装新依赖

4. 更新故事文档
   - 标记"更新小程序支付页面"任务为完成
   - 更新文件列表和完成的工作记录

🤖 Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 4 ngày trước cách đây
mục cha
commit
0fb5a0b8cc

+ 32 - 11
docs/stories/004.003.integrate-credit-payment.story.md

@@ -34,18 +34,24 @@ Draft
   - [x] 额度支付检查:由额度模块的`deductAmount`方法实现用户额度验证
   - [x] 额度扣减逻辑:调用`CreditBalanceService.deductAmount()`方法
 
+- [x] **在额度模块中添加当前用户额度查询路由** (AC: 1, 3, 6)
+  - [x] 创建`/api/credit-balance/me`路由,从上下文中获取当前用户ID
+  - [x] 参考认证模块的`me.route.mt.ts`实现
+  - [x] 更新额度模块路由索引,添加me路由
+  - [x] 编写集成测试验证me路由功能
+
 - [x] **实现额度恢复机制** (AC: 5)
   - [x] **结账恢复**:在信用管理UI中已实现`/api/credit-balance/checkout`接口调用(已完成)
   - [x] **取消订单恢复**:在订单模块取消订单时调用`CreditBalanceService.restoreBalanceForCancelOrder()`方法(已完成)
   - [x] **退款恢复说明**:额度支付是独立支付方式,不经过微信支付退款流程。额度支付订单的退款已在取消订单逻辑中处理
   - [x] **幂等性保证**:由额度模块的`restoreAmount`方法保证同一订单只能恢复一次额度
 
-- [ ] **更新小程序支付页面** (AC: 1, 3, 7)
-  - [ ] 检查小程序支付页面组件结构
-  - [ ] 在支付选项中添加"额度支付"按钮
-  - [ ] 实现额度支付选择逻辑:检查用户可用额度
-  - [ ] 额度为0的用户禁用额度支付选项
-  - [ ] 保持与微信支付选项的并行工作
+- [x] **更新小程序支付页面** (AC: 1, 3, 7)
+  - [x] 检查小程序支付页面组件结构
+  - [x] 在支付选项中添加"额度支付"按钮
+  - [x] 实现额度支付选择逻辑:检查用户可用额度
+  - [x] 额度为0的用户禁用额度支付选项
+  - [x] 保持与微信支付选项的并行工作
 
 - [ ] **在小程序个人中心显示欠款信息** (AC: 6)
   - [ ] 检查小程序个人中心页面结构
@@ -160,13 +166,16 @@ CREATE TABLE credit_balance_log_mt (
 
 ### API设计 [Source: docs/prd/epic-004-credit-payment.md#API设计]
 **额度模块对外API(已实现)**:
-1. `GET /api/credit-balance/{userId}` - 查询用户额度
+1. `GET /api/credit-balance/{userId}` - 查询用户额度(管理员/指定用户)
 2. `PUT /api/credit-balance/{userId}` - 设置用户额度
 3. `POST /api/credit-balance/{userId}/adjust` - 调整用户额度
 4. `GET /api/credit-balance/{userId}/logs` - 查询额度变更记录
 5. `POST /api/credit-balance/payment` - 额度支付
 6. `POST /api/credit-balance/checkout` - 结账恢复额度
 
+**需要新增的API**:
+7. `GET /api/credit-balance/me` - 查询当前用户额度(从上下文中获取用户ID)
+
 **额度模块服务接口(供其他模块调用)**:
 1. `CreditBalanceService.restoreBalanceForCancelOrder(orderId, userId, amount)` - 取消订单恢复额度
 2. `CreditBalanceService.restoreBalanceForRefund(orderId, userId, refundAmount)` - 退款恢复额度
@@ -433,8 +442,15 @@ Claude Code (d8d-model)
      - 导入CreditBalanceRoutes类型
      - 添加creditBalanceClient导出
 
-2. **待完成的工作**:
+2. **已完成的工作**:
+   - 在额度模块中添加`/api/credit-balance/me`路由,从上下文中获取当前用户ID
    - 更新小程序支付页面,添加"额度支付"选项
+   - 实现额度支付选择逻辑,检查用户可用额度
+   - 额度为0的用户禁用额度支付选项
+   - 保持与微信支付选项的并行工作
+   - 修复server包和订单模块的依赖,添加额度模块依赖
+
+3. **待完成的工作**:
    - 在小程序个人中心显示欠款信息
    - 按照小程序mini规范编写测试
    - 验证模块间集成
@@ -447,11 +463,16 @@ Claude Code (d8d-model)
 4. `packages/server/src/index.ts` - 集成额度模块路由和实体
 5. `mini/src/api.ts` - 添加creditBalanceClient导出
 6. `docs/stories/004.003.integrate-credit-payment.story.md` - 更新任务状态和集成说明
+7. `packages/credit-balance-module-mt/src/routes/me.mt.ts` - 新增当前用户额度查询路由
+8. `packages/credit-balance-module-mt/src/routes/index.ts` - 更新路由索引,添加me路由
+9. `packages/credit-balance-module-mt/tests/integration/credit-balance-routes.integration.test.ts` - 添加me路由集成测试
+10. `mini/src/pages/payment/index.tsx` - 添加额度支付选项和逻辑
+11. `packages/server/package.json` - 添加额度模块依赖
+12. `packages/orders-module-mt/package.json` - 添加额度模块依赖
 
 **需要创建/修改的文件**:
-1. `mini/src/pages/payment/index.tsx` - 添加额度支付选项(待完成)
-2. `mini/src/pages/profile/index.tsx` - 添加欠款信息显示(待完成)
-3. 测试文件(待完成)
+1. `mini/src/pages/profile/index.tsx` - 添加欠款信息显示(待完成)
+2. 测试文件(待完成)
 
 ## QA Results
 *此部分由QA代理在审查完成后填写*

+ 223 - 5
mini/src/pages/payment/index.tsx

@@ -4,7 +4,7 @@
  */
 
 import Taro, { useRouter } from '@tarojs/taro'
-import { useState } from 'react'
+import { useState, useEffect } from 'react'
 import { View, Text } from '@tarojs/components'
 import { useQuery } from '@tanstack/react-query'
 import { Button } from '@/components/ui/button'
@@ -16,7 +16,7 @@ import {
   PaymentRateLimiter,
   retryPayment
 } from '@/utils/payment'
-import { paymentClient } from '@/api'
+import { paymentClient, creditBalanceClient } from '@/api'
 
 interface PaymentData {
   timeStamp: string
@@ -26,10 +26,25 @@ interface PaymentData {
   paySign: string
 }
 
+interface CreditBalanceData {
+  totalLimit: number
+  usedAmount: number
+  availableAmount: number
+  isEnabled: boolean
+}
+
+enum PaymentMethod {
+  WECHAT = 'wechat',
+  CREDIT = 'credit'
+}
+
 const PaymentPage = () => {
   const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.PENDING)
   const [isProcessing, setIsProcessing] = useState(false)
   const [errorMessage, setErrorMessage] = useState('')
+  const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod>(PaymentMethod.WECHAT)
+  const [creditBalance, setCreditBalance] = useState<CreditBalanceData | null>(null)
+  const [isCreditBalanceLoading, setIsCreditBalanceLoading] = useState(false)
 
   // 使用useRouter钩子获取路由参数
   const router = useRouter()
@@ -38,6 +53,50 @@ const PaymentPage = () => {
   const amount = routerParams?.amount ? parseFloat(routerParams.amount) : 0
   const orderNo = routerParams?.orderNo
 
+  // 获取用户额度信息
+  const fetchCreditBalance = async () => {
+    setIsCreditBalanceLoading(true)
+    try {
+      const response = await creditBalanceClient.me.$get({})
+
+      if (response.status === 200) {
+        const balanceData = await response.json()
+        setCreditBalance(balanceData)
+      } else if (response.status === 404) {
+        // 用户没有额度记录,创建默认额度
+        setCreditBalance({
+          totalLimit: 0,
+          usedAmount: 0,
+          availableAmount: 0,
+          isEnabled: false
+        })
+      } else {
+        // 其他错误情况
+        setCreditBalance({
+          totalLimit: 0,
+          usedAmount: 0,
+          availableAmount: 0,
+          isEnabled: false
+        })
+      }
+    } catch (error) {
+      console.error('获取用户额度失败:', error)
+      setCreditBalance({
+        totalLimit: 0,
+        usedAmount: 0,
+        availableAmount: 0,
+        isEnabled: false
+      })
+    } finally {
+      setIsCreditBalanceLoading(false)
+    }
+  }
+
+
+  // 组件加载时获取额度信息
+  useEffect(() => {
+    fetchCreditBalance()
+  }, [])
 
   // 获取支付参数
   const { data: paymentData, isLoading: paymentLoading } = useQuery({
@@ -78,8 +137,72 @@ const PaymentPage = () => {
   const paymentStateManager = PaymentStateManager.getInstance()
   const rateLimiter = PaymentRateLimiter.getInstance()
 
+  // 处理额度支付
+  const handleCreditPayment = async () => {
+    if (!orderId || !amount) {
+      setErrorMessage('订单信息不完整')
+      return
+    }
+
+    if (!creditBalance?.isEnabled) {
+      setErrorMessage('额度支付未启用')
+      return
+    }
+
+    if (creditBalance.availableAmount < amount) {
+      setErrorMessage(`额度不足,可用额度: ¥${creditBalance.availableAmount.toFixed(2)}`)
+      return
+    }
+
+    setIsProcessing(true)
+    setErrorMessage('')
+    setPaymentStatus(PaymentStatus.PROCESSING)
+
+    try {
+      const response = await creditBalanceClient.payment.$post({
+        json: {
+          amount: amount,
+          referenceId: orderNo || `ORD${orderId}`,
+          remark: `订单支付 - ${orderNo || `ORD${orderId}`}`
+        }
+      })
+
+      if (response.status === 200) {
+        // 额度支付成功
+        setPaymentStatus(PaymentStatus.SUCCESS)
+
+        // 更新本地额度信息
+        const updatedBalance = await response.json()
+        setCreditBalance(updatedBalance)
+
+        // 跳转到支付成功页面
+        setTimeout(() => {
+          Taro.redirectTo({
+            url: `/pages/payment-success/index?orderId=${orderId}&amount=${amount}&paymentMethod=credit`
+          })
+        }, 1500)
+      } else {
+        const errorData = await response.json()
+        setPaymentStatus(PaymentStatus.FAILED)
+        setErrorMessage(errorData.message || '额度支付失败')
+      }
+    } catch (error: any) {
+      console.error('额度支付处理异常:', error)
+      setPaymentStatus(PaymentStatus.FAILED)
+      setErrorMessage(error.message || '额度支付异常')
+    } finally {
+      setIsProcessing(false)
+    }
+  }
+
   // 处理支付
   const handlePayment = async () => {
+    if (selectedPaymentMethod === PaymentMethod.CREDIT) {
+      await handleCreditPayment()
+      return
+    }
+
+    // 微信支付逻辑
     if (!paymentData || !orderId) {
       setErrorMessage('支付参数不完整')
       return
@@ -141,6 +264,11 @@ const PaymentPage = () => {
 
   // 重试支付
   const handleRetryPayment = async () => {
+    if (selectedPaymentMethod === PaymentMethod.CREDIT) {
+      await handleCreditPayment()
+      return
+    }
+
     if (!paymentData || !orderId) return
 
     setIsProcessing(true)
@@ -261,6 +389,88 @@ const PaymentPage = () => {
         </View>
       </View>
 
+      {/* 支付方式选择 */}
+      <View className="bg-white rounded-2xl p-6 mb-5">
+        <Text className="text-sm font-bold text-gray-800 block mb-4">选择支付方式</Text>
+
+        {/* 微信支付选项 */}
+        <View
+          className={`flex items-center justify-between p-4 mb-3 rounded-xl border-2 ${
+            selectedPaymentMethod === PaymentMethod.WECHAT
+              ? 'border-blue-500 bg-blue-50'
+              : 'border-gray-200'
+          }`}
+          onClick={() => setSelectedPaymentMethod(PaymentMethod.WECHAT)}
+        >
+          <View className="flex items-center">
+            <View className="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center mr-3">
+              <Text className="text-green-600 text-lg">💰</Text>
+            </View>
+            <View>
+              <Text className="text-sm font-bold text-gray-800">微信支付</Text>
+              <Text className="text-xs text-gray-500">使用微信支付完成付款</Text>
+            </View>
+          </View>
+          {selectedPaymentMethod === PaymentMethod.WECHAT && (
+            <View className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
+              <Text className="text-white text-xs">✓</Text>
+            </View>
+          )}
+        </View>
+
+        {/* 额度支付选项 */}
+        <View
+          className={`flex items-center justify-between p-4 rounded-xl border-2 ${
+            selectedPaymentMethod === PaymentMethod.CREDIT
+              ? 'border-blue-500 bg-blue-50'
+              : 'border-gray-200'
+          } ${(!creditBalance?.isEnabled || creditBalance?.availableAmount <= 0 || creditBalance?.availableAmount < amount) ? 'opacity-50' : ''}`}
+          onClick={() => {
+            if (creditBalance?.isEnabled && creditBalance?.availableAmount > 0 && creditBalance?.availableAmount >= amount) {
+              setSelectedPaymentMethod(PaymentMethod.CREDIT)
+            }
+          }}
+        >
+          <View className="flex items-center">
+            <View className="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center mr-3">
+              <Text className="text-purple-600 text-lg">💳</Text>
+            </View>
+            <View>
+              <Text className="text-sm font-bold text-gray-800">额度支付</Text>
+              {isCreditBalanceLoading ? (
+                <Text className="text-xs text-gray-500">加载中...</Text>
+              ) : creditBalance?.isEnabled ? (
+                <Text className="text-xs text-gray-500">
+                  可用额度: ¥{creditBalance?.availableAmount?.toFixed(2) || '0.00'}
+                  {creditBalance?.availableAmount < amount && ` (不足)`}
+                </Text>
+              ) : (
+                <Text className="text-xs text-red-500">额度未启用</Text>
+              )}
+            </View>
+          </View>
+          {selectedPaymentMethod === PaymentMethod.CREDIT ? (
+            <View className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
+              <Text className="text-white text-xs">✓</Text>
+            </View>
+          ) : (!creditBalance?.isEnabled || creditBalance?.availableAmount <= 0 || creditBalance?.availableAmount < amount) && (
+            <Text className="text-xs text-gray-400">不可用</Text>
+          )}
+        </View>
+
+        {/* 额度支付说明 */}
+        {selectedPaymentMethod === PaymentMethod.CREDIT && creditBalance && (
+          <View className="mt-4 p-3 bg-blue-50 rounded-lg">
+            <Text className="text-xs text-blue-700">
+              • 使用信用额度支付,无需立即付款{'\n'}
+              • 可用额度: ¥{creditBalance.availableAmount.toFixed(2)}{'\n'}
+              • 总额度: ¥{creditBalance.totalLimit.toFixed(2)}{'\n'}
+              • 已用额度: ¥{creditBalance.usedAmount.toFixed(2)}
+            </Text>
+          </View>
+        )}
+      </View>
+
       {/* 支付状态 */}
       <View className="bg-white rounded-2xl p-8 mb-5 text-center">
         {renderPaymentStatus()}
@@ -271,12 +481,20 @@ const PaymentPage = () => {
         {paymentStatus === PaymentStatus.PENDING && (
           <Button
             onClick={handlePayment}
-            disabled={isProcessing || paymentLoading}
-            className={`w-full h-22 bg-gradient-to-r from-orange-500 to-orange-400 text-white rounded-full text-lg font-bold ${
+            disabled={isProcessing || paymentLoading || (selectedPaymentMethod === PaymentMethod.CREDIT && (!creditBalance?.isEnabled || creditBalance?.availableAmount < amount))}
+            className={`w-full h-22 ${
+              selectedPaymentMethod === PaymentMethod.CREDIT
+                ? 'bg-gradient-to-r from-purple-500 to-purple-400'
+                : 'bg-gradient-to-r from-orange-500 to-orange-400'
+            } text-white rounded-full text-lg font-bold ${
               isProcessing ? 'bg-gray-400' : ''
             }`}
           >
-            {isProcessing ? '支付中...' : `确认支付 ¥${amount.toFixed(2)}`}
+            {isProcessing ? '支付中...' :
+              selectedPaymentMethod === PaymentMethod.CREDIT
+                ? `额度支付 ¥${amount.toFixed(2)}`
+                : `微信支付 ¥${amount.toFixed(2)}`
+            }
           </Button>
         )}
 

+ 3 - 0
packages/credit-balance-module-mt/src/routes/index.ts

@@ -8,9 +8,12 @@ import adjustLimitRoutes from './adjust-limit.mt';
 import getBalanceLogsRoutes from './get-balance-logs.mt';
 import paymentRoutes from './payment.mt';
 import checkoutRoutes from './checkout.mt';
+import meRoutes from './me.mt';
 
 // 聚合所有信用额度路由
+// 注意:meRoutes必须在getBalanceRoutes之前,否则/me会被/:userId匹配
 const creditBalanceRoutes = new OpenAPIHono<AuthContext>()
+  .route('/', meRoutes)
   .route('/', getBalanceRoutes)
   .route('/', setLimitRoutes)
   .route('/', adjustLimitRoutes)

+ 85 - 0
packages/credit-balance-module-mt/src/routes/me.mt.ts

@@ -0,0 +1,85 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { authMiddleware } from '@d8d/core-module-mt/auth-module-mt/middleware';
+import { AppDataSource, ErrorSchema } from '@d8d/shared-utils';
+import { AuthContext } from '@d8d/shared-types';
+import { CreditBalanceService } from '../services';
+import { CreditBalanceSchema } from '../schemas';
+
+const meRoute = createRoute({
+  method: 'get',
+  path: '/me',
+  middleware: [authMiddleware],
+  responses: {
+    200: {
+      description: '获取当前用户信用额度成功',
+      content: {
+        'application/json': {
+          schema: CreditBalanceSchema
+        }
+      }
+    },
+    400: {
+      description: '请求参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    401: {
+      description: '认证失败',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    403: {
+      description: '权限不足',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    404: {
+      description: '用户信用额度记录不存在',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器内部错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const meRoutes = new OpenAPIHono<AuthContext>()
+  .openapi(meRoute, async (c) => {
+    const user = c.get('user');
+
+    try {
+      const service = new CreditBalanceService(AppDataSource);
+      const balance = await service.getBalance(user.tenantId, user.id);
+      if (!balance) {
+        return c.json({ code: 404, message: '用户信用额度记录不存在' }, 404);
+      }
+
+      return c.json(CreditBalanceSchema.parse(balance), 200);
+    } catch (error) {
+      console.error('获取当前用户信用额度失败:', error);
+      return c.json(
+        { code: 500, message: error instanceof Error ? error.message : '获取当前用户信用额度失败' },
+        500
+      );
+    }
+  });
+
+export default meRoutes;

+ 71 - 0
packages/credit-balance-module-mt/tests/integration/credit-balance-routes.integration.test.ts

@@ -90,6 +90,77 @@ describe('多租户信用额度API集成测试', () => {
     });
   });
 
+  describe('查询当前用户信用额度 (/me)', () => {
+    it('应该返回404当当前用户信用额度记录不存在时', async () => {
+      const response = await client.me.$get({}, {
+        headers: {
+          'Authorization': `Bearer ${userToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+      if (response.status === 404) {
+        const data = await response.json();
+        expect(data.message).toBe('用户信用额度记录不存在');
+      }
+    });
+
+    it('应该返回当前用户的信用额度', async () => {
+      // 先创建信用额度记录
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const creditBalanceRepo = dataSource.getRepository(CreditBalanceMt);
+      await creditBalanceRepo.save({
+        tenantId: 1,
+        userId: testUser.id,
+        totalLimit: 8000.00,
+        usedAmount: 2000.00,
+        isEnabled: 1
+      });
+
+      const response = await client.me.$get({}, {
+        headers: {
+          'Authorization': `Bearer ${userToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.id).toBeDefined();
+        expect(data.tenantId).toBe(1);
+        expect(data.userId).toBe(testUser.id);
+        expect(data.totalLimit).toBe(8000);
+        expect(data.usedAmount).toBe(2000);
+        expect(data.availableAmount).toBe(6000);
+      }
+    });
+
+    it('应该正确计算可用额度', async () => {
+      // 先创建信用额度记录
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const creditBalanceRepo = dataSource.getRepository(CreditBalanceMt);
+      await creditBalanceRepo.save({
+        tenantId: 1,
+        userId: testUser.id,
+        totalLimit: 5000.00,
+        usedAmount: 1500.00,
+        isEnabled: 1
+      });
+
+      const response = await client.me.$get({}, {
+        headers: {
+          'Authorization': `Bearer ${userToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.availableAmount).toBe(3500); // 5000 - 1500
+      }
+    });
+  });
+
   describe('设置用户信用额度', () => {
     it('应该成功设置用户信用额度', async () => {
       const response = await client[':userId'].$put({

+ 1 - 0
packages/orders-module-mt/package.json

@@ -63,6 +63,7 @@
     "@d8d/file-module-mt": "workspace:*",
     "@d8d/geo-areas-mt": "workspace:*",
     "@d8d/mini-payment-mt": "workspace:*",
+    "@d8d/credit-balance-module-mt": "workspace:*",
     "@hono/zod-openapi": "^1.0.2",
     "typeorm": "^0.3.20",
     "zod": "^4.1.12"

+ 1 - 0
packages/server/package.json

@@ -46,6 +46,7 @@
     "@d8d/merchant-module-mt": "workspace:*",
     "@d8d/orders-module-mt": "workspace:*",
     "@d8d/supplier-module-mt": "workspace:*",
+    "@d8d/credit-balance-module-mt": "workspace:*",
     "@d8d/core-module-mt": "workspace:*",
     "axios": "^1.12.2",
     "bcrypt": "^6.0.0",

+ 6 - 0
pnpm-lock.yaml

@@ -3358,6 +3358,9 @@ importers:
       '@d8d/auth-module-mt':
         specifier: workspace:*
         version: link:../auth-module-mt
+      '@d8d/credit-balance-module-mt':
+        specifier: workspace:*
+        version: link:../credit-balance-module-mt
       '@d8d/delivery-address-module-mt':
         specifier: workspace:*
         version: link:../delivery-address-module-mt
@@ -3440,6 +3443,9 @@ importers:
       '@d8d/core-module-mt':
         specifier: workspace:*
         version: link:../core-module-mt
+      '@d8d/credit-balance-module-mt':
+        specifier: workspace:*
+        version: link:../credit-balance-module-mt
       '@d8d/delivery-address-module-mt':
         specifier: workspace:*
         version: link:../delivery-address-module-mt