Compare commits

...

77 Commits

Author SHA1 Message Date
李东云
c8f3acd62b chore(release): 1.4.0 2025-08-29 04:02:57 +08:00
李东云
d3b861a7f7 feat(invoice): 添加查询用户常用发票地址功能
- 新增 FrequentAddress 类表示常用地址
- 添加 FrequentAddressRepoInterface 接口和 FrequentAddressRepo 实现类
- 在 ConfigProvider 中注册 FrequentAddressRepo
- 编写单元测试验证查询功能
2025-08-29 04:02:48 +08:00
李东云
630d4ba7a4 refactor(invoice): 重构发票相关类的命名空间
-将 ValueObject 目录下的类移动到 Invoice 目录下
- 更新相关的命名空间
- 修改使用这些类的文件,以适应新的命名空间
2025-08-29 03:50:47 +08:00
李东云
b3317c5f8d feat(invoice): 添加创建发票功能
- 新增 CreateInvoiceCmd 类作为创建发票的命令对象
- 创建 Address 类表示发票地址信息
- 实现 Invoice 类作为发票的领域模型
- 添加 InvoiceRepoInterface 接口和 InvoiceRepo 实现类,用于处理发票创建逻辑
- 编写 CreateInvoiceTest测试用例验证发票创建功能
2025-08-29 01:42:42 +08:00
李东云
66a468e702 refactor(invoice): 更新 InvoiceProduct 类的位置
- 将 InvoiceProduct 类从 ValueObject 目录移动到 Aggregate/ValueObject 目录
- 更新类的命名空间以反映新的目录结构
2025-08-29 01:05:32 +08:00
李东云
dd212ad8a2 feat(invoice): 新增发票商品查询功能
- 添加 InvoiceProductRepoInterface 接口用于获取发票商品信息
- 实现 InvoiceProductRepo 类,通过 API 请求获取发票商品数据
- 创建 InvoiceProduct 类作为发票商品的价值对象
- 在 ConfigProvider 中注册 InvoiceProductRepo
- 添加单元测试 QueryCaseInvoiceProductTest 验证功能正确性
2025-08-28 18:52:49 +08:00
李东云
0d4f561ef0 chore(release): 1.3.1 2025-08-27 17:04:54 +08:00
李东云
aa8742923c refactor(account): 优化点数日志仓库接口和实现类
- 在 PointLogRepoInterface 中添加了空行,提高代码可读性
- 在 PointLogRepo 中移除了未使用的 PointType 引用,减少冗余代码
2025-08-27 17:04:40 +08:00
李东云
738d225fd8 chore(release): 1.3.0 2025-08-27 11:31:41 +08:00
李东云
38ce644c73 feat(account): 添加积分日志相关功能
- 新增 PointLog 类用于表示积分日志实体
- 添加 PointAction 枚举类,定义积分操作类型
- 实现 PointLogRepoInterface 接口,提供积分日志查询功能
- 在 ConfigProvider 中注册 PointLogRepo
- 编写单元测试以验证积分日志查询功能
2025-08-27 11:30:48 +08:00
李东云
f2a51fa1da refactor(Account): 重构账户相关类的命名空间
- 将 AccountBalance 和 PointsBalance 类移动到新的命名空间
- 更新相关文件中的 use 语句以适应新的命名空间
- 此改动仅影响文件组织结构,不改变类的功能或接口
2025-08-27 11:06:52 +08:00
李东云
59b5118b93 chore(release): 1.2.7 2025-08-26 16:09:03 +08:00
李东云
c468fdda0e build: 更新发布流程以使用阿里云镜像源
- 修改 /etc/apt/sources.list 文件,将 Debian 官方镜像源替换为阿里云镜像源
- 更新 security.debian.org 为阿里云镜像源
- 这些更改旨在提高构建过程中的下载速度和可靠性
2025-08-26 16:08:50 +08:00
李东云
24709acf09 chore(release): 1.2.6 2025-08-26 16:02:42 +08:00
李东云
f141989a8d refactor(Order): 重构订单资源以支持经典订单解析
- 新增 parseClassicOrder 方法用于解析经典订单
- 修改 toArray 方法,根据订单类型选择性调用 parseClassicOrder
- 引入 Money库以处理金额和货币
2025-08-26 16:02:31 +08:00
李东云
fd6548e144 chore(release): 1.2.5 2025-08-26 15:07:57 +08:00
李东云
acc3121320 fix(Sdk): 调整 WechatRpc 类中数组键名的命名风格
- 将 'goods_detail' 键名改为 'goodsDetail',采用驼峰命名法
- 将 'goods_name' 键名改为 'goodsName',同样采用驼峰命名法
2025-08-26 15:07:39 +08:00
李东云
b521e7c9e0 chore(release): 1.2.4 2025-08-21 18:18:31 +08:00
李东云
2276885ac1 refactor(transaction): 修改交易项中的价格字段名称
- 将 Item 类中的 'price' 字段重命名为 'unit_price'
- 更新 CallbackDomainSvc 中的回调函数,使用新的 'unit_price' 字段
2025-08-21 18:18:27 +08:00
李东云
8d2d3c8ba5 chore(release): 1.2.3 2025-08-20 20:00:51 +08:00
李东云
86939ea421 refactor(transaction): 重构交易信息构建方式
- 将 payType 和 type 字段的值存储为它们的 value 属性
-将 status 字段的值存储为它的 name 属性
2025-08-20 20:00:46 +08:00
李东云
960e3f1877 chore(release): 1.2.2 2025-08-20 19:36:33 +08:00
李东云
2ea5227185 fix(transaction): 修复回调服务中的商品价格字段
- 将 Item 类中的 price 字段从 $item['unit_price'] 修改为 $item['price']
- 确保回调服务正确使用商品的价格信息
2025-08-20 19:36:26 +08:00
李东云
c7f94bb396 chore(release): 1.2.1 2025-08-20 14:40:36 +08:00
李东云
9d61106e2e feat(TransactionRecord): 添加 TransactionRecord 类的 getter 方法
- 为 TransactionRecord 类添加了多个 getter 方法,以获取私有属性的值
- 新增的方法包括 getOrderNo、getUid、getPayType、getAction、getStatus、getSource、getItems、getTransactions、getRefunds、getExternal、getExternalId、getCreatedAt、getOccurredAt 和 getLastRefundedAt
- 这些方法提供了对交易记录各个字段的访问能力,便于在域内和其他对象进行交互
2025-08-20 14:40:20 +08:00
李东云
c3a256cb3d build(deps): 更新多个依赖至最新版本
- 更新 ergebnis/http-method 从 2.5.0 到 2.6.0
- 更新 react/promise 从 v2.11.0 到 v3.2.0
- 更新 singularity/hdk-core 从 1.0.0-beta.14 到1.0.1
- 更新多个 symfony/polyfill 包至最新版本
2025-08-20 14:40:04 +08:00
李东云
245f13b26d chore(release): 1.2.0 2025-08-20 13:50:37 +08:00
李东云
fc5cf453ae test: 更新 LuxPay 回调测试用例
移除了 LuxPayCallbackTest 中冗余的 only() 方法调用,简化了测试用例的编写。
2025-08-20 13:50:05 +08:00
李东云
8f71cbeb65 feat(transaction): 添加交易相关实体和回调服务
- 新增 CardTransaction、PointTransaction、Item、Transaction 和 TransactionRecord 类
- 实现 CallbackDomainSvc 类的 callback 方法,用于处理回调请求
- 添加 LuxPayCallbackTest 测试类,验证回调解析功能
2025-08-20 13:48:44 +08:00
李东云
9517b5bd62 chore(release): 1.1.0 2025-08-18 20:00:54 +08:00
李东云
c8ee3bb23b feat(pay): 新增消费命令和通用订单 DTO
- 添加 ConsumeCmd 类用于消费操作
- 重构 RechargeCmd 类,使其返回自身以便链式调用
- 将 RechargeDto 重命名为 OrderDto,作为通用订单数据传输对象
- 更新 OrderRepoInterface 接口,使用新的命令和 DTO 类
- 修改 AccountBalanceRepo、OrderRepo 和 ProductRepo,移除不必要的选项参数
- 新增 CreateConsumptionOrderTest 测试用例
- 更新 CreateRechargeOrderTest 测试用例以使用新的命令和 DTO 类
2025-08-18 19:58:42 +08:00
李东云
80dc1a3706 feat(pay): 添加充值功能
- 新增 RechargeCmd 类用于处理充值命令
- 新增 RechargeDto 类用于表示充值交易数据
- 新增 OrderAction、OrderStatus 和 PayType 枚举类
- 新增 OrderRepoInterface接口和 OrderRepo 实现类,用于处理订单相关操作
- 更新 ConfigProvider,添加新依赖项
- 新增 CreateRechargeOrderTest 测试用例
2025-08-18 19:12:07 +08:00
李东云
ca6000a5c8 chore(release): 1.0.0 2025-08-18 18:32:55 +08:00
李东云
1814adc30e feat(product): 增加 EMA 产品查询功能
- 移除了未使用的 ProductItem 引用
- 修改了 findEmaProduct 方法的参数,从 currentVersion 改为 uid
- 更新了 findEmaProduct 方法的实现,使用 uid 参数进行查询
- 重构了 RechargeProduct 对象的构建方式,提高了代码可读性
- 更新了单元测试,增加了 EMA 产品查询的测试用例
2025-08-18 16:57:14 +08:00
李东云
06dc4a2e65 feat(product): 新增充值产品相关数据结构和接口
- 添加 RechargeProduct、ProductItem、RechargeEffect 等实体和值对象
- 实现充值产品相关的数据传输对象 (DTO)
- 定义充值产品仓库接口并提供具体实现
- 增加相关单元测试
2025-08-18 16:41:37 +08:00
李东云
a2fc4cecf8 feat(pay): 添加产品类型枚举和兑换率接口
- 新增 ProductType 枚举类,用于定义产品类型
- 添加 ExchangeRepoInterface 接口,用于获取兑换率
- 实现 QueryPointRateTest 测试用例,验证兑换率查询功能
2025-08-18 15:33:13 +08:00
李东云
1bb8666b5e refactor(pay): 调整请求头构建逻辑
- 将 RequestHeaderBuilder trait 移至 AbstractRepo 中
- 删除 AccountBalanceRepo 中的重复 trait 引用
- 此改动简化了代码结构,避免了重复代码
2025-08-18 14:42:42 +08:00
李东云
5559f2c2a5 refactor(pay): 重构账户余额查询功能
- 新增 AbstractRepo 抽象类,用于统一封装请求服务
- 修正 Infrastructure 目录名称拼写错误
- 更新命名空间和类名引用
- 简化 AccountBalanceRepo 类的构造方法
2025-08-18 14:41:06 +08:00
李东云
4f0d402538 refactor(pay): 重构账户余额查询接口并添加单元测试
- 创建 RequestHeaderBuilder Trait 以简化请求头构建逻辑
- 在 AccountBalanceRepo 中使用 RequestHeaderBuilder Trait
- 添加 QueryPointBalanceTest 单元测试用例
2025-08-18 14:23:14 +08:00
李东云
336cb3a9b9 test(pay): 添加账户信息查询功能测试
- 新增 QueryAccountInformationTest 以测试账户信息查询功能
- 实现 getAccount 方法的单元测试
- 添加测试引导文件和 HTTP 测试用例基类
- 更新 composer.json 和 composer.lock 文件
2025-08-18 14:08:30 +08:00
李东云
3d91f76dc0 feat(domain): 新增账户余额和积分相关实体及接口
- 添加了 PointsBalance、AccountBalance、PointType 等实体类
- 新增了 AccountRepoInterface 接口及其实现类 AccountBalanceRepo
- 引入了 Carbon库用于处理时间相关的逻辑
- 删除了未使用的 WechatService 类
- 更新了 ConfigProvider 以注册新的 Repository
- 修改了 composer.json 和 composer.lock 文件,添加了新的依赖
2025-08-18 09:49:21 +08:00
李东云
7f7fe100e7 build(deps): 更新 Hyperf 和 Symfony 组件
- 更新 Hyperf constants 从 v3.1.42 到 v3.1.57
- 更新 Hyperf exception-handler 从 v3.1.42 到 v3.1.57
- 更新 Hyperf guzzle 从 v3.1.42 到 v3.1.58
- 更新 Hyperf http-message 从 v3.1.48 到 v3.1.57
- 更新 Hyperf http-server 从 v3.1.55 到 v3.1.59
- 更新 singularity/hdk-core从 1.0.0-beta.11 到 1.0.0-beta.14- 更新 Symfony 组件到最新版本
- 更新 fidry/cpu-core-counter 从 1.1.0 到 1.2.0
- 更新 myclabs/deep-copy 从 1.13.1 到 1.13.3
2025-08-17 09:16:38 +08:00
李东云
a0baa27c08 chore(release): 1.0.0-beta.5 2025-07-17 11:42:14 +08:00
李东云
14fc65dc34 feat(Goods): 添加 Stripe 信息并优化金额处理
- 在 Goods 类中添加 stripe 属性,包含 Stripe 的 id、product_id 和 price_id
- 优化 amount 属性的处理方式,使用命名参数提高代码可读性
- 使用 when 方法条件性地加载 Stripe 信息,提高数据处理灵活性
2025-07-17 11:41:52 +08:00
李东云
678881241f chore(release): 1.0.0-beta.4 2025-07-11 16:50:10 +08:00
李东云
8cbc81d3b7 feat(OrderRpc): 增加站点和环境参数并优化请求头
- 在 OrderRpc 类中添加了站点 (site) 和环境 (env) 参数
- 优化了请求头,包括接受语言、服务提供商 ID、站点和环境
- 更新了 composer.json 中的依赖版本
2025-07-11 16:49:47 +08:00
李东云
b669931e78 chore(release): 1.0.0-beta.3 2025-07-09 02:19:07 +08:00
李东云
ce1ad9561e feat(order): 兼容 v2 创建订单
- 新增 OrderRpc 类实现订单创建功能
- 新增 Order 资源类用于订单数据处理
- 更新 PaymentMethod 枚举,增加新的支付方式
- 修改 WebhooksNotificationHandler 特性,使用新的 Order 资源类
2025-07-09 02:17:54 +08:00
李东云
427b6cd7af feat(Order): 增加订单商品名称和数量字段
- 在 Order 类中添加了 $goodsName 和 $number 字段
- 更新了 toArray 方法,包含新的商品名称和数量字段
- 调整了类属性注释,明确了 amount 字段的类型
2024-12-09 16:19:33 +08:00
ch4o5
047b0ce67a chore(release): 1.0.0-beta.2 2024-12-06 10:30:44 +00:00
李东云
7216e2c207 feat(order): 增加订单商品数量参数并优化代码格式
- 在 Order 类中添加 number 字段,用于表示商品数量
- 修改 StripeRpc 和 WechatRpc 类,增加商品数量参数
-优化代码格式,调整缩进和空格
2024-12-06 18:29:59 +08:00
ch4o5
621fb057e4 chore(release): 1.0.0-beta.1 2024-02-27 07:36:20 +00:00
李东云
1065741a08 build(hyperf): 更新 guzzle 版本 2024-02-27 15:36:11 +08:00
ch4o5
271cdb3c0a chore(release): 1.0.0-beta.0 2024-02-27 07:29:22 +00:00
李东云
816649784d build(release): 发布 beta 2024-02-27 15:28:23 +08:00
李东云
d12858dda8 build(hyperf): 迁移到 hyperf3.1 2024-02-27 15:27:33 +08:00
ch4o5
4d1b5d2ab7 chore(release): 1.0.0-alpha.12 2023-10-23 12:52:55 +00:00
李东云
8cb8f7eb9e ci: 修复内容为空的问题 2023-10-23 20:52:47 +08:00
ch4o5
c13fc7e800 chore(release): 1.0.0-alpha.11 2023-10-23 12:27:29 +00:00
李东云
8d6c0189a0 ci: 迁移到 Gitea Actions 2023-10-23 20:27:14 +08:00
ch4o5
dd690e228e chore(release): 1.0.0-alpha.10 2023-09-26 08:25:04 +00:00
李东云
46ecc8117c build(composer): 更新依赖 2023-09-26 16:24:54 +08:00
ch4o5
456294c1a4 chore(release): 1.0.0-alpha.9 2023-09-26 04:00:52 +00:00
李东云
a6898bb1a0 feat(stripe): 创建 session 接口增加了语言选项 2023-09-26 12:00:39 +08:00
ch4o5
999f3633a5 chore(release): 1.0.0-alpha.8 2023-09-22 10:23:09 +00:00
李东云
6431fc010f fix(order): 修复类型错误的问题 2023-09-22 18:22:59 +08:00
ch4o5
cb0b3a3548 chore(release): 1.0.0-alpha.7 2023-09-22 09:41:40 +00:00
李东云
b8ebebde3c perf(callback): 增加了共用的通知业务系统的方法 2023-09-22 17:41:32 +08:00
ch4o5
1dba3b84a4 chore(release): 1.0.0-alpha.6 2023-09-22 08:15:10 +00:00
李东云
6cecc96c5f build(composer): 兼容 hyperf 的 RequestInterface 2023-09-22 16:15:01 +08:00
ch4o5
448569583c chore(release): 1.0.0-alpha.5 2023-09-22 07:43:53 +00:00
李东云
99af043376 feat(stripe): 增加 callback 对请求的转化 2023-09-22 15:42:22 +08:00
李东云
90dde97bc9 build(composer): 增加 hyperf/contract 包,更新依赖 2023-09-22 15:39:42 +08:00
ch4o5
f7e1386c78 chore(release): 1.0.0-alpha.4 2023-09-20 08:43:10 +00:00
李东云
a6b33ed0ab docs(resource): 更新了资源的字段注释 2023-09-20 16:43:00 +08:00
ch4o5
a532e21f02 chore(release): 1.0.0-alpha.3 2023-09-20 08:32:15 +00:00
李东云
64dd159f9c feat(stripe): 实现了 stripe 获取配置信息的服务方法 2023-09-20 16:30:54 +08:00
87 changed files with 9974 additions and 1325 deletions

View File

@@ -0,0 +1,30 @@
name: Release development version to registry
on:
push:
tags:
- '**.**'
jobs:
Publish on Tagged:
runs-on: ubuntu-latest
steps:
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v4
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- name: Zip files in the repository
run: |
sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list
sed -i 's|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list
apt-get update
apt-get install zip
zip -r dist.zip *
- name: Publish to registry
run: |
curl --user ch4o5:4fd300672472e666014314c1c94c604c634165a9 \
--upload-file ./dist.zip \
https://nest.doylee.cn/api/packages/HDK/composer?version=${{ gitea.ref_name }}
- run: echo "🍏 This job's status is ${{ job.status }}."

View File

@@ -1,4 +1,258 @@
# 版本更新日志
## [1.4.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.3.1...v1.4.0) (2025-08-28)
### ♻️ Code Refactoring | 代码重构
* **invoice:** 更新 InvoiceProduct 类的位置 ([66a468e](http://124.126.16.154:8888/singularity/hdk-pay/commit/66a468e702a0b550ce7445c66164b10b4edcc41a))
* **invoice:** 重构发票相关类的命名空间 ([630d4ba](http://124.126.16.154:8888/singularity/hdk-pay/commit/630d4ba7a427bc7bece24093f783634d3e707b2f))
### ✨ Features | 新功能
* **invoice:** 新增发票商品查询功能 ([dd212ad](http://124.126.16.154:8888/singularity/hdk-pay/commit/dd212ad8a26f03f2b6e204e5485120e267ba73a2))
* **invoice:** 添加创建发票功能 ([b3317c5](http://124.126.16.154:8888/singularity/hdk-pay/commit/b3317c5f8d65aae811130f4af9d09cce514047ff))
* **invoice:** 添加查询用户常用发票地址功能 ([d3b861a](http://124.126.16.154:8888/singularity/hdk-pay/commit/d3b861a7f75281168ec16562aec290f1904fda7e))
### [1.3.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.3.0...v1.3.1) (2025-08-27)
### ♻️ Code Refactoring | 代码重构
* **account:** 优化点数日志仓库接口和实现类 ([aa87429](http://124.126.16.154:8888/singularity/hdk-pay/commit/aa8742923c38ae90386c83b8a280a47dacc3537b))
## [1.3.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.7...v1.3.0) (2025-08-27)
### ♻️ Code Refactoring | 代码重构
* **Account:** 重构账户相关类的命名空间 ([f2a51fa](http://124.126.16.154:8888/singularity/hdk-pay/commit/f2a51fa1da67d5169761bc47cbe2b6da605051ae))
### ✨ Features | 新功能
* **account:** 添加积分日志相关功能 ([38ce644](http://124.126.16.154:8888/singularity/hdk-pay/commit/38ce644c7351740641901a0497351f45c0119339))
### [1.2.7](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.6...v1.2.7) (2025-08-26)
### 📦‍ Build System | 打包构建
* 更新发布流程以使用阿里云镜像源 ([c468fdd](http://124.126.16.154:8888/singularity/hdk-pay/commit/c468fdda0e4fd8bf5b2253527b368362ccb81954))
### [1.2.6](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.5...v1.2.6) (2025-08-26)
### ♻️ Code Refactoring | 代码重构
* **Order:** 重构订单资源以支持经典订单解析 ([f141989](http://124.126.16.154:8888/singularity/hdk-pay/commit/f141989a8d7c6055d0d740619cc0660a1ea0ef7f))
### [1.2.5](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.4...v1.2.5) (2025-08-26)
### 🐛 Bug Fixes | Bug 修复
* **Sdk:** 调整 WechatRpc 类中数组键名的命名风格 ([acc3121](http://124.126.16.154:8888/singularity/hdk-pay/commit/acc3121320d74e2ea3423e3502779afde9675ee8))
### [1.2.4](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.3...v1.2.4) (2025-08-21)
### ♻️ Code Refactoring | 代码重构
* **transaction:** 修改交易项中的价格字段名称 ([2276885](http://124.126.16.154:8888/singularity/hdk-pay/commit/2276885ac1759a330eb6fb73e1158235edcac941))
### [1.2.3](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.2...v1.2.3) (2025-08-20)
### ♻️ Code Refactoring | 代码重构
* **transaction:** 重构交易信息构建方式 ([86939ea](http://124.126.16.154:8888/singularity/hdk-pay/commit/86939ea421d2f14c621da4e3bb86e3ab8d8b7d33))
### [1.2.2](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.1...v1.2.2) (2025-08-20)
### 🐛 Bug Fixes | Bug 修复
* **transaction:** 修复回调服务中的商品价格字段 ([2ea5227](http://124.126.16.154:8888/singularity/hdk-pay/commit/2ea52271852e4e009b044fb9baeb222b3a54c111))
### [1.2.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.0...v1.2.1) (2025-08-20)
### 📦‍ Build System | 打包构建
* **deps:** 更新多个依赖至最新版本 ([c3a256c](http://124.126.16.154:8888/singularity/hdk-pay/commit/c3a256cb3d5e41a29a4969442b5a7f27c9000576))
### ✨ Features | 新功能
* **TransactionRecord:** 添加 TransactionRecord 类的 getter 方法 ([9d61106](http://124.126.16.154:8888/singularity/hdk-pay/commit/9d61106e2e8b04559fb34b606fb489a2890c34d5))
## [1.2.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.1.0...v1.2.0) (2025-08-20)
### ✨ Features | 新功能
* **transaction:** 添加交易相关实体和回调服务 ([8f71cbe](http://124.126.16.154:8888/singularity/hdk-pay/commit/8f71cbeb653d0722f231c300c2a1fabe79764580))
### ✅ Tests | 测试
* 更新 LuxPay 回调测试用例 ([fc5cf45](http://124.126.16.154:8888/singularity/hdk-pay/commit/fc5cf453aed133552514e1b60699fe63d2178543))
## [1.1.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0...v1.1.0) (2025-08-18)
### ✨ Features | 新功能
* **pay:** 新增消费命令和通用订单 DTO ([c8ee3bb](http://124.126.16.154:8888/singularity/hdk-pay/commit/c8ee3bb23bf41f39a64ca08db4a005d2893c1554))
* **pay:** 添加充值功能 ([80dc1a3](http://124.126.16.154:8888/singularity/hdk-pay/commit/80dc1a3706fa973f3360c70f1db34d403eb741df))
## [1.0.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-beta.5...v1.0.0) (2025-08-18)
### 📦‍ Build System | 打包构建
* **deps:** 更新 Hyperf 和 Symfony 组件 ([7f7fe10](http://124.126.16.154:8888/singularity/hdk-pay/commit/7f7fe100e71e98557de0399702ef2e3cb17d3b42))
### ✅ Tests | 测试
* **pay:** 添加账户信息查询功能测试 ([336cb3a](http://124.126.16.154:8888/singularity/hdk-pay/commit/336cb3a9b9baa0b9e47ca7cf5bb5a6bc04d48c90))
### ♻️ Code Refactoring | 代码重构
* **pay:** 调整请求头构建逻辑 ([1bb8666](http://124.126.16.154:8888/singularity/hdk-pay/commit/1bb8666b5e4432d720d9b8256baa06d4f570718a))
* **pay:** 重构账户余额查询功能 ([5559f2c](http://124.126.16.154:8888/singularity/hdk-pay/commit/5559f2c2a59326a8aa209ffb00693523d39fc319))
* **pay:** 重构账户余额查询接口并添加单元测试 ([4f0d402](http://124.126.16.154:8888/singularity/hdk-pay/commit/4f0d402538410020c89910b48bb418f2d306b1a0))
### ✨ Features | 新功能
* **domain:** 新增账户余额和积分相关实体及接口 ([3d91f76](http://124.126.16.154:8888/singularity/hdk-pay/commit/3d91f76dc0657bd24643ee18a3192ad631001530))
* **pay:** 添加产品类型枚举和兑换率接口 ([a2fc4ce](http://124.126.16.154:8888/singularity/hdk-pay/commit/a2fc4cecf8105616b440add0bfaab93b91abce09))
* **product:** 增加 EMA 产品查询功能 ([1814adc](http://124.126.16.154:8888/singularity/hdk-pay/commit/1814adc30e0c3801c11bfb270a666ebd9348d115))
* **product:** 新增充值产品相关数据结构和接口 ([06dc4a2](http://124.126.16.154:8888/singularity/hdk-pay/commit/06dc4a2e6555f92aab09271a3f8cd09fc8960e95))
## [1.0.0-beta.5](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2025-07-17)
### ✨ Features | 新功能
* **Goods:** 添加 Stripe 信息并优化金额处理 ([14fc65d](http://124.126.16.154:8888/singularity/hdk-pay/commit/14fc65dc3446d6dce489edf2613c1967a50274bf))
## [1.0.0-beta.4](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2025-07-11)
### ✨ Features | 新功能
* **OrderRpc:** 增加站点和环境参数并优化请求头 ([8cbc81d](http://124.126.16.154:8888/singularity/hdk-pay/commit/8cbc81d3b70ca408fbe8e905200d6a89f22cda40))
## [1.0.0-beta.3](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2025-07-08)
### ✨ Features | 新功能
* **order:** 兼容 v2 创建订单 ([ce1ad95](http://124.126.16.154:8888/singularity/hdk-pay/commit/ce1ad9561e0a780f670d766b8d4c841130a18697))
* **Order:** 增加订单商品名称和数量字段 ([427b6cd](http://124.126.16.154:8888/singularity/hdk-pay/commit/427b6cd7af1800c4c4d5ba685686c61cc6fbef52))
## [1.0.0-beta.2](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2024-12-06)
### ✨ Features | 新功能
* **order:** 增加订单商品数量参数并优化代码格式 ([7216e2c](http://124.126.16.154:8888/singularity/hdk-pay/commit/7216e2c20763801d9d3532aad4b4463afb6fcc7e))
## [1.0.0-beta.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-beta.0...v1.0.0-beta.1) (2024-02-27)
### 📦‍ Build System | 打包构建
* **hyperf:** 更新 guzzle 版本 ([1065741](http://124.126.16.154:8888/singularity/hdk-pay/commit/1065741a08cb8ac355cfa5b0e6f207ce5a8ed228))
## [1.0.0-beta.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.12...v1.0.0-beta.0) (2024-02-27)
### 📦‍ Build System | 打包构建
* **hyperf:** 迁移到 hyperf3.1 ([d12858d](http://124.126.16.154:8888/singularity/hdk-pay/commit/d12858dda85fd2ce66ca1daff7e27eb381450d2f))
* **release:** 发布 beta ([8166497](http://124.126.16.154:8888/singularity/hdk-pay/commit/816649784d5a85b7bd2d61e5415258c0b514dcc1))
## [1.0.0-alpha.12](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.11...v1.0.0-alpha.12) (2023-10-23)
### 👷 Continuous Integration | CI 配置
* 修复内容为空的问题 ([8cb8f7e](http://124.126.16.154:8888/singularity/hdk-pay/commit/8cb8f7eb9e8d97408a3a71fec75699579b058c09))
## [1.0.0-alpha.11](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.10...v1.0.0-alpha.11) (2023-10-23)
### 👷 Continuous Integration | CI 配置
* 迁移到 Gitea Actions ([8d6c018](http://124.126.16.154:8888/singularity/hdk-pay/commit/8d6c0189a0198d5d6e11e90cd8293107f414273c))
## [1.0.0-alpha.10](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.9...v1.0.0-alpha.10) (2023-09-26)
### 📦‍ Build System | 打包构建
* **composer:** 更新依赖 ([46ecc81](http://124.126.16.154:8888/singularity/hdk-pay/commit/46ecc8117ccb463ed498fa82e84bf41d9ebb752b))
## [1.0.0-alpha.9](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.8...v1.0.0-alpha.9) (2023-09-26)
### ✨ Features | 新功能
* **stripe:** 创建 session 接口增加了语言选项 ([a6898bb](http://124.126.16.154:8888/singularity/hdk-pay/commit/a6898bb1a097f3c66d3ef14f375725fdeaed4df5))
## [1.0.0-alpha.8](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.7...v1.0.0-alpha.8) (2023-09-22)
### 🐛 Bug Fixes | Bug 修复
* **order:** 修复类型错误的问题 ([6431fc0](http://124.126.16.154:8888/singularity/hdk-pay/commit/6431fc010fe3e989773b84506df62d3c8f7ffc81))
## [1.0.0-alpha.7](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.6...v1.0.0-alpha.7) (2023-09-22)
### ⚡ Performance Improvements | 性能优化
* **callback:** 增加了共用的通知业务系统的方法 ([b8ebebd](http://124.126.16.154:8888/singularity/hdk-pay/commit/b8ebebde3c5835bb0abc6e5078517c82341db95f))
## [1.0.0-alpha.6](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.5...v1.0.0-alpha.6) (2023-09-22)
### 📦‍ Build System | 打包构建
* **composer:** 兼容 hyperf 的 RequestInterface ([6cecc96](http://124.126.16.154:8888/singularity/hdk-pay/commit/6cecc96c5ffc223a1a72df6abd1f07d54cb7953d))
## [1.0.0-alpha.5](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.4...v1.0.0-alpha.5) (2023-09-22)
### 📦‍ Build System | 打包构建
* **composer:** 增加 hyperf/contract 包,更新依赖 ([90dde97](http://124.126.16.154:8888/singularity/hdk-pay/commit/90dde97bc90d33917eb2c1a4d09779dd45776d30))
### ✨ Features | 新功能
* **stripe:** 增加 callback 对请求的转化 ([99af043](http://124.126.16.154:8888/singularity/hdk-pay/commit/99af043376c4b44412a275d1b778e3e3584f5f96))
## [1.0.0-alpha.4](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.3...v1.0.0-alpha.4) (2023-09-20)
### ✏️ Documentation | 文档
* **resource:** 更新了资源的字段注释 ([a6b33ed](http://124.126.16.154:8888/singularity/hdk-pay/commit/a6b33ed0abed2d23b041407c0a6a58e8405e075c))
## [1.0.0-alpha.3](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.2...v1.0.0-alpha.3) (2023-09-20)
### ✨ Features | 新功能
* **stripe:** 实现了 stripe 获取配置信息的服务方法 ([64dd159](http://124.126.16.154:8888/singularity/hdk-pay/commit/64dd159f9c153183d22e51390a8d670e5bfd7385))
## [1.0.0-alpha.2](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2023-09-19)

View File

@@ -8,33 +8,42 @@
"Singularity\\HDK\\Pay\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Singularity\\HDK\\Test\\Pay\\": "tests/"
}
},
"extra": {
"hyperf": {
"config": "Singularity\\HDK\\Pay\\ConfigProvider"
}
},
"require": {
"php": ">=8.1",
"composer/composer": ">=2.5.8",
"hyperf/guzzle": "~3.0.0",
"php": ">=8.2",
"ext-bcmath": "*",
"composer/composer": ">=2.7.7",
"hyperf/config": "~3.1.0",
"hyperf/constants": "3.1.*",
"hyperf/contract": "~3.1.0",
"hyperf/di": "~3.1.0",
"hyperf/guzzle": "3.1.*",
"hyperf/http-server": "^3.1",
"moneyphp/money": "^4.1",
"singularity/hdk-core": "^1.0.0",
"hyperf/contract": "~3.0.0",
"hyperf/config": "~3.0.0",
"hyperf/di": "~3.0.0"
"nesbot/carbon": "*",
"singularity/hdk-core": "^1.0.0"
},
"require-dev": {
"cooper/hyperf-pest": "^1.1",
"firebase/php-jwt": "^6.8.0",
"phpunit/phpunit": "^9.6.9",
"swoole/ide-helper": "^4.8.13"
"hyperf/testing": "^3.1",
"friendsofhyperf/pest-plugin-hyperf": "3.1.*",
"swoole/ide-helper": "^4.8.13",
"nesbot/carbon": "^2.0"
},
"minimum-stability": "dev",
"minimum-stability": "beta",
"prefer-stable": true,
"config": {
"optimize-autoloader": true,
"sort-packages": true,
"secure-http": false,
"allow-plugins": {
"pestphp/pest-plugin": true
}
@@ -46,20 +55,20 @@
"test": [
"rm -rf runtime",
"Composer\\Config::disableProcessTimeout",
"co-pest --prepend tests/bootstrap.php -c phpunit.xml --colors=always"
"APP_STATUS=false pest --coroutine --prepend=tests/bootstrap.php --colors=always $1"
],
"cs-fix": "php-cs-fixer fix $1",
"analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config"
},
"repositories": {
"lux-map": {
"nest": {
"type": "composer",
"url": "https://satis.luxcreo.cn/"
"url": "https://nest.doylee.cn/api/packages/HDK/composer"
},
"packagist": {
"type": "composer",
"url": "https://mirrors.cloud.tencent.com/composer/"
"url": "https://mirrors.aliyun.com/composer/"
}
},
"version": "1.0.0-alpha.2"
"version": "1.4.0"
}

7726
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php"
colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd">
<coverage/>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/autoload.php"
colors="true"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
>
<testsuites>
<testsuite name="Pest">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<source/>
</phpunit>

View File

@@ -1,10 +1,9 @@
#!/usr/bin/env sh
docker run \
--pull always \
-ti --rm --name "hdk-pay" \
-w "/srv/www" \
-v "$(pwd)":/srv/www \
-v ~/.ssh:/root/.ssh \
harbor.luxcreo.cn/library/hyperf:8.1-swoole /bin/ash
harbor.luxcreo.cn/library/hyperf:8.2-swoole /bin/ash

View File

@@ -2,4 +2,4 @@
docker run --rm -it \
-v $(pwd):/app -e "GIT_AUTHOR_NAME=ch4o5" -e "EMAIL=dongyun.li@luxcreo.ai" \
detouched/standard-version:latest -p=alpha $1
detouched/standard-version:latest -p=beta $1

View File

@@ -0,0 +1,41 @@
<?php
/**
* ConsumeCmd.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Application\Command;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
use Singularity\HDK\Pay\Enum\PaymentMethod;
final class ConsumeCmd
{
public array $items = [];
public function __construct(
public string $uid,
public PayType $type,
public PaymentMethod|PointType $method,
public array $external,
public ?string $externalID = null,
public ?string $remark = null,
) {}
public function addItem(string $name, float $unitPrice, int $quantity = 1): ConsumeCmd
{
$this->items[] = [
'name' => $name,
'quantity' => $quantity,
'unit_price' => $unitPrice,
];
return $this;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* CreateInvoiceCmd.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Application\Command;
final readonly class CreateInvoiceCmd {
public function __construct(
public string $uid,
public string $caseId,
public bool $setFreqInvAddr,
public string $receiver,
public string $patientName,
public string $address,
public string $city,
public string $state,
public string $country,
public string $zipCode,
) {}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* RechargeCmd.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Application\Command;
final class RechargeCmd {
public array $items = [];
public function __construct(public string $uid) {}
public function addItem(int $productId, int $quantity = 1): RechargeCmd
{
$this->items[] = [
'product_id' => $productId,
'quantity' => $quantity,
];
return $this;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* AbstractDto.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Application\Dto;
use Hyperf\Codec\Json;
use Hyperf\Contract\Jsonable;
use Swoole\ArrayObject;
abstract class AbstractDto extends ArrayObject implements Jsonable
{
public function __toString(): string
{
return $this->toJson();
}
public function toJson(): string
{
return Json::encode($this->toArray());
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* RechargeProductsDto.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Application\Dto\Product;
use Singularity\HDK\Pay\Application\Dto\AbstractDto;
use Singularity\HDK\Pay\Domain\Product\Aggregate\Entities\ProductItem;
use Singularity\HDK\Pay\Domain\Product\Aggregate\RechargeProduct;
final class RechargeProductsDto extends AbstractDto
{
public function __construct(RechargeProduct $product)
{
parent::__construct(array_filter([
'one_time' => $this->parseProductItem($product->getOneTime()),
'renew' => $this->parseProductItem($product->getRenew()),
'plan' => array_map(
fn(ProductItem $item) => $this->parseProductItem($item),
$product->getPlans(),
),
'package' => array_map(
fn(ProductItem $item) => $this->parseProductItem($item),
$product->getPackages(),
),
]));
}
private function parseProductItem(?ProductItem $product): array
{
if (is_null($product)) {
return [];
}
$effect = $product->effect;
$price = $product->unitPrice;
return [
'id' => $product->id,
'name' => $product->description,
'currency' => $price->getCurrency()->getCode(),
'price' => (float)$price->getAmount(),
'total_points' => $effect->getPointTotal(),
'bonus_rate_pct' => (float)(bcmul(
num1: $effect->getPointTotal() == 0
? '0'
: bcdiv(
(string)$effect->pointBonus,
(string)$effect->getPointTotal(),
3,
),
num2: '100',
scale: 0,
)),
'point' => [
'total' => $effect->getPointTotal(),
'number' => $effect->pointBasic,
'bonus' => $effect->pointBonus,
],
];
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* RechargeDto.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Application\Dto\Transaction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderAction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
final readonly class OrderDto
{
public function __construct(
private string $orderNo,
private OrderAction $action,
private PayType $payType,
private OrderStatus $status,
) {}
public function getOrderNo(): string
{
return $this->orderNo;
}
public function getAction(): OrderAction
{
return $this->action;
}
public function getPayType(): PayType
{
return $this->payType;
}
public function getStatus(): OrderStatus
{
return $this->status;
}
}

View File

@@ -6,6 +6,21 @@ namespace Singularity\HDK\Pay;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Framework\Logger\StdoutLogger;
use Singularity\HDK\Pay\Domain\Account\Repository\AccountRepoInterface;
use Singularity\HDK\Pay\Domain\Account\Repository\PointLogRepoInterface;
use Singularity\HDK\Pay\Domain\Invoice\Repository\FrequentAddressRepoInterface;
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceProductRepoInterface;
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceRepoInterface;
use Singularity\HDK\Pay\Domain\Product\Repository\ExchangeRepoInterface;
use Singularity\HDK\Pay\Domain\Product\Repository\RechargeProductRepoInterface;
use Singularity\HDK\Pay\Domain\Transaction\Repository\OrderRepoInterface;
use Singularity\HDK\Pay\Infrastructure\Repository\AccountBalanceRepo;
use Singularity\HDK\Pay\Infrastructure\Repository\FrequentAddressRepo;
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceProductRepo;
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceRepo;
use Singularity\HDK\Pay\Infrastructure\Repository\OrderRepo;
use Singularity\HDK\Pay\Infrastructure\Repository\PointLogRepo;
use Singularity\HDK\Pay\Infrastructure\Repository\ProductRepo;
/**
* ConfigProvider.php@HyperfAuth
@@ -18,11 +33,27 @@ class ConfigProvider
{
public function __invoke(): array
{
/** @noinspection PhpUndefinedConstantInspection */
return [
// 合并到 config/autoload/dependencies.php 文件
'dependencies' => [
StdoutLoggerInterface::class => StdoutLogger::class,
// Repo
// account
AccountRepoInterface::class => AccountBalanceRepo::class,
PointLogRepoInterface::class => PointLogRepo::class,
// product
ExchangeRepoInterface::class => ProductRepo::class,
RechargeProductRepoInterface::class => ProductRepo::class,
// transaction
OrderRepoInterface::class => OrderRepo::class,
// invoice
InvoiceProductRepoInterface::class => InvoiceProductRepo::class,
InvoiceRepoInterface::class => InvoiceRepo::class,
FrequentAddressRepoInterface::class => FrequentAddressRepo::class,
],
// 合并到 config/autoload/annotations.php 文件
'annotations' => [

View File

@@ -0,0 +1,44 @@
<?php
/**
* AccountBalance.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/17
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Account\Aggregate\Account;
/**
* Singularity\HDK\Pay\Domain\Account\Aggregate\AccountBalance@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/17
*
* @template TPointType of string
* @template TPointsBalances of array<TPointType, PointsBalance>
*/
final readonly class AccountBalance
{
/**
* @param string $uid
* @param PointsBalance $pointsBalances
*/
public function __construct(
private string $uid,
private array $pointsBalances = [],
) {}
public function getUid(): string
{
return $this->uid;
}
public function getPointsBalances(): array
{
return $this->pointsBalances;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* PointsBalance.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Account\Aggregate\Account;
use Carbon\Carbon;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
final readonly class PointsBalance
{
public function __construct(
private PointType $type,
private float $total,
private float $cost,
public float $amount,
private string|null $version,
private Carbon|null $expiredAt,
) {
}
public function getType(): PointType
{
return $this->type;
}
public function getTotal(): float
{
return $this->total;
}
public function getCost(): float
{
return $this->cost;
}
public function getVersion(): ?string
{
return $this->version;
}
public function getExpiredAt(): ?Carbon
{
return $this->expiredAt;
}
public function getAmount(): float
{
return $this->amount;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* PointLog.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/21
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog;
use Carbon\Carbon;
use Singularity\HDK\Pay\Domain\AggregateRoot;
final class PointLog extends AggregateRoot
{
public function __construct(
private readonly int $id,
private readonly string $caseId,
private readonly string $uid,
private readonly float $credits,
private readonly string $source,
private readonly string $operator,
private readonly string $description,
private readonly Carbon $date,
) {}
public function getDescription(): string
{
return $this->description;
}
public function getSource(): string
{
return $this->source;
}
public function getDate(): Carbon
{
return $this->date;
}
public function getUid(): string
{
return $this->uid;
}
public function getCredits(): float
{
return $this->credits;
}
public function getOperator(): string
{
return $this->operator;
}
public function getCaseId(): ?string
{
return $this->caseId;
}
public function getId(): ?int
{
return $this->id;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* PointAction.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/21
*/
namespace Singularity\HDK\Pay\Domain\Account\Enum;
enum PointAction
{
case Initial;
case Deduct;
case Refund;
case Increase;
case Clear;
public static function tryFrom(string $action): ?PointAction
{
return match (strtolower($action)) {
'initial' => self::Initial,
'deduct' => self::Deduct,
'refund' => self::Refund,
'increase' => self::Increase,
'clear' => self::Clear,
default => null,
};
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* PointType.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
namespace Singularity\HDK\Pay\Domain\Account\Enum;
enum PointType: string
{
case LuxPoint = 'lux-point';
case EMA = 'ema';
case FtaiAligner = 'aligner';
case FtaiRetainer = 'retainer';
case Aligner4D = 'aligner-4d';
case NightguardAi = 'nightguard-ai';
public static function values(): array
{
return [
self::LuxPoint->value,
self::EMA->value,
self::FtaiAligner->value,
self::FtaiRetainer->value,
self::Aligner4D->value,
self::NightguardAi->value,
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* AccountBalanceRpc.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/17
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Account\Repository;
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\AccountBalance;
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\PointsBalance;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
interface AccountRepoInterface
{
public function getAccount(string $uid): AccountBalance;
public function getPointBalance(string $uid, PointType $pointType): PointsBalance;
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* PointLogRepoInterface.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/27
*/
namespace Singularity\HDK\Pay\Domain\Account\Repository;
use Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\PointLog;
interface PointLogRepoInterface
{
/**
* @param string $uid
* @return PointLog[]
*/
public function getList(string $uid): array;
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* AggregateRoot.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/31
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain;
abstract class AggregateRoot implements AggregateRootInterface {
/**
* @var array{}|DomainEvent[]
*/
protected array $domainEvents = [];
public function recordEvent(DomainEvent $event): void
{
$this->domainEvents[] = $event;
}
public function flushAllEvent(): array
{
$events = $this->domainEvents;
$this->domainEvents = [];
return $events;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* AbstractAggregateRoot.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/25
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain;
interface AggregateRootInterface {}

View File

@@ -0,0 +1,12 @@
<?php
/**
* DomainEvent.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/31
*/
namespace Singularity\HDK\Pay\Domain;
interface DomainEvent {}

View File

@@ -0,0 +1,14 @@
<?php
/**
* DomainException.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/8
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain;
class DomainException extends \RuntimeException {}

View File

@@ -0,0 +1,56 @@
<?php
/**
* QueryFreqAddrDto.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address;
use Singularity\HDK\Pay\Domain\AggregateRoot;
final class FrequentAddress extends AggregateRoot
{
public function __construct(
private readonly string $uid,
private readonly string $address,
private readonly string $city,
private readonly string $state,
private readonly string $country,
private readonly string $zipCode,
) {}
public function getUid(): string
{
return $this->uid;
}
public function getAddress(): string
{
return $this->address;
}
public function getCity(): string
{
return $this->city;
}
public function getState(): string
{
return $this->state;
}
public function getCountry(): string
{
return $this->country;
}
public function getZipCode(): string
{
return $this->zipCode;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Address.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/28
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
final readonly class Address {
public function __construct(
private string $patientName,
private string $address,
private string $city,
private string $state,
private string $country,
private string $zipCode,
) {}
public function getPatientName(): string
{
return $this->patientName;
}
public function getAddress(): string
{
return $this->address;
}
public function getCity(): string
{
return $this->city;
}
public function getState(): string
{
return $this->state;
}
public function getCountry(): string
{
return $this->country;
}
public function getZipCode(): string
{
return $this->zipCode;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* InvoiceDto.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/28
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
use Singularity\HDK\Pay\Domain\AggregateRoot;
final class Invoice extends AggregateRoot
{
public function __construct(
private readonly string $invoiceNo,
private readonly string $uid,
private readonly string $caseId,
private readonly Address $address,
private readonly bool $setFreqInvAddr,
private readonly string $receiver,
) {}
public function getInvoiceNo(): string
{
return $this->invoiceNo;
}
public function getUid(): string
{
return $this->uid;
}
public function getCaseId(): string
{
return $this->caseId;
}
public function isSetFreqInvAddr(): bool
{
return $this->setFreqInvAddr;
}
public function getReceiver(): string
{
return $this->receiver;
}
public function getPatientName(): string
{
return $this->address->getPatientName();
}
public function getAddress(): string
{
return $this->address->getAddress();
}
public function getCity(): string
{
return $this->address->getCity();
}
public function getState(): string
{
return $this->address->getState();
}
public function getCountry(): string
{
return $this->address->getCountry();
}
public function getZipCode(): string
{
return $this->address->getZipCode();
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* InvoiceProduct.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/28
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
final readonly class InvoiceProduct {
public function __construct(
public string $caseId,
public string $uid,
public string $name,
public string $sku,
public string $description,
) {}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* FrequentAddressRepoInterface.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
namespace Singularity\HDK\Pay\Domain\Invoice\Repository;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address\FrequentAddress;
interface FrequentAddressRepoInterface
{
/**
* @param string $uid
* @return FrequentAddress
*/
public function findByUser(string $uid): FrequentAddress;
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* InvoiceProductRepoInterface.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/28
*/
namespace Singularity\HDK\Pay\Domain\Invoice\Repository;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
interface InvoiceProductRepoInterface
{
/**
* @param string $uid
* @param string $caseId
* @return InvoiceProduct
*/
public function getCaseProduct(string $uid, string $caseId): InvoiceProduct;
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* InvoiceRepoInterface.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
namespace Singularity\HDK\Pay\Domain\Invoice\Repository;
use Singularity\HDK\Pay\Application\Command\CreateInvoiceCmd;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Invoice;
interface InvoiceRepoInterface
{
/**
* @param CreateInvoiceCmd $cmd
* @return Invoice
*/
public function create(CreateInvoiceCmd $cmd): Invoice;
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* RechargeProduct.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Product\Aggregate\Entities;
use Money\Money;
use Singularity\HDK\Pay\Domain\Product\Aggregate\ValueObject\RechargeEffect;
use Singularity\HDK\Pay\Domain\Product\Enum\ProductType;
final class ProductItem
{
public function __construct(
public int $id,
public string $description,
public Money $unitPrice,
public ProductType $productType,
public RechargeEffect $effect,
) {}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* RechargeProduct.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Product\Aggregate;
use Singularity\HDK\Pay\Domain\AggregateRoot;
use Singularity\HDK\Pay\Domain\Product\Aggregate\Entities\ProductItem;
/**
* App\Domain\Product\Aggregate\RechargeProduct@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*
* @property-read ProductItem[] $plans
* @property-read ProductItem[] $packages
* @property-read ProductItem $oneTime
*/
final class RechargeProduct extends AggregateRoot
{
public function __construct(
private readonly ?ProductItem $oneTime,
private readonly ?ProductItem $renew = null,
private readonly array $plans = [],
private readonly array $packages = [],
) {}
/**
* @param array $idList
* @return array{'id': int, 'quantity': int, 'detail': ProductItem}
*/
public function selectItemList(array $idList): array
{
$result = [];
/** @var ProductItem $item */
foreach ([$this->oneTime, $this->renew, ...$this->plans, ...$this->packages] as $item) {
if (in_array($item->id, $idList)) {
$result[] = $item;
}
}
return $result;
}
/**
* @return ProductItem[]
*/
public function getPlans(): array
{
return $this->plans;
}
public function getRenew(): ?ProductItem
{
return $this->renew;
}
/**
* @return ProductItem[]
*/
public function getPackages(): array
{
return $this->packages;
}
public function getOneTime(): ?ProductItem
{
return $this->oneTime;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* RechargeEffect.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Product\Aggregate\ValueObject;
use Carbon\Carbon;
use Hyperf\Framework\Exception\NotImplementedException;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
final class RechargeEffect
{
private float $pointTotal;
public function __construct(
public PointType $pointType,
public float $pointBasic,
public float $pointBonus,
public ?Carbon $expiredAt = null,
public ?string $version = null,
) {
$this->pointTotal = (float)bcadd((string)$pointBasic, (string)$pointBonus, 4);
}
public function getPointTotal(): float
{
return $this->pointTotal;
}
/**
* @template TPoints of array{'total': float, 'basic': float, 'bonus': float}
* @return array{'points'?: TPoints, 'version'?: string, 'expired_at'?: Carbon}
*/
public function toArray(): array
{
return array_filter([
'points' => $this->pointTotal == 0
? null
: [
'total' => $this->pointTotal,
'basic' => $this->pointBasic,
'bonus' => $this->pointBonus,
],
'version' => $this->version,
'expired_at' => $this->expiredAt,
]);
}
/**
* @param PointType $pointType
* @param string $range
* @return Carbon
*/
private static function parseExpiredAt(PointType $pointType, string $range): Carbon
{
if ($pointType !== PointType::EMA) {
throw new NotImplementedException();
}
$now = Carbon::now();
preg_match('/^(\d+)(\w)$/', $range, $matches);
$quantity = $matches[1]; // 数字部分
$unit = $matches[2]; // 单位部分
return match ($unit) {
'Y' => $now->addYears($quantity),
'm' => $now->addMonths($quantity),
'd' => $now->addDays($quantity),
'H' => $now->addHours($quantity),
'i' => $now->addMinutes($quantity),
's' => $now->addSeconds($quantity),
default => $now
};
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* ProductType.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
namespace Singularity\HDK\Pay\Domain\Product\Enum;
enum ProductType {
case plan;
case pack;
case oneTime;
case renew;
public static function tryFrom(string $type): ?ProductType
{
return match ($type) {
'plan' => self::plan,
'pack' => self::pack,
'oneTime' => self::oneTime,
'renew' => self::renew,
default => null,
};
}
public static function names(): array
{
$result = [];
foreach (self::cases() as $case) {
$result[] = $case->name;
}
return $result;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* ExchangeRepoInterface.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/29
*/
namespace Singularity\HDK\Pay\Domain\Product\Repository;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
interface ExchangeRepoInterface
{
/**
* @param PointType $source
* @param PointType $target
* @return float
*/
public function getRate(PointType $source, PointType $target): float;
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* RechargeProductRepoInterface.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/5
*/
namespace Singularity\HDK\Pay\Domain\Product\Repository;
use Singularity\HDK\Pay\Domain\Product\Aggregate\RechargeProduct;
interface RechargeProductRepoInterface
{
/**
* @return RechargeProduct
*/
public function findLuxPointProduct(): RechargeProduct;
/**
* @param string $uid
* @return RechargeProduct
*/
public function findEmaProduct(string $uid): RechargeProduct;
}

View File

@@ -0,0 +1,127 @@
<?php
/**
* TransactionRecord.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Transaction\Aggregate;
use Carbon\Carbon;
use Singularity\HDK\Pay\Domain\AggregateRoot;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Item;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction\CardTransaction;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction\PointTransaction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderAction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
final class TransactionRecord extends AggregateRoot
{
/**
* @param string $orderNo
* @param string $uid
* @param PayType $payType
* @param OrderAction $action
* @param OrderStatus $status
* @param string $source
* @param Item[] $items
* @param array<PointTransaction|CardTransaction> $transactions
* @param array $refunds
* @param array|null $external
* @param string|null $externalId
* @param Carbon|null $createdAt
* @param Carbon|null $occurredAt
* @param Carbon|null $lastRefundedAt
*/
public function __construct(
private readonly string $orderNo,
private readonly string $uid,
private readonly PayType $payType,
private readonly OrderAction $action,
private readonly OrderStatus $status,
private readonly string $source,
private readonly array $items,
private readonly array $transactions,
private readonly array $refunds,
private readonly ?array $external,
private readonly ?string $externalId,
private readonly ?Carbon $createdAt,
private readonly ?Carbon $occurredAt,
private readonly ?Carbon $lastRefundedAt,
) {}
public function getOrderNo(): string
{
return $this->orderNo;
}
public function getUid(): string
{
return $this->uid;
}
public function getPayType(): PayType
{
return $this->payType;
}
public function getAction(): OrderAction
{
return $this->action;
}
public function getStatus(): OrderStatus
{
return $this->status;
}
public function getSource(): string
{
return $this->source;
}
public function getItems(): array
{
return $this->items;
}
public function getTransactions(): array
{
return $this->transactions;
}
public function getRefunds(): array
{
return $this->refunds;
}
public function getExternal(): ?array
{
return $this->external;
}
public function getExternalId(): ?string
{
return $this->externalId;
}
public function getCreatedAt(): ?Carbon
{
return $this->createdAt;
}
public function getOccurredAt(): ?Carbon
{
return $this->occurredAt;
}
public function getLastRefundedAt(): ?Carbon
{
return $this->lastRefundedAt;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Item.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject;
use Swoole\ArrayObject;
final class Item extends ArrayObject
{
public function __construct(
private readonly string $name,
private readonly int $quantity,
private readonly float $price,
) {
parent::__construct([
'name' => $name,
'quantity' => $quantity,
'unit_price' => $price,
]);
}
public function getName(): string
{
return $this->name;
}
public function getQuantity(): int
{
return $this->quantity;
}
public function getPrice(): float
{
return $this->price;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Transaction.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
use Singularity\HDK\Pay\Enum\PaymentMethod;
use Swoole\ArrayObject;
/**
* Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
abstract class Transaction extends ArrayObject
{
public function __construct(
private PayType $payType,
private PaymentMethod|PointType $type,
private float $amount,
private OrderStatus $status,
) {
parent::__construct(
[
'pay_type' => $payType->value,
'type' => $type->value,
'amount' => $amount,
'status' => $status->name,
],
);
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* CardTransaction.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
use Singularity\HDK\Pay\Enum\PaymentMethod;
final class CardTransaction extends Transaction
{
public function __construct(
PaymentMethod $method,
float $amount,
OrderStatus $status,
) {
parent::__construct(
payType: PayType::Card,
type: $method,
amount: $amount,
status: $status,
);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* PointTransaction.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
final class PointTransaction extends Transaction
{
public function __construct(PointType $type, float $amount, OrderStatus $status)
{
parent::__construct(
payType: PayType::Point,
type: $type,
amount: $amount,
status: $status,
);
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* OrderAction.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/30
*/
namespace Singularity\HDK\Pay\Domain\Transaction\Enum;
enum OrderAction: string {
case Recharge = 'recharge';
case Consumption = 'consumption';
case Refund = 'refund';
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* OrderStatus.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2024/11/26
*/
namespace Singularity\HDK\Pay\Domain\Transaction\Enum;
enum OrderStatus
{
case created;
case paid;
case completed;
case refunded;
case cancelled;
case failed;
case closed;
public static function tryFrom(mixed $status): OrderStatus
{
$status = strtolower($status);
return match ($status) {
'paid' => self::paid,
'refunded' => self::refunded,
'cancelled' => self::cancelled,
'completed' => self::completed,
'failed' => self::failed,
'closed' => self::closed,
default => self::created,
};
}
public static function all(): array
{
return [
self::created,
self::paid,
self::completed,
self::refunded,
self::cancelled,
self::closed,
];
}
public static function values(): array
{
return array_column(self::all(), 'value');
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* PayType.php@LuxDesign
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/25
*/
namespace Singularity\HDK\Pay\Domain\Transaction\Enum;
Enum PayType: string {
case Point = 'point';
case Card = 'card';
case Hybrid = 'hybrid';
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* OrderRepoInterface.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
namespace Singularity\HDK\Pay\Domain\Transaction\Repository;
use Singularity\HDK\Pay\Application\Command\ConsumeCmd;
use Singularity\HDK\Pay\Application\Command\RechargeCmd;
use Singularity\HDK\Pay\Application\Dto\Transaction\OrderDto;
interface OrderRepoInterface
{
public function recharge(RechargeCmd $cmd): OrderDto;
public function consume(ConsumeCmd $cmd): OrderDto;
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* CallbackDomainSvc.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain\Transaction\Service;
use Carbon\Carbon;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\TransactionRecord;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Item;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction\CardTransaction;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\ValueObject\Transaction\PointTransaction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderAction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
use Singularity\HDK\Pay\Enum\PaymentMethod;
/**
* Singularity\HDK\Pay\Domain\Transaction\Service\CallbackDomainSvc@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
final class CallbackDomainSvc
{
/**
* @param array $order
* @return TransactionRecord
*/
public function callback(array $order): TransactionRecord
{
$payType = PayType::tryFrom($order['pay_type']);
return new TransactionRecord(
orderNo: $order['order_no'],
uid: $order['uid'],
payType: $payType,
action: OrderAction::tryFrom($order['action']),
status: OrderStatus::tryFrom($order['status']),
source: $order['source'],
items: array_map(
callback: fn(array $item) => new Item(
name: $item['name'],
quantity: $item['quantity'],
price: $item['unit_price'],
),
array: $order['items'],
),
transactions: array_map(
callback: fn(array $transaction)
=> $transaction['pay_type'] === PayType::Card->value
? new CardTransaction(
method: PaymentMethod::tryFrom($transaction['type']),
amount: $transaction['amount'],
status: OrderStatus::tryFrom($transaction['status']),
)
: new PointTransaction(
type: PointType::tryFrom($transaction['type']),
amount: $transaction['amount'],
status: OrderStatus::tryFrom($transaction['status']),
),
array: $order['transactions'],
),
refunds: $order['refunds'],
external: $order['external'],
externalId: $order['external_id'],
createdAt: isset($order['created_at']) ? new Carbon($order['created_at']) : null,
occurredAt: isset($order['occurred_at']) ? new Carbon($order['occurred_at']) : null,
lastRefundedAt: isset($order['last_refunded_at']) ? new Carbon($order['last_refunded_at']) : null,
);
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* ValueObject.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/12
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Domain;
use Swoole\ArrayObject;
abstract class ValueObject extends ArrayObject {}

17
src/Enum/Environment.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
/**
* Environment.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/8
*/
namespace Singularity\HDK\Pay\Enum;
enum Environment: string
{
case Dev = 'development';
case Prod = 'production';
}

View File

@@ -11,9 +11,12 @@ namespace Singularity\HDK\Pay\Enum;
enum PaymentMethod: string
{
case Stripe = 'stripe';
// case Stripe = 'stripe';
// case WechatPay = 'wechat';
case WechatPay = 'wechat';
case StripeCheckout = 'stripe.checkout';
case StripeIntent = 'stripe.intent';
case WechatNative = 'wechat.native';
/**
* @return array<string, string>

17
src/Enum/Site.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
/**
* Site.php@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/8
*/
namespace Singularity\HDK\Pay\Enum;
enum Site: string
{
case ChinaMainland = 'CN';
case NorthAmerica = 'NA';
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* AbstractRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use GuzzleHttp\RequestOptions;
use Singularity\HDK\Core\Http\RequestService;
use Singularity\HDK\Core\Http\RequestServiceFactory;
use Singularity\HDK\Pay\Trait\RequestHeaderBuilder;
use function Hyperf\Config\config;
abstract class AbstractRepo
{
use RequestHeaderBuilder;
protected RequestService $requestService;
public function __construct(?string $baseUrl = null)
{
$this->requestService = RequestServiceFactory::make([
'base_uri' => $baseUrl ?? config('payment.base_uri'),
RequestOptions::ALLOW_REDIRECTS => true,
'headers' => $this->headerBuilder(),
]);
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* AccountBalanceRpc.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/17
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\AccountBalance;
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\PointsBalance;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Account\Repository\AccountRepoInterface;
final class AccountBalanceRepo extends AbstractRepo implements AccountRepoInterface
{
public function getAccount(string $uid): AccountBalance
{
$response = $this->requestService->requestGet(
url: "/rpc/v2/account/$uid/balance",
params: [
'type' => join(',', PointType::values()),
],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new AccountBalance(
uid: $result['uid'],
pointsBalances: array_map(
callback: fn($pointBalance)
=> new PointsBalance(
type: PointType::from($pointBalance['name']),
total: $pointBalance['total'],
cost: $pointBalance['cost'],
amount: $pointBalance['amount'],
version: $pointBalance['version'],
expiredAt: $pointBalance['expired_at'],
),
array: $result['point_balance'],
),
);
}
public function getPointBalance(string $uid, PointType $pointType): PointsBalance
{
$response = $this->requestService->requestGet(
url: "/rpc/v2/account/$uid/balance/$pointType->value",
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new PointsBalance(
type: PointType::from($result['name']),
total: $result['total'],
cost: $result['cost'],
amount: $result['amount'],
version: $result['version'],
expiredAt: $result['expired_at'],
);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* FrequentAddressRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address\FrequentAddress;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
use Singularity\HDK\Pay\Domain\Invoice\Repository\FrequentAddressRepoInterface;
use Singularity\HDK\Pay\Infrastructure\Repository\AbstractRepo;
final class FrequentAddressRepo extends AbstractRepo implements FrequentAddressRepoInterface
{
/**
* @inheritDoc
*/
public function findByUser(string $uid): FrequentAddress
{
$response = $this->requestService->requestGet("/rpc/v2/account/$uid/inv-addr");
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new FrequentAddress(
uid: $result['uid'],
address: $result['address'],
city: $result['city'],
state: $result['state'],
country: $result['country'],
zipCode: $result['zip']
);
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* InvoiceProductRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/28
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceProductRepoInterface;
final class InvoiceProductRepo extends AbstractRepo implements InvoiceProductRepoInterface
{
/**
* @inheritDoc
*/
public function getCaseProduct(string $uid, string $caseId): InvoiceProduct
{
$response = $this->requestService->requestGet("/rpc/v2/account/$uid/logs/points/$caseId/product");
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new InvoiceProduct(
caseId: $result['case_id'],
uid: $result['uid'],
name: $result['name'],
sku: $result['sku'],
description: $result['description'],
);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* InvoiceRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Application\Command\CreateInvoiceCmd;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Address;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Invoice;
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceRepoInterface;
final class InvoiceRepo extends AbstractRepo implements InvoiceRepoInterface
{
public function create(CreateInvoiceCmd $cmd): Invoice
{
$response = $this->requestService->requestPost(
url: "/rpc/v2/account/$cmd->uid/logs/points/$cmd->caseId/invoices",
data: [
'set_freq_addr' => $cmd->setFreqInvAddr,
'receiver' => $cmd->receiver,
'patient_name' => $cmd->patientName,
'address' => $cmd->address,
'city' => $cmd->city,
'state' => $cmd->state,
'country' => $cmd->country,
'zip' => $cmd->zipCode,
],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new Invoice(
invoiceNo: $result['invoice_no'],
uid: $result['uid'],
caseId: $result['case_id'],
address: new Address(
patientName: $result['patient_name'],
address: $result['address'],
city: $result['city'],
state: $result['state'],
country: $result['country'],
zipCode: $result['zip'],
),
setFreqInvAddr: $result['set_freq_addr'],
receiver: $result['receiver'],
);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* OrderRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Application\Command\ConsumeCmd;
use Singularity\HDK\Pay\Application\Command\RechargeCmd;
use Singularity\HDK\Pay\Application\Dto\Transaction\OrderDto;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderAction;
use Singularity\HDK\Pay\Domain\Transaction\Enum\OrderStatus;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
use Singularity\HDK\Pay\Domain\Transaction\Repository\OrderRepoInterface;
final class OrderRepo extends AbstractRepo implements OrderRepoInterface
{
/**
* @param RechargeCmd $cmd
* @return OrderDto
* @throws GuzzleException
*/
public function recharge(RechargeCmd $cmd): OrderDto
{
$response = $this->requestService->requestPost(
url: '/rpc/v2/transaction/orders/recharge',
data: [
'uid' => $cmd->uid,
'items' => $cmd->items,
],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new OrderDto(
orderNo: $result['order_no'],
action: OrderAction::tryFrom($result['action']),
payType: PayType::tryFrom($result['pay_type']),
status: OrderStatus::tryFrom($result['status']),
);
}
public function consume(ConsumeCmd $cmd): OrderDto
{
$response = $this->requestService->requestPost(
url: '/rpc/v2/transaction/orders/consumption',
data: [
'uid' => $cmd->uid,
'type' => $cmd->type->value,
'method' => $cmd->method->value,
'items' => $cmd->items,
'external' => $cmd->external,
'external_id' => $cmd->externalID ?? '',
'remark' => $cmd->remark,
],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new OrderDto(
orderNo: $result['order_no'],
action: OrderAction::tryFrom($result['action']),
payType: PayType::tryFrom($result['pay_type']),
status: OrderStatus::tryFrom($result['status']),
);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* PointLogRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/27
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use Carbon\Carbon;
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\PointLog;
use Singularity\HDK\Pay\Domain\Account\Repository\PointLogRepoInterface;
final class PointLogRepo extends AbstractRepo implements PointLogRepoInterface
{
public function getList(string $uid): array
{
$response = $this->requestService->requestGet(url: "/rpc/v2/account/$uid/logs/points");
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return array_map(function ($item) {
return new PointLog(
id: $item['id'],
caseId: $item['case_id'],
uid: $item['uid'],
credits: $item['credits'],
source: $item['source'],
operator: $item['operator'],
description: $item['description'],
date: new Carbon($item['date']),
);
}, $result);
}
}

View File

@@ -0,0 +1,155 @@
<?php
/**
* ProductRepo.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Infrastructure\Repository;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\Codec\Json;
use Money\Currency;
use Money\Money;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Product\Aggregate\Entities\ProductItem;
use Singularity\HDK\Pay\Domain\Product\Aggregate\RechargeProduct;
use Singularity\HDK\Pay\Domain\Product\Aggregate\ValueObject\RechargeEffect;
use Singularity\HDK\Pay\Domain\Product\Enum\ProductType;
use Singularity\HDK\Pay\Domain\Product\Repository\ExchangeRepoInterface;
use Singularity\HDK\Pay\Domain\Product\Repository\RechargeProductRepoInterface;
final class ProductRepo extends AbstractRepo implements RechargeProductRepoInterface, ExchangeRepoInterface
{
/**
* @inheritDoc
*/
public function findLuxPointProduct(): RechargeProduct
{
$response = $this->requestService->requestGet(
url: '/rpc/v2/products/lux-point',
options: [
'headers' => $this->headerBuilder(),
],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new RechargeProduct(
oneTime: new ProductItem(
id: $result['one_time']['id'],
description: $result['one_time']['name'],
unitPrice: new Money(
amount: $result['one_time']['price'],
currency: new Currency($result['one_time']['currency']),
),
productType: ProductType::oneTime,
effect: new RechargeEffect(
pointType: PointType::LuxPoint,
pointBasic: $result['one_time']['point']['number'],
pointBonus: $result['one_time']['point']['bonus'],
),
),
packages: array_map(fn(array $item) => new ProductItem(
id: $item['id'],
description: $item['name'],
unitPrice: new Money(
amount: $item['price'],
currency: new Currency($item['currency']),
),
productType: ProductType::pack,
effect: new RechargeEffect(
pointType: PointType::LuxPoint,
pointBasic: $item['point']['number'],
pointBonus: $item['point']['bonus'],
),
), $result['package']),
);
}
/**
* @inheritDoc
*/
public function findEmaProduct(string $uid): RechargeProduct
{
$response = $this->requestService->requestGet(
url: '/rpc/v2/products/ema',
params: ['uid' => $uid],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new RechargeProduct(
oneTime: isset($result['one_time'])
? new ProductItem(
id: $result['one_time']['id'],
description: $result['one_time']['name'],
unitPrice: new Money(
amount: $result['one_time']['price'],
currency: new Currency($result['one_time']['currency']),
),
productType: ProductType::oneTime,
effect: new RechargeEffect(
pointType: PointType::EMA,
pointBasic: $result['one_time']['point']['number'],
pointBonus: $result['one_time']['point']['bonus'],
),
)
: null,
renew: isset($result['renew'])
? new ProductItem(
id: $result['renew']['id'],
description: $result['renew']['name'],
unitPrice: new Money(
amount: $result['renew']['price'],
currency: new Currency($result['renew']['currency']),
),
productType: ProductType::renew,
effect: new RechargeEffect(
pointType: PointType::EMA,
pointBasic: $result['renew']['point']['number'],
pointBonus: $result['renew']['point']['bonus'],
),
)
: null,
plans: array_map(fn(array $item) => new ProductItem(
id: $item['id'],
description: $item['name'],
unitPrice: new Money(
amount: $item['price'],
currency: new Currency($item['currency']),
),
productType: ProductType::plan,
effect: new RechargeEffect(
pointType: PointType::LuxPoint,
pointBasic: $item['point']['number'],
pointBonus: $item['point']['bonus'],
),
), $result['plan']),
);
}
/**
* @param PointType $source
* @param PointType $target
* @return float
* @throws GuzzleException
*/
public function getRate(PointType $source, PointType $target): float
{
$response = $this->requestService->requestGet(
url: "/rpc/v2/products/$target->value/exchange-rate/$source->value",
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return $result['rate'];
}
}

View File

@@ -25,7 +25,8 @@ use Money\Money;
* @property-read int $id
* @property-read string $goodsName
* @property-read array<string, mixed>|array{} $goodsSpec
* @property-read Money $amount
* @property-read Money|array{'price': int, 'currency': string} $amount
* @property-read array{'id': int, 'product_id': string, 'price_id': string} $stripe
*/
final class Goods extends JsonResource
{
@@ -40,7 +41,19 @@ final class Goods extends JsonResource
'id' => $this->id,
'goodsName' => $this->goodsName,
'goodsSpec' => $this->goodsSpec,
'amount' => new Money($this->amount['price'], new Currency($this->amount['currency']))
'amount' => new Money(
amount: $this->amount['price'],
currency: new Currency($this->amount['currency']),
),
'stripe' => $this->when(
condition: isset($this->resource->stripeProduct),
value: fn()
=> [
'id' => $this->stripe['id'],
'product_id' => $this->stripe['product_id'],
'price_id' => $this->stripe['price_id'],
],
),
];
}

View File

@@ -13,6 +13,13 @@ namespace Singularity\HDK\Pay\Resource;
use Hyperf\Resource\Json\ResourceCollection;
/**
* Singularity\HDK\Pay\Resource\ GoodsCollection@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/20
*/
final class GoodsCollection extends ResourceCollection
{
public ?string $wrap = null;

View File

@@ -3,6 +3,7 @@
namespace Singularity\HDK\Pay\Resource;
use Hyperf\Resource\Json\JsonResource;
use Money\Currency;
use Money\Money;
use Singularity\HDK\Pay\Enum\OrderStatus;
@@ -12,6 +13,16 @@ use Singularity\HDK\Pay\Enum\OrderStatus;
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/19
*
* @property-read string $orderNo
* @property-read string $transactionId
* @property-read string $goodsName
* @property-read Money|array{'amount': int, 'currency': string} $amount
* @property-read OrderStatus $state
* @property-read string $uid
* @property-read string $payment
* @property-read string $remark
* @property-read int $number
*/
class Order extends JsonResource
{
@@ -23,11 +34,13 @@ class Order extends JsonResource
* @return array{
* orderNo: string,
* transactionId: string,
* amount: array{amount: numeric, currency: string},
* state: string,
* goodsName: string,
* amount: Money,
* state: OrderStatus,
* uid: string,
* payment: string,
* remark: string
* remark: string,
* number: int
* }
*/
public function toArray(): array
@@ -37,11 +50,15 @@ class Order extends JsonResource
'orderNo' => $this->resource['orderNo'],
'transactionId' => $this->resource['transactionId'],
'goodsName' => $this->resource['goodsName'],
'amount' => new Money($this->resource['amount']['amount'], $this->resource['amount']['currency']),
'amount' => new Money(
$this->resource['amount']['amount'],
new Currency($this->resource['amount']['currency']),
),
'state' => OrderStatus::from($this->resource['state']),
'uid' => $this->resource['uid'],
'payment' => $this->resource['payment'],
'remark' => $this->resource['remark'],
'number' => $this->resource['number'] ?? 1,
// extends
// 'spId' => $this->spId,
// 'goodsDetail' => GoodsCollection::make([$this->goodsDetail])->resolve(),

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/**
* StripeConfiguration.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/20
*/
namespace Singularity\HDK\Pay\Resource;
use Hyperf\Resource\Json\JsonResource;
/**
* Singularity\HDK\Pay\Resource\StripeConfiguration@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/20
*
* @property-read string $id The unique identifier for this stripe account
* @property-read string $pk The publishable key for this stripe account
*/
final class StripeConfiguration extends JsonResource
{
public ?string $wrap = null;
public function toArray(): array
{
return [
'id' => $this->resource['id'],
'pk' => $this->resource['pk'],
'sk' => $this->when(isset($this->resource['sk']), fn() => $this->resource['sk']),
'webhookSecret' => $this->when(isset($this->webhookSecret), fn() =>$this->resource['webhookSecret']),
'notifyUrl' => $this->when(!empty($this->resource['notifyUrl']), fn() => $this->resource['notifyUrl'])
];
}
public function __get($key): ?string
{
return $this->resource[$key] ?? null;
}
}

94
src/Resource/V2/Order.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
namespace Singularity\HDK\Pay\Resource\V2;
use Hyperf\Resource\Json\JsonResource;
use Money\Currency;
use Money\Money;
use Singularity\HDK\Pay\Enum\OrderStatus;
use stdClass;
/**
* App\Resource\ Order@LuxPay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/19
*
* @property string $order_no 订单号
* @property OrderStatus $state 订单状态
* @property string $transaction_id 订单号
* @property string $uid 购买用户的 uid
* @property string $method 支付方式
* @property string $description 订单描述
* @property int $total_amount 金额,单位:分
* @property string $currency 币种,三个大写字母
* @property array $items 订单详情
* @property array $external 额外订单信息
* @property string $remark 订单备注
* @property array $notification 原始通知
* @property array more_details 支付信息
*/
class Order extends JsonResource
{
public ?string $wrap = null;
/**
* Transform the resource into an array.
*
* @return array{
* orderNo: string,
* transactionId: string,
* amount: array{amount: numeric, currency: string},
* state: string,
* uid: string,
* payment: string,
* remark: string
* }
*/
public function toArray(): array
{
if (empty($this->resource['order_no'])) {
return $this->parseClassicOrder();
}
return [
'order_no' => $this->order_no,
'state' => $this->state,
'transaction_id' => $this->transaction_id,
'uid' => $this->uid,
'method' => $this->method,
'description' => $this->description,
'total_amount' => $this->total_amount,
'currency' => $this->currency,
'items' => $this->items,
'external' => $this->external,
'remark' => $this->remark,
'notification' => $this->notification ?? new StdClass(),
'more_details' => $this->more_details,
];
}
private function parseClassicOrder(): array
{
return [
'order_no' => $this->orderNo,
'state' => $this->state,
'transaction_id' => $this->transactionId,
'uid' => $this->uid,
'method' => $this->payment,
'description' => $this->goodsName,
'total_amount' => new Money($this->amount['amount'], new Currency($this->amount['currency'])),
'currency' => $this->amount['currency'],
'items' => [],
'external' => [],
'remark' => $this->remark,
'notification' => $this->notification ?? [],
'more_details' => [],
];
}
public function __get($key)
{
return $this->resource->{$key} ?? $this->resource[$key];
}
}

83
src/Sdk/OrderRpc.php Normal file
View File

@@ -0,0 +1,83 @@
<?php
/**
* OrderRpc.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/7/9
*/
declare(strict_types=1);
namespace Singularity\HDK\Pay\Sdk;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Hyperf\Codec\Json;
use Hyperf\Context\Context;
use Lmc\HttpConstants\Header;
use Singularity\HDK\Core\Enumerations\Sp\ServiceProviderConfiguration;
use Singularity\HDK\Core\Http\RequestService;
use Singularity\HDK\Core\Http\RequestServiceFactory;
use Singularity\HDK\Core\I18n\Enum\Languages;
use Singularity\HDK\Pay\Resource\V2\Order;
use Singularity\HDK\Pay\Trait\WebhooksNotificationHandler;
use function Hyperf\Config\config;
final class OrderRpc implements ServiceProviderConfiguration
{
use WebhooksNotificationHandler;
private RequestService $requestService;
public function __construct(?string $baseUrl = null)
{
$this->requestService = RequestServiceFactory::make([
'base_uri' => $baseUrl ?? config('payment.base_uri'),
RequestOptions::ALLOW_REDIRECTS => true,
]);
}
/**
* @param array $data
* @param string|null $serviceProvider
* @param Languages|null $locale
* @param string|null $site
* @param string|null $env
* @return Order
* @throws GuzzleException
*/
public function create(
array $data,
?string $serviceProvider = null,
?Languages $locale = null,
?string $site = null,
?string $env = null,
): Order {
$data['method'] = 'stripe.checkout';
$response = $this->requestService->requestPost(
url: '/rpc/v2/orders',
data: $data,
options: [
'headers' => [
Header::CONTENT_TYPE => 'application/json',
Header::ACCEPT_LANGUAGE => $locale?->value ?? config('translation.locale', Languages::EN),
'X-SP-ID' => $serviceProvider ?? Context::get('sp') ?? config('app_name'),
'X-SITE' => match (strtoupper(trim($site ?? config('site', self::NA)))) {
'CN' => self::CN,
default => self::NA,
},
'X-ENV' => match (strtolower(trim($env ?? config('app_env', self::DEVELOPMENT)))) {
'prod', 'production' => self::PRODUCTION,
default => self::DEVELOPMENT
},
],
],
);
$content = $response->getBody()->getContents();
$result = Json::decode($content);
return new Order($result);
}
}

View File

@@ -14,10 +14,14 @@ namespace Singularity\HDK\Pay\Sdk;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Hyperf\Codec\Json;
use Lmc\HttpConstants\Header;
use Money\Money;
use Singularity\HDK\Core\Http\RequestService;
use Singularity\HDK\Core\Http\RequestServiceFactory;
use Singularity\HDK\Core\I18n\Enum\Languages;
use Singularity\HDK\Pay\Resource\Order;
use Singularity\HDK\Pay\Resource\StripeConfiguration;
use Singularity\HDK\Pay\Trait\WebhooksNotificationHandler;
use function Hyperf\Config\config;
@@ -30,6 +34,8 @@ use function Hyperf\Config\config;
*/
final class StripeRpc
{
use WebhooksNotificationHandler;
private RequestService $requestService;
public function __construct(?string $baseUrl = null)
@@ -40,12 +46,25 @@ final class StripeRpc
]);
}
public function getConfigurations(): StripeConfiguration
{
$resp = $this->requestService->requestGet('/rpc/v1/stripe/configurations');
$content = $resp->getBody()->getContents();
$order = Json::decode($content);
return StripeConfiguration::make($order);
}
/**
* @param Money $money
* @param string $uid
* @param int $goodsId
* @param string $goodsName
* @param array $goodsDetail
* @param Money $money 总金额
* @param string $uid
* @param int $goodsId
* @param string $goodsName
* @param array $goodsDetail
* @param int $number
* @param Languages|null $locale
* @param int|null $service
*
* @return Order
* @throws GuzzleException
*/
@@ -55,19 +74,31 @@ final class StripeRpc
int $goodsId,
string $goodsName,
array $goodsDetail,
?int $service = null
int $number = 1,
?Languages $locale = null,
?int $service = null,
): Order {
$response = $this->requestService->requestPost(
url: '/rpc/v1/stripe/order',
data: [
'service' => $service ?? config('payment.sp_id'),
'amount' => $money->getAmount(),
'currency' => $money->getCurrency(),
'currency' => (string)$money->getCurrency(),
'uid' => $uid,
'goodsDetail' => array_replace($goodsDetail, [
'id' => $goodsId,
'goodsName' => $goodsName,
]),
'goodsDetail' => array_replace(
$goodsDetail,
[
'id' => $goodsId,
'goodsName' => $goodsName,
],
),
'number' => $number,
],
options: [
'headers' => [
Header::CONTENT_TYPE => 'application/json',
Header::ACCEPT_LANGUAGE => $locale?->value ?? config('translation.locale'),
],
],
);

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/**
* WechatRpc.php@Pay
@@ -11,15 +12,14 @@ declare(strict_types=1);
namespace Singularity\HDK\Pay\Sdk;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Hyperf\Codec\Json;
use Hyperf\Di\Annotation\Inject;
use Lmc\HttpConstants\Header;
use Money\Money;
use Psr\Http\Message\RequestInterface;
use Singularity\HDK\Core\Http\RequestService;
use Singularity\HDK\Core\Http\RequestServiceFactory;
use Singularity\HDK\Pay\Trait\WebhooksNotificationHandler;
use function Hyperf\Config\config;
@@ -32,22 +32,26 @@ use function Hyperf\Config\config;
*/
final class WechatRpc
{
private RequestService $requestService;
use WebhooksNotificationHandler;
private string $baseUrl;
private RequestService $requestService;
public function __construct(?string $baseUrl = null)
{
$this->requestService = RequestServiceFactory::make();
$this->baseUrl = $baseUrl ?? config('payment.base_uri');
$this->requestService = RequestServiceFactory::make([
'base_uri' => $baseUrl ?? config('payment.base_uri'),
RequestOptions::ALLOW_REDIRECTS => true,
]);
}
/**
* @param RequestInterface $request
* @param string $type
* @param string $goodsName
* @param Money $money
* @param string $uid
* @param string $type
* @param string $goodsName
* @param Money $money
* @param string $uid
* @param int $number
*
* @return array{uri: string, orderNo: string}
* @throws GuzzleException
*/
@@ -56,25 +60,28 @@ final class WechatRpc
string $type,
string $goodsName,
Money $money,
string $uid
string $uid,
int $number = 1,
): array {
$url = $this->baseUrl . '/rpc/v1/wechat/qrcode';
$data = [
'service' => config('payment.sp_id'),
'amount' => (int)$money->getAmount(),
'fileType' => $type,
'uid' => $uid,
'goods_detail' => [
'goods_name' => $goodsName,
],
];
$response = $this->requestService
->setOptions([
'headers' => [
Header::ACCEPT => $request->getHeader(Header::ACCEPT),
],
])
->requestPost($url, $data);
->requestPost(
url: '/rpc/v1/wechat/qrcode',
data: [
'service' => config('payment.sp_id'),
'amount' => (int)$money->getAmount(),
'fileType' => $type,
'uid' => $uid,
'goodsDetail' => [
'goodsName' => $goodsName,
],
'number' => $number,
],
);
$content = $response->getBody()->getContents();
return Json::decode($content);

View File

@@ -1,24 +0,0 @@
<?php
declare(strict_types=1);
/**
* WechatService.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/7/17
*/
namespace Singularity\HDK\Pay\Service;
/**
* Singularity\HDK\Pay\Service\WechatService@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/7/17
*/
final class WechatService
{
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* RequestHeaderBuilder.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
namespace Singularity\HDK\Pay\Trait;
use Lmc\HttpConstants\Header;
use Singularity\HDK\Core\I18n\Enum\Languages;
use Singularity\HDK\Pay\Enum\Environment;
use Singularity\HDK\Pay\Enum\Site;
use function Hyperf\Config\config;
trait RequestHeaderBuilder {
public function headerBuilder(): array
{
return [
Header::CONTENT_TYPE => 'application/json',
Header::ACCEPT_LANGUAGE => $locale?->value ?? config('translation.locale', Languages::EN->value),
'X-SP-ID' => config('app_name'),
'X-SITE' => (match (strtoupper(config('site', Site::NorthAmerica->value))) {
'CN' => Site::ChinaMainland,
default => Site::NorthAmerica,
})->value,
'X-ENV' => (match (strtolower(config('app_env', Environment::Dev->value))) {
'prod', 'production' => Environment::Prod,
default => Environment::Dev,
})->value,
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Webhooks.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/22
*/
namespace Singularity\HDK\Pay\Trait;
use Hyperf\HttpServer\Contract\RequestInterface;
use Singularity\HDK\Pay\Resource\V2\Order;
/**
* Singularity\HDK\Pay\Trait\Webhooks@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/9/22
*/
trait WebhooksNotificationHandler
{
public function callback(RequestInterface $request): Order
{
return Order::make($request->post());
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* QueryAccountInformationTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\AccountBalance;
use Singularity\HDK\Pay\Infrastructure\Repository\AccountBalanceRepo;
use function Hyperf\Support\make;
it('should be able to query account information', function () {
$uid = '123456';
$repo = make(AccountBalanceRepo::class);
$result = $repo->getAccount($uid);
expect($result)
->toBeInstanceOf(AccountBalance::class)
->and($result->getUid())->tobe($uid);
});

View File

@@ -0,0 +1,24 @@
<?php
/**
* QueryPointBalanceTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\PointsBalance;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Infrastructure\Repository\AccountBalanceRepo;
use function Hyperf\Support\make;
it('should query point balance', function () {
$uid = 'cn3221';
$repo = make(AccountBalanceRepo::class);
$point_balance = $repo->getPointBalance($uid, PointType::EMA);
expect($point_balance)
->toBeInstanceOf(PointsBalance::class)
->and($point_balance->getType())->toBe(PointType::EMA);
});

View File

@@ -0,0 +1,25 @@
<?php
/**
* QueryPointLogListTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/27
*/
use Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\PointLog;
use Singularity\HDK\Pay\Infrastructure\Repository\PointLogRepo;
use function Hyperf\Support\make;
it('should can query point log list', function () {
$uid = '61a74db54f387';
$repo = make(PointLogRepo::class);
$list = $repo->getList($uid);
expect($list)
->not->toBeEmpty()
->each
->toBeInstanceOf(PointLog::class);
});

View File

@@ -0,0 +1,37 @@
<?php
/**
* CreateInvoiceTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
use Singularity\HDK\Pay\Application\Command\CreateInvoiceCmd;
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Invoice;
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceRepo;
use function Hyperf\Support\make;
it('should can create invoice', function () {
$repo = make(InvoiceRepo::class);
$invoice = $repo->create(
new CreateInvoiceCmd(
uid: '61dbe752d4caa',
caseId: '68affb136c01d',
setFreqInvAddr: true,
receiver: "dongyun.li@luxcreo.ai",
patientName: "Ms. Jennifer Durgan",
address: "12345 Magnolia Boulevard NortheastApartment 5678 Suite 910",
city: "Springfield",
state: "Illinois",
country: "United States",
zipCode: "67890-1234",
),
);
expect($invoice)
->toBeInstanceOf(Invoice::class);
});

View File

@@ -0,0 +1,26 @@
<?php
/**
* QueryCaseInvoiceProductTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/28
*/
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceProductRepo;
use function Hyperf\Support\make;
it('should can query case invoice product', function () {
$invoiceProductRepo = make(InvoiceProductRepo::class);
$uid = '61dbe752d4caa';
$caseId = '68affb136c01d';
$result = $invoiceProductRepo->getCaseProduct(uid: $uid, caseId: $caseId);
expect($result)->toBeInstanceOf(InvoiceProduct::class);
});

View File

@@ -0,0 +1,23 @@
<?php
/**
* QueryFreqAddrTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/29
*/
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address\FrequentAddress;
use Singularity\HDK\Pay\Infrastructure\Repository\FrequentAddressRepo;
use function Hyperf\Support\make;
it('should can query frequent invoice address', function () {
$uid = '61dbe752d4caa';
$addrRepo = make(FrequentAddressRepo::class);
$freq_addr = $addrRepo->findByUser($uid);
expect($freq_addr)->toBeInstanceOf(FrequentAddress::class);
});

View File

@@ -0,0 +1,24 @@
<?php
/**
* QueryPointRateTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Infrastructure\Repository\ProductRepo;
use function Hyperf\Support\make;
it('can query point rate', function () {
$repo = make(ProductRepo::class);
$from = PointType::FtaiAligner;
$to = PointType::LuxPoint;
$rate = $repo->getRate($from, $to);
expect($rate)->toBeFloat();
});

View File

@@ -0,0 +1,46 @@
<?php
/**
* QueryProductsTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
use Pest\Expectation;
use Singularity\HDK\Pay\Domain\Product\Aggregate\Entities\ProductItem;
use Singularity\HDK\Pay\Domain\Product\Aggregate\RechargeProduct;
use Singularity\HDK\Pay\Infrastructure\Repository\ProductRepo;
use function Hyperf\Support\make;
it('should can query LuxPoint products', function () {
$repo = make(ProductRepo::class);
$products = $repo->findLuxPointProduct();
expect($products)
->toBeInstanceOf(RechargeProduct::class)
->and($products->getOneTime())->toBeInstanceOf(ProductItem::class)
->and($products->getPackages())->each->toBeInstanceOf(ProductItem::class);
});
it('should can query EMA products', function () {
$repo = make(ProductRepo::class);
$uid = 'cn3321';
$products = $repo->findEmaProduct($uid);
expect($products)
->toBeInstanceOf(RechargeProduct::class)
->when(
$products->getOneTime() !== null,
fn(Expectation $expectation) => $expectation
->and($products->getOneTime())->toBeInstanceOf(ProductItem::class),
)
->when(
$products->getRenew() !== null,
fn(Expectation $expectation) => $expectation
->and($products->getRenew())->toBeInstanceOf(ProductItem::class),
)
->and($products->getPlans())->each->toBeInstanceOf(ProductItem::class);
});

View File

@@ -0,0 +1,34 @@
<?php
/**
* CreateConsumptionOrderTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
use Singularity\HDK\Pay\Application\Command\ConsumeCmd;
use Singularity\HDK\Pay\Application\Dto\Transaction\OrderDto;
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
use Singularity\HDK\Pay\Domain\Transaction\Enum\PayType;
use Singularity\HDK\Pay\Infrastructure\Repository\OrderRepo;
use function Hyperf\Support\make;
it('should can create consumption order', function () {
$uid = 'cn3321';
$cmd = (new ConsumeCmd(
uid: $uid,
type: PayType::Point,
method: PointType::FtaiAligner,
external: [],
externalID: null,
remark: '',
))->addItem('Design FTAI Aligner', 1, 5);
$repo = make(OrderRepo::class);
$result = $repo->consume($cmd);
expect($result)->toBeInstanceOf(OrderDto::class);
});

View File

@@ -0,0 +1,23 @@
<?php
/**
* CreateRechargeOrderTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/18
*/
use Singularity\HDK\Pay\Application\Command\RechargeCmd;
use Singularity\HDK\Pay\Application\Dto\Transaction\OrderDto;
use Singularity\HDK\Pay\Infrastructure\Repository\OrderRepo;
use function Hyperf\Support\make;
it('should can create recharge order', function () {
$cmd = (new RechargeCmd('cn3321'))->addItem(1, 2);
$repo = make(OrderRepo::class);
$result = $repo->recharge($cmd);
expect($result)->toBeInstanceOf(OrderDto::class);
});

View File

@@ -0,0 +1,99 @@
<?php
/**
* LuxPayCallbackTest.php@Pay
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2025/8/20
*/
use Hyperf\Codec\Json;
use Singularity\HDK\Pay\Domain\Transaction\Aggregate\TransactionRecord;
use Singularity\HDK\Pay\Domain\Transaction\Service\CallbackDomainSvc;
use function Hyperf\Support\make;
dataset('cases', [
'recharge' => [
<<<JSON
{
"order_no": "202508191929138659DTB",
"uid": "cn3321",
"pay_type": "card",
"action": "recharge",
"status": "created",
"source": "LuxDesign",
"items": [
{
"name": "Starter Pack",
"unit_price": 399,
"quantity": 1
}
],
"transactions": [
{
"pay_type": "card",
"type": "stripe.checkout",
"amount": 399,
"status": "created"
}
],
"refunds": [],
"external": [],
"external_id": "",
"created_at": "2025-08-20 10:44:46",
"occurred_at": null,
"last_refunded_at": null
}
JSON,
],
'consumption' => [
<<<JSON
{
"order_no": "202508190441101116DTB",
"uid": "cn3321",
"pay_type": "hybrid",
"action": "consumption",
"status": "created",
"source": "LuxDesign",
"items": [
{
"name": "Design - retainer",
"quantity": 1,
"unit_price": 3
}
],
"transactions": [
{
"pay_type": "card",
"type": "stripe.checkout",
"amount": 3,
"status": "created"
},
{
"pay_type": "point",
"type": "lux-point",
"amount": 3.05,
"status": "created"
}
],
"refunds": [],
"external": [],
"external_id": "",
"created_at": "2025-08-20 10:44:46",
"occurred_at": null,
"last_refunded_at": null
}
JSON,
]
]);
it(
'should parse request recharge body to TransactionRecord Entity',
function ($data) {
$data = Json::decode($data);
$entity = make(CallbackDomainSvc::class)->callback($data);
expect($entity)->toBeInstanceOf(TransactionRecord::class);
},
)->with('cases');

53
tests/HttpTestCase.php Normal file
View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Singularity\HDK\Test\Pay;
use Hyperf\Testing\Client;
use PHPUnit\Framework\TestCase;
use function Hyperf\Support\make;
/**
* Class HttpTestCase.
* @method get($uri, $data = [], $headers = [])
* @method post($uri, $data = [], $headers = [])
* @method json($uri, $data = [], $headers = [])
* @method file($uri, $data = [], $headers = [])
* @method request($method, $path, $options = [])
*/
abstract class HttpTestCase extends TestCase
{
/**
* @var Client
*/
protected Client $client;
/**
* @inheritDoc
*/
public function __construct(?string $name = null)
{
parent::__construct($name);
$this->client = make(Client::class);
}
/**
* @param string $name
* @param array<string, mixed> $arguments
* @return array<string, mixed>
*/
public function __call(string $name, array $arguments): array
{
return $this->client->{$name}(...$arguments);
}
}

32
tests/Pest.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
use Singularity\HDK\Test\Pay\HttpTestCase;
/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your tests functions is always bound to a specific PHPUnit tests
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "uses()" function to bind a different classes or traits.
|
*/
// uses(HttpTestCase::class)->in('Feature');
/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your tests files.
|
*/
/*function something()
{
// ..
}*/

View File

@@ -21,7 +21,7 @@ test(
* @throws GuzzleException
*/
function () {
$service = new GoodsRpc('http://192.168.2.218:9611');
$service = new GoodsRpc('http://192.168.2.246:9611');
$goods = $service->find(
1,
[
@@ -32,4 +32,4 @@ test(
);
expect($goods)->toBeInstanceOf(Goods::class);
}
);
)->skip();

View File

@@ -11,19 +11,33 @@
/** @var StripeRpc $service */
use Money\Money;
use Singularity\HDK\Core\I18n\Enum\Languages;
use Singularity\HDK\Pay\Resource\Order;
use Singularity\HDK\Pay\Resource\StripeConfiguration;
use Singularity\HDK\Pay\Sdk\StripeRpc;
test('能够正常创建 Stripe 订单', function () {
/** @var StripeRpc $service */
$service = \Hyperf\Support\make(StripeRpc::class,['baseUrl' => 'http://192.168.2.218:9611']);
$service = \Hyperf\Support\make(StripeRpc::class, ['baseUrl' => 'http://192.168.2.246:9611']);
$order = $service->createSession(
money: Money::USD(51),
uid: uniqid('NAT_'),
goodsId: 3,
goodsId: 1,
goodsName: 'HDK-Pay Test',
goodsDetail: [],
locale: Languages::EN,
service: 1
);
expect($order)->toBeInstanceOf(Order::class);
});
test('能够正常获取 Stripe 配置信息', function () {
/** @var StripeRpc $service */
$service = \Hyperf\Support\make(StripeRpc::class, ['baseUrl' => 'http://192.168.2.246:9611']);
$configures = $service->getConfigurations();
expect($configures)
->toBeInstanceOf(StripeConfiguration::class)
->toHaveKeys(['id', 'pk'])
->and($configures->resolve())->toBeArray();
});

View File

@@ -2,19 +2,16 @@
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Config\Config;
use Hyperf\Context\ApplicationContext;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\ClassLoader;
use Hyperf\Di\Container;
use Hyperf\Di\Definition\DefinitionSource;
use Psr\Container\ContainerInterface;
use Swoole\Runtime;
use function Hyperf\Support\env;
use function Hyperf\Support\make;
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');
@@ -22,16 +19,26 @@ ini_set('display_startup_errors', 'on');
error_reporting(E_ALL);
date_default_timezone_set('Asia/Shanghai');
!defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
!defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
Swoole\Runtime::enableCoroutine(true);
Runtime::enableCoroutine(SWOOLE_HOOK_FLAGS);
require BASE_PATH . '/vendor/autoload.php';
Hyperf\Di\ClassLoader::init();
ClassLoader::init();
$container = new Container(new DefinitionSource([]));
if (!$container instanceof ContainerInterface) {
throw new RuntimeException('The dependency injection container is invalid.');
}
$container = ApplicationContext::setContainer($container);
$container = ApplicationContext::setContainer($container);
/** @var Config $config */
$config = make(Config::class, [
[
'app_name' => 'LuxDesign',
'dependencies' => [],
'payment' => [
'base_uri' => env('LUX_PAY_BASE_URI', 'http://test-pay.luxcreo.com'),
],
],
]);
ApplicationContext::getContainer()->set(ConfigInterface::class, $config);