Compare commits

..

46 Commits

Author SHA1 Message Date
semantic-release-bot db0d9e003e 🔖 chore(release): v1.0.0-beta.30 [skip ci]
# [1.0.0-beta.30](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.29...v1.0.0-beta.30) (2025-03-15)

###  Features

* **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([48a1b97](https://github.com/perfect-panel/ppanel-web/commit/48a1b97))
* **email**: Add traffic exhaustion template ([bb3bd7b](https://github.com/perfect-panel/ppanel-web/commit/bb3bd7b))
* **formatting**: Update differenceInDays function to return whole days or two decimal places ([bf58f25](https://github.com/perfect-panel/ppanel-web/commit/bf58f25))
* **global**: Add custom data ([6dbebd1](https://github.com/perfect-panel/ppanel-web/commit/6dbebd1))
* **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([ce31972](https://github.com/perfect-panel/ppanel-web/commit/ce31972))
* **loading**: Replace loading animation with a simpler spinner and loading text ([f72df3a](https://github.com/perfect-panel/ppanel-web/commit/f72df3a))
* **node-form**: Update number input fields to enforce step, min, and max values ([3f7b6d1](https://github.com/perfect-panel/ppanel-web/commit/3f7b6d1))
* **payment**: Add isEdit prop to PaymentForm and disable fields when editing ([85f55de](https://github.com/perfect-panel/ppanel-web/commit/85f55de))
* **timeline**: Simplify timeline component layout and remove commented-out code ([fbad3b0](https://github.com/perfect-panel/ppanel-web/commit/fbad3b0))

### 🎫 Chores

* **release**: V1.0.0-beta.27 [skip ci] ([092477b](https://github.com/perfect-panel/ppanel-web/commit/092477b))
* **release**: V1.0.0-beta.28 [skip ci] ([786ba0e](https://github.com/perfect-panel/ppanel-web/commit/786ba0e))
* Merge branch 'beta' into develop ([f219c52](https://github.com/perfect-panel/ppanel-web/commit/f219c52))

### 🐛 Bug Fixes

* **dashboard**:  Update date display to use start_time if available ([e551232](https://github.com/perfect-panel/ppanel-web/commit/e551232))
2025-03-15 09:02:24 +00:00
web@ppanel f219c52306 🔧 chore: Merge branch 'beta' into develop 2025-03-15 15:58:37 +07:00
web@ppanel 3f7b6d16ed feat(node-form): Update number input fields to enforce step, min, and max values 2025-03-15 15:48:59 +07:00
web@ppanel 6dbebd152d feat(global): Add custom data 2025-03-15 15:39:55 +07:00
web@ppanel bb3bd7b50c feat(email): Add traffic exhaustion template 2025-03-14 23:28:36 +07:00
web@ppanel fbad3b0e51 feat(timeline): Simplify timeline component layout and remove commented-out code 2025-03-14 23:21:17 +07:00
web@ppanel e551232564 🐛 fix(dashboard): Update date display to use start_time if available 2025-03-14 22:10:24 +07:00
web@ppanel bf58f25072 feat(formatting): Update differenceInDays function to return whole days or two decimal places 2025-03-14 20:51:17 +07:00
web@ppanel 85f55def2e feat(payment): Add isEdit prop to PaymentForm and disable fields when editing 2025-03-14 14:04:10 +07:00
semantic-release-bot 29bc3c7722 🔖 chore(release): v1.0.0-beta.29 [skip ci]
# [1.0.0-beta.29](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2025-03-14)

###  Features

* **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([dddc21c](https://github.com/perfect-panel/ppanel-web/commit/dddc21c))
* **loading**: Replace loading animation with a simpler spinner and loading text ([b8316bb](https://github.com/perfect-panel/ppanel-web/commit/b8316bb))
2025-03-14 06:43:47 +00:00
web@ppanel f72df3a5e8 feat(loading): Replace loading animation with a simpler spinner and loading text 2025-03-14 13:42:58 +07:00
web@ppanel 48a1b97051 feat(api): Add CheckoutOrder request and response types, and update user purchase request parameters 2025-03-14 13:42:58 +07:00
semantic-release-bot 786ba0e41c 🔖 chore(release): v1.0.0-beta.28 [skip ci]
# [1.0.0-beta.28](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2025-03-13)

###  Features

* **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([94822d9](https://github.com/perfect-panel/ppanel-web/commit/94822d9))
2025-03-14 13:42:58 +07:00
web@ppanel ce3197298d feat(input): Add minimum value constraint and enhance number handling in EnhancedInput 2025-03-14 13:42:58 +07:00
semantic-release-bot 092477b2d3 🔖 chore(release): v1.0.0-beta.27 [skip ci]
* **payment**: Reconstruct the payment page ([7109472](https://github.com/perfect-panel/ppanel-web/commit/7109472))
* Enhance user navigation dropdown ui and styling ([d2732e6](https://github.com/perfect-panel/ppanel-web/commit/d2732e6))

* **cdn**: Add CDN URL configuration and update related references ([0c90733](https://github.com/perfect-panel/ppanel-web/commit/0c90733))
* **payment**: Add bank card payment ([7fa3a57](https://github.com/perfect-panel/ppanel-web/commit/7fa3a57))
* **subscription**:  Improve layout and organization of subscription detail tabs ([e4630f8](https://github.com/perfect-panel/ppanel-web/commit/e4630f8))
* **subscription**: Refactor subscription handling and update imports for better organization ([2215c7f](https://github.com/perfect-panel/ppanel-web/commit/2215c7f))

* **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))

* **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
* **changelog**: Update change log style ([cfa3fc0](https://github.com/perfect-panel/ppanel-web/commit/cfa3fc0))
* **payment**: Add notification URL field to payment management interface ([5c710e1](https://github.com/perfect-panel/ppanel-web/commit/5c710e1))
* **payment**: Fix payment related type definitions and update payment method references ([c3138a8](https://github.com/perfect-panel/ppanel-web/commit/c3138a8))
* **payment**: Refactor purchaseCheckout usage and remove redundant code ([a5e2079](https://github.com/perfect-panel/ppanel-web/commit/a5e2079))
* **payment**: Update checkout type from 'link' to 'url' for consistency ([136a1ab](https://github.com/perfect-panel/ppanel-web/commit/136a1ab))
* **payment**: Update payment information ([70d6a38](https://github.com/perfect-panel/ppanel-web/commit/70d6a38))
* **payment**: Update payment method update logic to include row data ([6752420](https://github.com/perfect-panel/ppanel-web/commit/6752420))
* **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
* **ui**: Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab ([fc0da76](https://github.com/perfect-panel/ppanel-web/commit/fc0da76))
* **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
2025-03-14 13:42:58 +07:00
web@ppanel b8316bba33 feat(loading): Replace loading animation with a simpler spinner and loading text 2025-03-14 13:40:56 +07:00
web@ppanel dddc21c686 feat(api): Add CheckoutOrder request and response types, and update user purchase request parameters 2025-03-14 13:34:36 +07:00
semantic-release-bot d10ecc9fdf 🔖 chore(release): v1.0.0-beta.28 [skip ci]
# [1.0.0-beta.28](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2025-03-13)

###  Features

* **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([94822d9](https://github.com/perfect-panel/ppanel-web/commit/94822d9))
2025-03-13 10:35:19 +00:00
web@ppanel 94822d902f feat(input): Add minimum value constraint and enhance number handling in EnhancedInput 2025-03-13 17:32:36 +07:00
semantic-release-bot 85fdc36c14 🔖 chore(release): v1.0.0-beta.27 [skip ci]
# [1.0.0-beta.27](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2025-03-13)

### ♻ Code Refactoring

* **payment**: Reconstruct the payment page ([7109472](https://github.com/perfect-panel/ppanel-web/commit/7109472))
* Enhance user navigation dropdown ui and styling ([d2732e6](https://github.com/perfect-panel/ppanel-web/commit/d2732e6))

###  Features

* **cdn**: Add CDN URL configuration and update related references ([0c90733](https://github.com/perfect-panel/ppanel-web/commit/0c90733))
* **payment**: Add bank card payment ([7fa3a57](https://github.com/perfect-panel/ppanel-web/commit/7fa3a57))
* **subscription**:  Improve layout and organization of subscription detail tabs ([e4630f8](https://github.com/perfect-panel/ppanel-web/commit/e4630f8))
* **subscription**: Refactor subscription handling and update imports for better organization ([2215c7f](https://github.com/perfect-panel/ppanel-web/commit/2215c7f))

### 🎫 Chores

* **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))

### 🐛 Bug Fixes

* **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
* **changelog**: Update change log style ([cfa3fc0](https://github.com/perfect-panel/ppanel-web/commit/cfa3fc0))
* **payment**: Add notification URL field to payment management interface ([5c710e1](https://github.com/perfect-panel/ppanel-web/commit/5c710e1))
* **payment**: Fix payment related type definitions and update payment method references ([c3138a8](https://github.com/perfect-panel/ppanel-web/commit/c3138a8))
* **payment**: Refactor purchaseCheckout usage and remove redundant code ([a5e2079](https://github.com/perfect-panel/ppanel-web/commit/a5e2079))
* **payment**: Update checkout type from 'link' to 'url' for consistency ([136a1ab](https://github.com/perfect-panel/ppanel-web/commit/136a1ab))
* **payment**: Update payment information ([70d6a38](https://github.com/perfect-panel/ppanel-web/commit/70d6a38))
* **payment**: Update payment method update logic to include row data ([6752420](https://github.com/perfect-panel/ppanel-web/commit/6752420))
* **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
* **ui**: Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab ([fc0da76](https://github.com/perfect-panel/ppanel-web/commit/fc0da76))
* **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
2025-03-13 04:27:12 +00:00
web@ppanel cfa3fc024d 🐛 fix(changelog): Update change log style 2025-03-13 11:23:14 +07:00
web@ppanel c06ea49d6f 🐛 fix(purchasing): Update payment type to lowercase and add optional chaining for discounts 2025-03-13 00:06:42 +07:00
web@ppanel c3138a863d 🐛 fix(payment): Fix payment related type definitions and update payment method references 2025-03-12 21:17:04 +07:00
web@ppanel 7fa3a57df4 feat(payment): Add bank card payment 2025-03-12 17:15:32 +07:00
web@ppanel 70d6a38a29 🐛 fix(payment): Update payment information 2025-03-12 13:43:06 +07:00
web@ppanel 5c710e1add 🐛 fix(payment): Add notification URL field to payment management interface 2025-03-11 21:22:18 +07:00
web@ppanel 6752420ba5 🐛 fix(payment): Update payment method update logic to include row data 2025-03-11 21:15:07 +07:00
web@ppanel 710947209a ♻️ refactor(payment): Reconstruct the payment page 2025-03-11 20:06:12 +07:00
web@ppanel fc0da761b5 🐛 fix(ui): Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab 2025-03-10 18:32:07 +07:00
web@ppanel 136a1abd8b 🐛 fix(payment): Update checkout type from 'link' to 'url' for consistency 2025-03-08 20:10:45 +07:00
web@ppanel a5e2079ddc 🐛 fix(payment): Refactor purchaseCheckout usage and remove redundant code 2025-03-08 20:04:33 +07:00
web@ppanel 35f92c9329 🐛 fix(affiliate): Update user identifier 2025-03-08 15:43:20 +07:00
web@ppanel f5d8fd3b65 🐛 fix(ui): Multiple display bugs 2025-03-08 14:46:20 +07:00
web@ppanel 0c907337e1 feat(cdn): Add CDN URL configuration and update related references 2025-03-08 12:54:58 +07:00
web@ppanel e4630f8ca9 feat(subscription): Improve layout and organization of subscription detail tabs 2025-03-08 12:43:57 +07:00
web@ppanel 2215c7f2b9 feat(subscription): Refactor subscription handling and update imports for better organization 2025-03-08 12:36:25 +07:00
semantic-release-bot 3222016799 🔧 chore(merge): Bump version to 1.0.0-beta.26 and update changelog 2025-03-08 12:36:02 +07:00
web@ppanel d0a04679ce Merge pull request #12 from turbolnk-com/develop
♻️ refactor: Enhance user navigation dropdown ui and styling
2025-03-07 11:46:01 +07:00
turbolnk d2732e650b ♻️ refactor: Enhance user navigation dropdown ui and styling 2025-03-06 09:34:39 +08:00
semantic-release-bot 79edea7973 🔖 chore(release): v1.0.0-beta.26 [skip ci]
# [1.0.0-beta.26](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2025-03-02)

### 🐛 Bug Fixes

* **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d))
2025-03-02 16:40:10 +00:00
web@ppanel f17bf8db6e 🐛 fix(icon): Comment out unused icon collection imports 2025-03-02 23:37:15 +07:00
semantic-release-bot 047a69886e 🔖 chore(release): v1.0.0-beta.25 [skip ci]
# [1.0.0-beta.25](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2025-03-01)

###  Features

* **auth**: Add privacy policy link to the footer ([8e16ef1](https://github.com/perfect-panel/ppanel-web/commit/8e16ef1))

### 🐛 Bug Fixes

* **dashboard**: Display subscription creation date in user dashboard ([d0e6df0](https://github.com/perfect-panel/ppanel-web/commit/d0e6df0))
* **request**: Add error code 40005 to trigger logout ([71bf002](https://github.com/perfect-panel/ppanel-web/commit/71bf002))
* **subscribe**: Update payment return URL ([2b80496](https://github.com/perfect-panel/ppanel-web/commit/2b80496))
2025-03-01 12:59:06 +00:00
web@ppanel 71bf002370 🐛 fix(request): Add error code 40005 to trigger logout 2025-03-01 19:55:55 +07:00
web@ppanel d0e6df044e 🐛 fix(dashboard): Display subscription creation date in user dashboard 2025-03-01 19:55:31 +07:00
web@ppanel 8e16ef17ed feat(auth): Add privacy policy link to the footer 2025-03-01 19:40:45 +07:00
web@ppanel 2b80496637 🐛 fix(subscribe): Update payment return URL 2025-03-01 19:35:32 +07:00
253 changed files with 4836 additions and 2703 deletions
+150 -10
View File
@@ -1,28 +1,168 @@
<a name="readme-top"></a>
# Changelog
# [1.0.0-beta.24](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2025-02-27)
### ♻ Code Refactoring
* **ui**: Optimize document display ([2ca2992](https://github.com/perfect-panel/ppanel-web/commit/2ca2992))
* Reduce code complexity and improve readability ([e11f18c](https://github.com/perfect-panel/ppanel-web/commit/e11f18c))
# [1.0.0-beta.30](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.29...v1.0.0-beta.30) (2025-03-15)
### ✨ Features
* **loading**: Add loading components and integrate them in Providers ([d5847fa](https://github.com/perfect-panel/ppanel-web/commit/d5847fa))
* **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([48a1b97](https://github.com/perfect-panel/ppanel-web/commit/48a1b97))
* **email**: Add traffic exhaustion template ([bb3bd7b](https://github.com/perfect-panel/ppanel-web/commit/bb3bd7b))
* **formatting**: Update differenceInDays function to return whole days or two decimal places ([bf58f25](https://github.com/perfect-panel/ppanel-web/commit/bf58f25))
* **global**: Add custom data ([6dbebd1](https://github.com/perfect-panel/ppanel-web/commit/6dbebd1))
* **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([ce31972](https://github.com/perfect-panel/ppanel-web/commit/ce31972))
* **loading**: Replace loading animation with a simpler spinner and loading text ([f72df3a](https://github.com/perfect-panel/ppanel-web/commit/f72df3a))
* **node-form**: Update number input fields to enforce step, min, and max values ([3f7b6d1](https://github.com/perfect-panel/ppanel-web/commit/3f7b6d1))
* **payment**: Add isEdit prop to PaymentForm and disable fields when editing ([85f55de](https://github.com/perfect-panel/ppanel-web/commit/85f55de))
* **timeline**: Simplify timeline component layout and remove commented-out code ([fbad3b0](https://github.com/perfect-panel/ppanel-web/commit/fbad3b0))
### 🎫 Chores
* **merge**: Add advertising module and device settings ([0130e02](https://github.com/perfect-panel/ppanel-web/commit/0130e02))
* **release**: V1.0.0-beta.27 [skip ci] ([092477b](https://github.com/perfect-panel/ppanel-web/commit/092477b))
* **release**: V1.0.0-beta.28 [skip ci] ([786ba0e](https://github.com/perfect-panel/ppanel-web/commit/786ba0e))
* Merge branch 'beta' into develop ([f219c52](https://github.com/perfect-panel/ppanel-web/commit/f219c52))
### 🐛 Bug Fixes
* **locales**: Order recharge related fields ([35210fe](https://github.com/perfect-panel/ppanel-web/commit/35210fe))
* **dashboard**: Update date display to use start_time if available ([e551232](https://github.com/perfect-panel/ppanel-web/commit/e551232))
<a name="readme-top"></a>
# Changelog
# [1.0.0-beta.29](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2025-03-14)
### ✨ Features
- **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([dddc21c](https://github.com/perfect-panel/ppanel-web/commit/dddc21c))
- **loading**: Replace loading animation with a simpler spinner and loading text ([b8316bb](https://github.com/perfect-panel/ppanel-web/commit/b8316bb))
<a name="readme-top"></a>
# Changelog
# [1.0.0-beta.28](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2025-03-13)
### ✨ Features
- **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([94822d9](https://github.com/perfect-panel/ppanel-web/commit/94822d9))
# [1.0.0-beta.27](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2025-03-13)
### ♻ Code Refactoring
- **payment**: Reconstruct the payment page ([7109472](https://github.com/perfect-panel/ppanel-web/commit/7109472))
- Enhance user navigation dropdown ui and styling ([d2732e6](https://github.com/perfect-panel/ppanel-web/commit/d2732e6))
### ✨ Features
- **cdn**: Add CDN URL configuration and update related references ([0c90733](https://github.com/perfect-panel/ppanel-web/commit/0c90733))
- **payment**: Add bank card payment ([7fa3a57](https://github.com/perfect-panel/ppanel-web/commit/7fa3a57))
- **subscription**: Improve layout and organization of subscription detail tabs ([e4630f8](https://github.com/perfect-panel/ppanel-web/commit/e4630f8))
- **subscription**: Refactor subscription handling and update imports for better organization ([2215c7f](https://github.com/perfect-panel/ppanel-web/commit/2215c7f))
### 🎫 Chores
- **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))
### 🐛 Bug Fixes
- **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
- **changelog**: Update change log style ([cfa3fc0](https://github.com/perfect-panel/ppanel-web/commit/cfa3fc0))
- **payment**: Add notification URL field to payment management interface ([5c710e1](https://github.com/perfect-panel/ppanel-web/commit/5c710e1))
- **payment**: Fix payment related type definitions and update payment method references ([c3138a8](https://github.com/perfect-panel/ppanel-web/commit/c3138a8))
- **payment**: Refactor purchaseCheckout usage and remove redundant code ([a5e2079](https://github.com/perfect-panel/ppanel-web/commit/a5e2079))
- **payment**: Update checkout type from 'link' to 'url' for consistency ([136a1ab](https://github.com/perfect-panel/ppanel-web/commit/136a1ab))
- **payment**: Update payment information ([70d6a38](https://github.com/perfect-panel/ppanel-web/commit/70d6a38))
- **payment**: Update payment method update logic to include row data ([6752420](https://github.com/perfect-panel/ppanel-web/commit/6752420))
- **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
- **ui**: Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab ([fc0da76](https://github.com/perfect-panel/ppanel-web/commit/fc0da76))
- **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
<a name="readme-top"></a>
# Changelog
# [1.0.0-beta.28](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2025-03-13)
### ✨ Features
- **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([94822d9](https://github.com/perfect-panel/ppanel-web/commit/94822d9))
# [1.0.0-beta.27](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2025-03-13)
### ♻ Code Refactoring
- **payment**: Reconstruct the payment page ([7109472](https://github.com/perfect-panel/ppanel-web/commit/7109472))
- Enhance user navigation dropdown ui and styling ([d2732e6](https://github.com/perfect-panel/ppanel-web/commit/d2732e6))
### ✨ Features
- **cdn**: Add CDN URL configuration and update related references ([0c90733](https://github.com/perfect-panel/ppanel-web/commit/0c90733))
- **payment**: Add bank card payment ([7fa3a57](https://github.com/perfect-panel/ppanel-web/commit/7fa3a57))
- **subscription**: Improve layout and organization of subscription detail tabs ([e4630f8](https://github.com/perfect-panel/ppanel-web/commit/e4630f8))
- **subscription**: Refactor subscription handling and update imports for better organization ([2215c7f](https://github.com/perfect-panel/ppanel-web/commit/2215c7f))
### 🎫 Chores
- **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))
### 🐛 Bug Fixes
- **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
- **changelog**: Update change log style ([cfa3fc0](https://github.com/perfect-panel/ppanel-web/commit/cfa3fc0))
- **payment**: Add notification URL field to payment management interface ([5c710e1](https://github.com/perfect-panel/ppanel-web/commit/5c710e1))
- **payment**: Fix payment related type definitions and update payment method references ([c3138a8](https://github.com/perfect-panel/ppanel-web/commit/c3138a8))
- **payment**: Refactor purchaseCheckout usage and remove redundant code ([a5e2079](https://github.com/perfect-panel/ppanel-web/commit/a5e2079))
- **payment**: Update checkout type from 'link' to 'url' for consistency ([136a1ab](https://github.com/perfect-panel/ppanel-web/commit/136a1ab))
- **payment**: Update payment information ([70d6a38](https://github.com/perfect-panel/ppanel-web/commit/70d6a38))
- **payment**: Update payment method update logic to include row data ([6752420](https://github.com/perfect-panel/ppanel-web/commit/6752420))
- **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
- **ui**: Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab ([fc0da76](https://github.com/perfect-panel/ppanel-web/commit/fc0da76))
- **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
<a name="readme-top"></a>
# Changelog
# [1.0.0-beta.26](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2025-03-02)
### 🐛 Bug Fixes
- **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d))
# [1.0.0-beta.25](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2025-03-01)
### ✨ Features
- **auth**: Add privacy policy link to the footer ([8e16ef1](https://github.com/perfect-panel/ppanel-web/commit/8e16ef1))
### 🐛 Bug Fixes
- **dashboard**: Display subscription creation date in user dashboard ([d0e6df0](https://github.com/perfect-panel/ppanel-web/commit/d0e6df0))
- **request**: Add error code 40005 to trigger logout ([71bf002](https://github.com/perfect-panel/ppanel-web/commit/71bf002))
- **subscribe**: Update payment return URL ([2b80496](https://github.com/perfect-panel/ppanel-web/commit/2b80496))
# [1.0.0-beta.24](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2025-02-27)
### ♻ Code Refactoring
- **ui**: Optimize document display ([2ca2992](https://github.com/perfect-panel/ppanel-web/commit/2ca2992))
- Reduce code complexity and improve readability ([e11f18c](https://github.com/perfect-panel/ppanel-web/commit/e11f18c))
### ✨ Features
- **loading**: Add loading components and integrate them in Providers ([d5847fa](https://github.com/perfect-panel/ppanel-web/commit/d5847fa))
### 🎫 Chores
- **merge**: Add advertising module and device settings ([0130e02](https://github.com/perfect-panel/ppanel-web/commit/0130e02))
### 🐛 Bug Fixes
- **locales**: Order recharge related fields ([35210fe](https://github.com/perfect-panel/ppanel-web/commit/35210fe))
<a name="readme-top"></a>
@@ -286,31 +286,34 @@ export default function Page() {
</TabsContent>
<TabsContent value='template'>
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3'>
{['verify_email_template', 'expiration_email_template', 'maintenance_email_template'].map(
(templateKey) => (
<Card key={templateKey}>
<CardHeader>
<CardTitle>{t(`${templateKey}`)}</CardTitle>
<CardDescription>
{t(`${templateKey}Description`, { after: '{{', before: '}}' })}
</CardDescription>
</CardHeader>
<CardContent>
<HTMLEditor
placeholder={t('inputPlaceholder')}
value={data?.config?.[templateKey] as string}
onBlur={(value) =>
updateConfig('config', {
...data?.config,
[templateKey]: value,
})
}
/>
</CardContent>
</Card>
),
)}
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
{[
'verify_email_template',
'expiration_email_template',
'maintenance_email_template',
'traffic_exceed_email_template',
].map((templateKey) => (
<Card key={templateKey}>
<CardHeader>
<CardTitle>{t(`${templateKey}`)}</CardTitle>
<CardDescription>
{t(`${templateKey}Description`, { after: '{{', before: '}}' })}
</CardDescription>
</CardHeader>
<CardContent>
<HTMLEditor
placeholder={t('inputPlaceholder')}
value={data?.config?.[templateKey] as string}
onBlur={(value) =>
updateConfig('config', {
...data?.config,
[templateKey]: value,
})
}
/>
</CardContent>
</Card>
))}
</div>
</TabsContent>
+1 -1
View File
@@ -116,7 +116,7 @@ export default function Page(props: any) {
<ul className='grid gap-3'>
<li className='flex items-center justify-between'>
<span className='text-muted-foreground'>{t('method')}</span>
<span>{t(`methods.${row.original.method}`)}</span>
<span>{row.original?.payment?.name || row.original?.payment?.platform}</span>
</li>
</ul>
</HoverCardContent>
@@ -1,256 +0,0 @@
'use client';
import { getAlipayF2FPaymentConfig, updateAlipayF2FPaymentConfig } from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { Textarea } from '@workspace/ui/components/textarea';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function AlipayF2F() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getAlipayF2FPaymentConfig'],
queryFn: async () => {
const { data } = await getAlipayF2FPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateAlipayF2FPaymentConfig({
...data,
[key]: value,
} as API.UpdateAlipayF2fRequest);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('enable')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={(checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.sandbox')}</Label>
<p className='text-muted-foreground text-xs'>{t('alipayf2f.sandboxDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.config.sandbox}
onCheckedChange={(checked) => {
updateConfig('config', {
...data?.config,
sandbox: checked,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => updateConfig('name', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon_url}
onValueBlur={(value) => updateConfig('icon', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => updateConfig('domain', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
maxLength={3}
value={data?.fee_percent}
onValueBlur={(value) => updateConfig('fee_percent', value)}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.appId')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.app_id}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
app_id: value,
})
}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.privateKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<Textarea
placeholder={t('inputPlaceholder')}
defaultValue={data?.config.private_key}
onBlur={(e) => {
updateConfig('config', {
...data?.config,
private_key: e.target.value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.publicKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<Textarea
placeholder={t('inputPlaceholder')}
defaultValue={data?.config.public_key}
onBlur={(e) => {
updateConfig('config', {
...data?.config,
public_key: e.target.value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.invoiceName')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('alipayf2f.invoiceNameDescription')}
value={data?.config.invoice_name}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
invoice_name: value,
})
}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
-221
View File
@@ -1,221 +0,0 @@
'use client';
import { getEpayPaymentConfig, updateEpayPaymentConfig } from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function Epay() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getEpayPaymentConfig'],
queryFn: async () => {
const { data } = await getEpayPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateEpayPaymentConfig({
...data,
[key]: value,
} as API.UpdateEpayRequest);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('enable')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={(checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => updateConfig('name', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon}
onValueBlur={(value) => updateConfig('icon', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => updateConfig('domain', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
maxLength={3}
value={data?.fee_percent}
onValueBlur={(value) => updateConfig('fee_percent', value)}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('epay.url')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.url}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
url: value,
})
}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('epay.pid')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.pid}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
pid: value,
})
}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('epay.key')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.key}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
key: value,
})
}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
+3 -26
View File
@@ -1,34 +1,11 @@
import Billing from '@/components/billing';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
import AlipayF2F from './alipayf2f';
import Epay from './epay';
import StripeAlipay from './stripe-alipay';
import StripeWeChatPay from './stripe-wechat-pay';
import PaymentTable from './payment-table';
export default async function Page() {
return (
<>
<Tabs defaultValue='Epay'>
<TabsList className='h-full flex-wrap'>
<TabsTrigger value='Epay'>Epay</TabsTrigger>
<TabsTrigger value='Stripe-Alipay'>Stripe(AliPay)</TabsTrigger>
<TabsTrigger value='Strip-WeChatPay'>Stripe(WeChatPay)</TabsTrigger>
<TabsTrigger value='AlipayF2F'>AlipayF2F</TabsTrigger>
</TabsList>
<TabsContent value='Epay'>
<Epay />
</TabsContent>
<TabsContent value='Stripe-Alipay'>
<StripeAlipay />
</TabsContent>
<TabsContent value='Strip-WeChatPay'>
<StripeWeChatPay />
</TabsContent>
<TabsContent value='AlipayF2F'>
<AlipayF2F />
</TabsContent>
</Tabs>
<div className='flex flex-col gap-3'>
<PaymentTable />
<div className='mt-5 flex flex-col gap-3'>
<Billing type='payment' />
</div>
</>
@@ -0,0 +1,418 @@
'use client';
import useGlobalStore from '@/config/use-global';
import { getPaymentPlatform } from '@/services/admin/payment';
import { zodResolver } from '@hookform/resolvers/zod';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@workspace/ui/components/form';
import { RadioGroup, RadioGroupItem } from '@workspace/ui/components/radio-group';
import { ScrollArea } from '@workspace/ui/components/scroll-area';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import {
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@workspace/ui/components/sheet';
import { MarkdownEditor } from '@workspace/ui/custom-components/editor';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { Icon } from '@workspace/ui/custom-components/icon';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
interface PaymentFormProps<T> {
trigger: React.ReactNode;
title: string;
loading?: boolean;
initialValues?: T;
onSubmit: (values: T) => Promise<boolean>;
isEdit?: boolean;
}
export default function PaymentForm<T>({
trigger,
title,
loading,
initialValues,
onSubmit,
isEdit,
}: PaymentFormProps<T>) {
const t = useTranslations('payment');
const { common } = useGlobalStore();
const { currency } = common;
const [open, setOpen] = useState(false);
const { data: platformData } = useQuery({
queryKey: ['getPaymentPlatform'],
queryFn: async () => {
const { data } = await getPaymentPlatform();
return data?.data?.list || [];
},
});
const formSchema = z.object({
name: z.string().min(1, { message: t('nameRequired') }),
platform: z.string().optional(),
icon: z.string().optional(),
domain: z.string().optional(),
config: z.any(),
fee_mode: z.coerce.number().min(0).max(2),
fee_percent: z.coerce.number().optional(),
fee_amount: z.coerce.number().optional(),
description: z.string().optional(),
});
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: '',
platform: '',
icon: '',
domain: '',
config: {},
fee_mode: 0,
fee_percent: 0,
fee_amount: 0,
...(initialValues as any),
},
});
const feeMode = form.watch('fee_mode');
const platformValue = form.watch('platform');
const configValues = form.watch('config');
const currentPlatform = platformData?.find((p) => p.platform === platformValue);
const currentFieldDescriptions = currentPlatform?.platform_field_description || {};
const configFields = Object.keys(currentFieldDescriptions) || [];
const platformUrl = currentPlatform?.platform_url || '';
useEffect(() => {
if (feeMode === 0) {
form.setValue('fee_amount', 0);
form.setValue('fee_percent', 0);
} else if (feeMode === 1) {
form.setValue('fee_amount', 0);
} else if (feeMode === 2) {
form.setValue('fee_percent', 0);
}
}, [feeMode, form]);
const handleClose = () => {
form.reset();
setOpen(false);
};
const handleSubmit = async (values: z.infer<typeof formSchema>) => {
const cleanedValues = { ...values };
if (values.fee_mode === 0) {
cleanedValues.fee_amount = undefined;
cleanedValues.fee_percent = undefined;
} else if (values.fee_mode === 1) {
cleanedValues.fee_amount = undefined;
} else if (values.fee_mode === 2) {
cleanedValues.fee_percent = undefined;
}
const success = await onSubmit(cleanedValues as unknown as T);
if (success) {
handleClose();
}
};
const openPlatformUrl = () => {
if (platformUrl) {
window.open(platformUrl, '_blank');
}
};
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>{trigger}</SheetTrigger>
<SheetContent className='w-[550px] max-w-full md:max-w-screen-md'>
<SheetHeader>
<SheetTitle>{title}</SheetTitle>
</SheetHeader>
<ScrollArea className='-mx-6 h-[calc(100vh-48px-36px-36px-env(safe-area-inset-top))]'>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className='space-y-6 px-6 pt-4'>
{/* 基本信息分组 */}
<div className='space-y-4'>
<div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('namePlaceholder')}
value={field.value}
onValueChange={(value) => form.setValue('name', value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='icon'
render={({ field }) => (
<FormItem>
<FormLabel>{t('icon')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('iconPlaceholder')}
value={field.value}
onValueChange={(value) => form.setValue('icon', value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name='domain'
render={({ field }) => (
<FormItem>
<FormLabel>{t('domain')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('domainPlaceholder', { example: 'http(s)://example.com' })}
value={field.value}
onValueChange={(value) => form.setValue('domain', value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className='space-y-4'>
<FormField
control={form.control}
name='fee_mode'
render={({ field }) => (
<FormItem>
<FormLabel>{t('handlingFee')}</FormLabel>
<FormControl>
<RadioGroup
onValueChange={(value) => field.onChange(parseInt(value))}
value={field.value.toString()}
className='flex flex-wrap gap-4'
>
<FormItem className='flex items-center space-x-2'>
<FormControl>
<RadioGroupItem value='0' />
</FormControl>
<FormLabel className='!mt-0 cursor-pointer'>{t('noFee')}</FormLabel>
</FormItem>
<FormItem className='flex items-center space-x-2'>
<FormControl>
<RadioGroupItem value='1' />
</FormControl>
<FormLabel className='!mt-0 cursor-pointer'>
{t('percentFee')}
</FormLabel>
</FormItem>
<FormItem className='flex items-center space-x-2'>
<FormControl>
<RadioGroupItem value='2' />
</FormControl>
<FormLabel className='!mt-0 cursor-pointer'>{t('fixedFee')}</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{feeMode === 1 && (
<div className='grid grid-cols-1 sm:w-1/2'>
<FormField
control={form.control}
name='fee_percent'
render={({ field }) => (
<FormItem>
<FormLabel>{t('feePercent')}</FormLabel>
<FormControl>
<EnhancedInput
type='number'
step='0.01'
suffix='%'
value={field.value}
onValueChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
{feeMode === 2 && (
<div className='grid grid-cols-1 sm:w-1/2'>
<FormField
control={form.control}
name='fee_amount'
render={({ field }) => (
<FormItem>
<FormLabel>{t('feeAmount')}</FormLabel>
<FormControl>
<EnhancedInput
type='number'
step='0.01'
prefix={currency.currency_symbol}
suffix={currency.currency_unit}
value={unitConversion('centsToDollars', field.value)}
onValueChange={(value) =>
field.onChange(unitConversion('dollarsToCents', value))
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</div>
<div className='space-y-4'>
{(!platformValue || platformData?.find((p) => p.platform === platformValue)) && (
<FormField
control={form.control}
name='platform'
render={({ field }) => (
<FormItem>
<FormLabel>{t('platform')}</FormLabel>
<Select
onValueChange={(value) => {
form.setValue('platform', value as string);
form.setValue('config', {});
}}
defaultValue={field.value}
value={field.value}
disabled={isEdit}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={t('selectPlatform')} />
</SelectTrigger>
</FormControl>
<SelectContent>
{platformData?.map((platform) => (
<SelectItem key={platform.platform} value={platform.platform}>
{platform.platform}
</SelectItem>
))}
</SelectContent>
</Select>
{platformUrl ? (
<div className='mt-1 flex justify-end'>
<Button
variant='ghost'
size='sm'
className='h-6 px-2 text-xs'
onClick={openPlatformUrl}
>
<Icon icon='tabler:external-link' className='mr-1 h-3 w-3' />
{t('applyForPayment')}
</Button>
</div>
) : (
<div className='mt-1 h-6'></div>
)}
<FormMessage />
</FormItem>
)}
/>
)}
{configFields.length > 0 && (
<div className='mt-4 space-y-4'>
{configFields.map((fieldKey) => (
<FormItem key={fieldKey}>
<FormLabel>{currentFieldDescriptions[fieldKey]}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('configPlaceholder', {
field: currentFieldDescriptions[fieldKey],
})}
value={
configValues && configValues[fieldKey] !== undefined
? configValues[fieldKey]
: ''
}
onValueChange={(value) => {
const newConfig = { ...configValues };
newConfig[fieldKey] = value;
form.setValue('config', newConfig);
}}
/>
</FormControl>
</FormItem>
))}
</div>
)}
<FormField
control={form.control}
name='description'
render={({ field }) => (
<FormItem>
<FormLabel>{t('description')}</FormLabel>
<FormControl>
<MarkdownEditor
value={field.value}
onChange={(value) => form.setValue(field.name, value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</form>
</Form>
</ScrollArea>
<SheetFooter className='flex-row justify-end gap-2 pt-3'>
<Button variant='outline' disabled={loading} onClick={handleClose}>
{t('cancel')}
</Button>
<Button disabled={loading} onClick={form.handleSubmit(handleSubmit)}>
{loading && <Icon icon='mdi:loading' className='mr-2 animate-spin' />}
{t('submit')}
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}
@@ -0,0 +1,223 @@
'use client';
import { Display } from '@/components/display';
import { ProTable, ProTableActions } from '@/components/pro-table';
import {
createPaymentMethod,
deletePaymentMethod,
getPaymentMethodList,
updatePaymentMethod,
} from '@/services/admin/payment';
import { Avatar, AvatarFallback, AvatarImage } from '@workspace/ui/components/avatar';
import { Badge } from '@workspace/ui/components/badge';
import { Button } from '@workspace/ui/components/button';
import { Switch } from '@workspace/ui/components/switch';
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
import { useTranslations } from 'next-intl';
import { useRef, useState } from 'react';
import { toast } from 'sonner';
import PaymentForm from './payment-form';
export default function PaymentTable() {
const t = useTranslations('payment');
const [loading, setLoading] = useState(false);
const ref = useRef<ProTableActions>(null);
return (
<ProTable<API.PaymentConfig, { search: string }>
action={ref}
header={{
title: t('paymentManagement'),
toolbar: (
<PaymentForm<API.CreatePaymentMethodRequest>
trigger={<Button>{t('create')}</Button>}
title={t('createPayment')}
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await createPaymentMethod({
...values,
enable: false,
});
toast.success(t('createSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>
),
}}
columns={[
{
accessorKey: 'enable',
header: t('enable'),
cell: ({ row }) => {
return (
<Switch
checked={Boolean(row.getValue('enable'))}
onCheckedChange={async (checked) => {
await updatePaymentMethod({
...row.original,
enable: checked,
});
ref.current?.refresh();
}}
/>
);
},
},
{
accessorKey: 'icon',
header: t('icon'),
cell: ({ row }) => {
const icon = row.getValue('icon') as string;
return (
<Avatar className='h-8 w-8'>
{icon ? <AvatarImage src={icon} alt={row.getValue('name')} /> : null}
<AvatarFallback>
{(row.getValue('name') as string)?.charAt(0) || '?'}
</AvatarFallback>
</Avatar>
);
},
},
{
accessorKey: 'name',
header: t('name'),
},
{
accessorKey: 'platform',
header: t('platform'),
cell: ({ row }) => <Badge>{t(row.getValue('platform'))}</Badge>,
},
{
accessorKey: 'notify_url',
header: t('notify_url'),
},
{
accessorKey: 'fee',
header: t('handlingFee'),
cell: ({ row }) => {
const feeMode = row.original.fee_mode;
if (feeMode === 1) {
return <Badge>{row.original.fee_percent}%</Badge>;
} else if (feeMode === 2) {
return (
<Badge>
<Display value={row.original.fee_amount} type='currency' />
</Badge>
);
}
return '--';
},
},
]}
params={[
{
key: 'search',
placeholder: t('searchPlaceholder'),
},
]}
request={async (pagination, filter) => {
const { data } = await getPaymentMethodList({
...pagination,
...filter,
});
return {
list: data?.data?.list || [],
total: data?.data?.total || 0,
};
}}
actions={{
render: (row) => [
<PaymentForm<API.UpdatePaymentMethodRequest>
isEdit
key='edit'
trigger={<Button>{t('edit')}</Button>}
title={t('editPayment')}
loading={loading}
initialValues={row}
onSubmit={async (values) => {
setLoading(true);
try {
await updatePaymentMethod({
...row,
...values,
});
toast.success(t('updateSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>,
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('delete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
await deletePaymentMethod({
id: row.id,
});
toast.success(t('deleteSuccess'));
ref.current?.refresh();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
<Button
key='copy'
variant='outline'
onClick={async () => {
setLoading(true);
try {
const { id, ...params } = row;
await createPaymentMethod({
...params,
enable: false,
});
toast.success(t('copySuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
>
{t('copy')}
</Button>,
],
batchRender(rows) {
return [
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('batchDelete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
for (const row of rows) {
await deletePaymentMethod({ id: row.id });
}
toast.success(t('deleteSuccess'));
ref.current?.refresh();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
];
},
}}
/>
);
}
@@ -1,232 +0,0 @@
'use client';
import {
getStripeAlipayPaymentConfig,
updateStripeAlipayPaymentConfig,
} from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function Stripe() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getStripeAlipayPaymentConfig'],
queryFn: async () => {
const { data } = await getStripeAlipayPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateStripeAlipayPaymentConfig({
...data,
mark: 'stripe_alipay',
[key]: value,
} as any);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('aliPay')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={async (checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => {
updateConfig('name', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon}
onValueBlur={(value) => {
updateConfig('icon', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => {
updateConfig('domain', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
value={data?.fee_percent}
onValueBlur={(value) => {
updateConfig('fee_percent', value);
}}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.publicKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.public_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
public_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.secretKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.secret_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
secret_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.webhookSecret')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.webhook_secret}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
webhook_secret: value,
});
}}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
@@ -1,232 +0,0 @@
'use client';
import {
getStripeWeChatPayPaymentConfig,
updateStripeWeChatPayPaymentConfig,
} from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function StripeWeChatPay() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getStripeWeChatPayPaymentConfig'],
queryFn: async () => {
const { data } = await getStripeWeChatPayPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateStripeWeChatPayPaymentConfig({
...data,
mark: 'stripe_wechat_pay',
[key]: value,
} as any);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('wechatPay')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={async (checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => {
updateConfig('name', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon}
onValueBlur={(value) => {
updateConfig('icon', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => {
updateConfig('domain', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
value={data?.fee_percent}
onValueBlur={(value) => {
updateConfig('fee_percent', value);
}}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.publicKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.public_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
public_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.secretKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.secret_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
secret_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.webhookSecret')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.webhook_secret}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
webhook_secret: value,
});
}}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
@@ -362,9 +362,10 @@ export default function NodeForm<T extends { [x: string]: any }>({
<EnhancedInput
{...field}
type='number'
placeholder='1-65535'
step={1}
min={1}
max={65535}
placeholder='1-65535'
onValueChange={(value) => {
form.setValue(field.name, value);
}}
@@ -418,6 +419,9 @@ export default function NodeForm<T extends { [x: string]: any }>({
<EnhancedInput
{...field}
type='number'
step={1}
min={1}
max={65535}
placeholder='1-65535'
onValueChange={(value) => {
form.setValue(field.name, value);
@@ -899,6 +903,7 @@ export default function NodeForm<T extends { [x: string]: any }>({
{
name: 'port',
type: 'number',
step: 1,
min: 1,
max: 65535,
placeholder: t('form.relayPort'),
+44
View File
@@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { Textarea } from '@workspace/ui/components/textarea';
import { JSONEditor } from '@workspace/ui/custom-components/editor';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
import { useTranslations } from 'next-intl';
@@ -130,6 +131,49 @@ export default function Site() {
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('customData')}</Label>
<p className='text-muted-foreground text-xs'>{t('customDataDescription')}</p>
</TableCell>
<TableCell className='w-1/2 text-right'>
<JSONEditor
schema={{
type: 'object',
additionalProperties: true,
properties: {
website: { type: 'string', title: 'Website' },
contacts: {
type: 'object',
title: 'Contacts',
additionalProperties: true,
properties: {
email: { type: 'string', title: 'Email' },
telephone: { type: 'string', title: 'Telephone' },
address: { type: 'string', title: 'Address' },
},
},
community: {
type: 'object',
title: 'Community',
additionalProperties: true,
properties: {
telegram: { type: 'string', title: 'Telegram' },
twitter: { type: 'string', title: 'Twitter' },
discord: { type: 'string', title: 'Discord' },
instagram: { type: 'string', title: 'Instagram' },
linkedin: { type: 'string', title: 'Linkedin' },
facebook: { type: 'string', title: 'Facebook' },
github: { type: 'string', title: 'Github' },
},
},
},
}}
value={data?.custom_data}
onBlur={(value) => updateConfig('custom_data', value)}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
@@ -113,6 +113,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
<FormControl>
<EnhancedInput
type='number'
min={0}
value={field.value}
prefix={currency?.currency_symbol ?? '$'}
formatInput={(value) => unitConversion('centsToDollars', value)}
@@ -136,6 +137,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
<FormControl>
<EnhancedInput
type='number'
min={0}
value={field.value}
prefix={currency?.currency_symbol ?? '$'}
formatInput={(value) => unitConversion('centsToDollars', value)}
@@ -159,6 +161,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
<FormControl>
<EnhancedInput
type='number'
min={0}
value={field.value}
prefix={currency?.currency_symbol ?? '$'}
formatInput={(value) => unitConversion('centsToDollars', value)}
@@ -57,150 +57,154 @@ export function SubscriptionDetail({
{t('onlineDevices')}
</TabsTrigger>
</TabsList>
<TabsContent value='logs'>
<ProTable<API.UserSubscribeLog, Record<string, unknown>>
columns={[
{
accessorKey: 'ip',
header: 'IP',
},
{
accessorKey: 'user_agent',
header: t('userAgent'),
},
{
accessorKey: 'token',
header: t('token'),
},
{
accessorKey: 'created_at',
header: t('time'),
cell: ({ row }) => formatDate(row.getValue('created_at')),
},
]}
request={async (pagination) => {
const { data } = await getUserSubscribeLogs({
user_id: userId,
subscribe_id: subscriptionId,
...pagination,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
/>
</TabsContent>
<TabsContent value='traffic'>
<ProTable<API.TrafficLog, Record<string, unknown>>
columns={[
{
accessorKey: 'download',
header: t('download'),
cell: ({ row }) => <Display type='traffic' value={row.getValue('download')} />,
},
{
accessorKey: 'upload',
header: t('upload'),
cell: ({ row }) => <Display type='traffic' value={row.getValue('upload')} />,
},
{
accessorKey: 'timestamp',
header: t('time'),
cell: ({ row }) => formatDate(row.getValue('timestamp')),
},
]}
request={async (pagination) => {
const { data } = await getUserSubscribeTrafficLogs({
user_id: userId,
subscribe_id: subscriptionId,
...pagination,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
/>
</TabsContent>
<TabsContent value='devices'>
<ProTable<API.UserDevice, Record<string, unknown>>
columns={[
{
accessorKey: 'enabled',
header: t('enable'),
cell: ({ row }) => (
<Switch
checked={row.getValue('enabled')}
onChange={(checked) => {
console.log('Switch:', checked);
}}
/>
),
},
{
accessorKey: 'id',
header: 'ID',
},
{
accessorKey: 'identifier',
header: 'IMEI',
},
{
accessorKey: 'user_agent',
header: t('userAgent'),
},
{
accessorKey: 'ip',
header: 'IP',
},
{
accessorKey: 'online',
header: t('loginStatus'),
cell: ({ row }) => (
<Badge variant={row.getValue('online') ? 'default' : 'destructive'}>
{row.getValue('online') ? t('online') : t('offline')}
</Badge>
),
},
{
accessorKey: 'updated_at',
header: t('lastSeen'),
cell: ({ row }) => formatDate(row.getValue('updated_at')),
},
]}
request={async (pagination) => {
const { data } = await getUserSubscribeDevices({
user_id: userId,
subscribe_id: subscriptionId,
...pagination,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
actions={{
render: (row) => {
if (!row.identifier) return [];
return [
<ConfirmButton
key='offline'
trigger={<Button variant='destructive'>{t('confirmOffline')}</Button>}
title={t('confirmOffline')}
description={t('kickOfflineConfirm', { ip: row.ip })}
onConfirm={async () => {
await kickOfflineByUserDevice({ id: row.id });
toast.success(t('kickOfflineSuccess'));
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
];
},
}}
/>
</TabsContent>
<div className='mt-4 max-h-[60dvh] overflow-y-auto'>
<TabsContent value='logs'>
<ProTable<API.UserSubscribeLog, Record<string, unknown>>
columns={[
{
accessorKey: 'ip',
header: 'IP',
},
{
accessorKey: 'user_agent',
header: t('userAgent'),
},
{
accessorKey: 'token',
header: t('token'),
},
{
accessorKey: 'created_at',
header: t('time'),
cell: ({ row }) => formatDate(row.getValue('created_at')),
},
]}
request={async (pagination) => {
const { data } = await getUserSubscribeLogs({
user_id: userId,
subscribe_id: subscriptionId,
...pagination,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
/>
</TabsContent>
<TabsContent value='traffic'>
<ProTable<API.TrafficLog, Record<string, unknown>>
columns={[
{
accessorKey: 'download',
header: t('download'),
cell: ({ row }) => (
<Display type='traffic' value={row.getValue('download')} />
),
},
{
accessorKey: 'upload',
header: t('upload'),
cell: ({ row }) => <Display type='traffic' value={row.getValue('upload')} />,
},
{
accessorKey: 'timestamp',
header: t('time'),
cell: ({ row }) => formatDate(row.getValue('timestamp')),
},
]}
request={async (pagination) => {
const { data } = await getUserSubscribeTrafficLogs({
user_id: userId,
subscribe_id: subscriptionId,
...pagination,
} as API.GetUserSubscribeTrafficLogsParams);
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
/>
</TabsContent>
<TabsContent value='devices'>
<ProTable<API.UserDevice, Record<string, unknown>>
columns={[
{
accessorKey: 'enabled',
header: t('enable'),
cell: ({ row }) => (
<Switch
checked={row.getValue('enabled')}
onChange={(checked) => {
console.log('Switch:', checked);
}}
/>
),
},
{
accessorKey: 'id',
header: 'ID',
},
{
accessorKey: 'identifier',
header: 'IMEI',
},
{
accessorKey: 'user_agent',
header: t('userAgent'),
},
{
accessorKey: 'ip',
header: 'IP',
},
{
accessorKey: 'online',
header: t('loginStatus'),
cell: ({ row }) => (
<Badge variant={row.getValue('online') ? 'default' : 'destructive'}>
{row.getValue('online') ? t('online') : t('offline')}
</Badge>
),
},
{
accessorKey: 'updated_at',
header: t('lastSeen'),
cell: ({ row }) => formatDate(row.getValue('updated_at')),
},
]}
request={async (pagination) => {
const { data } = await getUserSubscribeDevices({
user_id: userId,
subscribe_id: subscriptionId,
...pagination,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
actions={{
render: (row) => {
if (!row.identifier) return [];
return [
<ConfirmButton
key='offline'
trigger={<Button variant='destructive'>{t('confirmOffline')}</Button>}
title={t('confirmOffline')}
description={t('kickOfflineConfirm', { ip: row.ip })}
onConfirm={async () => {
await kickOfflineByUserDevice({ id: row.id });
toast.success(t('kickOfflineSuccess'));
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
];
},
}}
/>
</TabsContent>
</div>
</Tabs>
</div>
</DialogContent>
+14 -1
View File
@@ -58,8 +58,21 @@ export default function Page() {
<Switch
defaultChecked={row.getValue('enable')}
onCheckedChange={async (checked) => {
const {
auth_methods,
user_devices,
enable_balance_notify,
enable_login_notify,
enable_subscribe_notify,
enable_trade_notify,
updated_at,
created_at,
id,
...rest
} = row.original;
await updateUserBasicInfo({
...row.original,
user_id: id,
...rest,
enable: checked,
} as unknown as API.UpdateUserBasiceInfoRequest);
toast.success(t('updateSuccess'));
+2
View File
@@ -16,6 +16,7 @@ export const useGlobalStore = create<GlobalStore>((set) => ({
site_logo: '',
keywords: '',
custom_html: '',
custom_data: '',
},
verify: {
turnstile_site_key: '',
@@ -63,6 +64,7 @@ export const useGlobalStore = create<GlobalStore>((set) => ({
verify_code_interval: 60,
},
oauth_methods: [],
web_ad: false,
},
user: undefined,
setCommon: (common) =>
+2
View File
@@ -39,6 +39,8 @@
"status": "Stav",
"subject": "Předmět",
"template": "Šablona",
"traffic_exceed_email_template": "Šablona oznámení o překročení provozu",
"traffic_exceed_email_templateDescription": "Zástupné symboly {after}.variable{before} budou nahrazeny skutečnými daty. Ujistěte se, že tyto proměnné zachováte.",
"verify_email_template": "Šablona ověřovacího e-mailu",
"verify_email_templateDescription": "Zástupné symboly {after}.variable{before} budou nahrazeny skutečnými daty. Ujistěte se, že tyto proměnné zachováte.",
"whitelistSuffixes": "Povolené přípony",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Platba Alipay",
"alipayf2f": {
"appId": "ID aplikace Alipay",
"invoiceName": "Název vlastního produktu",
"invoiceNameDescription": "Bude zobrazeno na faktuře Alipay",
"privateKey": "Soukromý klíč Alipay",
"publicKey": "Veřejný klíč Alipay",
"sandbox": "Režim sandbox",
"sandboxDescription": "Povolte režim sandbox pro testování plateb"
},
"AlipayF2F": "Alipay Osobně",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Žádat o platbu",
"balance": "Zůstatek",
"batchDelete": "Hromadné smazání",
"cancel": "Zrušit",
"config": "Nastavení",
"configInfo": "Informace o nastavení",
"configPlaceholder": "Vyplňte prosím poskytnutou konfiguraci {field}",
"confirm": "Potvrdit",
"confirmDelete": "Potvrdit smazání",
"copy": "Kopírovat",
"copySuccess": "Úspěšně zkopírováno",
"create": "Přidat platební metodu",
"createPayment": "Přidat platební metodu",
"createSuccess": "Úspěšně vytvořeno",
"delete": "Smazat",
"deleteSuccess": "Úspěšně smazáno",
"deleteWarning": "Opravdu chcete smazat tuto platební metodu? Tuto akci nelze vrátit zpět.",
"description": "Popis",
"descriptionPlaceholder": "Zadejte popis platební metody",
"domain": "Doména",
"domainPlaceholder": "volitelné, např. {example}",
"edit": "Upravit",
"editPayment": "Upravit platební metodu",
"enable": "Povolit",
"enableDescription": "Povolit tento způsob platby",
"epay": {
"key": "KLÍČ",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Pevná částka",
"feeMode": "Způsob účtování",
"feeModeDescription": "Způsob účtování dodatečných poplatků",
"feeModeItems": ["Bez poplatku", "Procento", "Pevná částka", "Obojí výše uvedené"],
"feePercent": "Procentuální poplatek (volitelně)",
"feePercentDescription": "Přidání poplatku k částce objednávky",
"fixedFee": "Pevný poplatek (volitelný)",
"fixedFeeDescription": "Přidání pevného poplatku k částce objednávky",
"iconUrl": "URL ikony (volitelné)",
"iconUrlDescription": "Používá se pro zobrazení na frontendu",
"inputPlaceholder": "Prosím zadejte",
"key": "KLÍČ",
"notifyUrl": "Vlastní oznámení URL (volitelné)",
"notifyUrlDescription": "Oznámení brány budou odeslána na tuto doménu",
"payForRecommendations": "Zaplatit za doporučení",
"pid": "PID",
"saveSuccess": "Uložení bylo úspěšné",
"showName": "Zobrazit název",
"showNameDescription": "Používá se pro zobrazení na frontendu",
"stripe": {
"publicKey": "VEŘEJNÝ KLÍČ",
"secretKey": "TAJNÝ KLÍČ",
"webhookSecret": "PODPIS WEBHOOK KLÍČE"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Poplatek za zpracování",
"icon": "Ikona",
"iconPlaceholder": "Zadejte URL ikony",
"mark": "Identifikátor",
"markPlaceholder": "Zadejte identifikátor platební metody",
"name": "Název",
"namePlaceholder": "Zadejte název platební metody",
"nameRequired": "Název je povinný",
"noFee": "Žádný poplatek",
"notify_url": "URL pro oznámení",
"paymentManagement": "Správa plateb",
"percentFee": "Procento",
"platform": "Platforma",
"searchPlaceholder": "Zadejte hledané výrazy",
"selectPlatform": "Vyberte platformu",
"selectType": "Vyberte typ platby",
"submit": "Odeslat",
"type": "Typ",
"updateSuccess": "Úspěšně aktualizováno"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Zásady ochrany osobních údajů"
},
"site": {
"customData": "Vlastní data",
"customDataDescription": "Vlastní data, používaná pro vlastní data webu",
"customHtml": "Vlastní HTML",
"customHtmlDescription": "Vlastní HTML kód, který bude vložen na konec tagu body na stránce.",
"keywords": "Klíčová slova",
+2
View File
@@ -39,6 +39,8 @@
"status": "Status",
"subject": "Betreff",
"template": "Vorlage",
"traffic_exceed_email_template": "Benachrichtigung über überschreitenden Verkehr",
"traffic_exceed_email_templateDescription": "Die Platzhalter {after}.variable{before} werden durch tatsächliche Daten ersetzt. Stellen Sie sicher, dass Sie diese Variablen beibehalten.",
"verify_email_template": "E-Mail-Bestätigungsvorlage",
"verify_email_templateDescription": "Die Platzhalter {after}.variable{before} werden durch tatsächliche Daten ersetzt. Stellen Sie sicher, dass diese Variablen beibehalten werden.",
"whitelistSuffixes": "Whitelist-Suffixe",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Alipay-Zahlung",
"alipayf2f": {
"appId": "Alipay Anwendungs-ID",
"invoiceName": "Benutzerdefinierter Produktname",
"invoiceNameDescription": "Wird in der Alipay-Rechnung angezeigt",
"privateKey": "Alipay Privatschlüssel",
"publicKey": "Alipay Öffentlicher Schlüssel",
"sandbox": "Sandbox-Modus",
"sandboxDescription": "Aktivieren Sie den Sandbox-Modus, um Zahlungen zu testen"
},
"AlipayF2F": "Alipay Gesicht zu Gesicht",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Zahlung beantragen",
"balance": "Guthaben",
"batchDelete": "Massenlöschung",
"cancel": "Abbrechen",
"config": "Konfiguration",
"configInfo": "Konfigurationsinformationen",
"configPlaceholder": "Bitte füllen Sie die bereitgestellte {field} Konfiguration aus",
"confirm": "Bestätigen",
"confirmDelete": "Löschen bestätigen",
"copy": "Kopieren",
"copySuccess": "Erfolgreich kopiert",
"create": "Zahlungsmethode hinzufügen",
"createPayment": "Zahlungsmethode hinzufügen",
"createSuccess": "Erfolgreich erstellt",
"delete": "Löschen",
"deleteSuccess": "Erfolgreich gelöscht",
"deleteWarning": "Sind Sie sicher, dass Sie diese Zahlungsmethode löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"description": "Beschreibung",
"descriptionPlaceholder": "Geben Sie die Beschreibung der Zahlungsmethode ein",
"domain": "Domain",
"domainPlaceholder": "optional, z.B. {example}",
"edit": "Bearbeiten",
"editPayment": "Zahlungsmethode bearbeiten",
"enable": "Aktivieren",
"enableDescription": "Diese Zahlungsmethode aktivieren",
"epay": {
"key": "SCHLÜSSEL",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Fester Betrag",
"feeMode": "Gebührenmodus",
"feeModeDescription": "Gebührenmodus für zusätzliche Gebühren",
"feeModeItems": ["Keine Gebühr", "Prozentsatz", "Fester Betrag", "Beides"],
"feePercent": "Prozentgebühr (optional)",
"feePercentDescription": "Gebühren werden zusätzlich zum Bestellbetrag hinzugefügt",
"fixedFee": "Feste Gebühr (optional)",
"fixedFeeDescription": "Feste Gebühr zusätzlich zum Bestellbetrag hinzufügen",
"iconUrl": "Symbol-URL (optional)",
"iconUrlDescription": "Zur Anzeige im Frontend verwendet",
"inputPlaceholder": "Bitte eingeben",
"key": "SCHLÜSSEL",
"notifyUrl": "Benutzerdefinierte Benachrichtigungs-URL (optional)",
"notifyUrlDescription": "Gateway-Benachrichtigungen werden an diese Domain gesendet",
"payForRecommendations": "Für Empfehlungen bezahlen",
"pid": "PID",
"saveSuccess": "Erfolgreich gespeichert",
"showName": "Anzeigename",
"showNameDescription": "Zur Anzeige im Frontend verwendet",
"stripe": {
"publicKey": "ÖFFENTLICHER SCHLÜSSEL",
"secretKey": "GEHEIMER SCHLÜSSEL",
"webhookSecret": "WEBHOOK-SCHLÜSSEL-SIGNATUR"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Bearbeitungsgebühr",
"icon": "Symbol",
"iconPlaceholder": "Geben Sie die URL des Symbols ein",
"mark": "Kennung",
"markPlaceholder": "Geben Sie die Kennung der Zahlungsmethode ein",
"name": "Name",
"namePlaceholder": "Geben Sie den Namen der Zahlungsmethode ein",
"nameRequired": "Name ist erforderlich",
"noFee": "Keine Gebühr",
"notify_url": "Benachrichtigungs-URL",
"paymentManagement": "Zahlungsverwaltung",
"percentFee": "Prozentsatz",
"platform": "Plattform",
"searchPlaceholder": "Suchbegriffe eingeben",
"selectPlatform": "Plattform auswählen",
"selectType": "Zahlungsart auswählen",
"submit": "Einreichen",
"type": "Typ",
"updateSuccess": "Erfolgreich aktualisiert"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Datenschutzrichtlinie"
},
"site": {
"customData": "Benutzerdefinierte Daten",
"customDataDescription": "Benutzerdefinierte Daten, die für benutzerdefinierte Daten der Website verwendet werden",
"customHtml": "Benutzerdefiniertes HTML",
"customHtmlDescription": "Benutzerdefinierter HTML-Code, der am Ende des body-Tags der Seite eingefügt werden soll.",
"keywords": "Schlüsselwörter",
+2
View File
@@ -39,6 +39,8 @@
"status": "Status",
"subject": "Subject",
"template": "Template",
"traffic_exceed_email_template": "Traffic Exceed Notice Template",
"traffic_exceed_email_templateDescription": "The {after}.variable{before} placeholders will be replaced with actual data. Ensure to keep these variables.",
"verify_email_template": "Verification Email Template",
"verify_email_templateDescription": "The {after}.variable{before} placeholders will be replaced with actual data. Ensure to keep these variables.",
"whitelistSuffixes": "Whitelist Suffixes",
+48 -40
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Alipay payment",
"alipayf2f": {
"appId": "Alipay App ID",
"invoiceName": "Custom Product Name",
"invoiceNameDescription": "Will be displayed on Alipay bill",
"privateKey": "Alipay Private Key",
"publicKey": "Alipay Public Key",
"sandbox": "Sandbox Mode",
"sandboxDescription": "Enable sandbox mode to test payments"
},
"AlipayF2F": "Alipay Face to Face",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Apply for Payment",
"balance": "Balance",
"batchDelete": "Batch Delete",
"cancel": "Cancel",
"config": "Configuration",
"configInfo": "Configuration Information",
"configPlaceholder": "Please fill in the provided {field} configuration",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"copy": "Copy",
"copySuccess": "Copied successfully",
"create": "Add Payment Method",
"createPayment": "Add Payment Method",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteSuccess": "Deleted successfully",
"deleteWarning": "Are you sure you want to delete this payment method? This action cannot be undone.",
"description": "Description",
"descriptionPlaceholder": "Enter payment method description",
"domain": "Domain",
"domainPlaceholder": "optional, e.g. {example}",
"edit": "Edit",
"editPayment": "Edit Payment Method",
"enable": "Enable",
"enableDescription": "Enable this payment method",
"epay": {
"key": "KEY",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Fixed Amount",
"feeMode": "Fee Mode",
"feeModeDescription": "Fee charging method for additional fees",
"feeModeItems": ["No Fee", "Percentage", "Fixed Amount", "Both"],
"feePercent": "Percentage Fee (optional)",
"feePercentDescription": "Add fee on top of order amount",
"fixedFee": "Fixed Fee (optional)",
"fixedFeeDescription": "Add fixed fee on top of order amount",
"iconUrl": "Icon URL (optional)",
"iconUrlDescription": "Used for frontend display",
"inputPlaceholder": "Please enter",
"key": "KEY",
"notifyUrl": "Custom Notification Domain (optional)",
"notifyUrlDescription": "Gateway notifications will be sent to this domain",
"payForRecommendations": "Pay for Recommendations",
"pid": "PID",
"saveSuccess": "Save Successful",
"showName": "Display Name",
"showNameDescription": "Used for frontend display",
"stripe": {
"publicKey": "PUBLIC KEY",
"secretKey": "SECRET KEY",
"webhookSecret": "WEBHOOK Secret Key"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"feePercent": "Fee Percentage",
"fixedFee": "Fixed Amount",
"handlingFee": "Handling Fee",
"icon": "Icon",
"iconPlaceholder": "Enter icon URL",
"mark": "Identifier",
"markPlaceholder": "Enter payment method identifier",
"name": "Name",
"namePlaceholder": "Enter payment method name",
"nameRequired": "Name is required",
"noFee": "No Fee",
"notify_url": "Notify URL",
"paymentManagement": "Payment Management",
"percentFee": "Percentage",
"platform": "Platform",
"searchPlaceholder": "Enter search terms",
"selectPlatform": "Select Platform",
"selectType": "Select payment type",
"submit": "Submit",
"type": "Type",
"updateSuccess": "Updated successfully"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Privacy Policy"
},
"site": {
"customData": "Custom Data",
"customDataDescription": "Custom Data, used for custom data of the site",
"customHtml": "Custom HTML",
"customHtmlDescription": "Custom HTML code to be injected into the bottom of the site's body tag.",
"keywords": "Keywords",
+2
View File
@@ -39,6 +39,8 @@
"status": "Estado",
"subject": "Asunto",
"template": "Plantilla",
"traffic_exceed_email_template": "Plantilla de Notificación de Exceso de Tráfico",
"traffic_exceed_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
"verify_email_template": "Plantilla de Correo Electrónico de Verificación",
"verify_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrese de mantener estas variables.",
"whitelistSuffixes": "Sufijos de la lista blanca",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Pago con Alipay",
"alipayf2f": {
"appId": "ID de la aplicación de Alipay",
"invoiceName": "Nombre personalizado del producto",
"invoiceNameDescription": "Se mostrará en la factura de Alipay",
"privateKey": "Clave privada de Alipay",
"publicKey": "Clave pública de Alipay",
"sandbox": "Modo sandbox",
"sandboxDescription": "Habilitar el modo sandbox para probar el pago"
},
"AlipayF2F": "Alipay Cara a Cara",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Solicitar Pago",
"balance": "Saldo",
"batchDelete": "Eliminar en Lote",
"cancel": "Cancelar",
"config": "Configuración",
"configInfo": "Información de Configuración",
"configPlaceholder": "Por favor, complete la configuración de {field} proporcionada",
"confirm": "Confirmar",
"confirmDelete": "Confirmar Eliminación",
"copy": "Copiar",
"copySuccess": "Copiado con éxito",
"create": "Agregar Método de Pago",
"createPayment": "Agregar Método de Pago",
"createSuccess": "Creado con éxito",
"delete": "Eliminar",
"deleteSuccess": "Eliminado con éxito",
"deleteWarning": "¿Está seguro de que desea eliminar este método de pago? Esta acción no se puede deshacer.",
"description": "Descripción",
"descriptionPlaceholder": "Ingrese la descripción del método de pago",
"domain": "Dominio",
"domainPlaceholder": "opcional, p. ej. {example}",
"edit": "Editar",
"editPayment": "Editar Método de Pago",
"enable": "Habilitar",
"enableDescription": "Habilitar este método de pago",
"epay": {
"key": "CLAVE",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Monto Fijo",
"feeMode": "Modo de cobro",
"feeModeDescription": "Modo de cobro de tarifas adicionales",
"feeModeItems": ["Sin tarifa", "Porcentaje", "Cantidad fija", "Ambos anteriores"],
"feePercent": "Porcentaje de tarifa (opcional)",
"feePercentDescription": "Se añade una tarifa sobre el monto del pedido",
"fixedFee": "Tarifa fija (opcional)",
"fixedFeeDescription": "Se añade una tarifa fija sobre el monto del pedido",
"iconUrl": "URL del icono (opcional)",
"iconUrlDescription": "Usado para mostrar en el frontend",
"inputPlaceholder": "Por favor, introduzca",
"key": "CLAVE",
"notifyUrl": "Dominio de notificación personalizado (opcional)",
"notifyUrlDescription": "La notificación del gateway se enviará a este dominio",
"payForRecommendations": "Pagar por recomendaciones",
"pid": "PID",
"saveSuccess": "Guardado exitosamente",
"showName": "Mostrar nombre",
"showNameDescription": "Usado para mostrar en el frontend",
"stripe": {
"publicKey": "CLAVE PÚBLICA",
"secretKey": "CLAVE SECRETA",
"webhookSecret": "FIRMA DE CLAVE WEBHOOK"
},
"url": "URL",
"wechatPay": "Pago con WeChat"
"handlingFee": "Tarifa de manejo",
"icon": "Ícono",
"iconPlaceholder": "Ingrese la URL del ícono",
"mark": "Identificador",
"markPlaceholder": "Ingrese el identificador del método de pago",
"name": "Nombre",
"namePlaceholder": "Ingrese el nombre del método de pago",
"nameRequired": "El nombre es obligatorio",
"noFee": "Sin Tarifa",
"notify_url": "URL de notificación",
"paymentManagement": "Gestión de Pagos",
"percentFee": "Porcentaje",
"platform": "Plataforma",
"searchPlaceholder": "Ingrese términos de búsqueda",
"selectPlatform": "Seleccionar Plataforma",
"selectType": "Seleccionar tipo de pago",
"submit": "Enviar",
"type": "Tipo",
"updateSuccess": "Actualizado con éxito"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Política de Privacidad"
},
"site": {
"customData": "Datos Personalizados",
"customDataDescription": "Datos Personalizados, utilizados para datos personalizados del sitio",
"customHtml": "HTML personalizado",
"customHtmlDescription": "Código HTML personalizado que se inyectará en la parte inferior de la etiqueta body del sitio.",
"keywords": "Palabras clave",
+2
View File
@@ -39,6 +39,8 @@
"status": "Estado",
"subject": "Asunto",
"template": "Plantilla",
"traffic_exceed_email_template": "Plantilla de Notificación de Exceso de Tráfico",
"traffic_exceed_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
"verify_email_template": "Plantilla de Correo Electrónico de Verificación",
"verify_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
"whitelistSuffixes": "Sufijos de Lista Blanca",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Pago con Alipay",
"alipayf2f": {
"appId": "ID de la aplicación de Alipay",
"invoiceName": "Nombre personalizado del producto",
"invoiceNameDescription": "Se mostrará en la factura de Alipay",
"privateKey": "Clave privada de Alipay",
"publicKey": "Clave pública de Alipay",
"sandbox": "Modo sandbox",
"sandboxDescription": "Habilitar el modo sandbox para probar el pago"
},
"AlipayF2F": "Alipay Cara a Cara",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Solicitar Pago",
"balance": "Saldo",
"batchDelete": "Eliminar en Lote",
"cancel": "Cancelar",
"config": "Configuración",
"configInfo": "Información de Configuración",
"configPlaceholder": "Por favor, complete la configuración de {field} proporcionada",
"confirm": "Confirmar",
"confirmDelete": "Confirmar Eliminación",
"copy": "Copiar",
"copySuccess": "Copiado con éxito",
"create": "Agregar Método de Pago",
"createPayment": "Agregar Método de Pago",
"createSuccess": "Creado con éxito",
"delete": "Eliminar",
"deleteSuccess": "Eliminado con éxito",
"deleteWarning": "¿Está seguro de que desea eliminar este método de pago? Esta acción no se puede deshacer.",
"description": "Descripción",
"descriptionPlaceholder": "Ingresa la descripción del método de pago",
"domain": "Dominio",
"domainPlaceholder": "opcional, p. ej. {example}",
"edit": "Editar",
"editPayment": "Editar Método de Pago",
"enable": "Habilitar",
"enableDescription": "Habilitar este método de pago",
"epay": {
"key": "CLAVE",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Monto Fijo",
"feeMode": "Modo de cobro",
"feeModeDescription": "Modo de cobro de tarifas adicionales",
"feeModeItems": ["Sin tarifa", "Porcentaje", "Cantidad fija", "Ambos"],
"feePercent": "Porcentaje de tarifa (opcional)",
"feePercentDescription": "Se añade una tarifa sobre el monto del pedido",
"fixedFee": "Cuota fija (opcional)",
"fixedFeeDescription": "Agregar una tarifa fija sobre el monto del pedido",
"iconUrl": "URL del ícono (opcional)",
"iconUrlDescription": "Usado para mostrar en el frontend",
"inputPlaceholder": "Por favor ingrese",
"key": "LLAVE",
"notifyUrl": "Dominio de notificación personalizado (opcional)",
"notifyUrlDescription": "La notificación del gateway se enviará a este dominio",
"payForRecommendations": "Pagar por recomendaciones",
"pid": "PID",
"saveSuccess": "Guardado exitoso",
"showName": "Mostrar nombre",
"showNameDescription": "Usado para mostrar en el frontend",
"stripe": {
"publicKey": "CLAVE PÚBLICA",
"secretKey": "CLAVE SECRETA",
"webhookSecret": "FIRMA DE SECRETO DE WEBHOOK"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Cargo por manejo",
"icon": "Ícono",
"iconPlaceholder": "Ingrese la URL del ícono",
"mark": "Identificador",
"markPlaceholder": "Ingrese el identificador del método de pago",
"name": "Nombre",
"namePlaceholder": "Ingrese el nombre del método de pago",
"nameRequired": "El nombre es obligatorio",
"noFee": "Sin Tarifa",
"notify_url": "URL de notificación",
"paymentManagement": "Gestión de Pagos",
"percentFee": "Porcentaje",
"platform": "Plataforma",
"searchPlaceholder": "Ingrese términos de búsqueda",
"selectPlatform": "Seleccionar Plataforma",
"selectType": "Seleccionar tipo de pago",
"submit": "Enviar",
"type": "Tipo",
"updateSuccess": "Actualizado con éxito"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Política de Privacidad"
},
"site": {
"customData": "Datos Personalizados",
"customDataDescription": "Datos Personalizados, utilizados para datos personalizados del sitio",
"customHtml": "HTML personalizado",
"customHtmlDescription": "Código HTML personalizado que se inyectará al final de la etiqueta body del sitio.",
"keywords": "Palabras clave",
+2
View File
@@ -39,6 +39,8 @@
"status": "وضعیت",
"subject": "موضوع",
"template": "الگو",
"traffic_exceed_email_template": "الگوی اعلان ترافیک بیش از حد",
"traffic_exceed_email_templateDescription": "جایگزین‌های {after}.variable{before} با داده‌های واقعی خواهد شد. اطمینان حاصل کنید که این متغیرها را حفظ کنید.",
"verify_email_template": "قالب ایمیل تأیید",
"verify_email_templateDescription": "جای‌گذاری‌های {after}.variable{before} با داده‌های واقعی جایگزین خواهند شد. اطمینان حاصل کنید که این متغیرها را حفظ کنید.",
"whitelistSuffixes": "پسوندهای لیست سفید",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "پرداخت علی‌پی",
"alipayf2f": {
"appId": "شناسه برنامه Alipay",
"invoiceName": "نام محصول سفارشی",
"invoiceNameDescription": "در صورتحساب Alipay نمایش داده خواهد شد",
"privateKey": "کلید خصوصی Alipay",
"publicKey": "کلید عمومی Alipay",
"sandbox": "حالت Sandbox",
"sandboxDescription": "حالت sandbox را برای آزمایش پرداخت‌ها فعال کنید"
},
"AlipayF2F": "علی‌پی چهره به چهره",
"EPay": "ای‌پی",
"Stripe": "استرایپ",
"applyForPayment": "درخواست پرداخت",
"balance": "موجودی",
"batchDelete": "حذف دسته‌ای",
"cancel": "لغو",
"config": "پیکربندی",
"configInfo": "اطلاعات پیکربندی",
"configPlaceholder": "لطفاً پیکربندی {field} ارائه شده را پر کنید",
"confirm": "تأیید",
"confirmDelete": "تأیید حذف",
"copy": "کپی",
"copySuccess": "با موفقیت کپی شد",
"create": "افزودن روش پرداخت",
"createPayment": "افزودن روش پرداخت",
"createSuccess": "با موفقیت ایجاد شد",
"delete": "حذف",
"deleteSuccess": "با موفقیت حذف شد",
"deleteWarning": "آیا مطمئن هستید که می‌خواهید این روش پرداخت را حذف کنید؟ این عمل قابل بازگشت نیست.",
"description": "توضیحات",
"descriptionPlaceholder": "توضیحات روش پرداخت را وارد کنید",
"domain": "دامنه",
"domainPlaceholder": "اختیاری، به عنوان مثال {example}",
"edit": "ویرایش",
"editPayment": "ویرایش روش پرداخت",
"enable": "فعال کردن",
"enableDescription": "این روش پرداخت را فعال کنید",
"epay": {
"key": "کلید",
"pid": "شناسه محصول",
"url": "نشانی وب"
},
"feeAmount": "مقدار ثابت",
"feeMode": "حالت هزینه",
"feeModeDescription": "روش دریافت هزینه برای هزینه‌های اضافی",
"feeModeItems": ["بدون هزینه", "درصدی", "مبلغ ثابت", "هر دو"],
"feePercent": "درصد کارمزد (اختیاری)",
"feePercentDescription": "افزودن کارمزد به مبلغ سفارش",
"fixedFee": "هزینه ثابت (اختیاری)",
"fixedFeeDescription": "افزودن هزینه ثابت به مبلغ سفارش",
"iconUrl": درس آیکون (اختیاری)",
"iconUrlDescription": "برای نمایش در رابط کاربری استفاده می‌شود",
"inputPlaceholder": "لطفاً وارد کنید",
"key": "کلید",
"notifyUrl": "دامنه اعلان سفارشی (اختیاری)",
"notifyUrlDescription": "اعلان‌های درگاه به این دامنه ارسال خواهند شد",
"payForRecommendations": "پرداخت برای توصیه‌ها",
"pid": "شناسه فرآیند",
"saveSuccess": "ذخیره با موفقیت انجام شد",
"showName": "نمایش نام",
"showNameDescription": "برای نمایش در قسمت جلویی استفاده می‌شود",
"stripe": {
"publicKey": "کلید عمومی",
"secretKey": "کلید مخفی",
"webhookSecret": "کلید مخفی وب‌هوک"
},
"url": "آدرس اینترنتی",
"wechatPay": "وی‌چت پی"
"handlingFee": "هزینه خدمات",
"icon": یکون",
"iconPlaceholder": "آدرس URL آیکون را وارد کنید",
"mark": "شناسه",
"markPlaceholder": "شناسه روش پرداخت را وارد کنید",
"name": "نام",
"namePlaceholder": "نام روش پرداخت را وارد کنید",
"nameRequired": "نام الزامی است",
"noFee": "بدون هزینه",
"notify_url": "آدرس اطلاع‌رسانی",
"paymentManagement": "مدیریت پرداخت",
"percentFee": "درصد",
"platform": "پلتفرم",
"searchPlaceholder": "عبارات جستجو را وارد کنید",
"selectPlatform": "انتخاب پلتفرم",
"selectType": "انتخاب نوع پرداخت",
"submit": "ارسال",
"type": "نوع",
"updateSuccess": "با موفقیت به‌روزرسانی شد"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "سیاست حفظ حریم خصوصی"
},
"site": {
"customData": "داده‌های سفارشی",
"customDataDescription": "داده‌های سفارشی، استفاده شده برای داده‌های سفارشی سایت",
"customHtml": "HTML سفارشی",
"customHtmlDescription": "کد HTML سفارشی که باید در انتهای تگ body سایت وارد شود.",
"keywords": "کلمات کلیدی",
+2
View File
@@ -39,6 +39,8 @@
"status": "Tila",
"subject": "Aihe",
"template": "Malline",
"traffic_exceed_email_template": "Liikenteen Ylitysilmoitusmalli",
"traffic_exceed_email_templateDescription": "{after}.variable{before} -paikat korvataan todellisilla tiedoilla. Varmista, että säilytät nämä muuttujat.",
"verify_email_template": "Sähköpostin Vahvistusmalli",
"verify_email_templateDescription": "Paikkamerkit {after}.variable{before} korvataan todellisilla tiedoilla. Varmista, että pidät nämä muuttujat.",
"whitelistSuffixes": "Sallittujen luetteloiden päätteet",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Alipay-maksu",
"alipayf2f": {
"appId": "Alipay-sovelluksen ID",
"invoiceName": "Mukautettu tuotteen nimi",
"invoiceNameDescription": "Näkyy Alipay-laskussa",
"privateKey": "Alipay-yksityinen avain",
"publicKey": "Alipay-julkinen avain",
"sandbox": "Hiekkalaatikkotila",
"sandboxDescription": "Ota hiekkalaatikkotila käyttöön maksujen testaamiseksi"
},
"AlipayF2F": "Alipay Kasvokkain",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Hae maksua",
"balance": "Saldo",
"batchDelete": "Ryhmän poisto",
"cancel": "Peruuta",
"config": "Konfiguraatio",
"configInfo": "Konfiguraatio tiedot",
"configPlaceholder": "Täytä annettu {field} konfiguraatio",
"confirm": "Vahvista",
"confirmDelete": "Vahvista poisto",
"copy": "Kopioi",
"copySuccess": "Kopiointi onnistui",
"create": "Lisää maksutapa",
"createPayment": "Lisää maksutapa",
"createSuccess": "Luonti onnistui",
"delete": "Poista",
"deleteSuccess": "Poisto onnistui",
"deleteWarning": "Oletko varma, että haluat poistaa tämän maksutavan? Tätä toimintoa ei voi peruuttaa.",
"description": "Kuvaus",
"descriptionPlaceholder": "Syötä maksutavan kuvaus",
"domain": "Verkkotunnus",
"domainPlaceholder": "valinnainen, esim. {esimerkki}",
"edit": "Muokkaa",
"editPayment": "Muokkaa maksutapaa",
"enable": "Ota käyttöön",
"enableDescription": "Ota tämä maksutapa käyttöön",
"epay": {
"key": "AVAIN",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Kiinteä summa",
"feeMode": "Maksutapa",
"feeModeDescription": "Lisämaksun veloitustapa",
"feeModeItems": ["Ei käsittelymaksua", "Prosenttiosuus", "Kiinteä summa", "Molemmat yllä olevat"],
"feePercent": "Prosenttiosuusmaksu (valinnainen)",
"feePercentDescription": "Lisätään maksu tilauksen summan päälle",
"fixedFee": "Kiinteä maksu (valinnainen)",
"fixedFeeDescription": "Lisää kiinteä maksu tilauksen summan päälle",
"iconUrl": "Kuvakkeen URL (valinnainen)",
"iconUrlDescription": "Käytetään etunäytössä",
"inputPlaceholder": "Kirjoita tähän",
"key": "AVAIN",
"notifyUrl": "Mukautettu ilmoitusalue (valinnainen)",
"notifyUrlDescription": "Yhdyskäytävän ilmoitus lähetetään tähän verkkotunnukseen",
"payForRecommendations": "Maksa suosituksista",
"pid": "PID",
"saveSuccess": "Tallennus onnistui",
"showName": "Näytä nimi",
"showNameDescription": "Näytetään käyttöliittymässä",
"stripe": {
"publicKey": "JULKINEN AVAIN",
"secretKey": "SALAINEN AVAIN",
"webhookSecret": "WEBHOOK-SALAINEN ALLEKIRJOITUS"
},
"url": "URL",
"wechatPay": "WeChat-maksu"
"handlingFee": "Käsittelymaksu",
"icon": "Ikoni",
"iconPlaceholder": "Syötä ikonin URL-osoite",
"mark": "Tunniste",
"markPlaceholder": "Syötä maksutavan tunniste",
"name": "Nimi",
"namePlaceholder": "Syötä maksutavan nimi",
"nameRequired": "Nimi on pakollinen",
"noFee": "Ei maksua",
"notify_url": "Ilmoitus-URL",
"paymentManagement": "Maksujen hallinta",
"percentFee": "Prosentti",
"platform": "Alusta",
"searchPlaceholder": "Syötä hakusanat",
"selectPlatform": "Valitse alusta",
"selectType": "Valitse maksutyyppi",
"submit": "Lähetä",
"type": "Tyyppi",
"updateSuccess": "Päivitys onnistui"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Tietosuojakäytäntö"
},
"site": {
"customData": "Mukautettu tieto",
"customDataDescription": "Mukautettu tieto, jota käytetään sivuston mukautettuun tietoon",
"customHtml": "Mukautettu HTML",
"customHtmlDescription": "Mukautettu HTML-koodi, joka lisätään sivuston body -tagin loppuun.",
"keywords": "Avainsanat",
+2
View File
@@ -39,6 +39,8 @@
"status": "Statut",
"subject": "Sujet",
"template": "Modèle",
"traffic_exceed_email_template": "Modèle de notification de dépassement de trafic",
"traffic_exceed_email_templateDescription": "Les espaces réservés {after}.variable{before} seront remplacés par des données réelles. Assurez-vous de conserver ces variables.",
"verify_email_template": "Modèle d'Email de Vérification",
"verify_email_templateDescription": "Les espaces réservés {after}.variable{before} seront remplacés par des données réelles. Assurez-vous de conserver ces variables.",
"whitelistSuffixes": "Suffixes de la liste blanche",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Paiement par Alipay",
"alipayf2f": {
"appId": "ID de l'application Alipay",
"invoiceName": "Nom personnalisé du produit",
"invoiceNameDescription": "S'affichera sur la facture Alipay",
"privateKey": "Clé privée Alipay",
"publicKey": "Clé publique Alipay",
"sandbox": "Mode bac à sable",
"sandboxDescription": "Activez le mode bac à sable pour tester le paiement"
},
"AlipayF2F": "Alipay Face à Face",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Demander un paiement",
"balance": "Solde",
"batchDelete": "Suppression par lot",
"cancel": "Annuler",
"config": "Configuration",
"configInfo": "Informations de configuration",
"configPlaceholder": "Veuillez remplir la configuration {field} fournie",
"confirm": "Confirmer",
"confirmDelete": "Confirmer la suppression",
"copy": "Copier",
"copySuccess": "Copié avec succès",
"create": "Ajouter un mode de paiement",
"createPayment": "Ajouter un mode de paiement",
"createSuccess": "Créé avec succès",
"delete": "Supprimer",
"deleteSuccess": "Supprimé avec succès",
"deleteWarning": "Êtes-vous sûr de vouloir supprimer ce mode de paiement ? Cette action ne peut pas être annulée.",
"description": "Description",
"descriptionPlaceholder": "Entrez la description du mode de paiement",
"domain": "Domaine",
"domainPlaceholder": "facultatif, par exemple {exemple}",
"edit": "Modifier",
"editPayment": "Modifier le mode de paiement",
"enable": "Activer",
"enableDescription": "Activer ce mode de paiement",
"epay": {
"key": "CLÉ",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Montant fixe",
"feeMode": "Mode de facturation",
"feeModeDescription": "Mode de facturation des frais supplémentaires",
"feeModeItems": ["Pas de frais", "Pourcentage", "Montant fixe", "Les deux ci-dessus"],
"feePercent": "Pourcentage de frais (optionnel)",
"feePercentDescription": "Ajouter des frais au montant de la commande",
"fixedFee": "Frais fixes (optionnel)",
"fixedFeeDescription": "Ajouter des frais fixes au montant de la commande",
"iconUrl": "URL de l'icône (facultatif)",
"iconUrlDescription": "Utilisé pour l'affichage frontal",
"inputPlaceholder": "Veuillez entrer",
"key": "CLÉ",
"notifyUrl": "Domaine de notification personnalisé (facultatif)",
"notifyUrlDescription": "Les notifications de la passerelle seront envoyées à ce domaine",
"payForRecommendations": "Payer pour les recommandations",
"pid": "PID",
"saveSuccess": "Enregistrement réussi",
"showName": "Afficher le nom",
"showNameDescription": "Utilisé pour l'affichage frontal",
"stripe": {
"publicKey": "CLÉ PUBLIQUE",
"secretKey": "CLÉ SECRÈTE",
"webhookSecret": "SIGNATURE DE CLÉ WEBHOOK"
},
"url": "URL",
"wechatPay": "Paiement WeChat"
"handlingFee": "Frais de traitement",
"icon": "Icône",
"iconPlaceholder": "Entrez l'URL de l'icône",
"mark": "Identifiant",
"markPlaceholder": "Entrez l'identifiant du mode de paiement",
"name": "Nom",
"namePlaceholder": "Entrez le nom du mode de paiement",
"nameRequired": "Le nom est requis",
"noFee": "Pas de frais",
"notify_url": "URL de notification",
"paymentManagement": "Gestion des paiements",
"percentFee": "Pourcentage",
"platform": "Plateforme",
"searchPlaceholder": "Entrez des termes de recherche",
"selectPlatform": "Sélectionner la plateforme",
"selectType": "Sélectionner le type de paiement",
"submit": "Soumettre",
"type": "Type",
"updateSuccess": "Mis à jour avec succès"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Politique de confidentialité"
},
"site": {
"customData": "Données personnalisées",
"customDataDescription": "Données personnalisées, utilisées pour les données personnalisées du site",
"customHtml": "HTML personnalisé",
"customHtmlDescription": "Code HTML personnalisé à injecter dans le bas de la balise body du site.",
"keywords": "Mots-clés",
+2
View File
@@ -39,6 +39,8 @@
"status": "स्थिति",
"subject": "विषय",
"template": "टेम्पलेट",
"traffic_exceed_email_template": "ट्रैफिक अधिकता सूचना टेम्पलेट",
"traffic_exceed_email_templateDescription": "{after}.variable{before} प्लेसहोल्डर्स को वास्तविक डेटा के साथ प्रतिस्थापित किया जाएगा। सुनिश्चित करें कि इन वेरिएबल्स को बनाए रखें।",
"verify_email_template": "सत्यापन ईमेल टेम्पलेट",
"verify_email_templateDescription": "{after}.variable{before} प्लेसहोल्डर्स को वास्तविक डेटा से बदला जाएगा। सुनिश्चित करें कि इन वेरिएबल्स को बनाए रखें।",
"whitelistSuffixes": "श्वेतसूची प्रत्यय",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "अलीपे भुगतान",
"alipayf2f": {
"appId": "अलीपे एप्लिकेशन आईडी",
"invoiceName": "कस्टम उत्पाद नाम",
"invoiceNameDescription": "अलीपे बिल में प्रदर्शित होगा",
"privateKey": "अलीपे निजी कुंजी",
"publicKey": "अलीपे सार्वजनिक कुंजी",
"sandbox": "सैंडबॉक्स मोड",
"sandboxDescription": "भुगतान का परीक्षण करने के लिए सैंडबॉक्स मोड सक्षम करें"
},
"AlipayF2F": "अलीपे फेस टू फेस",
"EPay": "ईपे",
"Stripe": "स्ट्राइप",
"applyForPayment": "भुगतान के लिए आवेदन करें",
"balance": "शेष",
"batchDelete": "बैच हटाएं",
"cancel": "रद्द करें",
"config": "कॉन्फ़िगरेशन",
"configInfo": "कॉन्फ़िगरेशन जानकारी",
"configPlaceholder": "कृपया प्रदान किए गए {field} कॉन्फ़िगरेशन को भरें",
"confirm": "पुष्टि करें",
"confirmDelete": "हटाने की पुष्टि करें",
"copy": "कॉपी करें",
"copySuccess": "सफलता से कॉपी किया गया",
"create": "भुगतान विधि जोड़ें",
"createPayment": "भुगतान विधि जोड़ें",
"createSuccess": "सफलता से बनाया गया",
"delete": "हटाएं",
"deleteSuccess": "सफलता से हटाया गया",
"deleteWarning": "क्या आप वास्तव में इस भुगतान विधि को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।",
"description": "विवरण",
"descriptionPlaceholder": "भुगतान विधि का विवरण दर्ज करें",
"domain": "डोमेन",
"domainPlaceholder": "वैकल्पिक, जैसे कि {example}",
"edit": "संपादित करें",
"editPayment": "भुगतान विधि संपादित करें",
"enable": "सक्षम करें",
"enableDescription": "इस भुगतान विधि को सक्षम करें",
"epay": {
"key": "कुंजी",
"pid": "पीआईडी",
"url": "यूआरएल"
},
"feeAmount": "निश्चित राशि",
"feeMode": "शुल्क मोड",
"feeModeDescription": "अतिरिक्त शुल्क की वसूली का तरीका",
"feeModeItems": ["कोई शुल्क नहीं", "प्रतिशत", "निश्चित राशि", "उपरोक्त दोनों"],
"feePercent": "प्रतिशत शुल्क (वैकल्पिक)",
"feePercentDescription": "ऑर्डर राशि के ऊपर शुल्क जोड़ा जाएगा",
"fixedFee": "निश्चित शुल्क (वैकल्पिक)",
"fixedFeeDescription": "ऑर्डर राशि के ऊपर एक निश्चित शुल्क जोड़ा जाता है",
"iconUrl": "आइकन URL (वैकल्पिक)",
"iconUrlDescription": "फ्रंटएंड प्रदर्शन के लिए उपयोग किया जाता है",
"inputPlaceholder": "कृपया दर्ज करें",
"key": "कुंजी",
"notifyUrl": "कस्टम अधिसूचना डोमेन (वैकल्पिक)",
"notifyUrlDescription": "गेटवे सूचना इस डोमेन पर भेजी जाएगी",
"payForRecommendations": "सिफारिशों के लिए भुगतान करें",
"pid": "पीआईडी",
"saveSuccess": "सहेजा गया",
"showName": "नाम दिखाएं",
"showNameDescription": "फ्रंटएंड प्रदर्शन के लिए उपयोग किया जाता है",
"stripe": {
"publicKey": "सार्वजनिक कुंजी",
"secretKey": "गुप्त कुंजी",
"webhookSecret": "वेबहुक कुंजी हस्ताक्षर"
},
"url": "यूआरएल",
"wechatPay": "वीचैट पे"
"handlingFee": "हैंडलिंग शुल्क",
"icon": "चिन्ह",
"iconPlaceholder": "चिन्ह URL दर्ज करें",
"mark": "पहचानकर्ता",
"markPlaceholder": "भुगतान विधि पहचानकर्ता दर्ज करें",
"name": "नाम",
"namePlaceholder": "भुगतान विधि का नाम दर्ज करें",
"nameRequired": "नाम आवश्यक है",
"noFee": "कोई शुल्क नहीं",
"notify_url": "सूचना URL",
"paymentManagement": "भुगतान प्रबंधन",
"percentFee": "प्रतिशत",
"platform": "प्लेटफ़ॉर्म",
"searchPlaceholder": "खोज शब्द दर्ज करें",
"selectPlatform": "प्लेटफ़ॉर्म चुनें",
"selectType": "भुगतान प्रकार चुनें",
"submit": "जमा करें",
"type": "प्रकार",
"updateSuccess": "सफलता से अपडेट किया गया"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "गोपनीयता नीति"
},
"site": {
"customData": "कस्टम डेटा",
"customDataDescription": "कस्टम डेटा, साइट के कस्टम डेटा के लिए उपयोग किया जाता है",
"customHtml": "कस्टम HTML",
"customHtmlDescription": "साइट के bodyैग के नीचे इंजेक्ट करने के लिए कस्टम HTML कोड।",
"keywords": "कीवर्ड",
+2
View File
@@ -39,6 +39,8 @@
"status": "Állapot",
"subject": "Tárgy",
"template": "Sablon",
"traffic_exceed_email_template": "Forgalmi Túllépés Értesítő Sablon",
"traffic_exceed_email_templateDescription": "A {after}.variable{before} helyettesítők tényleges adatokkal lesznek helyettesítve. Győződjön meg róla, hogy ezeket a változókat megőrzi.",
"verify_email_template": "Ellenőrző Email Sablon",
"verify_email_templateDescription": "A {after}.variable{before} helyőrzőket tényleges adatokkal fogjuk helyettesíteni. Ügyeljen arra, hogy ezeket a változókat megtartsa.",
"whitelistSuffixes": "Engedélyezett utótagok",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Alipay fizetés",
"alipayf2f": {
"appId": "Alipay alkalmazásazonosító",
"invoiceName": "Egyedi terméknév",
"invoiceNameDescription": "A számlán az Alipay-ben fog megjelenni",
"privateKey": "Alipay privát kulcs",
"publicKey": "Alipay nyilvános kulcs",
"sandbox": "Tesztkörnyezet",
"sandboxDescription": "Tesztkörnyezet engedélyezése a fizetések teszteléséhez"
},
"AlipayF2F": "Alipay Személyesen",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Kérjen kifizetést",
"balance": "Egyenleg",
"batchDelete": "Tömeges törlés",
"cancel": "Mégse",
"config": "Beállítások",
"configInfo": "Beállítási információk",
"configPlaceholder": "Kérjük, töltse ki a megadott {field} beállítást",
"confirm": "Megerősít",
"confirmDelete": "Törlés megerősítése",
"copy": "Másolás",
"copySuccess": "Sikeresen másolva",
"create": "Fizetési mód hozzáadása",
"createPayment": "Fizetési mód hozzáadása",
"createSuccess": "Sikeresen létrehozva",
"delete": "Törlés",
"deleteSuccess": "Sikeresen törölve",
"deleteWarning": "Biztosan törölni szeretné ezt a fizetési módot? Ez a művelet nem vonható vissza.",
"description": "Leírás",
"descriptionPlaceholder": "Írja be a fizetési mód leírását",
"domain": "Domain",
"domainPlaceholder": "opcionális, pl. {példa}",
"edit": "Szerkesztés",
"editPayment": "Fizetési mód szerkesztése",
"enable": "Engedélyezés",
"enableDescription": "Engedélyezze ezt a fizetési módot",
"epay": {
"key": "KULCS",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Fix összeg",
"feeMode": "Díjfizetési mód",
"feeModeDescription": "További díjak felszámításának módja",
"feeModeItems": ["Nincs díj", "Százalékos", "Fix összeg", "Mindkettő"],
"feePercent": "Díjszázalék (opcionális)",
"feePercentDescription": "Díj hozzáadása a rendelés összegéhez",
"fixedFee": "Fix díj (opcionális)",
"fixedFeeDescription": "Rögzített díj hozzáadása a rendelés összegéhez",
"iconUrl": "Ikon URL (opcionális)",
"iconUrlDescription": "Előnézeti kép megjelenítéséhez",
"inputPlaceholder": "Kérjük, írja be",
"key": "KULCS",
"notifyUrl": "Egyéni értesítési domain (opcionális)",
"notifyUrlDescription": "A kapu értesítéseket erre a tartományra küldi",
"payForRecommendations": "Fizetés ajánlásokért",
"pid": "PID",
"saveSuccess": "Sikeres mentés",
"showName": "Megjelenítési név",
"showNameDescription": "Előnézet megjelenítésére használatos",
"stripe": {
"publicKey": "NYILVÁNOS KULCS",
"secretKey": "TITKOS KULCS",
"webhookSecret": "WEBHOOK TITKOS ALÁÍRÁS"
},
"url": "URL",
"wechatPay": "WeChat fizetés"
"handlingFee": "Kezelési díj",
"icon": "Ikon",
"iconPlaceholder": "Adja meg az ikon URL-jét",
"mark": "Azonosító",
"markPlaceholder": "Adja meg a fizetési mód azonosítóját",
"name": "Név",
"namePlaceholder": "Adja meg a fizetési mód nevét",
"nameRequired": "A név megadása kötelező",
"noFee": "Nincs díj",
"notify_url": "Értesítési URL",
"paymentManagement": "Fizetéskezelés",
"percentFee": "Százalékos díj",
"platform": "Platform",
"searchPlaceholder": "Adja meg a keresési kifejezéseket",
"selectPlatform": "Válassza ki a platformot",
"selectType": "Válassza ki a fizetési típust",
"submit": "Beküldés",
"type": "Típus",
"updateSuccess": "Sikeresen frissítve"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Adatvédelmi irányelvek"
},
"site": {
"customData": "Egyedi adatok",
"customDataDescription": "Egyedi adatok, a webhely egyedi adatainak használatára",
"customHtml": "Egyedi HTML",
"customHtmlDescription": "Egyedi HTML kód, amelyet a webhely bodyímkéjének aljára kell beilleszteni.",
"keywords": "Kulcsszavak",
+2
View File
@@ -39,6 +39,8 @@
"status": "ステータス",
"subject": "件名",
"template": "テンプレート",
"traffic_exceed_email_template": "トラフィック超過通知テンプレート",
"traffic_exceed_email_templateDescription": "{after}と{before}の変数は実際のデータに置き換えられます。これらの変数を保持することを確認してください。",
"verify_email_template": "確認メールテンプレート",
"verify_email_templateDescription": "{after}.variable{before} のプレースホルダーは実際のデータに置き換えられます。これらの変数を保持してください。",
"whitelistSuffixes": "ホワイトリストのサフィックス",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "アリペイ決済",
"alipayf2f": {
"appId": "支付宝アプリID",
"invoiceName": "カスタム製品名",
"invoiceNameDescription": "支付宝の請求書に表示されます",
"privateKey": "支付宝プライベートキー",
"publicKey": "支付宝パブリックキー",
"sandbox": "サンドボックスモード",
"sandboxDescription": "支払いをテストするためにサンドボックスモードを有効にします"
},
"AlipayF2F": "アリペイ対面",
"EPay": "EPay",
"Stripe": "ストライプ",
"applyForPayment": "支払いを申請する",
"balance": "残高",
"batchDelete": "一括削除",
"cancel": "キャンセル",
"config": "設定",
"configInfo": "設定情報",
"configPlaceholder": "提供された{field}の設定を入力してください",
"confirm": "確認",
"confirmDelete": "削除を確認",
"copy": "コピー",
"copySuccess": "コピー成功",
"create": "支払い方法を追加",
"createPayment": "支払い方法を追加",
"createSuccess": "作成成功",
"delete": "削除",
"deleteSuccess": "削除成功",
"deleteWarning": "この支払い方法を削除してもよろしいですか?この操作は元に戻せません。",
"description": "説明",
"descriptionPlaceholder": "支払い方法の説明を入力してください",
"domain": "ドメイン",
"domainPlaceholder": "任意、例:{example}",
"edit": "編集",
"editPayment": "支払い方法を編集",
"enable": "有効",
"enableDescription": "この支払い方法を有効にする",
"epay": {
"key": "キー",
"pid": "PID",
"url": "URL"
},
"feeAmount": "固定額",
"feeMode": "料金方式",
"feeModeDescription": "追加手数料の課金方式",
"feeModeItems": ["手数料なし", "パーセンテージ", "固定金額", "上記の2つ"],
"feePercent": "パーセンテージ手数料(オプション)",
"feePercentDescription": "注文金額に追加される手数料",
"fixedFee": "固定料金(オプション)",
"fixedFeeDescription": "注文金額に固定料金を追加する",
"iconUrl": "アイコンURL(任意)",
"iconUrlDescription": "フロントエンド表示用",
"inputPlaceholder": "入力してください",
"key": "キー",
"notifyUrl": "カスタム通知ドメイン(オプション)",
"notifyUrlDescription": "ゲートウェイ通知はこのドメインに送信されます",
"payForRecommendations": "おすすめの支払い",
"pid": "PID",
"saveSuccess": "保存が成功しました",
"showName": "表示名",
"showNameDescription": "フロントエンド表示用",
"stripe": {
"publicKey": "パブリックキー",
"secretKey": "シークレットキー",
"webhookSecret": "WEBHOOK シークレットサイン"
},
"url": "URL",
"wechatPay": "WeChatペイ"
"handlingFee": "手数料",
"icon": "アイコン",
"iconPlaceholder": "アイコンのURLを入力してください",
"mark": "識別子",
"markPlaceholder": "支払い方法の識別子を入力してください",
"name": "名前",
"namePlaceholder": "支払い方法の名前を入力してください",
"nameRequired": "名前は必須です",
"noFee": "手数料なし",
"notify_url": "通知URL",
"paymentManagement": "支払い管理",
"percentFee": "パーセンテージ",
"platform": "プラットフォーム",
"searchPlaceholder": "検索用語を入力してください",
"selectPlatform": "プラットフォームを選択",
"selectType": "支払いタイプを選択",
"submit": "送信",
"type": "タイプ",
"updateSuccess": "更新成功"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "プライバシーポリシー"
},
"site": {
"customData": "カスタムデータ",
"customDataDescription": "サイトのカスタムデータに使用されるカスタムデータ",
"customHtml": "カスタムHTML",
"customHtmlDescription": "サイトの body タグの下部に挿入されるカスタム HTML コード。",
"keywords": "キーワード",
+2
View File
@@ -39,6 +39,8 @@
"status": "상태",
"subject": "제목",
"template": "템플릿",
"traffic_exceed_email_template": "트래픽 초과 알림 템플릿",
"traffic_exceed_email_templateDescription": "{after}.variable{before} 자리표시는 실제 데이터로 대체됩니다. 이러한 변수를 유지해야 합니다.",
"verify_email_template": "이메일 인증 템플릿",
"verify_email_templateDescription": "{after}.variable{before} 자리표시는 실제 데이터로 대체됩니다. 이 변수를 반드시 유지하십시오.",
"whitelistSuffixes": "허용 목록 접미사",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "알리페이 결제",
"alipayf2f": {
"appId": "알리페이 애플리케이션 ID",
"invoiceName": "사용자 정의 제품 이름",
"invoiceNameDescription": "알리페이 청구서에 표시됩니다",
"privateKey": "알리페이 개인 키",
"publicKey": "알리페이 공개 키",
"sandbox": "샌드박스 모드",
"sandboxDescription": "결제를 테스트하기 위해 샌드박스 모드를 활성화합니다"
},
"AlipayF2F": "알리페이 대면 결제",
"EPay": "이페이",
"Stripe": "스트라이프",
"applyForPayment": "결제 신청",
"balance": "잔액",
"batchDelete": "일괄 삭제",
"cancel": "취소",
"config": "설정",
"configInfo": "설정 정보",
"configPlaceholder": "{field} 설정을 입력해 주세요",
"confirm": "확인",
"confirmDelete": "삭제 확인",
"copy": "복사",
"copySuccess": "복사 성공",
"create": "결제 수단 추가",
"createPayment": "결제 수단 추가",
"createSuccess": "생성 성공",
"delete": "삭제",
"deleteSuccess": "삭제 성공",
"deleteWarning": "이 결제 수단을 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
"description": "설명",
"descriptionPlaceholder": "결제 수단 설명을 입력하세요",
"domain": "도메인",
"domainPlaceholder": "선택 사항, 예: {example}",
"edit": "편집",
"editPayment": "결제 수단 편집",
"enable": "사용",
"enableDescription": "이 결제 수단을 활성화합니다",
"epay": {
"key": "키",
"pid": "PID",
"url": "URL"
},
"feeAmount": "고정 금액",
"feeMode": "요금 방식",
"feeModeDescription": "추가 수수료의 부과 방식",
"feeModeItems": ["수수료 없음", "백분율", "고정 금액", "이상 두 가지"],
"feePercent": "수수료 비율(선택 사항)",
"feePercentDescription": "주문 금액에 추가 요금 부과",
"fixedFee": "고정 수수료(선택 사항)",
"fixedFeeDescription": "주문 금액에 고정 요금 추가",
"iconUrl": "아이콘 URL(선택 사항)",
"iconUrlDescription": "프론트엔드 표시용",
"inputPlaceholder": "입력하세요",
"key": "키",
"notifyUrl": "사용자 정의 알림 도메인 (선택 사항)",
"notifyUrlDescription": "게이트웨이 알림이 이 도메인으로 전송됩니다",
"payForRecommendations": "추천에 대한 지불",
"pid": "PID",
"saveSuccess": "저장 성공",
"showName": "이름 표시",
"showNameDescription": "프론트엔드 표시용",
"stripe": {
"publicKey": "공개 키",
"secretKey": "비밀 키",
"webhookSecret": "웹훅 비밀 서명"
},
"url": "URL",
"wechatPay": "위챗페이"
"handlingFee": "수수료",
"icon": "아이콘",
"iconPlaceholder": "아이콘 URL 입력",
"mark": "식별자",
"markPlaceholder": "결제 수단 식별자 입력",
"name": "이름",
"namePlaceholder": "결제 수단 이름 입력",
"nameRequired": "이름은 필수입니다",
"noFee": "수수료 없음",
"notify_url": "알림 URL",
"paymentManagement": "결제 관리",
"percentFee": "백분율",
"platform": "플랫폼",
"searchPlaceholder": "검색어 입력",
"selectPlatform": "플랫폼 선택",
"selectType": "결제 유형 선택",
"submit": "제출",
"type": "유형",
"updateSuccess": "업데이트 성공"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "개인정보 처리방침"
},
"site": {
"customData": "사용자 정의 데이터",
"customDataDescription": "사이트의 사용자 정의 데이터에 사용되는 사용자 정의 데이터",
"customHtml": "사용자 정의 HTML",
"customHtmlDescription": "사이트의 body그 하단에 삽입될 사용자 정의 HTML 코드입니다.",
"keywords": "키워드",
+2
View File
@@ -39,6 +39,8 @@
"status": "Status",
"subject": "Emne",
"template": "Mal",
"traffic_exceed_email_template": "Varsel om overskridelse av trafikk",
"traffic_exceed_email_templateDescription": "Plassholderne {after}.variable{before} vil bli erstattet med faktiske data. Sørg for å beholde disse variablene.",
"verify_email_template": "Verifikasjons-e-postmal",
"verify_email_templateDescription": "Plassholderne {after}.variable{before} vil bli erstattet med faktiske data. Sørg for å beholde disse variablene.",
"whitelistSuffixes": "Hvitelistesuffikser",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Betaling med Alipay",
"alipayf2f": {
"appId": "Alipay-applikasjons-ID",
"invoiceName": "Tilpasset produktnavn",
"invoiceNameDescription": "Vil vises på Alipay-fakturaen",
"privateKey": "Alipay privat nøkkel",
"publicKey": "Alipay offentlig nøkkel",
"sandbox": "Sandkassemodus",
"sandboxDescription": "Aktiver sandkassemodus for å teste betaling"
},
"AlipayF2F": "Alipay Ansikt-til-Ansikt",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Søk om betaling",
"balance": "Saldo",
"batchDelete": "Slett i batch",
"cancel": "Avbryt",
"config": "Konfigurasjon",
"configInfo": "Konfigurasjonsinformasjon",
"configPlaceholder": "Vennligst fyll inn den angitte {field} konfigurasjonen",
"confirm": "Bekreft",
"confirmDelete": "Bekreft sletting",
"copy": "Kopier",
"copySuccess": "Kopiert med suksess",
"create": "Legg til betalingsmetode",
"createPayment": "Legg til betalingsmetode",
"createSuccess": "Opprettet med suksess",
"delete": "Slett",
"deleteSuccess": "Slettet med suksess",
"deleteWarning": "Er du sikker på at du vil slette denne betalingsmetoden? Denne handlingen kan ikke angres.",
"description": "Beskrivelse",
"descriptionPlaceholder": "Skriv inn beskrivelse av betalingsmetode",
"domain": "Domene",
"domainPlaceholder": "valgfritt, f.eks. {example}",
"edit": "Rediger",
"editPayment": "Rediger betalingsmetode",
"enable": "Aktiver",
"enableDescription": "Aktiver denne betalingsmetoden",
"epay": {
"key": "NØKKEL",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Fast beløp",
"feeMode": "Avgiftsmåte",
"feeModeDescription": "Gebyrmodus for ekstra avgifter",
"feeModeItems": ["Ingen gebyr", "Prosentandel", "Fast beløp", "Begge ovennevnte"],
"feePercent": "Prosentavgift (valgfritt)",
"feePercentDescription": "Legg til et gebyr på toppen av ordrebeløpet",
"fixedFee": "Fast gebyr (valgfritt)",
"fixedFeeDescription": "Legg til et fast gebyr i tillegg til ordrebeløpet",
"iconUrl": "Ikon URL (valgfritt)",
"iconUrlDescription": "Brukes for visning på frontend",
"inputPlaceholder": "Vennligst skriv inn",
"key": "NØKKEL",
"notifyUrl": "Tilpasset varslingsdomene (valgfritt)",
"notifyUrlDescription": "Gateway-varsler vil bli sendt til dette domenet",
"payForRecommendations": "Betal for anbefalinger",
"pid": "PID",
"saveSuccess": "Lagring vellykket",
"showName": "Vis navn",
"showNameDescription": "Brukes for visning på frontend",
"stripe": {
"publicKey": "OFFENTLIG NØKKEL",
"secretKey": "HEMMELIG NØKKEL",
"webhookSecret": "WEBHOOK SIGNATUR"
},
"url": "URL",
"wechatPay": "WeChat-betaling"
"handlingFee": "Håndteringsgebyr",
"icon": "Ikon",
"iconPlaceholder": "Skriv inn ikon-URL",
"mark": "Identifikator",
"markPlaceholder": "Skriv inn identifikator for betalingsmetode",
"name": "Navn",
"namePlaceholder": "Skriv inn navn på betalingsmetode",
"nameRequired": "Navn er påkrevd",
"noFee": "Ingen gebyr",
"notify_url": "Varslings-URL",
"paymentManagement": "Betalingsadministrasjon",
"percentFee": "Prosentandel",
"platform": "Plattform",
"searchPlaceholder": "Skriv inn søkeord",
"selectPlatform": "Velg plattform",
"selectType": "Velg betalingstype",
"submit": "Send inn",
"type": "Type",
"updateSuccess": "Oppdatert med suksess"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Personvernerklæring"
},
"site": {
"customData": "Egendata",
"customDataDescription": "Egendata, brukt for egendata på nettstedet",
"customHtml": "Egendefinert HTML",
"customHtmlDescription": "Egendefinert HTML-kode som skal settes inn i bunnen av nettstedets body-tag.",
"keywords": "Nøkkelord",
+2
View File
@@ -39,6 +39,8 @@
"status": "Status",
"subject": "Temat",
"template": "Szablon",
"traffic_exceed_email_template": "Szablon powiadomienia o przekroczeniu ruchu",
"traffic_exceed_email_templateDescription": "Miejsca {after}.variable{before} zostaną zastąpione rzeczywistymi danymi. Upewnij się, że te zmienne są zachowane.",
"verify_email_template": "Szablon e-maila weryfikacyjnego",
"verify_email_templateDescription": "Elementy zastępcze {after}.variable{before} zostaną zastąpione rzeczywistymi danymi. Upewnij się, że te zmienne zostaną zachowane.",
"whitelistSuffixes": "Dozwolone Sufiksy",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Płatność Alipay",
"alipayf2f": {
"appId": "ID aplikacji Alipay",
"invoiceName": "Niestandardowa nazwa produktu",
"invoiceNameDescription": "Będzie wyświetlana na rachunku Alipay",
"privateKey": "Klucz prywatny Alipay",
"publicKey": "Klucz publiczny Alipay",
"sandbox": "Tryb piaskownicy",
"sandboxDescription": "Włącz tryb piaskownicy, aby przetestować płatności"
},
"AlipayF2F": "Alipay Twarzą w Twarz",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Złóż wniosek o płatność",
"balance": "Saldo",
"batchDelete": "Usuwanie zbiorcze",
"cancel": "Anuluj",
"config": "Konfiguracja",
"configInfo": "Informacje o konfiguracji",
"configPlaceholder": "Proszę wypełnić konfigurację {field}",
"confirm": "Potwierdź",
"confirmDelete": "Potwierdź usunięcie",
"copy": "Kopiuj",
"copySuccess": "Skopiowano pomyślnie",
"create": "Dodaj metodę płatności",
"createPayment": "Dodaj metodę płatności",
"createSuccess": "Utworzono pomyślnie",
"delete": "Usuń",
"deleteSuccess": "Usunięto pomyślnie",
"deleteWarning": "Czy na pewno chcesz usunąć tę metodę płatności? Ta akcja jest nieodwracalna.",
"description": "Opis",
"descriptionPlaceholder": "Wprowadź opis metody płatności",
"domain": "Domena",
"domainPlaceholder": "opcjonalnie, np. {example}",
"edit": "Edytuj",
"editPayment": "Edytuj metodę płatności",
"enable": "Włącz",
"enableDescription": "Włącz tę metodę płatności",
"epay": {
"key": "KLUCZ",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Kwota stała",
"feeMode": "Sposób naliczania opłat",
"feeModeDescription": "Sposób naliczania dodatkowej opłaty",
"feeModeItems": ["Bez opłaty", "Procent", "Stała kwota", "Oba powyższe"],
"feePercent": "Procentowa opłata (opcjonalnie)",
"feePercentDescription": "Dodaj opłatę do kwoty zamówienia",
"fixedFee": "Stała opłata (opcjonalnie)",
"fixedFeeDescription": "Dodaj stałą opłatę do kwoty zamówienia",
"iconUrl": "URL ikony (opcjonalnie)",
"iconUrlDescription": "Używane do wyświetlania na froncie",
"inputPlaceholder": "Wprowadź tekst",
"key": "KLUCZ",
"notifyUrl": "Niestandardowa domena powiadomień (opcjonalnie)",
"notifyUrlDescription": "Powiadomienie bramki zostanie wysłane na tę domenę",
"payForRecommendations": "Zapłać za rekomendacje",
"pid": "PID",
"saveSuccess": "Zapisano pomyślnie",
"showName": "Pokaż nazwę",
"showNameDescription": "Używane do wyświetlania na froncie",
"stripe": {
"publicKey": "KLUCZ PUBLICZNY",
"secretKey": "KLUCZ TAJNY",
"webhookSecret": "PODPIS KLUCZA WEBHOOK"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Opłata manipulacyjna",
"icon": "Ikona",
"iconPlaceholder": "Wprowadź URL ikony",
"mark": "Identyfikator",
"markPlaceholder": "Wprowadź identyfikator metody płatności",
"name": "Nazwa",
"namePlaceholder": "Wprowadź nazwę metody płatności",
"nameRequired": "Nazwa jest wymagana",
"noFee": "Brak opłaty",
"notify_url": "URL powiadomienia",
"paymentManagement": "Zarządzanie płatnościami",
"percentFee": "Procent",
"platform": "Platforma",
"searchPlaceholder": "Wprowadź hasła do wyszukiwania",
"selectPlatform": "Wybierz platformę",
"selectType": "Wybierz typ płatności",
"submit": "Wyślij",
"type": "Typ",
"updateSuccess": "Zaktualizowano pomyślnie"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Polityka prywatności"
},
"site": {
"customData": "Dane niestandardowe",
"customDataDescription": "Dane niestandardowe, używane do niestandardowych danych witryny",
"customHtml": "Niestandardowy HTML",
"customHtmlDescription": "Niestandardowy kod HTML, który ma być wstrzyknięty na końcu tagu bodyitryny.",
"keywords": "Słowa kluczowe",
+2
View File
@@ -39,6 +39,8 @@
"status": "Status",
"subject": "Assunto",
"template": "Modelo",
"traffic_exceed_email_template": "Modelo de Notificação de Excesso de Tráfego",
"traffic_exceed_email_templateDescription": "Os espaços reservados {after}.variable{before} serão substituídos por dados reais. Certifique-se de manter essas variáveis.",
"verify_email_template": "Modelo de E-mail de Verificação",
"verify_email_templateDescription": "Os marcadores {after}.variable{before} serão substituídos por dados reais. Certifique-se de manter essas variáveis.",
"whitelistSuffixes": "Sufixos de Lista Branca",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Pagamento com Alipay",
"alipayf2f": {
"appId": "ID do aplicativo Alipay",
"invoiceName": "Nome personalizado do produto",
"invoiceNameDescription": "Será exibido na fatura do Alipay",
"privateKey": "Chave privada do Alipay",
"publicKey": "Chave pública do Alipay",
"sandbox": "Modo sandbox",
"sandboxDescription": "Ative o modo sandbox para testar pagamentos"
},
"AlipayF2F": "Alipay Face a Face",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Solicitar Pagamento",
"balance": "Saldo",
"batchDelete": "Excluir em Lote",
"cancel": "Cancelar",
"config": "Configuração",
"configInfo": "Informações de Configuração",
"configPlaceholder": "Por favor, preencha a configuração de {field} fornecida",
"confirm": "Confirmar",
"confirmDelete": "Confirmar Exclusão",
"copy": "Copiar",
"copySuccess": "Copiado com sucesso",
"create": "Adicionar Método de Pagamento",
"createPayment": "Adicionar Método de Pagamento",
"createSuccess": "Criado com sucesso",
"delete": "Excluir",
"deleteSuccess": "Excluído com sucesso",
"deleteWarning": "Você tem certeza de que deseja excluir este método de pagamento? Esta ação não pode ser desfeita.",
"description": "Descrição",
"descriptionPlaceholder": "Insira a descrição do método de pagamento",
"domain": "Domínio",
"domainPlaceholder": "opcional, por exemplo, {exemplo}",
"edit": "Editar",
"editPayment": "Editar Método de Pagamento",
"enable": "Habilitar",
"enableDescription": "Ativar este método de pagamento",
"epay": {
"key": "CHAVE",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Valor Fixo",
"feeMode": "Modo de Cobrança",
"feeModeDescription": "Modo de cobrança de taxas adicionais",
"feeModeItems": ["Sem taxa", "Porcentagem", "Valor fixo", "Ambos os anteriores"],
"feePercent": "Percentual de taxa (opcional)",
"feePercentDescription": "Adicionar taxa sobre o valor do pedido",
"fixedFee": "Taxa fixa (opcional)",
"fixedFeeDescription": "Adicionar uma taxa fixa sobre o valor do pedido",
"iconUrl": "URL do ícone (opcional)",
"iconUrlDescription": "Usado para exibição no front-end",
"inputPlaceholder": "Por favor, insira",
"key": "CHAVE",
"notifyUrl": "Domínio de notificação personalizado (opcional)",
"notifyUrlDescription": "A notificação do gateway será enviada para este domínio",
"payForRecommendations": "Pagar por recomendações",
"pid": "PID",
"saveSuccess": "Salvo com sucesso",
"showName": "Mostrar Nome",
"showNameDescription": "Usado para exibição no front-end",
"stripe": {
"publicKey": "CHAVE PÚBLICA",
"secretKey": "CHAVE SECRETA",
"webhookSecret": "ASSINATURA DA CHAVE DO WEBHOOK"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Taxa de Manipulação",
"icon": "Ícone",
"iconPlaceholder": "Digite a URL do ícone",
"mark": "Identificador",
"markPlaceholder": "Digite o identificador do método de pagamento",
"name": "Nome",
"namePlaceholder": "Digite o nome do método de pagamento",
"nameRequired": "O nome é obrigatório",
"noFee": "Sem Taxa",
"notify_url": "URL de Notificação",
"paymentManagement": "Gerenciamento de Pagamento",
"percentFee": "Porcentagem",
"platform": "Plataforma",
"searchPlaceholder": "Digite os termos de pesquisa",
"selectPlatform": "Selecionar Plataforma",
"selectType": "Selecionar tipo de pagamento",
"submit": "Enviar",
"type": "Tipo",
"updateSuccess": "Atualizado com sucesso"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Política de Privacidade"
},
"site": {
"customData": "Dados Personalizados",
"customDataDescription": "Dados personalizados, usados para dados personalizados do site",
"customHtml": "HTML Personalizado",
"customHtmlDescription": "Código HTML personalizado a ser inserido no final da tag bodyo site.",
"keywords": "Palavras-chave",
+2
View File
@@ -39,6 +39,8 @@
"status": "Stare",
"subject": "Subiect",
"template": "Șablon",
"traffic_exceed_email_template": "Șablon de notificare a depășirii traficului",
"traffic_exceed_email_templateDescription": "Plasamentele {after}.variable{before} vor fi înlocuite cu datele reale. Asigurați-vă că păstrați aceste variabile.",
"verify_email_template": "Șablon de e-mail pentru verificare",
"verify_email_templateDescription": "Locurile rezervate {after}.variable{before} vor fi înlocuite cu date reale. Asigurați-vă că păstrați aceste variabile.",
"whitelistSuffixes": "Sufixe pe lista albă",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Plată cu Alipay",
"alipayf2f": {
"appId": "ID aplicație Alipay",
"invoiceName": "Nume personalizat produs",
"invoiceNameDescription": "Va apărea în factura Alipay",
"privateKey": "Cheie privată Alipay",
"publicKey": "Cheie publică Alipay",
"sandbox": "Modul sandbox",
"sandboxDescription": "Activează modul sandbox pentru a testa plățile"
},
"AlipayF2F": "Alipay Față în Față",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Solicită Plată",
"balance": "Sold",
"batchDelete": "Ștergere în Lot",
"cancel": "Anulează",
"config": "Configurare",
"configInfo": "Informații de Configurare",
"configPlaceholder": "Te rugăm să completezi configurația {field} furnizată",
"confirm": "Confirmă",
"confirmDelete": "Confirmă Ștergerea",
"copy": "Copiază",
"copySuccess": "Copiat cu succes",
"create": "Adaugă Metodă de Plată",
"createPayment": "Adaugă Metodă de Plată",
"createSuccess": "Creat cu succes",
"delete": "Șterge",
"deleteSuccess": "Șters cu succes",
"deleteWarning": "Ești sigur că vrei să ștergi această metodă de plată? Această acțiune nu poate fi anulată.",
"description": "Descriere",
"descriptionPlaceholder": "Introduceți descrierea metodei de plată",
"domain": "Domeniu",
"domainPlaceholder": "opțional, de exemplu {example}",
"edit": "Editează",
"editPayment": "Editează Metodă de Plată",
"enable": "Activare",
"enableDescription": "Activează această metodă de plată",
"epay": {
"key": "CHEIE",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Sumă Fixă",
"feeMode": "Mod de taxare",
"feeModeDescription": "Modul de taxare a comisioanelor suplimentare",
"feeModeItems": ["Fără comision", "Procentaj", "Sumă fixă", "Ambele"],
"feePercent": "Taxă procentuală (opțional)",
"feePercentDescription": "Se adaugă o taxă peste suma comenzii",
"fixedFee": "Taxă fixă (opțional)",
"fixedFeeDescription": "Adăugarea unei taxe fixe peste suma comenzii",
"iconUrl": "URL pictogramă (opțional)",
"iconUrlDescription": "Utilizat pentru afișarea în front-end",
"inputPlaceholder": "Introduceți",
"key": "CHEIE",
"notifyUrl": "Domeniu de notificare personalizat (opțional)",
"notifyUrlDescription": "Notificările de la gateway vor fi trimise la acest domeniu",
"payForRecommendations": "Plătește pentru recomandări",
"pid": "PID",
"saveSuccess": "Salvare reușită",
"showName": "Afișează Numele",
"showNameDescription": "Utilizat pentru afișarea în interfața utilizatorului",
"stripe": {
"publicKey": "CHEIE PUBLICĂ",
"secretKey": "CHEIE SECRETĂ",
"webhookSecret": "SEMNĂTURA CHEII WEBHOOK"
},
"url": "URL",
"wechatPay": "Plată WeChat"
"handlingFee": "Taxă de manipulare",
"icon": "Iconiță",
"iconPlaceholder": "Introdu URL-ul iconiței",
"mark": "Identificator",
"markPlaceholder": "Introdu identificatorul metodei de plată",
"name": "Nume",
"namePlaceholder": "Introdu numele metodei de pla",
"nameRequired": "Numele este necesar",
"noFee": "Fără Taxă",
"notify_url": "URL de notificare",
"paymentManagement": "Gestionare Plăți",
"percentFee": "Procentaj",
"platform": "Platformă",
"searchPlaceholder": "Introdu termeni de căutare",
"selectPlatform": "Selectează Platforma",
"selectType": "Selectează tipul de plată",
"submit": "Trimite",
"type": "Tip",
"updateSuccess": "Actualizat cu succes"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Politica de confidențialitate"
},
"site": {
"customData": "Date personalizate",
"customDataDescription": "Date personalizate, utilizate pentru datele personalizate ale site-ului",
"customHtml": "HTML personalizat",
"customHtmlDescription": "Cod HTML personalizat care va fi injectat în partea de jos a etichetei body a site-ului.",
"keywords": "Cuvinte cheie",
+2
View File
@@ -39,6 +39,8 @@
"status": "Статус",
"subject": "Тема",
"template": "Шаблон",
"traffic_exceed_email_template": "Шаблон уведомления о превышении трафика",
"traffic_exceed_email_templateDescription": "Места {after}.variable{before} будут заменены на фактические данные. Убедитесь, что эти переменные сохранены.",
"verify_email_template": "Шаблон письма для подтверждения",
"verify_email_templateDescription": "Заполнители {after}.variable{before} будут заменены фактическими данными. Убедитесь, что эти переменные сохранены.",
"whitelistSuffixes": "Белый список суффиксов",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Оплата через Alipay",
"alipayf2f": {
"appId": "ID приложения Alipay",
"invoiceName": "Пользовательское название продукта",
"invoiceNameDescription": "Будет отображаться в счете Alipay",
"privateKey": "Приватный ключ Alipay",
"publicKey": "Публичный ключ Alipay",
"sandbox": "Режим песочницы",
"sandboxDescription": "Включите режим песочницы для тестирования платежей"
},
"AlipayF2F": "Alipay Лицом к Лицу",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Подать заявку на оплату",
"balance": "Баланс",
"batchDelete": "Пакетное удаление",
"cancel": "Отмена",
"config": "Конфигурация",
"configInfo": "Информация о конфигурации",
"configPlaceholder": "Пожалуйста, заполните предоставленную конфигурацию {field}",
"confirm": "Подтвердить",
"confirmDelete": "Подтвердить удаление",
"copy": "Копировать",
"copySuccess": "Успешно скопировано",
"create": "Добавить способ оплаты",
"createPayment": "Добавить способ оплаты",
"createSuccess": "Успешно создано",
"delete": "Удалить",
"deleteSuccess": "Успешно удалено",
"deleteWarning": "Вы уверены, что хотите удалить этот способ оплаты? Это действие нельзя отменить.",
"description": "Описание",
"descriptionPlaceholder": "Введите описание метода оплаты",
"domain": "Домен",
"domainPlaceholder": "необязательно, например, {example}",
"edit": "Редактировать",
"editPayment": "Редактировать способ оплаты",
"enable": "Включить",
"enableDescription": "Включить этот способ оплаты",
"epay": {
"key": "КЛЮЧ",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Фиксированная сумма",
"feeMode": "Способ оплаты",
"feeModeDescription": "Способ взимания дополнительной комиссии",
"feeModeItems": ["Без комиссии", "Процент", "Фиксированная сумма", "Оба вышеуказанных"],
"feePercent": "Процентная комиссия (необязательно)",
"feePercentDescription": "Добавление комиссии к сумме заказа",
"fixedFee": "Фиксированная плата (по желанию)",
"fixedFeeDescription": "Добавление фиксированной платы к сумме заказа",
"iconUrl": "URL значка (необязательно)",
"iconUrlDescription": "Используется для отображения на фронтенде",
"inputPlaceholder": "Пожалуйста, введите",
"key": "КЛЮЧ",
"notifyUrl": "Пользовательский домен уведомлений (необязательно)",
"notifyUrlDescription": "Уведомление шлюза будет отправлено на этот домен",
"payForRecommendations": "Оплатить рекомендации",
"pid": "PID",
"saveSuccess": "Сохранение успешно",
"showName": "Показать имя",
"showNameDescription": "Используется для отображения на фронтенде",
"stripe": {
"publicKey": "ОТКРЫТЫЙ КЛЮЧ",
"secretKey": "СЕКРЕТНЫЙ КЛЮЧ",
"webhookSecret": "СЕКРЕТ ПОДПИСИ ВЕБХУКА"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Плата за обработку",
"icon": "Иконка",
"iconPlaceholder": "Введите URL иконки",
"mark": "Идентификатор",
"markPlaceholder": "Введите идентификатор способа оплаты",
"name": "Название",
"namePlaceholder": "Введите название способа оплаты",
"nameRequired": "Название обязательно",
"noFee": "Без комиссии",
"notify_url": "URL уведомления",
"paymentManagement": "Управление платежами",
"percentFee": "Процент",
"platform": "Платформа",
"searchPlaceholder": "Введите поисковые запросы",
"selectPlatform": "Выберите платформу",
"selectType": "Выберите тип оплаты",
"submit": "Отправить",
"type": "Тип",
"updateSuccess": "Успешно обновлено"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Политика конфиденциальности"
},
"site": {
"customData": "Пользовательские данные",
"customDataDescription": "Пользовательские данные, используемые для пользовательских данных сайта",
"customHtml": "Пользовательский HTML",
"customHtmlDescription": "Пользовательский HTML-код, который будет вставлен в конец тега body сайта.",
"keywords": "Ключевые слова",
+2
View File
@@ -39,6 +39,8 @@
"status": "สถานะ",
"subject": "เรื่อง",
"template": "แม่แบบ",
"traffic_exceed_email_template": "เทมเพลตการแจ้งเตือนการเกินการจราจร",
"traffic_exceed_email_templateDescription": "ตัวแปร {after}.variable{before} จะถูกแทนที่ด้วยข้อมูลจริง โปรดตรวจสอบให้แน่ใจว่าได้เก็บตัวแปรเหล่านี้ไว้",
"verify_email_template": "เทมเพลตอีเมลยืนยัน",
"verify_email_templateDescription": "ตัวแปร {after}.variable{before} จะถูกแทนที่ด้วยข้อมูลจริง โปรดตรวจสอบให้แน่ใจว่าเก็บตัวแปรเหล่านี้ไว้",
"whitelistSuffixes": "อนุญาตให้ใช้คำต่อท้าย",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "ชำระเงินผ่านอาลีเพย์",
"alipayf2f": {
"appId": "รหัสแอปพลิเคชัน Alipay",
"invoiceName": "ชื่อผลิตภัณฑ์ที่กำหนดเอง",
"invoiceNameDescription": "จะแสดงในใบแจ้งหนี้ของ Alipay",
"privateKey": "กุญแจส่วนตัวของ Alipay",
"publicKey": "กุญแจสาธารณะของ Alipay",
"sandbox": "โหมดแซนด์บ็อกซ์",
"sandboxDescription": "เปิดใช้งานโหมดแซนด์บ็อกซ์เพื่อทดสอบการชำระเงิน"
},
"AlipayF2F": "Alipay แบบพบหน้า",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "ขอชำระเงิน",
"balance": "ยอดคงเหลือ",
"batchDelete": "ลบเป็นชุด",
"cancel": "ยกเลิก",
"config": "การตั้งค่า",
"configInfo": "ข้อมูลการตั้งค่า",
"configPlaceholder": "กรุณากรอกการตั้งค่า {field} ที่ให้มา",
"confirm": "ยืนยัน",
"confirmDelete": "ยืนยันการลบ",
"copy": "คัดลอก",
"copySuccess": "คัดลอกสำเร็จ",
"create": "เพิ่มวิธีการชำระเงิน",
"createPayment": "เพิ่มวิธีการชำระเงิน",
"createSuccess": "สร้างสำเร็จ",
"delete": "ลบ",
"deleteSuccess": "ลบสำเร็จ",
"deleteWarning": "คุณแน่ใจหรือไม่ว่าต้องการลบวิธีการชำระเงินนี้? การกระทำนี้ไม่สามารถย้อนกลับได้",
"description": "คำอธิบาย",
"descriptionPlaceholder": "กรอกคำอธิบายวิธีการชำระเงิน",
"domain": "โดเมน",
"domainPlaceholder": "ไม่บังคับ เช่น {example}",
"edit": "แก้ไข",
"editPayment": "แก้ไขวิธีการชำระเงิน",
"enable": "เปิดใช้งาน",
"enableDescription": "เปิดใช้งานวิธีการชำระเงินนี้",
"epay": {
"key": "คีย์",
"pid": "พีไอดี",
"url": "ยูอาร์แอล"
},
"feeAmount": "จำนวนคงที่",
"feeMode": "วิธีการคิดค่าบริการ",
"feeModeDescription": "วิธีการคิดค่าธรรมเนียมเพิ่มเติม",
"feeModeItems": ["ไม่มีค่าธรรมเนียม", "เปอร์เซ็นต์", "จำนวนเงินคงที่", "ทั้งสองอย่าง"],
"feePercent": "เปอร์เซ็นต์ค่าธรรมเนียม (ไม่บังคับ)",
"feePercentDescription": "เพิ่มค่าธรรมเนียมจากยอดสั่งซื้อ",
"fixedFee": "ค่าธรรมเนียมคงที่ (ไม่บังคับ)",
"fixedFeeDescription": "เพิ่มค่าธรรมเนียมคงที่เหนือจำนวนเงินในคำสั่งซื้อ",
"iconUrl": "URL ไอคอน (ไม่บังคับ)",
"iconUrlDescription": "ใช้สำหรับแสดงผลด้านหน้า",
"inputPlaceholder": "กรุณาใส่ข้อมูล",
"key": "กุญแจ",
"notifyUrl": "โดเมนการแจ้งเตือนที่กำหนดเอง (ไม่บังคับ)",
"notifyUrlDescription": "การแจ้งเตือนเกตเวย์จะถูกส่งไปยังโดเมนนี้",
"payForRecommendations": "ชำระเงินสำหรับคำแนะนำ",
"pid": "PID",
"saveSuccess": "บันทึกสำเร็จ",
"showName": "แสดงชื่อ",
"showNameDescription": "ใช้สำหรับแสดงผลด้านหน้า",
"stripe": {
"publicKey": "PUBLIC KEY",
"secretKey": "SECRET KEY",
"webhookSecret": "WEBHOOK คีย์ลับ"
},
"url": "URL",
"wechatPay": "วีแชทเพย์"
"handlingFee": "ค่าธรรมเนียมการจัดการ",
"icon": "ไอคอน",
"iconPlaceholder": "กรอก URL ไอคอน",
"mark": "ตัวระบุ",
"markPlaceholder": "กรอกตัวระบุวิธีการชำระเงิน",
"name": "ชื่อ",
"namePlaceholder": "กรอกชื่อวิธีการชำระเงิน",
"nameRequired": "ชื่อเป็นสิ่งจำเป็น",
"noFee": "ไม่มีค่าธรรมเนียม",
"notify_url": "URL การแจ้งเตือน",
"paymentManagement": "การจัดการการชำระเงิน",
"percentFee": "เปอร์เซ็นต์",
"platform": "แพลตฟอร์ม",
"searchPlaceholder": "กรอกคำค้นหา",
"selectPlatform": "เลือกแพลตฟอร์ม",
"selectType": "เลือกประเภทการชำระเงิน",
"submit": "ส่ง",
"type": "ประเภท",
"updateSuccess": "อัปเดตสำเร็จ"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "นโยบายความเป็นส่วนตัว"
},
"site": {
"customData": "ข้อมูลที่กำหนดเอง",
"customDataDescription": "ข้อมูลที่กำหนดเอง ใช้สำหรับข้อมูลที่กำหนดเองของเว็บไซต์",
"customHtml": "HTML ที่กำหนดเอง",
"customHtmlDescription": "รหัส HTML ที่กำหนดเองเพื่อแทรกลงในส่วนล่างของแท็ก body ของเว็บไซต์.",
"keywords": "คำสำคัญ",
+2
View File
@@ -39,6 +39,8 @@
"status": "Durum",
"subject": "Konu",
"template": "Şablon",
"traffic_exceed_email_template": "Trafik Aşım Bildirimi Şablonu",
"traffic_exceed_email_templateDescription": "{after}.variable{before} yer tutucuları gerçek verilerle değiştirilecektir. Bu değişkenleri koruduğunuzdan emin olun.",
"verify_email_template": "Doğrulama E-posta Şablonu",
"verify_email_templateDescription": "{after}.variable{before} yer tutucuları gerçek verilerle değiştirilecektir. Bu değişkenleri koruduğunuzdan emin olun.",
"whitelistSuffixes": "Beyaz Liste Sonekleri",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Alipay ile ödeme",
"alipayf2f": {
"appId": "Alipay Uygulama Kimliği",
"invoiceName": "Özel Ürün Adı",
"invoiceNameDescription": "Alipay faturasına gösterilecektir",
"privateKey": "Alipay Özel Anahtarı",
"publicKey": "Alipay Genel Anahtarı",
"sandbox": "Sandbox Modu",
"sandboxDescription": "Ödemeyi test etmek için sandbox modunu etkinleştirin"
},
"AlipayF2F": "Alipay Yüz Yüze",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Ödeme Başvurusu",
"balance": "Bakiye",
"batchDelete": "Toplu Sil",
"cancel": "İptal",
"config": "Yapılandırma",
"configInfo": "Yapılandırma Bilgisi",
"configPlaceholder": "Lütfen sağlanan {field} yapılandırmasını doldurun",
"confirm": "Onayla",
"confirmDelete": "Silme İşlemini Onayla",
"copy": "Kopyala",
"copySuccess": "Başarıyla kopyalandı",
"create": "Ödeme Yöntemi Ekle",
"createPayment": "Ödeme Yöntemi Ekle",
"createSuccess": "Başarıyla oluşturuldu",
"delete": "Sil",
"deleteSuccess": "Başarıyla silindi",
"deleteWarning": "Bu ödeme yöntemini silmek istediğinize emin misiniz? Bu işlem geri alınamaz.",
"description": "Açıklama",
"descriptionPlaceholder": "Ödeme yöntemi açıklamasını girin",
"domain": "Alan Adı",
"domainPlaceholder": "isteğe bağlı, örn. {example}",
"edit": "Düzenle",
"editPayment": "Ödeme Yöntemini Düzenle",
"enable": "Etkinleştir",
"enableDescription": "Bu ödeme yöntemini etkinleştir",
"epay": {
"key": "ANAHTAR",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Sabit Tutar",
"feeMode": "Ücretlendirme Modu",
"feeModeDescription": "Ekstra ücretin tahsilat yöntemi",
"feeModeItems": ["Ücretsiz", "Yüzde", "Sabit Tutar", "Her İkisi"],
"feePercent": "Yüzde Ücreti (isteğe bağlı)",
"feePercentDescription": "Sipariş tutarının üzerine eklenen ücret",
"fixedFee": "Sabit Ücret (isteğe bağlı)",
"fixedFeeDescription": "Sipariş tutarına ek olarak sabit ücret eklenir",
"iconUrl": "Simge URL'si (isteğe bağlı)",
"iconUrlDescription": "Ön uçta görüntüleme için kullanılır",
"inputPlaceholder": "Lütfen girin",
"key": "ANAHTAR",
"notifyUrl": "Özel bildirim alanı (isteğe bağlı)",
"notifyUrlDescription": "Ağ geçidi bildirimi bu alana gönderilecektir",
"payForRecommendations": "Öneriler için ödeme yap",
"pid": "PID",
"saveSuccess": "Başarıyla kaydedildi",
"showName": "Görünen Ad",
"showNameDescription": "Ön uçta görüntüleme için kullanılır",
"stripe": {
"publicKey": "GENEL ANAHTAR",
"secretKey": "GİZLİ ANAHTAR",
"webhookSecret": "WEBHOOK GİZLİ İMZA"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "İşlem Ücreti",
"icon": "Simge",
"iconPlaceholder": "Simge URL'sini girin",
"mark": "Tanımlayıcı",
"markPlaceholder": "Ödeme yöntemi tanımlayıcısını girin",
"name": "İsim",
"namePlaceholder": "Ödeme yöntemi adını girin",
"nameRequired": "İsim gereklidir",
"noFee": "Ücret Yok",
"notify_url": "Bildirim URL'si",
"paymentManagement": "Ödeme Yönetimi",
"percentFee": "Yüzde",
"platform": "Platform",
"searchPlaceholder": "Arama terimlerini girin",
"selectPlatform": "Platform Seçin",
"selectType": "Ödeme türünü seçin",
"submit": "Gönder",
"type": "Tür",
"updateSuccess": "Başarıyla güncellendi"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Gizlilik Politikası"
},
"site": {
"customData": "Özel Veri",
"customDataDescription": "Özel Veri, sitenin özel verileri için kullanılır",
"customHtml": "Özel HTML",
"customHtmlDescription": "Site'in body etiketinin altına yerleştirilecek özel HTML kodu.",
"keywords": "Anahtar Kelimeler",
+2
View File
@@ -39,6 +39,8 @@
"status": "Статус",
"subject": "Тема",
"template": "Шаблон",
"traffic_exceed_email_template": "Шаблон сповіщення про перевищення трафіку",
"traffic_exceed_email_templateDescription": "Місця для змінних {after}.variable{before} будуть замінені на фактичні дані. Переконайтеся, що ці змінні залишаються.",
"verify_email_template": "Шаблон електронного листа для підтвердження",
"verify_email_templateDescription": "Заповнювачі {after}.variable{before} будуть замінені на фактичні дані. Переконайтеся, що зберегли ці змінні.",
"whitelistSuffixes": "Білий список суфіксів",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Оплата через Alipay",
"alipayf2f": {
"appId": "Ідентифікатор додатку Alipay",
"invoiceName": "Назва продукту на замовлення",
"invoiceNameDescription": "Буде відображено у рахунку Alipay",
"privateKey": "Приватний ключ Alipay",
"publicKey": "Публічний ключ Alipay",
"sandbox": "Режим пісочниці",
"sandboxDescription": "Увімкніть режим пісочниці для тестування платежів"
},
"AlipayF2F": "Alipay Face to Face",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Подати заявку на оплату",
"balance": "Баланс",
"batchDelete": "Пакетне видалення",
"cancel": "Скасувати",
"config": "Налаштування",
"configInfo": "Інформація про налаштування",
"configPlaceholder": "Будь ласка, заповніть надану конфігурацію {field}",
"confirm": "Підтвердити",
"confirmDelete": "Підтвердити видалення",
"copy": "Копіювати",
"copySuccess": "Успішно скопійовано",
"create": "Додати спосіб оплати",
"createPayment": "Додати спосіб оплати",
"createSuccess": "Успішно створено",
"delete": "Видалити",
"deleteSuccess": "Успішно видалено",
"deleteWarning": "Ви впевнені, що хочете видалити цей спосіб оплати? Цю дію не можна скасувати.",
"description": "Опис",
"descriptionPlaceholder": "Введіть опис способу оплати",
"domain": "Домен",
"domainPlaceholder": "необов'язково, наприклад, {example}",
"edit": "Редагувати",
"editPayment": "Редагувати спосіб оплати",
"enable": "Увімкнути",
"enableDescription": "Увімкнути цей спосіб оплати",
"epay": {
"key": "КЛЮЧ",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Фіксована сума",
"feeMode": "Спосіб оплати",
"feeModeDescription": "Спосіб стягнення додаткової комісії",
"feeModeItems": ["Без комісії", "Відсоток", "Фіксована сума", "Обидва варіанти"],
"feePercent": "Відсоткова комісія (необов'язково)",
"feePercentDescription": "Додати комісію до суми замовлення",
"fixedFee": "Фіксована плата (необов'язково)",
"fixedFeeDescription": "Додати фіксовану плату до суми замовлення",
"iconUrl": "URL значка (необов'язково)",
"iconUrlDescription": "Використовується для відображення на фронтенді",
"inputPlaceholder": "Введіть, будь ласка",
"key": "КЛЮЧ",
"notifyUrl": "Користувацька область сповіщень (необов'язково)",
"notifyUrlDescription": "Сповіщення шлюзу буде надіслано на цей домен",
"payForRecommendations": "Оплатити рекомендації",
"pid": "PID",
"saveSuccess": "Збережено успішно",
"showName": "Показати ім'я",
"showNameDescription": "Використовується для відображення на фронтенді",
"stripe": {
"publicKey": "ПУБЛІЧНИЙ КЛЮЧ",
"secretKey": "СЕКРЕТНИЙ КЛЮЧ",
"webhookSecret": "ПІДПИС ВЕБХУКА"
},
"url": "URL",
"wechatPay": "WeChat Pay"
"handlingFee": "Плата за обробку",
"icon": "Іконка",
"iconPlaceholder": "Введіть URL іконки",
"mark": "Ідентифікатор",
"markPlaceholder": "Введіть ідентифікатор способу оплати",
"name": "Назва",
"namePlaceholder": "Введіть назву способу оплати",
"nameRequired": "Назва є обов'язковою",
"noFee": "Без комісії",
"notify_url": "URL сповіщення",
"paymentManagement": "Управління платежами",
"percentFee": "Відсоток",
"platform": "Платформа",
"searchPlaceholder": "Введіть пошукові терміни",
"selectPlatform": "Виберіть платформу",
"selectType": "Виберіть тип оплати",
"submit": "Надіслати",
"type": "Тип",
"updateSuccess": "Успішно оновлено"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Політика конфіденційності"
},
"site": {
"customData": "Користувацькі дані",
"customDataDescription": "Користувацькі дані, що використовуються для користувацьких даних сайту",
"customHtml": "Користувацький HTML",
"customHtmlDescription": "Користувацький HTML-код, який буде вставлено в кінець тегу body сайту.",
"keywords": "Ключові слова",
+2
View File
@@ -39,6 +39,8 @@
"status": "Trạng thái",
"subject": "Chủ đề",
"template": "Mẫu",
"traffic_exceed_email_template": "Mẫu Thông Báo Vượt Quá Lưu Lượng",
"traffic_exceed_email_templateDescription": "Các biến {after}.variable{before} sẽ được thay thế bằng dữ liệu thực tế. Đảm bảo giữ nguyên các biến này.",
"verify_email_template": "Mẫu Email Xác Minh",
"verify_email_templateDescription": "Các chỗ trống {after}.variable{before} sẽ được thay thế bằng dữ liệu thực tế. Đảm bảo giữ nguyên các biến này.",
"whitelistSuffixes": "Danh sách trắng Hậu tố",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "Thanh toán qua Alipay",
"alipayf2f": {
"appId": "ID ứng dụng Alipay",
"invoiceName": "Tên sản phẩm tùy chỉnh",
"invoiceNameDescription": "Sẽ hiển thị trên hóa đơn Alipay",
"privateKey": "Khóa riêng Alipay",
"publicKey": "Khóa công khai Alipay",
"sandbox": "Chế độ sandbox",
"sandboxDescription": "Kích hoạt chế độ sandbox để kiểm tra thanh toán"
},
"AlipayF2F": "Alipay Gặp Mặt",
"EPay": "EPay",
"Stripe": "Stripe",
"applyForPayment": "Đăng Ký Thanh Toán",
"balance": "Số Dư",
"batchDelete": "Xóa Theo Lô",
"cancel": "Hủy",
"config": "Cấu Hình",
"configInfo": "Thông Tin Cấu Hình",
"configPlaceholder": "Vui lòng điền vào cấu hình {field} đã cung cấp",
"confirm": "Xác Nhận",
"confirmDelete": "Xác Nhận Xóa",
"copy": "Sao Chép",
"copySuccess": "Sao chép thành công",
"create": "Thêm Phương Thức Thanh Toán",
"createPayment": "Thêm Phương Thức Thanh Toán",
"createSuccess": "Tạo thành công",
"delete": "Xóa",
"deleteSuccess": "Xóa thành công",
"deleteWarning": "Bạn có chắc chắn muốn xóa phương thức thanh toán này không? Hành động này không thể hoàn tác.",
"description": "Mô tả",
"descriptionPlaceholder": "Nhập mô tả phương thức thanh toán",
"domain": "Miền",
"domainPlaceholder": "tùy chọn, ví dụ: {example}",
"edit": "Chỉnh Sửa",
"editPayment": "Chỉnh Sửa Phương Thức Thanh Toán",
"enable": "Kích hoạt",
"enableDescription": "Kích hoạt phương thức thanh toán này",
"epay": {
"key": "KHÓA",
"pid": "PID",
"url": "URL"
},
"feeAmount": "Số Tiền Cố Định",
"feeMode": "Phương thức tính phí",
"feeModeDescription": "Cách tính phí bổ sung",
"feeModeItems": ["Không phí dịch vụ", "Phần trăm", "Số tiền cố định", "Hai loại trên"],
"feePercent": "Phí phần trăm (tùy chọn)",
"feePercentDescription": "Thêm phí trên số tiền đơn hàng",
"fixedFee": "Phí cố định (tùy chọn)",
"fixedFeeDescription": "Thêm phí cố định vào trên số tiền đơn hàng",
"iconUrl": "URL biểu tượng (tùy chọn)",
"iconUrlDescription": "Dùng để hiển thị phía trước",
"inputPlaceholder": "Vui lòng nhập",
"key": "KEY",
"notifyUrl": "Miền thông báo tùy chỉnh (tùy chọn)",
"notifyUrlDescription": "Thông báo cổng sẽ được gửi đến miền này",
"payForRecommendations": "Thanh toán cho đề xuất",
"pid": "PID",
"saveSuccess": "Lưu thành công",
"showName": "Hiển thị tên",
"showNameDescription": "Dùng để hiển thị phía trước",
"stripe": {
"publicKey": "KHÓA CÔNG KHAI",
"secretKey": "KHÓA BÍ MẬT",
"webhookSecret": "CHỮ KÝ KHÓA WEBHOOK"
},
"url": "URL",
"wechatPay": "Thanh toán WeChat"
"handlingFee": "Phí Xử Lý",
"icon": "Biểu Tượng",
"iconPlaceholder": "Nhập URL biểu tượng",
"mark": "Định Danh",
"markPlaceholder": "Nhập định danh phương thức thanh toán",
"name": "Tên",
"namePlaceholder": "Nhập tên phương thức thanh toán",
"nameRequired": "Tên là bắt buộc",
"noFee": "Không Có Phí",
"notify_url": "URL Thông Báo",
"paymentManagement": "Quản Lý Thanh Toán",
"percentFee": "Phần Trăm",
"platform": "Nền Tảng",
"searchPlaceholder": "Nhập từ khóa tìm kiếm",
"selectPlatform": "Chọn Nền Tảng",
"selectType": "Chọn loại thanh toán",
"submit": "Gửi",
"type": "Loại",
"updateSuccess": "Cập nhật thành công"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "Chính sách bảo mật"
},
"site": {
"customData": "Dữ liệu tùy chỉnh",
"customDataDescription": "Dữ liệu tùy chỉnh, được sử dụng cho dữ liệu tùy chỉnh của trang web",
"customHtml": "HTML tùy chỉnh",
"customHtmlDescription": "Mã HTML tùy chỉnh để được chèn vào cuối thẻ body của trang.",
"keywords": "Từ khóa",
+2
View File
@@ -39,6 +39,8 @@
"status": "状态",
"subject": "主题",
"template": "模板",
"traffic_exceed_email_template": "流量超限通知模板",
"traffic_exceed_email_templateDescription": "{after}和{before}变量将被实际数据替换。请确保保留这些变量。",
"verify_email_template": "验证电子邮件模板",
"verify_email_templateDescription": "{after}.variable{before} 占位符将被实际数据替换。请确保保留这些变量。",
"whitelistSuffixes": "白名单后缀",
+48 -40
View File
@@ -1,44 +1,52 @@
{
"aliPay": "支付宝支付",
"alipayf2f": {
"appId": "支付宝应用ID",
"invoiceName": "自定义产品名称",
"invoiceNameDescription": "将显示在支付宝账单中",
"privateKey": "支付宝私钥",
"publicKey": "支付宝公钥",
"sandbox": "沙箱模式",
"sandboxDescription": "启用沙箱模式以测试支付"
},
"AlipayF2F": "支付宝面对面",
"EPay": "电子支付",
"Stripe": "Stripe",
"applyForPayment": "申请付款",
"balance": "余额",
"batchDelete": "批量删除",
"cancel": "取消",
"config": "配置",
"configInfo": "配置信息",
"configPlaceholder": "请填写提供的 {field} 配置",
"confirm": "确认",
"confirmDelete": "确认删除",
"copy": "复制",
"copySuccess": "复制成功",
"create": "添加支付方式",
"createPayment": "添加支付方式",
"createSuccess": "创建成功",
"delete": "删除",
"deleteSuccess": "删除成功",
"deleteWarning": "您确定要删除此支付方式吗?此操作无法撤销。",
"description": "描述",
"descriptionPlaceholder": "输入支付方式描述",
"domain": "域名",
"domainPlaceholder": "可选,例如 {example}",
"edit": "编辑",
"editPayment": "编辑支付方式",
"enable": "启用",
"enableDescription": "启用此支付方式",
"epay": {
"key": "KEY",
"pid": "PID",
"url": "URL"
},
"feeAmount": "固定金额",
"feeMode": "收费方式",
"feeModeDescription": "额外手续费的收费方式",
"feeModeItems": ["无手续费", "百分比", "固定金额", "以上两种"],
"feePercent": "百分比费用(可选)",
"feePercentDescription": "在订单金额之上添加费用",
"fixedFee": "固定费用(可选)",
"fixedFeeDescription": "在订单金额之上添加固定费用",
"iconUrl": "图标 URL(可选)",
"iconUrlDescription": "用于前端显示",
"inputPlaceholder": "输入",
"key": "KEY",
"notifyUrl": "自定义通知域(可选)",
"notifyUrlDescription": "网关通知将发送到此域",
"payForRecommendations": "支付推荐",
"pid": "PID",
"saveSuccess": "保存成功",
"showName": "显示名称",
"showNameDescription": "用于前端显示",
"stripe": {
"publicKey": "PUBLIC KEY",
"secretKey": "SECRET KEY",
"webhookSecret": "WEBHOOK 密钥签名"
},
"url": "URL",
"wechatPay": "微信支付"
"feePercent": "百分比费用",
"fixedFee": "固定费用",
"handlingFee": "手续费",
"icon": "图标",
"iconPlaceholder": "输入图标 URL",
"mark": "标识符",
"markPlaceholder": "输入支付方式标识符",
"name": "名称",
"namePlaceholder": "输入支付方式名称",
"nameRequired": "名称是必填项",
"noFee": "无费用",
"notify_url": "通知网址",
"paymentManagement": "支付管理",
"percentFee": "百分比",
"platform": "平台",
"searchPlaceholder": "输入搜索词",
"selectPlatform": "选择平台",
"selectType": "选择支付类型",
"submit": "提交",
"type": "类型",
"updateSuccess": "更新成功"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "隐私政策"
},
"site": {
"customData": "自定义数据",
"customDataDescription": "自定义数据,用于网站的自定义数据",
"customHtml": "自定义HTML",
"customHtmlDescription": "要注入到网站 body 标签底部的自定义 HTML 代码。",
"keywords": "关键词",
+2
View File
@@ -39,6 +39,8 @@
"status": "狀態",
"subject": "主題",
"template": "模板",
"traffic_exceed_email_template": "流量超標通知模板",
"traffic_exceed_email_templateDescription": "{after}和{before}變量將會被實際數據替換。請確保保留這些變量。",
"verify_email_template": "驗證電郵範本",
"verify_email_templateDescription": "{after}.variable{before} 佔位符將被實際數據替換。請確保保留這些變數。",
"whitelistSuffixes": "白名單後綴",
+46 -38
View File
@@ -1,44 +1,52 @@
{
"aliPay": "支付寶支付",
"alipayf2f": {
"appId": "支付寶應用ID",
"invoiceName": "自訂產品名稱",
"invoiceNameDescription": "將顯示在支付寶賬單中",
"privateKey": "支付寶私鑰",
"publicKey": "支付寶公鑰",
"sandbox": "沙箱模式",
"sandboxDescription": "啟用沙箱模式以測試支付"
},
"AlipayF2F": "支付寶面對面",
"EPay": "電子支付",
"Stripe": "Stripe",
"applyForPayment": "申請付款",
"balance": "餘額",
"batchDelete": "批量刪除",
"cancel": "取消",
"config": "配置",
"configInfo": "配置資訊",
"configPlaceholder": "請填寫提供的 {field} 配置",
"confirm": "確認",
"confirmDelete": "確認刪除",
"copy": "複製",
"copySuccess": "複製成功",
"create": "新增付款方式",
"createPayment": "新增付款方式",
"createSuccess": "創建成功",
"delete": "刪除",
"deleteSuccess": "刪除成功",
"deleteWarning": "您確定要刪除此付款方式嗎?此操作無法撤銷。",
"description": "描述",
"descriptionPlaceholder": "輸入付款方式描述",
"domain": "域名",
"domainPlaceholder": "可選,例如 {example}",
"edit": "編輯",
"editPayment": "編輯付款方式",
"enable": "啟用",
"enableDescription": "啟用此支付方式",
"epay": {
"key": "KEY",
"pid": "PID",
"url": "URL"
},
"feeAmount": "固定金額",
"feeMode": "收費方式",
"feeModeDescription": "額外手續費的收費方式",
"feeModeItems": ["無手續費", "百分比", "固定金額", "以上兩種"],
"feePercent": "百分比費用(可選)",
"feePercentDescription": "在訂單金額之上添加費用",
"fixedFee": "固定費用(可選)",
"fixedFeeDescription": "在訂單金額之上添加固定費用",
"iconUrl": "圖示 URL(可選)",
"iconUrlDescription": "用於前端顯示",
"inputPlaceholder": "請輸入",
"key": "KEY",
"notifyUrl": "自訂通知域(可選)",
"notifyUrlDescription": "閘道通知將發送到此網域",
"payForRecommendations": "支付推薦",
"pid": "PID",
"saveSuccess": "儲存成功",
"showName": "顯示名稱",
"showNameDescription": "用於前端顯示",
"stripe": {
"publicKey": "PUBLIC KEY",
"secretKey": "SECRET KEY",
"webhookSecret": "WEBHOOK 密鑰簽名"
},
"url": "URL",
"wechatPay": "微信支付"
"handlingFee": "手續費",
"icon": "圖",
"iconPlaceholder": "輸入圖標網址",
"mark": "標識",
"markPlaceholder": "輸入付款方式標識",
"name": "名稱",
"namePlaceholder": "輸入付款方式名稱",
"nameRequired": "名稱為必填",
"noFee": "無費用",
"notify_url": "通知網址",
"paymentManagement": "付款管理",
"percentFee": "百分比",
"platform": "平台",
"searchPlaceholder": "輸入搜索詞",
"selectPlatform": "選擇平台",
"selectType": "選擇付款類型",
"submit": "提交",
"type": "類型",
"updateSuccess": "更新成功"
}
+2
View File
@@ -13,6 +13,8 @@
"title": "私隱政策"
},
"site": {
"customData": "自定義數據",
"customDataDescription": "自定義數據,用於網站的自定義數據",
"customHtml": "自定義HTML",
"customHtmlDescription": "自定義 HTML 代碼,將注入到網站 body 標籤的底部。",
"keywords": "關鍵字",
+1 -1
View File
@@ -1,5 +1,5 @@
// @ts-ignore
// API 更新时间:
// API 唯一标识:
import * as ads from './ads';
+48 -83
View File
@@ -2,20 +2,12 @@
/* eslint-disable */
import request from '@/utils/request';
/** Get alipay f2f payment config GET /v1/admin/payment/alipay_f2f */
export async function getAlipayF2FPaymentConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.PaymentConfig }>('/v1/admin/payment/alipay_f2f', {
method: 'GET',
...(options || {}),
});
}
/** Update alipay f2f payment config PUT /v1/admin/payment/alipay_f2f */
export async function updateAlipayF2FPaymentConfig(
body: API.UpdateAlipayF2fRequest,
/** Update Payment Method PUT /v1/admin/payment/ */
export async function updatePaymentMethod(
body: API.UpdatePaymentMethodRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/payment/alipay_f2f', {
return request<API.Response & { data?: API.PaymentConfig }>('/v1/admin/payment/', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
@@ -25,85 +17,58 @@ export async function updateAlipayF2FPaymentConfig(
});
}
/** Get all payment config GET /v1/admin/payment/all */
export async function getAllPaymentConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetAllPaymentConfigResponse }>(
'/v1/admin/payment/all',
/** Create Payment Method POST /v1/admin/payment/ */
export async function createPaymentMethod(
body: API.CreatePaymentMethodRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PaymentConfig }>('/v1/admin/payment/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Delete Payment Method DELETE /v1/admin/payment/ */
export async function deletePaymentMethod(
body: API.DeletePaymentMethodRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/payment/', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Get Payment Method List GET /v1/admin/payment/list */
export async function getPaymentMethodList(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.GetPaymentMethodListParams,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.GetPaymentMethodListResponse }>(
'/v1/admin/payment/list',
{
method: 'GET',
params: {
...params,
},
...(options || {}),
},
);
}
/** Get epay payment config GET /v1/admin/payment/epay */
export async function getEpayPaymentConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.PaymentConfig }>('/v1/admin/payment/epay', {
/** Get supported payment platform GET /v1/admin/payment/platform */
export async function getPaymentPlatform(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.PlatformResponse }>('/v1/admin/payment/platform', {
method: 'GET',
...(options || {}),
});
}
/** Update epay payment config PUT /v1/admin/payment/epay */
export async function updateEpayPaymentConfig(
body: API.UpdateEpayRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/payment/epay', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Get stripe alipay payment config GET /v1/admin/payment/stripe_alipay */
export async function getStripeAlipayPaymentConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.PaymentConfig }>('/v1/admin/payment/stripe_alipay', {
method: 'GET',
...(options || {}),
});
}
/** Update stripe alipay payment config PUT /v1/admin/payment/stripe_alipay */
export async function updateStripeAlipayPaymentConfig(
body: API.UpdateStripeRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/payment/stripe_alipay', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Get stripe wechat pay payment config GET /v1/admin/payment/stripe_wechat_pay */
export async function getStripeWeChatPayPaymentConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.PaymentConfig }>(
'/v1/admin/payment/stripe_wechat_pay',
{
method: 'GET',
...(options || {}),
},
);
}
/** Update stripe wechat pay payment config PUT /v1/admin/payment/stripe_wechat_pay */
export async function updateStripeWeChatPayPaymentConfig(
body: API.UpdateStripeRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/payment/stripe_wechat_pay', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
+134 -70
View File
@@ -13,12 +13,8 @@ declare namespace API {
updated_at: number;
};
type AlipayF2FConfig = {
app_id: string;
private_key: string;
public_key: string;
invoice_name: string;
sandbox: boolean;
type AlipayNotifyResponse = {
return_code: string;
};
type Announcement = {
@@ -239,12 +235,25 @@ declare namespace API {
coupon_discount?: number;
commission: number;
fee_amount: number;
method?: string;
payment_id: number;
trade_no?: string;
status?: number;
subscribe_id?: number;
};
type CreatePaymentMethodRequest = {
name: string;
platform: string;
description: string;
icon?: string;
domain?: string;
config: Record<string, any>;
fee_mode: number;
fee_percent?: number;
fee_amount?: number;
enable: boolean;
};
type CreateRuleGroupRequest = {
name: string;
icon: string;
@@ -352,6 +361,10 @@ declare namespace API {
id: number;
};
type DeletePaymentMethodRequest = {
id: number;
};
type DeleteRuleGroupRequest = {
id: number;
};
@@ -398,10 +411,17 @@ declare namespace API {
domain_suffix_list: string;
};
type EpayConfig = {
pid: string;
url: string;
key: string;
type EPayNotifyRequest = {
pid: number;
trade_no: string;
out_trade_no: string;
type: string;
name: string;
money: string;
trade_status: string;
param: string;
sign: string;
sign_type: string;
};
type Follow = {
@@ -440,10 +460,6 @@ declare namespace API {
list: Ads[];
};
type GetAllPaymentConfigResponse = {
list: PaymentConfig[];
};
type GetAnnouncementListParams = {
page: number;
size: number;
@@ -488,7 +504,7 @@ declare namespace API {
};
type GetAvailablePaymentMethodsResponse = {
list: PaymentConfig[];
list: PaymentMethod[];
};
type GetCouponListParams = {
@@ -625,6 +641,27 @@ declare namespace API {
list: Order[];
};
type GetPaymentMethodListParams = {
page: number;
size: number;
platform?: string;
search?: string;
enable?: boolean;
};
type GetPaymentMethodListRequest = {
page: number;
size: number;
platform?: string;
search?: string;
enable?: boolean;
};
type GetPaymentMethodListResponse = {
total: number;
list: PaymentMethodDetail[];
};
type GetRuleGroupResponse = {
total: number;
list: ServerRuleGroup[];
@@ -797,6 +834,8 @@ declare namespace API {
size: number;
user_id: number;
subscribe_id: number;
start_time: number;
end_time: number;
};
type GetUserSubscribeTrafficLogsRequest = {
@@ -804,6 +843,8 @@ declare namespace API {
size: number;
user_id: number;
subscribe_id: number;
start_time: number;
end_time: number;
};
type GetUserSubscribeTrafficLogsResponse = {
@@ -891,7 +932,7 @@ declare namespace API {
coupon: string;
coupon_discount: number;
commission?: number;
method: string;
payment: PaymentMethod;
fee_amount: number;
trade_no: string;
status: number;
@@ -913,6 +954,7 @@ declare namespace API {
coupon: string;
coupon_discount: number;
commission?: number;
payment: PaymentMethod;
method: string;
fee_amount: number;
trade_no: string;
@@ -934,7 +976,8 @@ declare namespace API {
type PaymentConfig = {
id: number;
name: string;
mark: string;
platform: string;
description: string;
icon?: string;
domain?: string;
config: Record<string, any>;
@@ -944,6 +987,32 @@ declare namespace API {
enable: boolean;
};
type PaymentMethod = {
id: number;
name: string;
platform: string;
description: string;
icon: string;
fee_mode: number;
fee_percent: number;
fee_amount: number;
};
type PaymentMethodDetail = {
id: number;
name: string;
platform: string;
description: string;
icon: string;
domain: string;
config: Record<string, any>;
fee_mode: number;
fee_percent: number;
fee_amount: number;
enable: boolean;
notify_url: string;
};
type PlatformInfo = {
platform: string;
platform_url: string;
@@ -986,7 +1055,7 @@ declare namespace API {
type PurchaseOrderRequest = {
subscribe_id: number;
quantity: number;
payment: string;
payment: number;
coupon?: string;
};
@@ -1029,9 +1098,29 @@ declare namespace API {
list: OrderDetail[];
};
type QuerySubscribeGroupListResponse = {
list: SubscribeGroup[];
total: number;
};
type QuerySubscribeListResponse = {
list: Subscribe[];
total: number;
};
type QueryUserAffiliateListRequest = {
page: number;
size: number;
};
type QueryUserAffiliateListResponse = {
list: UserAffiliate[];
total: number;
};
type RechargeOrderRequest = {
amount: number;
payment: string;
payment: number;
};
type RechargeOrderResponse = {
@@ -1052,7 +1141,7 @@ declare namespace API {
type RenewalOrderRequest = {
user_subscribe_id: number;
quantity: number;
payment: string;
payment: number;
coupon?: string;
};
@@ -1062,7 +1151,7 @@ declare namespace API {
type ResetTrafficOrderRequest = {
user_subscribe_id: number;
payment: string;
payment: number;
};
type ResetTrafficOrderResponse = {
@@ -1180,6 +1269,13 @@ declare namespace API {
site_logo: string;
keywords: string;
custom_html: string;
custom_data: string;
};
type SiteCustomDataContacts = {
email: string;
telephone: string;
address: string;
};
type SortItem = {
@@ -1187,13 +1283,6 @@ declare namespace API {
sort: number;
};
type StripeConfig = {
public_key: string;
secret_key: string;
webhook_secret: string;
payment: string;
};
type StripePayment = {
method: string;
client_secret: string;
@@ -1365,19 +1454,6 @@ declare namespace API {
status: number;
};
type UpdateAlipayF2fRequest = {
id: number;
name: string;
mark: string;
icon?: string;
domain?: string;
config: AlipayF2FConfig;
fee_mode: number;
fee_percent?: number;
fee_amount?: number;
enable: boolean;
};
type UpdateAnnouncementEnableRequest = {
id: number;
enable: boolean;
@@ -1441,19 +1517,6 @@ declare namespace API {
show: boolean;
};
type UpdateEpayRequest = {
id: number;
name: string;
mark: string;
icon?: string;
domain?: string;
config: EpayConfig;
fee_mode: number;
fee_percent?: number;
fee_amount?: number;
enable: boolean;
};
type UpdateNodeGroupRequest = {
id: number;
name: string;
@@ -1481,10 +1544,24 @@ declare namespace API {
type UpdateOrderStatusRequest = {
id: number;
status: number;
method?: string;
payment_id?: number;
trade_no?: string;
};
type UpdatePaymentMethodRequest = {
id: number;
name: string;
platform: string;
description: string;
icon?: string;
domain?: string;
config: Record<string, any>;
fee_mode: number;
fee_percent?: number;
fee_amount?: number;
enable: boolean;
};
type UpdateRuleGroupRequest = {
id: number;
name: string;
@@ -1493,19 +1570,6 @@ declare namespace API {
enable: boolean;
};
type UpdateStripeRequest = {
id: number;
name: string;
mark: string;
icon?: string;
domain?: string;
config: StripeConfig;
fee_mode: number;
fee_percent?: number;
fee_amount?: number;
enable: boolean;
};
type UpdateSubscribeGroupRequest = {
id: number;
name: string;
@@ -1603,8 +1667,8 @@ declare namespace API {
};
type UserAffiliate = {
email: string;
avatar: string;
identifier: string;
registered_at: number;
enable: boolean;
};
+15 -11
View File
@@ -2,6 +2,21 @@
/* eslint-disable */
import request from '@/utils/request';
/** Get Ads GET /v1/common/ads */
export async function getAds(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.GetAdsParams,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.GetAdsResponse }>('/v1/common/ads', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** Get Tos Content GET /v1/common/application */
export async function getApplication(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetAppcationResponse }>('/v1/common/application', {
@@ -58,17 +73,6 @@ export async function getStat(options?: { [key: string]: any }) {
});
}
/** Get Subscription GET /v1/common/site/subscribe */
export async function getSubscription(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetSubscriptionResponse }>(
'/v1/common/site/subscribe',
{
method: 'GET',
...(options || {}),
},
);
}
/** Get Tos Content GET /v1/common/site/tos */
export async function getTos(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetTosResponse }>('/v1/common/site/tos', {
+1 -1
View File
@@ -1,5 +1,5 @@
// @ts-ignore
// API 更新时间:
// API 唯一标识:
import * as auth from './auth';
+119 -12
View File
@@ -13,6 +13,10 @@ declare namespace API {
updated_at: number;
};
type AlipayNotifyResponse = {
return_code: string;
};
type Announcement = {
id: number;
title: string;
@@ -160,6 +164,19 @@ declare namespace API {
domain_suffix_list: string;
};
type EPayNotifyRequest = {
pid: number;
trade_no: string;
out_trade_no: string;
type: string;
name: string;
money: string;
trade_status: string;
param: string;
sign: string;
sign_type: string;
};
type Follow = {
id: number;
ticket_id: number;
@@ -169,13 +186,27 @@ declare namespace API {
created_at: number;
};
type GetAdsParams = {
device: string;
position: string;
};
type GetAdsRequest = {
device: string;
position: string;
};
type GetAdsResponse = {
list: Ads[];
};
type GetAppcationResponse = {
config: ApplicationConfig;
applications: ApplicationResponseInfo[];
};
type GetAvailablePaymentMethodsResponse = {
list: PaymentConfig[];
list: PaymentMethod[];
};
type GetGlobalConfigResponse = {
@@ -187,6 +218,7 @@ declare namespace API {
subscribe: SubscribeConfig;
verify_code: PubilcVerifyCodeConfig;
oauth_methods: string[];
web_ad: boolean;
};
type GetStatResponse = {
@@ -196,14 +228,24 @@ declare namespace API {
protocol: string[];
};
type GetSubscriptionResponse = {
list: Subscribe[];
};
type GetTosResponse = {
tos_content: string;
};
type GetUserSubscribeTrafficLogsRequest = {
page: number;
size: number;
user_id: number;
subscribe_id: number;
start_time: number;
end_time: number;
};
type GetUserSubscribeTrafficLogsResponse = {
list: TrafficLog[];
total: number;
};
type GoogleLoginCallbackRequest = {
code: string;
state: string;
@@ -297,7 +339,7 @@ declare namespace API {
coupon: string;
coupon_discount: number;
commission?: number;
method: string;
payment: PaymentMethod;
fee_amount: number;
trade_no: string;
status: number;
@@ -319,6 +361,7 @@ declare namespace API {
coupon: string;
coupon_discount: number;
commission?: number;
payment: PaymentMethod;
method: string;
fee_amount: number;
trade_no: string;
@@ -332,7 +375,8 @@ declare namespace API {
type PaymentConfig = {
id: number;
name: string;
mark: string;
platform: string;
description: string;
icon?: string;
domain?: string;
config: Record<string, any>;
@@ -342,6 +386,42 @@ declare namespace API {
enable: boolean;
};
type PaymentMethod = {
id: number;
name: string;
platform: string;
description: string;
icon: string;
fee_mode: number;
fee_percent: number;
fee_amount: number;
};
type PaymentMethodDetail = {
id: number;
name: string;
platform: string;
description: string;
icon: string;
domain: string;
config: Record<string, any>;
fee_mode: number;
fee_percent: number;
fee_amount: number;
enable: boolean;
notify_url: string;
};
type PlatformInfo = {
platform: string;
platform_url: string;
platform_field_description: Record<string, any>;
};
type PlatformResponse = {
list: PlatformInfo[];
};
type PreOrderResponse = {
price: number;
amount: number;
@@ -374,7 +454,7 @@ declare namespace API {
type PurchaseOrderRequest = {
subscribe_id: number;
quantity: number;
payment: string;
payment: number;
coupon?: string;
};
@@ -417,9 +497,29 @@ declare namespace API {
list: OrderDetail[];
};
type QuerySubscribeGroupListResponse = {
list: SubscribeGroup[];
total: number;
};
type QuerySubscribeListResponse = {
list: Subscribe[];
total: number;
};
type QueryUserAffiliateListRequest = {
page: number;
size: number;
};
type QueryUserAffiliateListResponse = {
list: UserAffiliate[];
total: number;
};
type RechargeOrderRequest = {
amount: number;
payment: string;
payment: number;
};
type RechargeOrderResponse = {
@@ -440,7 +540,7 @@ declare namespace API {
type RenewalOrderRequest = {
user_subscribe_id: number;
quantity: number;
payment: string;
payment: number;
coupon?: string;
};
@@ -457,7 +557,7 @@ declare namespace API {
type ResetTrafficOrderRequest = {
user_subscribe_id: number;
payment: string;
payment: number;
};
type ResetTrafficOrderResponse = {
@@ -559,6 +659,13 @@ declare namespace API {
site_logo: string;
keywords: string;
custom_html: string;
custom_data: string;
};
type SiteCustomDataContacts = {
email: string;
telephone: string;
address: string;
};
type SortItem = {
@@ -737,8 +844,8 @@ declare namespace API {
};
type UserAffiliate = {
email: string;
avatar: string;
identifier: string;
registered_at: number;
enable: boolean;
};
+1 -1
View File
@@ -7,7 +7,7 @@ import { getAuthorization, Logout } from './common';
async function handleError(response: any) {
const code = response.data?.code;
if ([40002, 40003, 40004].includes(code)) return Logout();
if ([40002, 40003, 40004, 40005].includes(code)) return Logout();
if (response?.config?.skipErrorHandler) return;
if (!isBrowser()) return;
+1
View File
@@ -4,6 +4,7 @@ NEXT_PUBLIC_DEFAULT_LANGUAGE=en-US
# Site URL and API URL
NEXT_PUBLIC_SITE_URL=https://user.ppanel.dev
NEXT_PUBLIC_API_URL=https://api.ppanel.dev
NEXT_PUBLIC_CDN_URL=https://cdn.jsdelivr.net
# Home Page Settings
NEXT_PUBLIC_HOME_USER_COUNT=999
@@ -32,8 +32,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/componen
import { Separator } from '@workspace/ui/components/separator';
import { Tabs, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
import { Icon } from '@workspace/ui/custom-components/icon';
import { isBrowser } from '@workspace/ui/utils';
import { differenceInDays } from 'date-fns';
import { differenceInDays, formatDate, isBrowser } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import Image from 'next/image';
import Link from 'next/link';
@@ -58,7 +57,11 @@ export default function Content() {
const [protocol, setProtocol] = useState('');
const { data: userSubscribe = [], refetch } = useQuery({
const {
data: userSubscribe = [],
refetch,
isLoading,
} = useQuery({
queryKey: ['queryUserSubscribe'],
queryFn: async () => {
const { data } = await queryUserSubscribe();
@@ -89,10 +92,27 @@ export default function Content() {
<>
{userSubscribe.length ? (
<>
<h2 className='flex items-center gap-1.5 font-semibold'>
<Icon icon='uil:servers' className='size-5' />
{t('mySubscriptions')}
</h2>
<div className='flex items-center justify-between'>
<h2 className='flex items-center gap-1.5 font-semibold'>
<Icon icon='uil:servers' className='size-5' />
{t('mySubscriptions')}
</h2>
<div className='flex gap-2'>
<Button
size='sm'
variant='outline'
onClick={() => {
refetch();
}}
className={isLoading ? 'animate-pulse' : ''}
>
<Icon icon='uil:sync' />
</Button>
<Button size='sm' asChild>
<Link href='/subscribe'>{t('purchaseSubscription')}</Link>
</Button>
</div>
</div>
<div className='flex flex-wrap justify-between gap-4'>
<Tabs
value={platform}
@@ -142,7 +162,10 @@ export default function Content() {
{userSubscribe.map((item) => (
<Card key={item.id}>
<CardHeader className='flex flex-row flex-wrap items-center justify-between gap-2 space-y-0'>
<CardTitle className='font-medium'>{item.subscribe.name}</CardTitle>
<CardTitle className='font-medium'>
{item.subscribe.name}
<p className='text-foreground/50 mt-1 text-sm'>{formatDate(item.start_time)}</p>
</CardTitle>
<div className='flex flex-wrap gap-2'>
<AlertDialog>
<AlertDialogTrigger asChild>
@@ -201,12 +224,13 @@ export default function Content() {
<span className='text-2xl font-semibold'>
{item.reset_time
? differenceInDays(new Date(item.reset_time), new Date())
: t('unknown')}
: t('noReset')}
</span>
</li>
<li>
<span className='text-muted-foreground'>{t('expirationDays')}</span>
<span className='text-2xl font-semibold'>
{}
{item.expire_time
? differenceInDays(new Date(item.expire_time), new Date()) || t('unknown')
: t('noLimit')}
+16 -9
View File
@@ -1,10 +1,12 @@
'use client';
import { Display } from '@/components/display';
import StripePayment from '@/components/payment/stripe';
import { SubscribeBilling } from '@/components/subscribe/billing';
import { SubscribeDetail } from '@/components/subscribe/detail';
import useGlobalStore from '@/config/use-global';
import { checkoutOrder, queryOrderDetail } from '@/services/user/order';
import { queryOrderDetail } from '@/services/user/order';
import { purchaseCheckout } from '@/services/user/portal';
import { useQuery } from '@tanstack/react-query';
import { Badge } from '@workspace/ui/components/badge';
import { Button } from '@workspace/ui/components/button';
@@ -24,7 +26,6 @@ import { useTranslations } from 'next-intl';
import Link from 'next/link';
import { QRCodeCanvas } from 'qrcode.react';
import { useEffect, useState } from 'react';
import StripePayment from './stripe';
export default function Page() {
const t = useTranslations('order');
@@ -48,9 +49,15 @@ export default function Page() {
const { data: payment } = useQuery({
enabled: !!orderNo && data?.status === 1,
queryKey: ['checkoutOrder', orderNo],
queryKey: ['purchaseCheckout', orderNo],
queryFn: async () => {
const { data } = await checkoutOrder({ orderNo: orderNo!, returnUrl: window.location.href });
const { data } = await purchaseCheckout({
orderNo: orderNo!,
returnUrl: window.location.href,
});
if (data.data?.type === 'url' && data.data.checkout_url) {
window.open(data.data.checkout_url, '_blank');
}
return data?.data;
},
});
@@ -99,7 +106,7 @@ export default function Page() {
<dl className='grid gap-3'>
<div className='flex items-center justify-between'>
<dt className='text-muted-foreground'>
{data?.method && <Badge>{t(`methods.${data?.method}`)}</Badge>}
<Badge>{data?.payment.name || data?.payment.platform}</Badge>
</dt>
</div>
</dl>
@@ -169,7 +176,7 @@ export default function Page() {
</div>
</div>
)}
{data?.status === 1 && payment?.type === 'link' && (
{data?.status === 1 && payment?.type === 'url' && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('waitingForPayment')}</h3>
<p className='flex items-center text-3xl font-bold'>{countdownDisplay}</p>
@@ -218,17 +225,17 @@ export default function Page() {
{data?.status === 1 && payment?.type === 'stripe' && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('scanToPay')}</h3>
<h3 className='text-2xl font-bold tracking-tight'>{t('waitingForPayment')}</h3>
<p className='flex items-center text-3xl font-bold'>{countdownDisplay}</p>
{payment.stripe && <StripePayment {...payment.stripe} />}
<div className='flex gap-4'>
{/* <div className='flex gap-4'>
<Button asChild>
<Link href='/subscribe'>{t('productList')}</Link>
</Button>
<Button asChild variant='outline'>
<Link href='/order'>{t('orderList')}</Link>
</Button>
</div>
</div> */}
</div>
)}
@@ -1,109 +0,0 @@
import { Elements, useStripe } from '@stripe/react-stripe-js';
import { loadStripe, PaymentIntentResult } from '@stripe/stripe-js';
import { QRCodeCanvas } from 'qrcode.react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
interface StripePaymentProps {
method: string;
client_secret: string;
publishable_key: string;
}
const StripePayment: React.FC<StripePaymentProps> = ({
method,
client_secret,
publishable_key,
}) => {
const stripePromise = useMemo(() => loadStripe(publishable_key), [publishable_key]);
return (
<Elements stripe={stripePromise}>
<CheckoutForm method={method} client_secret={client_secret} />
</Elements>
);
};
const CheckoutForm: React.FC<Omit<StripePaymentProps, 'publishable_key'>> = ({
client_secret,
method,
}) => {
const stripe = useStripe();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);
const [isSubmitted, setIsSubmitted] = useState(false);
const handleError = useCallback((message: string) => {
setErrorMessage(message);
setIsSubmitted(false);
}, []);
const confirmPayment = useCallback(async (): Promise<PaymentIntentResult | null> => {
if (!stripe) {
handleError('Stripe.js is not loaded.');
return null;
}
if (method === 'alipay') {
return await stripe.confirmAlipayPayment(
client_secret,
{ return_url: window.location.href },
{ handleActions: false },
);
}
return await stripe.confirmWechatPayPayment(
client_secret,
{
payment_method_options: { wechat_pay: { client: 'web' } },
},
{ handleActions: false },
);
}, [client_secret, method, stripe, handleError]);
const autoSubmit = useCallback(async () => {
if (isSubmitted) return;
setIsSubmitted(true);
try {
const result = await confirmPayment();
if (!result) return;
const { error, paymentIntent } = result;
if (error) return handleError(error.message!);
if (paymentIntent?.status === 'requires_action') {
const nextAction = paymentIntent.next_action as any;
const qrUrl =
method === 'alipay'
? nextAction?.alipay_handle_redirect?.url
: nextAction?.wechat_pay_display_qr_code?.image_url_svg;
setQrCodeUrl(qrUrl || null);
}
} catch (error) {
handleError('An unexpected error occurred');
}
}, [confirmPayment, isSubmitted, handleError, method]);
useEffect(() => {
autoSubmit();
}, [autoSubmit]);
return qrCodeUrl ? (
<QRCodeCanvas
value={qrCodeUrl}
size={208}
imageSettings={{
src: `/payment/${method}.svg`,
width: 24,
height: 24,
excavate: true,
}}
/>
) : (
errorMessage
);
};
export default StripePayment;
+2 -2
View File
@@ -21,11 +21,11 @@ export default function Page() {
<>
<Card className='mb-4'>
<CardContent className='p-6'>
<h2 className='text-foreground mb-4 text-2xl font-bold'>{t('totalAssets')}</h2>
<h2 className='text-foreground mb-4 text-2xl font-bold'>{t('assetOverview')}</h2>
<div className='mb-4'>
<div className='flex items-center justify-between'>
<div>
<p className='text-sm font-medium'></p>
<p className='text-sm font-medium'>{t('totalAssets')}</p>
<p className='text-3xl font-bold'>
<Display type='currency' value={totalAssets} />
</p>
+1 -1
View File
@@ -1,6 +1,6 @@
import { GlobalMap } from '@/components/main/global-map';
import { Hero } from '@/components/main/hero';
import { ProductShowcase } from '@/components/main/product-showcase';
import { ProductShowcase } from '@/components/main/product-showcase/index';
import { Stats } from '@/components/main/stats';
export default function Home() {
+224
View File
@@ -0,0 +1,224 @@
'use client';
import { SubscribeBilling } from '@/components/subscribe/billing';
import CouponInput from '@/components/subscribe/coupon-input';
import { SubscribeDetail } from '@/components/subscribe/detail';
import DurationSelector from '@/components/subscribe/duration-selector';
import PaymentMethods from '@/components/subscribe/payment-methods';
import useGlobalStore from '@/config/use-global';
import { prePurchaseOrder, purchase } from '@/services/user/portal';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import { Card, CardContent, CardHeader } from '@workspace/ui/components/card';
import { Separator } from '@workspace/ui/components/separator';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { cn } from '@workspace/ui/lib/utils';
import { LoaderCircle } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useState, useTransition } from 'react';
export default function Content({ subscription }: { subscription?: API.Subscribe }) {
const t = useTranslations('subscribe');
const { common } = useGlobalStore();
const router = useRouter();
const [params, setParams] = useState<API.PortalPurchaseRequest>({
quantity: 1,
subscribe_id: 0,
payment: -1,
coupon: '',
auth_type: 'email',
identifier: '',
password: '',
});
const [loading, startTransition] = useTransition();
const [isEmailValid, setIsEmailValid] = useState({
valid: false,
message: '',
});
const { data: order } = useQuery({
enabled: !!subscription?.id && !!params.payment,
queryKey: ['preCreateOrder', params.coupon, params.quantity, params.payment],
queryFn: async () => {
const { data } = await prePurchaseOrder({
...params,
subscribe_id: subscription?.id as number,
} as API.PrePurchaseOrderRequest);
return data.data;
},
});
useEffect(() => {
if (subscription) {
setParams((prev) => ({
...prev,
quantity: 1,
subscribe_id: subscription?.id,
}));
}
}, [subscription]);
const handleChange = useCallback((field: keyof typeof params, value: string | number) => {
setParams((prev) => ({
...prev,
[field]: value,
}));
}, []);
const handleSubmit = useCallback(async () => {
startTransition(async () => {
try {
const { data } = await purchase(params);
const { order_no } = data.data!;
if (order_no) {
localStorage.setItem(
order_no,
JSON.stringify({
auth_type: params.auth_type,
identifier: params.identifier,
}),
);
router.push(`/purchasing/order?order_no=${order_no}`);
}
} catch (error) {
console.log(error);
}
});
}, [params, router, subscription?.id]);
if (!subscription) {
return <div className='p-6 text-center'>{t('subscriptionNotFound')}</div>;
}
return (
<div className='mx-auto mt-8 flex max-w-4xl flex-col gap-8 md:grid md:grid-cols-2 md:flex-row'>
<div className='flex flex-col gap-6'>
<Card>
<CardHeader> {common.site.site_name} </CardHeader>
<CardContent className='flex flex-col gap-2'>
<div className='flex flex-col gap-2'>
<EnhancedInput
className={cn({
'border-destructive': !isEmailValid.valid && params.identifier !== '',
})}
placeholder='Email'
type='email'
value={params.identifier || ''}
onValueChange={(value) => {
const email = value as string;
setParams((prev) => ({
...prev,
identifier: email,
}));
const reg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!reg.test(email)) {
setIsEmailValid({
valid: false,
message: '请输入有效的邮箱地址',
});
} else if (common.auth.email.enable_domain_suffix) {
const domain = email.split('@')[1];
const isValid = common.auth.email?.domain_suffix_list
.split('\n')
.includes(domain || '');
if (!isValid) {
setIsEmailValid({
valid: false,
message: '邮箱域名不在白名单中',
});
return;
}
} else {
setIsEmailValid({
valid: true,
message: '',
});
}
}}
required
/>
<p
className={cn('text-muted-foreground text-xs', {
'text-destructive': !isEmailValid.valid && params.identifier !== '',
})}
>
{isEmailValid.message || '请填写您的电子邮件地址。'}
</p>
</div>
{params.identifier && isEmailValid.valid && (
<div className='flex flex-col gap-2'>
<EnhancedInput
placeholder='Password'
type='password'
value={params.password || ''}
onValueChange={(value) => handleChange('password', value)}
/>
<p className='text-muted-foreground text-xs'>
</p>
</div>
)}
{/* <div>
<OAuthMethods />
</div> */}
</CardContent>
</Card>
<Card>
<CardContent className='grid gap-3 p-6 text-sm'>
<h2 className='text-xl font-semibold'>{subscription.name}</h2>
<p className='text-muted-foreground'>{subscription.description}</p>
<SubscribeDetail
subscribe={{
...subscription,
quantity: params.quantity,
}}
/>
<Separator />
<SubscribeBilling
order={{
...order,
quantity: params.quantity,
unit_price: subscription?.unit_price,
}}
/>
</CardContent>
</Card>
</div>
<div className='flex flex-col gap-6'>
<Card>
<CardContent className='p-6'>
<div className='grid gap-6'>
<DurationSelector
quantity={params.quantity!}
unitTime={subscription?.unit_time}
discounts={subscription?.discount}
onChange={(value) => handleChange('quantity', value)}
/>
<CouponInput
coupon={params.coupon}
onChange={(value) => handleChange('coupon', value)}
/>
<PaymentMethods
balance={false}
value={params.payment!}
onChange={(value) => handleChange('payment', value)}
/>
</div>
</CardContent>
</Card>
<Button
className='w-full'
size='lg'
disabled={!isEmailValid.valid || loading}
onClick={handleSubmit}
>
{loading && <LoaderCircle className='mr-2 animate-spin' />}
{t('buyNow')}
</Button>
</div>
</div>
);
}
@@ -0,0 +1,269 @@
'use client';
import { Display } from '@/components/display';
import StripePayment from '@/components/payment/stripe';
import { SubscribeBilling } from '@/components/subscribe/billing';
import { SubscribeDetail } from '@/components/subscribe/detail';
import useGlobalStore from '@/config/use-global';
import { purchaseCheckout, queryPurchaseOrder } from '@/services/user/portal';
import { setAuthorization } from '@/utils/common';
import { useQuery } from '@tanstack/react-query';
import { Badge } from '@workspace/ui/components/badge';
import { Button } from '@workspace/ui/components/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@workspace/ui/components/card';
import { Separator } from '@workspace/ui/components/separator';
import { Icon } from '@workspace/ui/custom-components/icon';
import { formatDate } from '@workspace/ui/utils';
import { useCountDown } from 'ahooks';
import { addMinutes, format } from 'date-fns';
import { useTranslations } from 'next-intl';
import Link from 'next/link';
import { QRCodeCanvas } from 'qrcode.react';
import { useEffect, useState } from 'react';
export default function Page() {
const t = useTranslations('order');
const { getUserInfo } = useGlobalStore();
const [orderNo, setOrderNo] = useState<string>();
const [enabled, setEnabled] = useState<boolean>(false);
const { data } = useQuery({
enabled: enabled,
queryKey: ['queryPurchaseOrder', orderNo],
queryFn: async () => {
if (!orderNo) return;
const params = localStorage.getItem(orderNo);
const authParams = params ? JSON.parse(params) : {};
const { data } = await queryPurchaseOrder({ order_no: orderNo!, ...authParams });
if (data?.data?.status !== 1) {
setEnabled(false);
if (data?.data?.token) {
setAuthorization(data?.data?.token);
await new Promise((resolve) => setTimeout(resolve, 100));
await getUserInfo();
}
}
return data?.data;
},
refetchInterval: 3000,
});
const { data: payment } = useQuery({
enabled: !!orderNo && data?.status === 1,
queryKey: ['purchaseCheckout', orderNo],
queryFn: async () => {
const { data } = await purchaseCheckout({
orderNo: orderNo!,
returnUrl: window.location.href,
});
if (data.data?.type === 'url' && data.data?.checkout_url) {
window.open(data.data.checkout_url, '_blank');
}
return data?.data;
},
});
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
if (searchParams.get('order_no')) {
setOrderNo(searchParams.get('order_no')!);
setEnabled(true);
}
}, []);
const [countDown, formattedRes] = useCountDown({
targetDate: data && format(addMinutes(data?.created_at, 15), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"),
});
const { hours, minutes, seconds } = formattedRes;
const countdownDisplay =
countDown > 0 ? (
<>
{hours.toString().length === 1 ? `0${hours}` : hours} :{' '}
{minutes.toString().length === 1 ? `0${minutes}` : minutes} :{' '}
{seconds.toString().length === 1 ? `0${seconds}` : seconds}
</>
) : (
<>{t('timeExpired')}</>
);
return (
<main className='container lg:mt-16'>
<div className='grid gap-4 xl:grid-cols-2'>
<Card className='order-2 xl:order-1'>
<CardHeader className='bg-muted/50 flex flex-row items-start'>
<div className='grid gap-0.5'>
<CardTitle className='flex flex-col text-lg'>
{t('orderNumber')}
<span>{data?.order_no}</span>
</CardTitle>
<CardDescription>
{t('createdAt')}: {formatDate(data?.created_at)}
</CardDescription>
</div>
</CardHeader>
<CardContent className='grid gap-3 p-6 text-sm'>
<div className='font-semibold'>{t('paymentMethod')}</div>
<dl className='grid gap-3'>
<div className='flex items-center justify-between'>
<dt className='text-muted-foreground'>
<Badge>{data?.payment.name || data?.payment.platform}</Badge>
</dt>
</div>
</dl>
<Separator />
{data?.status && [1, 2].includes(data.status) && (
<SubscribeDetail
subscribe={{
...data?.subscribe,
quantity: data?.quantity,
}}
/>
)}
{data?.status === 3 && (
<>
<div className='font-semibold'>{t('resetTraffic')}</div>
<ul className='grid grid-cols-2 gap-3 *:flex *:items-center *:justify-between lg:grid-cols-1'>
<li className='flex items-center justify-between'>
<span className='text-muted-foreground line-clamp-2 flex-1'>
{t('resetPrice')}
</span>
<span>
<Display type='currency' value={data.amount} />
</span>
</li>
</ul>
</>
)}
{data?.status === 4 && (
<>
<div className='font-semibold'>{t('balanceRecharge')}</div>
<ul className='grid grid-cols-2 gap-3 *:flex *:items-center *:justify-between lg:grid-cols-1'>
<li className='flex items-center justify-between'>
<span className='text-muted-foreground line-clamp-2 flex-1'>
{t('rechargeAmount')}
</span>
<span>
<Display type='currency' value={data.amount} />
</span>
</li>
</ul>
</>
)}
<Separator />
<SubscribeBilling
order={{
...data,
unit_price: data?.subscribe?.unit_price,
}}
/>
</CardContent>
</Card>
<Card className='order-1 flex flex-auto items-center justify-center xl:order-2'>
<CardContent className='py-16'>
{data?.status && [2, 5].includes(data?.status) && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('paymentSuccess')}</h3>
<Icon icon='mdi:success-circle-outline' className='text-7xl text-green-500' />
<div className='flex gap-4'>
<Button asChild>
<Link href='/dashboard'>{t('subscribeNow')}</Link>
</Button>
<Button variant='outline'>
<Link href='/document'>{t('viewDocument')}</Link>
</Button>
</div>
</div>
)}
{data?.status === 1 && payment?.type === 'url' && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('waitingForPayment')}</h3>
<p className='flex items-center text-3xl font-bold'>{countdownDisplay}</p>
<Icon icon='mdi:access-time' className='text-muted-foreground text-7xl' />
<div className='flex gap-4'>
<Button
onClick={() => {
if (payment?.checkout_url) {
window.location.href = payment?.checkout_url;
}
}}
>
{t('goToPayment')}
</Button>
<Button variant='outline'>
<Link href='/'>{t('productList')}</Link>
</Button>
</div>
</div>
)}
{data?.status === 1 && payment?.type === 'qr' && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('scanToPay')}</h3>
<p className='flex items-center text-3xl font-bold'>{countdownDisplay}</p>
<QRCodeCanvas
value={payment?.checkout_url || ''}
size={208}
imageSettings={{
src: `/payment/alipay_f2f.svg`,
width: 24,
height: 24,
excavate: true,
}}
/>
<div className='flex gap-4'>
<Button asChild>
<Link href='/subscribe'>{t('productList')}</Link>
</Button>
<Button asChild variant='outline'>
<Link href='/order'>{t('orderList')}</Link>
</Button>
</div>
</div>
)}
{data?.status === 1 && payment?.type === 'stripe' && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('waitingForPayment')}</h3>
<p className='flex items-center text-3xl font-bold'>{countdownDisplay}</p>
{payment.stripe && <StripePayment {...payment.stripe} />}
{/* <div className='flex gap-4'>
<Button asChild>
<Link href='/subscribe'>{t('productList')}</Link>
</Button>
<Button asChild variant='outline'>
<Link href='/order'>{t('orderList')}</Link>
</Button>
</div> */}
</div>
)}
{data?.status && [3, 4].includes(data?.status) && (
<div className='flex flex-col items-center gap-8 text-center'>
<h3 className='text-2xl font-bold tracking-tight'>{t('orderClosed')}</h3>
<Icon icon='mdi:cancel' className='text-7xl text-red-500' />
<div className='flex gap-4'>
<Button asChild>
<Link href='/subscribe'>{t('productList')}</Link>
</Button>
<Button asChild variant='outline'>
<Link href='/order'>{t('orderList')}</Link>
</Button>
</div>
</div>
)}
</CardContent>
</Card>
</div>
</main>
);
}

Some files were not shown because too many files have changed in this diff Show More