@easypayment/medusa-paypal 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.medusa/server/src/admin/index.js +7 -7
  2. package/.medusa/server/src/admin/index.mjs +7 -7
  3. package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -1
  4. package/.medusa/server/src/api/store/paypal/create-order/route.js +10 -3
  5. package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -1
  6. package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.js +22 -22
  7. package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.js +11 -11
  8. package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.js +18 -18
  9. package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.js +16 -16
  10. package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.js +20 -20
  11. package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js +14 -14
  12. package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.js +15 -15
  13. package/README.md +142 -142
  14. package/package.json +75 -75
  15. package/src/admin/index.ts +7 -7
  16. package/src/admin/routes/settings/paypal/_components/Tabs.tsx +52 -52
  17. package/src/admin/routes/settings/paypal/_components/Toast.tsx +51 -51
  18. package/src/admin/routes/settings/paypal/additional-settings/page.tsx +200 -200
  19. package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +183 -183
  20. package/src/admin/routes/settings/paypal/apple-pay/page.tsx +5 -5
  21. package/src/admin/routes/settings/paypal/connection/page.tsx +754 -754
  22. package/src/admin/routes/settings/paypal/google-pay/page.tsx +5 -5
  23. package/src/admin/routes/settings/paypal/pay-later-messaging/page.tsx +5 -5
  24. package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +376 -376
  25. package/src/api/admin/payment-collections/[id]/payment-sessions/route.ts +24 -24
  26. package/src/api/admin/paypal/disconnect/route.ts +8 -8
  27. package/src/api/admin/paypal/environment/route.ts +25 -25
  28. package/src/api/admin/paypal/onboard-complete/route.ts +44 -44
  29. package/src/api/admin/paypal/onboarding-link/route.ts +45 -45
  30. package/src/api/admin/paypal/onboarding-status/route.ts +18 -18
  31. package/src/api/admin/paypal/rotate-credentials/route.ts +8 -8
  32. package/src/api/admin/paypal/save-credentials/route.ts +14 -14
  33. package/src/api/admin/paypal/settings/route.ts +14 -14
  34. package/src/api/admin/paypal/status/route.ts +12 -12
  35. package/src/api/store/payment-collections/[id]/payment-sessions/route.ts +65 -65
  36. package/src/api/store/paypal/capture-order/route.ts +276 -276
  37. package/src/api/store/paypal/config/route.ts +102 -102
  38. package/src/api/store/paypal/create-order/route.ts +13 -3
  39. package/src/api/store/paypal/settings/route.ts +19 -19
  40. package/src/api/store/paypal/webhook/route.ts +246 -246
  41. package/src/api/store/paypal-complete/route.ts +75 -75
  42. package/src/jobs/paypal-reconcile.ts +112 -112
  43. package/src/jobs/paypal-webhook-retry.ts +85 -85
  44. package/src/modules/paypal/clients/paypal-seller.client.ts +59 -59
  45. package/src/modules/paypal/index.ts +8 -8
  46. package/src/modules/paypal/migrations/20260115120000_create_paypal_connection.ts +33 -33
  47. package/src/modules/paypal/migrations/20260123090000_create_paypal_settings.ts +22 -22
  48. package/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.ts +29 -29
  49. package/src/modules/paypal/migrations/20260401090000_create_paypal_metric.ts +27 -27
  50. package/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.ts +31 -31
  51. package/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.ts +25 -25
  52. package/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.ts +26 -26
  53. package/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.ts +11 -11
  54. package/src/modules/paypal/models/paypal_connection.ts +21 -21
  55. package/src/modules/paypal/models/paypal_metric.ts +9 -9
  56. package/src/modules/paypal/models/paypal_settings.ts +8 -8
  57. package/src/modules/paypal/models/paypal_webhook_event.ts +19 -19
  58. package/src/modules/paypal/payment-provider/README.md +22 -22
  59. package/src/modules/paypal/payment-provider/card-service.ts +760 -760
  60. package/src/modules/paypal/payment-provider/index.ts +19 -19
  61. package/src/modules/paypal/payment-provider/service.ts +1121 -1121
  62. package/src/modules/paypal/payment-provider/webhook-utils.ts +88 -88
  63. package/src/modules/paypal/service.ts +1247 -1247
  64. package/src/modules/paypal/types/config.ts +47 -47
  65. package/src/modules/paypal/utils/amounts.ts +41 -41
  66. package/src/modules/paypal/utils/crypto.ts +51 -51
  67. package/src/modules/paypal/utils/currencies.ts +84 -84
  68. package/src/modules/paypal/utils/paypal-auth.ts +32 -32
  69. package/src/modules/paypal/utils/provider-ids.ts +15 -15
  70. package/src/modules/paypal/webhook-processor.ts +215 -215
package/README.md CHANGED
@@ -1,142 +1,142 @@
1
- # @easypayment/medusa-paypal
2
-
3
- Production-ready PayPal integration for **Medusa v2** with:
4
-
5
- - PayPal Wallet provider
6
- - PayPal Advanced Card provider
7
- - Admin settings pages (connection, wallet settings, advanced card settings, additional checkout behavior)
8
- - Store API routes used by storefront integrations
9
-
10
- ---
11
-
12
- ## Compatibility
13
-
14
- - Medusa: `^2.12.0`
15
- - Node.js: `>=20`
16
-
17
- ---
18
-
19
- ## Step-by-step setup (GitHub style)
20
-
21
- ### 1) Install the package
22
-
23
- ```bash
24
- npm install @easypayment/medusa-paypal
25
- # or
26
- pnpm add @easypayment/medusa-paypal
27
- # or
28
- yarn add @easypayment/medusa-paypal
29
- ```
30
-
31
- ### 2) Register plugin + providers in `medusa-config.ts`
32
-
33
- Add the plugin and both payment providers:
34
-
35
- ```ts
36
- module.exports = defineConfig({
37
- plugins: [
38
- {
39
- resolve: "@easypayment/medusa-paypal",
40
- options: {},
41
- },
42
- ],
43
-
44
- modules: [
45
- {
46
- resolve: "@medusajs/medusa/payment",
47
- options: {
48
- providers: [
49
- {
50
- resolve: "@easypayment/medusa-paypal/providers/paypal",
51
- id: "paypal",
52
- },
53
- {
54
- resolve: "@easypayment/medusa-paypal/providers/paypal_card",
55
- id: "paypal_card",
56
- },
57
- ],
58
- },
59
- },
60
- ],
61
- })
62
- ```
63
-
64
- ### 3) Add backend environment variables
65
-
66
- Set these in your backend `.env`:
67
-
68
- ```bash
69
- # required for API + onboarding defaults
70
- PAYPAL_ENV=sandbox
71
- PAYPAL_CURRENCY=EUR
72
-
73
- # optional but recommended
74
- STOREFRONT_URL=https://your-storefront.example.com
75
- PAYPAL_CREDENTIALS_ENCRYPTION_KEY=your-32-byte-or-strong-secret
76
- ```
77
-
78
- > Tip: PayPal API credentials are managed from the Medusa Admin PayPal settings UI.
79
-
80
- ### 4) Start backend and open Admin
81
-
82
- - Run your Medusa backend.
83
- - Open Medusa Admin.
84
- - Go to the PayPal settings pages.
85
-
86
- ### 5) Connect PayPal and configure checkout behavior
87
-
88
- Use the Admin UI to set credentials and runtime behavior.
89
-
90
- Runtime-relevant settings include:
91
-
92
- - **PayPal Settings**
93
- - `enabled`
94
- - `title`
95
- - `disableButtons`
96
- - `buttonColor`, `buttonShape`, `buttonWidth`, `buttonHeight`, `buttonLabel`
97
- - **Advanced Card Payments**
98
- - `enabled`
99
- - `title`
100
- - `threeDS`
101
- - **Additional Settings**
102
- - `paymentAction` (`capture` or `authorize`)
103
- - `brandName`
104
- - `landingPage`
105
- - `requireInstantPayment`
106
- - `sendItemDetails`
107
- - `invoicePrefix`
108
- - `creditCardStatementName`
109
-
110
- ### 6) Use the generated provider IDs in your storefront
111
-
112
- With the sample config above, Medusa provider IDs are:
113
-
114
- - `pp_paypal_paypal`
115
- - `pp_paypal_card_paypal_card`
116
-
117
- Your storefront package should use these exact IDs unless you customize provider IDs.
118
-
119
- ---
120
-
121
- ## Quick checklist
122
-
123
- - [ ] Package installed
124
- - [ ] `medusa-config.ts` updated (plugin + both providers)
125
- - [ ] Required env vars set
126
- - [ ] PayPal credentials configured in Admin
127
- - [ ] Storefront uses correct provider IDs
128
-
129
- ---
130
-
131
- ## Build and publish
132
-
133
- ```bash
134
- npm run build
135
- npm publish --access public
136
- ```
137
-
138
- ---
139
-
140
- ## License
141
-
142
- MIT
1
+ # @easypayment/medusa-paypal
2
+
3
+ Production-ready PayPal integration for **Medusa v2** with:
4
+
5
+ - PayPal Wallet provider
6
+ - PayPal Advanced Card provider
7
+ - Admin settings pages (connection, wallet settings, advanced card settings, additional checkout behavior)
8
+ - Store API routes used by storefront integrations
9
+
10
+ ---
11
+
12
+ ## Compatibility
13
+
14
+ - Medusa: `^2.12.0`
15
+ - Node.js: `>=20`
16
+
17
+ ---
18
+
19
+ ## Step-by-step setup (GitHub style)
20
+
21
+ ### 1) Install the package
22
+
23
+ ```bash
24
+ npm install @easypayment/medusa-paypal
25
+ # or
26
+ pnpm add @easypayment/medusa-paypal
27
+ # or
28
+ yarn add @easypayment/medusa-paypal
29
+ ```
30
+
31
+ ### 2) Register plugin + providers in `medusa-config.ts`
32
+
33
+ Add the plugin and both payment providers:
34
+
35
+ ```ts
36
+ module.exports = defineConfig({
37
+ plugins: [
38
+ {
39
+ resolve: "@easypayment/medusa-paypal",
40
+ options: {},
41
+ },
42
+ ],
43
+
44
+ modules: [
45
+ {
46
+ resolve: "@medusajs/medusa/payment",
47
+ options: {
48
+ providers: [
49
+ {
50
+ resolve: "@easypayment/medusa-paypal/providers/paypal",
51
+ id: "paypal",
52
+ },
53
+ {
54
+ resolve: "@easypayment/medusa-paypal/providers/paypal_card",
55
+ id: "paypal_card",
56
+ },
57
+ ],
58
+ },
59
+ },
60
+ ],
61
+ })
62
+ ```
63
+
64
+ ### 3) Add backend environment variables
65
+
66
+ Set these in your backend `.env`:
67
+
68
+ ```bash
69
+ # required for API + onboarding defaults
70
+ PAYPAL_ENV=sandbox
71
+ PAYPAL_CURRENCY=EUR
72
+
73
+ # optional but recommended
74
+ STOREFRONT_URL=https://your-storefront.example.com
75
+ PAYPAL_CREDENTIALS_ENCRYPTION_KEY=your-32-byte-or-strong-secret
76
+ ```
77
+
78
+ > Tip: PayPal API credentials are managed from the Medusa Admin PayPal settings UI.
79
+
80
+ ### 4) Start backend and open Admin
81
+
82
+ - Run your Medusa backend.
83
+ - Open Medusa Admin.
84
+ - Go to the PayPal settings pages.
85
+
86
+ ### 5) Connect PayPal and configure checkout behavior
87
+
88
+ Use the Admin UI to set credentials and runtime behavior.
89
+
90
+ Runtime-relevant settings include:
91
+
92
+ - **PayPal Settings**
93
+ - `enabled`
94
+ - `title`
95
+ - `disableButtons`
96
+ - `buttonColor`, `buttonShape`, `buttonWidth`, `buttonHeight`, `buttonLabel`
97
+ - **Advanced Card Payments**
98
+ - `enabled`
99
+ - `title`
100
+ - `threeDS`
101
+ - **Additional Settings**
102
+ - `paymentAction` (`capture` or `authorize`)
103
+ - `brandName`
104
+ - `landingPage`
105
+ - `requireInstantPayment`
106
+ - `sendItemDetails`
107
+ - `invoicePrefix`
108
+ - `creditCardStatementName`
109
+
110
+ ### 6) Use the generated provider IDs in your storefront
111
+
112
+ With the sample config above, Medusa provider IDs are:
113
+
114
+ - `pp_paypal_paypal`
115
+ - `pp_paypal_card_paypal_card`
116
+
117
+ Your storefront package should use these exact IDs unless you customize provider IDs.
118
+
119
+ ---
120
+
121
+ ## Quick checklist
122
+
123
+ - [ ] Package installed
124
+ - [ ] `medusa-config.ts` updated (plugin + both providers)
125
+ - [ ] Required env vars set
126
+ - [ ] PayPal credentials configured in Admin
127
+ - [ ] Storefront uses correct provider IDs
128
+
129
+ ---
130
+
131
+ ## Build and publish
132
+
133
+ ```bash
134
+ npm run build
135
+ npm publish --access public
136
+ ```
137
+
138
+ ---
139
+
140
+ ## License
141
+
142
+ MIT
package/package.json CHANGED
@@ -1,75 +1,75 @@
1
- {
2
- "name": "@easypayment/medusa-paypal",
3
- "version": "0.4.6",
4
- "description": "Industry-standard PayPal integration for Medusa v2",
5
- "license": "MIT",
6
- "main": "./.medusa/server/src/index.js",
7
- "types": "./.medusa/server/src/index.d.ts",
8
- "files": [
9
- ".medusa/server",
10
- "src",
11
- "scripts",
12
- "package.json",
13
- "README.md",
14
- "LICENSE",
15
- "tsconfig.json"
16
- ],
17
- "exports": {
18
- "./package.json": "./package.json",
19
- "./admin": "./.medusa/server/src/admin/index.mjs",
20
- "./workflows": "./.medusa/server/src/workflows/index.js",
21
- "./providers/paypal": "./.medusa/server/src/providers/paypal/index.js",
22
- "./providers/paypal_card": "./.medusa/server/src/providers/paypal_card/index.js",
23
- "./modules/paypal": "./.medusa/server/src/modules/paypal/index.js",
24
- ".": "./.medusa/server/src/index.js",
25
- "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
26
- "./.medusa/server/src/modules/paypal": "./.medusa/server/src/modules/paypal/index.js",
27
- "./.medusa/server/src/providers/*": "./.medusa/server/src/providers/*/index.js",
28
- "./.medusa/server/src/providers/paypal": "./.medusa/server/src/providers/paypal/index.js",
29
- "./.medusa/server/src/providers/paypal_card": "./.medusa/server/src/providers/paypal_card/index.js",
30
- "./.medusa/server/src/admin": "./.medusa/server/src/admin/index.mjs",
31
- "./.medusa/server/src/workflows": "./.medusa/server/src/workflows/index.js",
32
- "./.medusa/server/src/*": "./.medusa/server/src/*.js",
33
- "./.medusa/*": "./.medusa/*"
34
- },
35
- "scripts": {
36
- "build": "medusa plugin:build",
37
- "develop": "medusa plugin:develop",
38
- "prepublishOnly": "npm run build"
39
- },
40
- "devDependencies": {
41
- "@medusajs/admin-sdk": "2.12.5",
42
- "@medusajs/cli": "2.12.5",
43
- "@medusajs/framework": "2.12.5",
44
- "@medusajs/medusa": "2.12.5",
45
- "@medusajs/test-utils": "2.12.5",
46
- "@medusajs/ui": "^4.0.0",
47
- "@medusajs/icons": "2.12.5",
48
- "@swc/core": "^1.5.7",
49
- "typescript": "^5.6.2"
50
- },
51
- "peerDependencies": {
52
- "@medusajs/admin-sdk": "^2.12.0",
53
- "@medusajs/framework": "^2.12.0",
54
- "@medusajs/medusa": "^2.12.0",
55
- "@medusajs/ui": "^4.0.0",
56
- "@medusajs/icons": "^2.12.0"
57
- },
58
- "publishConfig": {
59
- "access": "public"
60
- },
61
- "engines": {
62
- "node": ">=20"
63
- },
64
- "repository": {
65
- "type": "git",
66
- "url": "https://github.com/easypayment/medusa-paypal.git"
67
- },
68
- "keywords": [
69
- "medusa",
70
- "paypal",
71
- "payment",
72
- "ecommerce",
73
- "plugin"
74
- ]
75
- }
1
+ {
2
+ "name": "@easypayment/medusa-paypal",
3
+ "version": "0.4.8",
4
+ "description": "Industry-standard PayPal integration for Medusa v2",
5
+ "license": "MIT",
6
+ "main": "./.medusa/server/src/index.js",
7
+ "types": "./.medusa/server/src/index.d.ts",
8
+ "files": [
9
+ ".medusa/server",
10
+ "src",
11
+ "scripts",
12
+ "package.json",
13
+ "README.md",
14
+ "LICENSE",
15
+ "tsconfig.json"
16
+ ],
17
+ "exports": {
18
+ "./package.json": "./package.json",
19
+ "./admin": "./.medusa/server/src/admin/index.mjs",
20
+ "./workflows": "./.medusa/server/src/workflows/index.js",
21
+ "./providers/paypal": "./.medusa/server/src/providers/paypal/index.js",
22
+ "./providers/paypal_card": "./.medusa/server/src/providers/paypal_card/index.js",
23
+ "./modules/paypal": "./.medusa/server/src/modules/paypal/index.js",
24
+ ".": "./.medusa/server/src/index.js",
25
+ "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
26
+ "./.medusa/server/src/modules/paypal": "./.medusa/server/src/modules/paypal/index.js",
27
+ "./.medusa/server/src/providers/*": "./.medusa/server/src/providers/*/index.js",
28
+ "./.medusa/server/src/providers/paypal": "./.medusa/server/src/providers/paypal/index.js",
29
+ "./.medusa/server/src/providers/paypal_card": "./.medusa/server/src/providers/paypal_card/index.js",
30
+ "./.medusa/server/src/admin": "./.medusa/server/src/admin/index.mjs",
31
+ "./.medusa/server/src/workflows": "./.medusa/server/src/workflows/index.js",
32
+ "./.medusa/server/src/*": "./.medusa/server/src/*.js",
33
+ "./.medusa/*": "./.medusa/*"
34
+ },
35
+ "scripts": {
36
+ "build": "medusa plugin:build",
37
+ "develop": "medusa plugin:develop",
38
+ "prepublishOnly": "npm run build"
39
+ },
40
+ "devDependencies": {
41
+ "@medusajs/admin-sdk": "2.12.5",
42
+ "@medusajs/cli": "2.12.5",
43
+ "@medusajs/framework": "2.12.5",
44
+ "@medusajs/medusa": "2.12.5",
45
+ "@medusajs/test-utils": "2.12.5",
46
+ "@medusajs/ui": "^4.0.0",
47
+ "@medusajs/icons": "2.12.5",
48
+ "@swc/core": "^1.5.7",
49
+ "typescript": "^5.6.2"
50
+ },
51
+ "peerDependencies": {
52
+ "@medusajs/admin-sdk": "^2.12.0",
53
+ "@medusajs/framework": "^2.12.0",
54
+ "@medusajs/medusa": "^2.12.0",
55
+ "@medusajs/ui": "^4.0.0",
56
+ "@medusajs/icons": "^2.12.0"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "engines": {
62
+ "node": ">=20"
63
+ },
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "https://github.com/easypayment/medusa-paypal.git"
67
+ },
68
+ "keywords": [
69
+ "medusa",
70
+ "paypal",
71
+ "payment",
72
+ "ecommerce",
73
+ "plugin"
74
+ ]
75
+ }
@@ -1,7 +1,7 @@
1
- import { definePlugin } from "@medusajs/admin-sdk"
2
- import paypalRoute from "./routes/settings/paypal/page"
3
-
4
- export default definePlugin({
5
- id: "paypal-backend",
6
- routes: [paypalRoute],
7
- })
1
+ import { definePlugin } from "@medusajs/admin-sdk"
2
+ import paypalRoute from "./routes/settings/paypal/page"
3
+
4
+ export default definePlugin({
5
+ id: "paypal-backend",
6
+ routes: [paypalRoute],
7
+ })
@@ -1,52 +1,52 @@
1
- import React from "react"
2
- import { Link, useLocation } from "react-router-dom"
3
-
4
- type Tab = {
5
- label: string
6
- to: string
7
- }
8
-
9
- const BASE = "/settings/paypal"
10
-
11
- const TABS: Tab[] = [
12
- { label: "PayPal Connection", to: `${BASE}/connection` },
13
- { label: "PayPal Settings", to: `${BASE}/paypal-settings` },
14
- { label: "Advanced Card Payments", to: `${BASE}/advanced-card-payments` },
15
- /* { label: "Google Pay", to: `${BASE}/google-pay` },
16
- { label: "Apple Pay", to: `${BASE}/apple-pay` },
17
- { label: "Pay Later Messaging", to: `${BASE}/pay-later-messaging` }, */
18
- { label: "Additional Settings", to: `${BASE}/additional-settings` },
19
- ]
20
-
21
- function isActive(pathname: string, to: string) {
22
- // exact match OR nested route under same tab
23
- return pathname === to || pathname.startsWith(to + "/")
24
- }
25
-
26
- export default function PayPalTabs() {
27
- const { pathname } = useLocation()
28
-
29
- return (
30
- <div className="border-b border-ui-border-base">
31
- <div className="flex flex-wrap gap-6 text-sm">
32
- {TABS.map((t) => {
33
- const active = isActive(pathname, t.to)
34
-
35
- return (
36
- <Link
37
- key={t.to}
38
- to={t.to}
39
- className={
40
- active
41
- ? "border-b-2 border-ui-fg-base pb-2 font-medium text-ui-fg-base"
42
- : "pb-2 text-ui-fg-subtle hover:text-ui-fg-base"
43
- }
44
- >
45
- {t.label}
46
- </Link>
47
- )
48
- })}
49
- </div>
50
- </div>
51
- )
52
- }
1
+ import React from "react"
2
+ import { Link, useLocation } from "react-router-dom"
3
+
4
+ type Tab = {
5
+ label: string
6
+ to: string
7
+ }
8
+
9
+ const BASE = "/settings/paypal"
10
+
11
+ const TABS: Tab[] = [
12
+ { label: "PayPal Connection", to: `${BASE}/connection` },
13
+ { label: "PayPal Settings", to: `${BASE}/paypal-settings` },
14
+ { label: "Advanced Card Payments", to: `${BASE}/advanced-card-payments` },
15
+ /* { label: "Google Pay", to: `${BASE}/google-pay` },
16
+ { label: "Apple Pay", to: `${BASE}/apple-pay` },
17
+ { label: "Pay Later Messaging", to: `${BASE}/pay-later-messaging` }, */
18
+ { label: "Additional Settings", to: `${BASE}/additional-settings` },
19
+ ]
20
+
21
+ function isActive(pathname: string, to: string) {
22
+ // exact match OR nested route under same tab
23
+ return pathname === to || pathname.startsWith(to + "/")
24
+ }
25
+
26
+ export default function PayPalTabs() {
27
+ const { pathname } = useLocation()
28
+
29
+ return (
30
+ <div className="border-b border-ui-border-base">
31
+ <div className="flex flex-wrap gap-6 text-sm">
32
+ {TABS.map((t) => {
33
+ const active = isActive(pathname, t.to)
34
+
35
+ return (
36
+ <Link
37
+ key={t.to}
38
+ to={t.to}
39
+ className={
40
+ active
41
+ ? "border-b-2 border-ui-fg-base pb-2 font-medium text-ui-fg-base"
42
+ : "pb-2 text-ui-fg-subtle hover:text-ui-fg-base"
43
+ }
44
+ >
45
+ {t.label}
46
+ </Link>
47
+ )
48
+ })}
49
+ </div>
50
+ </div>
51
+ )
52
+ }
@@ -1,51 +1,51 @@
1
- import React, { useEffect } from "react"
2
-
3
- export type ToastKind = "success" | "error"
4
-
5
- export type ToastState = {
6
- kind: ToastKind
7
- message: string
8
- } | null
9
-
10
- type Props = {
11
- toast: ToastState
12
- onClose: () => void
13
- }
14
-
15
- export default function Toast({ toast, onClose }: Props) {
16
- useEffect(() => {
17
- if (!toast) return
18
- const t = setTimeout(() => onClose(), 2500)
19
- return () => clearTimeout(t)
20
- }, [toast, onClose])
21
-
22
- if (!toast) return null
23
-
24
- const isSuccess = toast.kind === "success"
25
-
26
- return (
27
- <div className="fixed right-6 top-6 z-[9999]">
28
- <div
29
- className={[
30
- "min-w-[280px] max-w-[420px] rounded-lg border px-4 py-3 shadow-md",
31
- isSuccess ? "border-emerald-500/30 bg-emerald-500/10" : "border-rose-500/30 bg-rose-500/10",
32
- ].join(" ")}
33
- role="status"
34
- aria-live="polite"
35
- >
36
- <div className="flex items-start gap-3">
37
- <div className={["mt-0.5 h-2.5 w-2.5 rounded-full", isSuccess ? "bg-emerald-500" : "bg-rose-500"].join(" ")} />
38
- <div className="flex-1 text-sm text-ui-fg-base">{toast.message}</div>
39
- <button
40
- type="button"
41
- onClick={onClose}
42
- className="text-ui-fg-subtle hover:text-ui-fg-base"
43
- aria-label="Close"
44
- >
45
- ×
46
- </button>
47
- </div>
48
- </div>
49
- </div>
50
- )
51
- }
1
+ import React, { useEffect } from "react"
2
+
3
+ export type ToastKind = "success" | "error"
4
+
5
+ export type ToastState = {
6
+ kind: ToastKind
7
+ message: string
8
+ } | null
9
+
10
+ type Props = {
11
+ toast: ToastState
12
+ onClose: () => void
13
+ }
14
+
15
+ export default function Toast({ toast, onClose }: Props) {
16
+ useEffect(() => {
17
+ if (!toast) return
18
+ const t = setTimeout(() => onClose(), 2500)
19
+ return () => clearTimeout(t)
20
+ }, [toast, onClose])
21
+
22
+ if (!toast) return null
23
+
24
+ const isSuccess = toast.kind === "success"
25
+
26
+ return (
27
+ <div className="fixed right-6 top-6 z-[9999]">
28
+ <div
29
+ className={[
30
+ "min-w-[280px] max-w-[420px] rounded-lg border px-4 py-3 shadow-md",
31
+ isSuccess ? "border-emerald-500/30 bg-emerald-500/10" : "border-rose-500/30 bg-rose-500/10",
32
+ ].join(" ")}
33
+ role="status"
34
+ aria-live="polite"
35
+ >
36
+ <div className="flex items-start gap-3">
37
+ <div className={["mt-0.5 h-2.5 w-2.5 rounded-full", isSuccess ? "bg-emerald-500" : "bg-rose-500"].join(" ")} />
38
+ <div className="flex-1 text-sm text-ui-fg-base">{toast.message}</div>
39
+ <button
40
+ type="button"
41
+ onClick={onClose}
42
+ className="text-ui-fg-subtle hover:text-ui-fg-base"
43
+ aria-label="Close"
44
+ >
45
+ ×
46
+ </button>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ )
51
+ }