@easypayment/medusa-payment-paypal 0.9.29 → 0.9.31
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.
- package/.medusa/server/src/admin/index.js +15 -13
- package/.medusa/server/src/admin/index.mjs +15 -13
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +24 -4
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/paypal/capture-order/route.js +4 -2
- package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/webhook/route.js +1 -1
- package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270301000000_add_webhook_transmission_id_index.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20270301000000_add_webhook_transmission_id_index.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270301000000_add_webhook_transmission_id_index.js +14 -0
- package/.medusa/server/src/modules/paypal/migrations/20270301000000_add_webhook_transmission_id_index.js.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +9 -35
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts +2 -5
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/service.js +14 -66
- package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/service.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/service.js +41 -22
- package/.medusa/server/src/modules/paypal/service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/amounts.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/utils/amounts.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/amounts.js +17 -6
- package/.medusa/server/src/modules/paypal/utils/amounts.js.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/credential-resolver.d.ts +42 -0
- package/.medusa/server/src/modules/paypal/utils/credential-resolver.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/credential-resolver.js +186 -0
- package/.medusa/server/src/modules/paypal/utils/credential-resolver.js.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/payment-session.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/payment-session.js +7 -1
- package/.medusa/server/src/modules/paypal/utils/payment-session.js.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/paypal-fetch.d.ts +3 -11
- package/.medusa/server/src/modules/paypal/utils/paypal-fetch.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/paypal-fetch.js +119 -18
- package/.medusa/server/src/modules/paypal/utils/paypal-fetch.js.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/secret-crypto.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/secret-crypto.js +6 -0
- package/.medusa/server/src/modules/paypal/utils/secret-crypto.js.map +1 -1
- package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/webhook-processor.js +10 -9
- package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -1
- package/package.json +15 -8
- package/src/admin/routes/settings/paypal/connection/page.tsx +6 -4
- package/src/api/admin/paypal/onboarding-link/route.ts +30 -5
- package/src/api/store/paypal/capture-order/route.ts +4 -2
- package/src/api/store/paypal/webhook/route.ts +1 -1
- package/src/modules/paypal/migrations/20270301000000_add_webhook_transmission_id_index.ts +15 -0
- package/src/modules/paypal/payment-provider/card-service.ts +9 -40
- package/src/modules/paypal/payment-provider/service.ts +18 -68
- package/src/modules/paypal/service.ts +43 -23
- package/src/modules/paypal/utils/amounts.ts +17 -6
- package/src/modules/paypal/utils/credential-resolver.ts +257 -0
- package/src/modules/paypal/utils/payment-session.ts +11 -2
- package/src/modules/paypal/utils/paypal-fetch.ts +139 -17
- package/src/modules/paypal/utils/secret-crypto.ts +5 -0
- package/src/modules/paypal/webhook-processor.ts +15 -10
- package/.medusa/server/src/admin/index.d.ts +0 -2
- package/.medusa/server/src/admin/index.d.ts.map +0 -1
- package/.medusa/server/src/admin/index.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.d.ts +0 -8
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.js +0 -8
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.d.ts +0 -8
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.js +0 -8
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.js +0 -25
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.d.ts +0 -12
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.js +0 -22
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.d.ts +0 -7
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.js +0 -40
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.js +0 -76
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.js +0 -81
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.js +0 -9
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts +0 -22
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +0 -595
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.js +0 -9
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/page.d.ts +0 -4
- package/.medusa/server/src/admin/routes/settings/paypal/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/page.js +0 -14
- package/.medusa/server/src/admin/routes/settings/paypal/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.js +0 -9
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.js.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.d.ts +0 -2
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.d.ts.map +0 -1
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.js +0 -109
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.js.map +0 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { paypalFetch } from "./paypal-fetch"
|
|
2
|
+
import { decryptSecret, encryptSecret } from "./secret-crypto"
|
|
3
|
+
|
|
4
|
+
const BN_CODE = "MBJTechnolabs_SI_SPB"
|
|
5
|
+
const TOKEN_MARGIN_MS = 2 * 60 * 1000
|
|
6
|
+
|
|
7
|
+
type Environment = "sandbox" | "live"
|
|
8
|
+
|
|
9
|
+
interface ActiveCredentials {
|
|
10
|
+
environment: Environment
|
|
11
|
+
client_id: string
|
|
12
|
+
client_secret: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface AccessTokenResult {
|
|
16
|
+
accessToken: string
|
|
17
|
+
base: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ResolvedSettings {
|
|
21
|
+
additionalSettings: Record<string, unknown>
|
|
22
|
+
advancedCardSettings: Record<string, unknown>
|
|
23
|
+
apiDetails: Record<string, unknown>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Self-contained credential and token management for PayPal payment providers.
|
|
28
|
+
*
|
|
29
|
+
* Payment providers in Medusa v2 run inside the payment module's scoped
|
|
30
|
+
* container, which is isolated from the application container where the
|
|
31
|
+
* PayPal module service lives. This resolver bypasses the container boundary
|
|
32
|
+
* entirely by reading credentials directly from the database via pgConnection
|
|
33
|
+
* (knex), which Medusa injects into every module container.
|
|
34
|
+
*
|
|
35
|
+
* Same source of truth (paypal_connection / paypal_settings tables), zero
|
|
36
|
+
* cross-container dependencies.
|
|
37
|
+
*/
|
|
38
|
+
export class PayPalCredentialResolver {
|
|
39
|
+
private db: any
|
|
40
|
+
private tokenRefreshPromise: Promise<string> | null = null
|
|
41
|
+
|
|
42
|
+
constructor(pgConnection: any) {
|
|
43
|
+
this.db = pgConnection
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private async getConnectionRow(): Promise<any | null> {
|
|
47
|
+
const rows = await this.db("paypal_connection")
|
|
48
|
+
.select("*")
|
|
49
|
+
.orderBy("created_at", "desc")
|
|
50
|
+
.limit(1)
|
|
51
|
+
|
|
52
|
+
return rows?.[0] ?? null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private getEnvCreds(
|
|
56
|
+
row: any,
|
|
57
|
+
env: Environment
|
|
58
|
+
): { clientId?: string; clientSecret?: string } {
|
|
59
|
+
const meta = (row?.metadata || {}) as any
|
|
60
|
+
const creds = meta?.credentials?.[env] || {}
|
|
61
|
+
return {
|
|
62
|
+
clientId: creds.client_id || creds.clientId || undefined,
|
|
63
|
+
clientSecret:
|
|
64
|
+
decryptSecret(creds.client_secret || creds.clientSecret) || undefined,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getActiveCredentials(): Promise<ActiveCredentials> {
|
|
69
|
+
const row = await this.getConnectionRow()
|
|
70
|
+
if (!row) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"PayPal connection not found. Please complete onboarding in the admin panel."
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const env: Environment =
|
|
77
|
+
(row.environment as Environment) === "sandbox" ? "sandbox" : "live"
|
|
78
|
+
const c = this.getEnvCreds(row, env)
|
|
79
|
+
|
|
80
|
+
if (!c.clientId || !c.clientSecret) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`PayPal credentials missing for environment "${env}". Please save credentials in the admin panel.`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
environment: env,
|
|
88
|
+
client_id: c.clientId,
|
|
89
|
+
client_secret: c.clientSecret,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getAccessToken(): Promise<AccessTokenResult> {
|
|
94
|
+
const creds = await this.getActiveCredentials()
|
|
95
|
+
const base =
|
|
96
|
+
creds.environment === "live"
|
|
97
|
+
? "https://api-m.paypal.com"
|
|
98
|
+
: "https://api-m.sandbox.paypal.com"
|
|
99
|
+
|
|
100
|
+
const row = await this.getConnectionRow()
|
|
101
|
+
if (row?.app_access_token && row.app_access_token_expires_at) {
|
|
102
|
+
const expiresAt = new Date(row.app_access_token_expires_at)
|
|
103
|
+
if (expiresAt.getTime() - Date.now() > TOKEN_MARGIN_MS) {
|
|
104
|
+
return {
|
|
105
|
+
accessToken: decryptSecret(row.app_access_token) as string,
|
|
106
|
+
base,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.tokenRefreshPromise) {
|
|
112
|
+
const accessToken = await this.tokenRefreshPromise
|
|
113
|
+
return { accessToken, base }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.tokenRefreshPromise = this.refreshAccessToken(row, creds).finally(
|
|
117
|
+
() => {
|
|
118
|
+
this.tokenRefreshPromise = null
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const accessToken = await this.tokenRefreshPromise
|
|
123
|
+
return { accessToken, base }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async refreshAccessToken(
|
|
127
|
+
row: any,
|
|
128
|
+
creds: ActiveCredentials
|
|
129
|
+
): Promise<string> {
|
|
130
|
+
const base =
|
|
131
|
+
creds.environment === "live"
|
|
132
|
+
? "https://api-m.paypal.com"
|
|
133
|
+
: "https://api-m.sandbox.paypal.com"
|
|
134
|
+
const basic = Buffer.from(
|
|
135
|
+
`${creds.client_id}:${creds.client_secret}`
|
|
136
|
+
).toString("base64")
|
|
137
|
+
|
|
138
|
+
const body = new URLSearchParams()
|
|
139
|
+
body.set("grant_type", "client_credentials")
|
|
140
|
+
|
|
141
|
+
const res = await paypalFetch(`${base}/v1/oauth2/token`, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: {
|
|
144
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
145
|
+
Authorization: `Basic ${basic}`,
|
|
146
|
+
"PayPal-Partner-Attribution-Id": BN_CODE,
|
|
147
|
+
},
|
|
148
|
+
body,
|
|
149
|
+
signal: AbortSignal.timeout(30_000),
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const json = await res.json().catch(() => ({}))
|
|
153
|
+
if (!res.ok) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`PayPal client_credentials failed (${res.status}): ${JSON.stringify(json)}`
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const accessToken = String(json.access_token || "")
|
|
160
|
+
if (!accessToken) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
"PayPal client_credentials succeeded but access_token is missing."
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const expiresIn = Number(json.expires_in || 3600)
|
|
167
|
+
const newExpiresAt = new Date(Date.now() + expiresIn * 1000)
|
|
168
|
+
|
|
169
|
+
if (row?.id) {
|
|
170
|
+
await this.db("paypal_connection").where("id", row.id).update({
|
|
171
|
+
app_access_token: encryptSecret(accessToken),
|
|
172
|
+
app_access_token_expires_at: newExpiresAt,
|
|
173
|
+
updated_at: new Date(),
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return accessToken
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async getSettings(): Promise<ResolvedSettings> {
|
|
181
|
+
try {
|
|
182
|
+
const rows = await this.db("paypal_settings")
|
|
183
|
+
.select("data")
|
|
184
|
+
.limit(1)
|
|
185
|
+
|
|
186
|
+
const data = (rows?.[0]?.data || {}) as Record<string, any>
|
|
187
|
+
return {
|
|
188
|
+
additionalSettings: (data.additional_settings || {}) as Record<
|
|
189
|
+
string,
|
|
190
|
+
unknown
|
|
191
|
+
>,
|
|
192
|
+
advancedCardSettings: (data.advanced_card_payments || {}) as Record<
|
|
193
|
+
string,
|
|
194
|
+
unknown
|
|
195
|
+
>,
|
|
196
|
+
apiDetails: (data.api_details || {}) as Record<string, unknown>,
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
return {
|
|
200
|
+
additionalSettings: {},
|
|
201
|
+
advancedCardSettings: {},
|
|
202
|
+
apiDetails: {},
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async recordAuditEvent(
|
|
208
|
+
eventType: string,
|
|
209
|
+
metadata?: Record<string, unknown>
|
|
210
|
+
) {
|
|
211
|
+
try {
|
|
212
|
+
console.info(
|
|
213
|
+
JSON.stringify({
|
|
214
|
+
log: "paypal_audit",
|
|
215
|
+
event_type: eventType,
|
|
216
|
+
at: new Date().toISOString(),
|
|
217
|
+
...(metadata ? { metadata } : {}),
|
|
218
|
+
})
|
|
219
|
+
)
|
|
220
|
+
} catch {
|
|
221
|
+
// audit logging must never break the payment flow
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async recordMetric(name: string, metadata?: Record<string, unknown>) {
|
|
226
|
+
try {
|
|
227
|
+
const rows = await this.db("paypal_metric")
|
|
228
|
+
.select("*")
|
|
229
|
+
.where("name", name)
|
|
230
|
+
.limit(1)
|
|
231
|
+
const row = rows?.[0]
|
|
232
|
+
const current = (row?.data || {}) as Record<string, any>
|
|
233
|
+
const next = {
|
|
234
|
+
...current,
|
|
235
|
+
...(metadata || {}),
|
|
236
|
+
count: Number(current.count || 0) + 1,
|
|
237
|
+
last_recorded_at: new Date().toISOString(),
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (row) {
|
|
241
|
+
await this.db("paypal_metric")
|
|
242
|
+
.where("id", row.id)
|
|
243
|
+
.update({ data: JSON.stringify(next), updated_at: new Date() })
|
|
244
|
+
} else {
|
|
245
|
+
await this.db("paypal_metric").insert({
|
|
246
|
+
id: `ppmet_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
|
|
247
|
+
name,
|
|
248
|
+
data: JSON.stringify(next),
|
|
249
|
+
created_at: new Date(),
|
|
250
|
+
updated_at: new Date(),
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
} catch {
|
|
254
|
+
// metrics must never break the payment flow
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -98,7 +98,16 @@ export async function updatePayPalSessionData(
|
|
|
98
98
|
amount: existing?.amount,
|
|
99
99
|
currency_code: existing?.currency_code,
|
|
100
100
|
})
|
|
101
|
-
} catch (e:
|
|
102
|
-
|
|
101
|
+
} catch (e: unknown) {
|
|
102
|
+
const msg = e instanceof Error ? e.message : String(e)
|
|
103
|
+
console.error("[PayPal] updatePayPalSessionData failed:", msg)
|
|
104
|
+
const hasCaptureData = "capture_id" in extraData || "order_id" in extraData
|
|
105
|
+
if (hasCaptureData) {
|
|
106
|
+
console.error(
|
|
107
|
+
"[PayPal] CRITICAL: Payment data (capture/order) was NOT persisted to session.",
|
|
108
|
+
{ sessionId, keys: Object.keys(extraData) }
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
throw e
|
|
103
112
|
}
|
|
104
113
|
}
|
|
@@ -1,30 +1,152 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Without a timeout, a slow or hung PayPal socket holds a Node request handler
|
|
5
|
-
* (and any DB connection it owns) open indefinitely; under load these
|
|
6
|
-
* accumulate and exhaust the event loop / connection pool, cascading into
|
|
7
|
-
* checkout-wide failures. Override with PAYPAL_HTTP_TIMEOUT_MS.
|
|
2
|
+
* Outbound HTTP for PayPal API calls with timeout, circuit breaker, and
|
|
3
|
+
* optional retry for critical payment operations.
|
|
8
4
|
*/
|
|
5
|
+
|
|
9
6
|
export const PAYPAL_HTTP_TIMEOUT_MS = (() => {
|
|
10
7
|
const v = Number(process.env.PAYPAL_HTTP_TIMEOUT_MS)
|
|
11
8
|
return Number.isFinite(v) && v > 0 ? v : 30_000
|
|
12
9
|
})()
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Circuit breaker
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
const CB_FAILURE_THRESHOLD = 5
|
|
16
|
+
const CB_WINDOW_MS = 30_000
|
|
17
|
+
const CB_HALF_OPEN_MS = 60_000
|
|
18
|
+
|
|
19
|
+
type CBState = "closed" | "open" | "half-open"
|
|
20
|
+
|
|
21
|
+
let cbState: CBState = "closed"
|
|
22
|
+
let cbFailures: number[] = []
|
|
23
|
+
let cbOpenedAt = 0
|
|
24
|
+
let cbHalfOpenProbeInFlight = false
|
|
25
|
+
|
|
26
|
+
function cbRecordSuccess() {
|
|
27
|
+
cbState = "closed"
|
|
28
|
+
cbFailures = []
|
|
29
|
+
cbHalfOpenProbeInFlight = false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function cbRecordFailure() {
|
|
33
|
+
cbHalfOpenProbeInFlight = false
|
|
34
|
+
const now = Date.now()
|
|
35
|
+
cbFailures = cbFailures.filter((t) => now - t < CB_WINDOW_MS)
|
|
36
|
+
cbFailures.push(now)
|
|
37
|
+
if (cbFailures.length >= CB_FAILURE_THRESHOLD) {
|
|
38
|
+
cbState = "open"
|
|
39
|
+
cbOpenedAt = now
|
|
40
|
+
console.warn(
|
|
41
|
+
`[PayPal] circuit breaker OPEN after ${CB_FAILURE_THRESHOLD} failures in ${CB_WINDOW_MS / 1000}s`
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cbAllowRequest(): boolean {
|
|
47
|
+
if (cbState === "closed") return true
|
|
48
|
+
if (cbState === "open") {
|
|
49
|
+
if (Date.now() - cbOpenedAt >= CB_HALF_OPEN_MS) {
|
|
50
|
+
cbState = "half-open"
|
|
51
|
+
cbHalfOpenProbeInFlight = true
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
// half-open: allow only one probe at a time
|
|
57
|
+
if (cbHalfOpenProbeInFlight) return false
|
|
58
|
+
cbHalfOpenProbeInFlight = true
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class CircuitOpenError extends Error {
|
|
63
|
+
constructor() {
|
|
64
|
+
super(
|
|
65
|
+
"PayPal circuit breaker is open — API calls are temporarily blocked after repeated failures. Will retry automatically."
|
|
66
|
+
)
|
|
67
|
+
this.name = "CircuitOpenError"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Core fetch with timeout + circuit breaker
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
19
75
|
export function paypalFetch(
|
|
20
76
|
input: string | URL,
|
|
21
77
|
init: RequestInit = {}
|
|
22
78
|
): Promise<Response> {
|
|
23
|
-
if (
|
|
24
|
-
return
|
|
79
|
+
if (!cbAllowRequest()) {
|
|
80
|
+
return Promise.reject(new CircuitOpenError())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const fetchPromise = init.signal
|
|
84
|
+
? fetch(input, init)
|
|
85
|
+
: fetch(input, {
|
|
86
|
+
...init,
|
|
87
|
+
signal: AbortSignal.timeout(PAYPAL_HTTP_TIMEOUT_MS),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
return fetchPromise.then(
|
|
91
|
+
(res) => {
|
|
92
|
+
if (res.status >= 500) {
|
|
93
|
+
cbRecordFailure()
|
|
94
|
+
} else {
|
|
95
|
+
cbRecordSuccess()
|
|
96
|
+
}
|
|
97
|
+
return res
|
|
98
|
+
},
|
|
99
|
+
(err) => {
|
|
100
|
+
cbRecordFailure()
|
|
101
|
+
throw err
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Retry wrapper for critical payment operations (capture, refund)
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
const MAX_RETRIES = 2
|
|
111
|
+
const BASE_DELAY_MS = 500
|
|
112
|
+
|
|
113
|
+
function jitteredDelay(attempt: number): number {
|
|
114
|
+
const base = BASE_DELAY_MS * Math.pow(2, attempt)
|
|
115
|
+
return base + Math.random() * base
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isRetryable(err: unknown): boolean {
|
|
119
|
+
if (err instanceof CircuitOpenError) return true
|
|
120
|
+
if (err instanceof Error) {
|
|
121
|
+
if (err.name === "TimeoutError" || err.name === "AbortError") return true
|
|
122
|
+
}
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isRetryableStatus(status: number): boolean {
|
|
127
|
+
return status >= 500 || status === 429
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function paypalFetchWithRetry(
|
|
131
|
+
input: string | URL,
|
|
132
|
+
init: RequestInit = {}
|
|
133
|
+
): Promise<Response> {
|
|
134
|
+
let lastError: unknown
|
|
135
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
136
|
+
try {
|
|
137
|
+
const res = await paypalFetch(input, init)
|
|
138
|
+
if (isRetryableStatus(res.status) && attempt < MAX_RETRIES) {
|
|
139
|
+
const delay = jitteredDelay(attempt)
|
|
140
|
+
await new Promise((r) => setTimeout(r, delay))
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
return res
|
|
144
|
+
} catch (err) {
|
|
145
|
+
lastError = err
|
|
146
|
+
if (!isRetryable(err) || attempt >= MAX_RETRIES) throw err
|
|
147
|
+
const delay = jitteredDelay(attempt)
|
|
148
|
+
await new Promise((r) => setTimeout(r, delay))
|
|
149
|
+
}
|
|
25
150
|
}
|
|
26
|
-
|
|
27
|
-
...init,
|
|
28
|
-
signal: AbortSignal.timeout(PAYPAL_HTTP_TIMEOUT_MS),
|
|
29
|
-
})
|
|
151
|
+
throw lastError
|
|
30
152
|
}
|
|
@@ -36,6 +36,7 @@ const SALT_LEN = 16
|
|
|
36
36
|
let cachedRaw: string | undefined
|
|
37
37
|
let cachedRawKey: string | null = null
|
|
38
38
|
let legacyV1Key: Buffer | null = null
|
|
39
|
+
const V2_KEY_CACHE_MAX = 100
|
|
39
40
|
const v2KeyCache = new Map<string, Buffer>()
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -70,6 +71,10 @@ function deriveV2Key(raw: string, salt: Buffer): Buffer {
|
|
|
70
71
|
r: SCRYPT_R,
|
|
71
72
|
p: SCRYPT_P,
|
|
72
73
|
})
|
|
74
|
+
if (v2KeyCache.size >= V2_KEY_CACHE_MAX) {
|
|
75
|
+
const oldest = v2KeyCache.keys().next().value
|
|
76
|
+
if (oldest !== undefined) v2KeyCache.delete(oldest)
|
|
77
|
+
}
|
|
73
78
|
v2KeyCache.set(cacheKey, key)
|
|
74
79
|
}
|
|
75
80
|
return key
|
|
@@ -183,7 +183,7 @@ async function findPayPalSession(
|
|
|
183
183
|
|
|
184
184
|
const sessions = await paymentModule.listPaymentSessions({
|
|
185
185
|
payment_collection_id: collection.id,
|
|
186
|
-
})
|
|
186
|
+
}, { take: 50 })
|
|
187
187
|
|
|
188
188
|
const paypalSession = (sessions || [])
|
|
189
189
|
.filter((s: any) => isPayPalProviderId(s.provider_id))
|
|
@@ -300,21 +300,18 @@ export async function processPayPalWebhookEvent(
|
|
|
300
300
|
if (!cartId && (orderId || captureId)) {
|
|
301
301
|
try {
|
|
302
302
|
const paymentModule = container.resolve(Modules.PAYMENT) as any
|
|
303
|
-
// Fallback when the webhook resource carries no custom_id: locate the
|
|
304
|
-
// PayPal session whose stored data matches this order_id / capture_id.
|
|
305
|
-
// Paginate to exhaustion rather than scanning a single fixed page — on a
|
|
306
|
-
// busy store the matching session is frequently beyond the first page, so
|
|
307
|
-
// a hard cap would silently drop capture/refund status updates.
|
|
308
303
|
const PAGE_SIZE = 200
|
|
309
|
-
const MAX_PAGES =
|
|
304
|
+
const MAX_PAGES = 5
|
|
310
305
|
let matchedSession: Record<string, unknown> | null = null
|
|
306
|
+
let totalScanned = 0
|
|
311
307
|
|
|
312
308
|
for (let page = 0; page < MAX_PAGES && !matchedSession; page++) {
|
|
313
309
|
const sessions = await paymentModule.listPaymentSessions(
|
|
314
310
|
{ provider_id: [...PAYPAL_PROVIDER_IDS] },
|
|
315
|
-
{ take: PAGE_SIZE, skip: page * PAGE_SIZE }
|
|
311
|
+
{ take: PAGE_SIZE, skip: page * PAGE_SIZE, order: { created_at: "DESC" } }
|
|
316
312
|
)
|
|
317
313
|
if (!sessions || sessions.length === 0) break
|
|
314
|
+
totalScanned += sessions.length
|
|
318
315
|
matchedSession =
|
|
319
316
|
sessions.find((s: Record<string, unknown>) => {
|
|
320
317
|
const pp =
|
|
@@ -326,6 +323,14 @@ export async function processPayPalWebhookEvent(
|
|
|
326
323
|
if (sessions.length < PAGE_SIZE) break
|
|
327
324
|
}
|
|
328
325
|
|
|
326
|
+
if (!matchedSession && totalScanned >= MAX_PAGES * PAGE_SIZE) {
|
|
327
|
+
console.warn(
|
|
328
|
+
`[PayPal] webhook: cartId fallback scan hit limit (${totalScanned} sessions). ` +
|
|
329
|
+
`Event ${input.eventType} may be lost. Ensure custom_id is set on PayPal orders.`,
|
|
330
|
+
{ orderId, captureId }
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
|
|
329
334
|
if (matchedSession?.payment_collection_id) {
|
|
330
335
|
const colls = await paymentModule.listPaymentCollections(
|
|
331
336
|
{ id: [matchedSession.payment_collection_id] },
|
|
@@ -333,10 +338,10 @@ export async function processPayPalWebhookEvent(
|
|
|
333
338
|
)
|
|
334
339
|
cartId = String(colls?.[0]?.cart_id || "").trim() || null
|
|
335
340
|
}
|
|
336
|
-
} catch (e:
|
|
341
|
+
} catch (e: unknown) {
|
|
337
342
|
console.warn(
|
|
338
343
|
`[PayPal] webhook: cartId fallback lookup failed for ${input.eventType}:`,
|
|
339
|
-
e
|
|
344
|
+
e instanceof Error ? e.message : e
|
|
340
345
|
)
|
|
341
346
|
}
|
|
342
347
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/admin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/admin/index.ts"],"names":[],"mappings":""}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export default function FieldRow({ label, hint, htmlFor, children, }: {
|
|
3
|
-
label: string;
|
|
4
|
-
hint?: React.ReactNode;
|
|
5
|
-
htmlFor?: string;
|
|
6
|
-
children: React.ReactNode;
|
|
7
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
//# sourceMappingURL=FieldRow.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FieldRow.d.ts","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/FieldRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAC/B,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,2CAgBA"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = FieldRow;
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
function FieldRow({ label, hint, htmlFor, children, }) {
|
|
6
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-12 items-start gap-4 py-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "col-span-12 md:col-span-4", children: [htmlFor ? ((0, jsx_runtime_1.jsx)("label", { htmlFor: htmlFor, className: "text-sm font-medium text-ui-fg-base", children: label })) : ((0, jsx_runtime_1.jsx)("div", { className: "text-sm font-medium text-ui-fg-base", children: label })), hint ? (0, jsx_runtime_1.jsx)("div", { className: "mt-1 text-xs text-ui-fg-subtle", children: hint }) : null] }), (0, jsx_runtime_1.jsx)("div", { className: "col-span-12 md:col-span-8", children: children })] }));
|
|
7
|
-
}
|
|
8
|
-
//# sourceMappingURL=FieldRow.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FieldRow.js","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/FieldRow.tsx"],"names":[],"mappings":";;AAEA,2BA0BC;;AA1BD,SAAwB,QAAQ,CAAC,EAC/B,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,GAMT;IACC,OAAO,CACL,iCAAK,SAAS,EAAC,0CAA0C,aACvD,iCAAK,SAAS,EAAC,2BAA2B,aACvC,OAAO,CAAC,CAAC,CAAC,CACT,kCAAO,OAAO,EAAE,OAAO,EAAE,SAAS,EAAC,qCAAqC,YACrE,KAAK,GACA,CACT,CAAC,CAAC,CAAC,CACF,gCAAK,SAAS,EAAC,qCAAqC,YAAE,KAAK,GAAO,CACnE,EACA,IAAI,CAAC,CAAC,CAAC,gCAAK,SAAS,EAAC,gCAAgC,YAAE,IAAI,GAAO,CAAC,CAAC,CAAC,IAAI,IACvE,EACN,gCAAK,SAAS,EAAC,2BAA2B,YAAE,QAAQ,GAAO,IACvD,CACP,CAAA;AACH,CAAC"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export default function SectionCard({ title, description, children, right, }: {
|
|
3
|
-
title: string;
|
|
4
|
-
description?: React.ReactNode;
|
|
5
|
-
children: React.ReactNode;
|
|
6
|
-
right?: React.ReactNode;
|
|
7
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
//# sourceMappingURL=SectionCard.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SectionCard.d.ts","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/SectionCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,KAAK,EACL,WAAW,EACX,QAAQ,EACR,KAAK,GACN,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACxB,2CAaA"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = SectionCard;
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
function SectionCard({ title, description, children, right, }) {
|
|
6
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-4 border-b border-ui-border-base p-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-base font-semibold text-ui-fg-base", children: title }), description ? (0, jsx_runtime_1.jsx)("div", { className: "mt-1 text-sm text-ui-fg-subtle", children: description }) : null] }), right] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4", children: children })] }));
|
|
7
|
-
}
|
|
8
|
-
//# sourceMappingURL=SectionCard.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SectionCard.js","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/SectionCard.tsx"],"names":[],"mappings":";;AAEA,8BAuBC;;AAvBD,SAAwB,WAAW,CAAC,EAClC,KAAK,EACL,WAAW,EACX,QAAQ,EACR,KAAK,GAMN;IACC,OAAO,CACL,iCAAK,SAAS,EAAC,iEAAiE,aAC9E,iCAAK,SAAS,EAAC,2EAA2E,aACxF,4CACE,gCAAK,SAAS,EAAC,yCAAyC,YAAE,KAAK,GAAO,EACrE,WAAW,CAAC,CAAC,CAAC,gCAAK,SAAS,EAAC,gCAAgC,YAAE,WAAW,GAAO,CAAC,CAAC,CAAC,IAAI,IACrF,EACL,KAAK,IACF,EACN,gCAAK,SAAS,EAAC,KAAK,YAAE,QAAQ,GAAO,IACjC,CACP,CAAA;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/Tabs.tsx"],"names":[],"mappings":"AAqBA,MAAM,CAAC,OAAO,UAAU,UAAU,4CA0BjC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = PayPalTabs;
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const react_router_dom_1 = require("react-router-dom");
|
|
6
|
-
const BASE = "/settings/paypal";
|
|
7
|
-
const TABS = [
|
|
8
|
-
{ label: "PayPal Connection", to: `${BASE}/connection` },
|
|
9
|
-
{ label: "PayPal Settings", to: `${BASE}/paypal-settings` },
|
|
10
|
-
{ label: "Advanced Card Payments", to: `${BASE}/advanced-card-payments` },
|
|
11
|
-
{ label: "Additional Settings", to: `${BASE}/additional-settings` },
|
|
12
|
-
];
|
|
13
|
-
function isActive(pathname, to) {
|
|
14
|
-
return pathname === to || pathname.startsWith(to + "/");
|
|
15
|
-
}
|
|
16
|
-
function PayPalTabs() {
|
|
17
|
-
const { pathname } = (0, react_router_dom_1.useLocation)();
|
|
18
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "border-b border-ui-border-base", children: (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-6 text-sm", children: TABS.map((t) => {
|
|
19
|
-
const active = isActive(pathname, t.to);
|
|
20
|
-
return ((0, jsx_runtime_1.jsx)(react_router_dom_1.Link, { to: t.to, className: active
|
|
21
|
-
? "border-b-2 border-ui-fg-base pb-2 font-medium text-ui-fg-base"
|
|
22
|
-
: "pb-2 text-ui-fg-subtle hover:text-ui-fg-base", children: t.label }, t.to));
|
|
23
|
-
}) }) }));
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=Tabs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.js","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/Tabs.tsx"],"names":[],"mappings":";;AAqBA,6BA0BC;;AA9CD,uDAAoD;AAOpD,MAAM,IAAI,GAAG,kBAAkB,CAAA;AAE/B,MAAM,IAAI,GAAU;IAClB,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,EAAE,GAAG,IAAI,aAAa,EAAE;IACxD,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,EAAE,GAAG,IAAI,kBAAkB,EAAE;IAC3D,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,EAAE,GAAG,IAAI,yBAAyB,EAAE;IACzE,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE,GAAG,IAAI,sBAAsB,EAAE;CACpE,CAAA;AAED,SAAS,QAAQ,CAAC,QAAgB,EAAE,EAAU;IAC5C,OAAO,QAAQ,KAAK,EAAE,IAAI,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,GAAG,CAAC,CAAA;AACzD,CAAC;AAED,SAAwB,UAAU;IAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,8BAAW,GAAE,CAAA;IAElC,OAAO,CACL,gCAAK,SAAS,EAAC,gCAAgC,YAC7C,gCAAK,SAAS,EAAC,8BAA8B,YAC1C,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACd,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAA;gBAEvC,OAAO,CACL,uBAAC,uBAAI,IAEH,EAAE,EAAE,CAAC,CAAC,EAAE,EACR,SAAS,EACP,MAAM;wBACJ,CAAC,CAAC,+DAA+D;wBACjE,CAAC,CAAC,8CAA8C,YAGnD,CAAC,CAAC,KAAK,IARH,CAAC,CAAC,EAAE,CASJ,CACR,CAAA;YACH,CAAC,CAAC,GACE,GACF,CACP,CAAA;AACH,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export type ToastKind = "success" | "error";
|
|
2
|
-
export type ToastState = {
|
|
3
|
-
kind: ToastKind;
|
|
4
|
-
message: string;
|
|
5
|
-
} | null;
|
|
6
|
-
type Props = {
|
|
7
|
-
toast: ToastState;
|
|
8
|
-
onClose: () => void;
|
|
9
|
-
};
|
|
10
|
-
export default function Toast({ toast, onClose }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
-
export {};
|
|
12
|
-
//# sourceMappingURL=Toast.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/_components/Toast.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAE3C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,IAAI,CAAA;AAER,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,UAAU,CAAA;IACjB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,kDAqCtD"}
|