@easypayment/medusa-paypal 0.6.5 → 0.6.6
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 +6 -6
- package/.medusa/server/src/admin/index.mjs +6 -6
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts +0 -8
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/service.js +129 -131
- package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/service.d.ts +0 -46
- package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/service.js +0 -94
- package/.medusa/server/src/modules/paypal/service.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/paypal/payment-provider/service.ts +196 -244
- package/src/modules/paypal/service.ts +0 -98
|
@@ -319,7 +319,7 @@ function AdvancedCardPaymentsTab() {
|
|
|
319
319
|
)
|
|
320
320
|
] }) });
|
|
321
321
|
}
|
|
322
|
-
function
|
|
322
|
+
function PayPalGooglePayPage() {
|
|
323
323
|
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
324
324
|
}
|
|
325
325
|
const config = adminSdk.defineRouteConfig({
|
|
@@ -916,7 +916,7 @@ function PayPalConnectionPage() {
|
|
|
916
916
|
` })
|
|
917
917
|
] });
|
|
918
918
|
}
|
|
919
|
-
function
|
|
919
|
+
function PayPalApplePayPage() {
|
|
920
920
|
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
921
921
|
}
|
|
922
922
|
function PayPalPayLaterMessagingPage() {
|
|
@@ -1211,16 +1211,16 @@ const routeModule = {
|
|
|
1211
1211
|
path: "/settings/paypal/advanced-card-payments"
|
|
1212
1212
|
},
|
|
1213
1213
|
{
|
|
1214
|
-
Component:
|
|
1215
|
-
path: "/settings/paypal/
|
|
1214
|
+
Component: PayPalGooglePayPage,
|
|
1215
|
+
path: "/settings/paypal/google-pay"
|
|
1216
1216
|
},
|
|
1217
1217
|
{
|
|
1218
1218
|
Component: PayPalConnectionPage,
|
|
1219
1219
|
path: "/settings/paypal/connection"
|
|
1220
1220
|
},
|
|
1221
1221
|
{
|
|
1222
|
-
Component:
|
|
1223
|
-
path: "/settings/paypal/
|
|
1222
|
+
Component: PayPalApplePayPage,
|
|
1223
|
+
path: "/settings/paypal/apple-pay"
|
|
1224
1224
|
},
|
|
1225
1225
|
{
|
|
1226
1226
|
Component: PayPalPayLaterMessagingPage,
|
|
@@ -318,7 +318,7 @@ function AdvancedCardPaymentsTab() {
|
|
|
318
318
|
)
|
|
319
319
|
] }) });
|
|
320
320
|
}
|
|
321
|
-
function
|
|
321
|
+
function PayPalGooglePayPage() {
|
|
322
322
|
return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
323
323
|
}
|
|
324
324
|
const config = defineRouteConfig({
|
|
@@ -915,7 +915,7 @@ function PayPalConnectionPage() {
|
|
|
915
915
|
` })
|
|
916
916
|
] });
|
|
917
917
|
}
|
|
918
|
-
function
|
|
918
|
+
function PayPalApplePayPage() {
|
|
919
919
|
return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
920
920
|
}
|
|
921
921
|
function PayPalPayLaterMessagingPage() {
|
|
@@ -1210,16 +1210,16 @@ const routeModule = {
|
|
|
1210
1210
|
path: "/settings/paypal/advanced-card-payments"
|
|
1211
1211
|
},
|
|
1212
1212
|
{
|
|
1213
|
-
Component:
|
|
1214
|
-
path: "/settings/paypal/
|
|
1213
|
+
Component: PayPalGooglePayPage,
|
|
1214
|
+
path: "/settings/paypal/google-pay"
|
|
1215
1215
|
},
|
|
1216
1216
|
{
|
|
1217
1217
|
Component: PayPalConnectionPage,
|
|
1218
1218
|
path: "/settings/paypal/connection"
|
|
1219
1219
|
},
|
|
1220
1220
|
{
|
|
1221
|
-
Component:
|
|
1222
|
-
path: "/settings/paypal/
|
|
1221
|
+
Component: PayPalApplePayPage,
|
|
1222
|
+
path: "/settings/paypal/apple-pay"
|
|
1223
1223
|
},
|
|
1224
1224
|
{
|
|
1225
1225
|
Component: PayPalPayLaterMessagingPage,
|
|
@@ -23,10 +23,6 @@ declare class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
|
|
|
23
23
|
private recordSuccess;
|
|
24
24
|
private recordPaymentEvent;
|
|
25
25
|
createAccountHolder(input: CreateAccountHolderInput): Promise<CreateAccountHolderOutput>;
|
|
26
|
-
/**
|
|
27
|
-
* Create a payment session when the customer selects PayPal.
|
|
28
|
-
* Must return an object containing an `id` and `data`.
|
|
29
|
-
*/
|
|
30
26
|
initiatePayment(input: InitiatePaymentInput): Promise<InitiatePaymentOutput>;
|
|
31
27
|
updatePayment(input: UpdatePaymentInput): Promise<UpdatePaymentOutput>;
|
|
32
28
|
authorizePayment(input: AuthorizePaymentInput): Promise<AuthorizePaymentOutput>;
|
|
@@ -36,10 +32,6 @@ declare class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
|
|
|
36
32
|
refundPayment(input: RefundPaymentInput): Promise<RefundPaymentOutput>;
|
|
37
33
|
cancelPayment(input: CancelPaymentInput): Promise<CancelPaymentOutput>;
|
|
38
34
|
deletePayment(_input: DeletePaymentInput): Promise<DeletePaymentOutput>;
|
|
39
|
-
/**
|
|
40
|
-
* Required by AbstractPaymentProvider in Medusa v2.
|
|
41
|
-
* This is used by /hooks/payment/{identifier}_{providerId}
|
|
42
|
-
*/
|
|
43
35
|
getWebhookActionAndData(payload: ProviderWebhookPayload["payload"]): Promise<WebhookActionResult>;
|
|
44
36
|
}
|
|
45
37
|
export default PayPalPaymentProvider;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/paypal/payment-provider/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAEnE,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,2BAA2B,CAAA;AASlC,KAAK,OAAO,GAAG,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/paypal/payment-provider/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAEnE,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,2BAA2B,CAAA;AASlC,KAAK,OAAO,GAAG,EAAE,CAAA;AAUjB,cAAM,qBAAsB,SAAQ,uBAAuB,CAAC,OAAO,CAAC;IAClE,MAAM,CAAC,UAAU,SAAW;IAE5B,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;gBAExB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO;IAKzD,OAAO,CAAC,oBAAoB;IAWtB,eAAe;4BAW6C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;YAoB3D,uBAAuB;YAQvB,oBAAoB;YA0DpB,eAAe;IAkB7B,OAAO,CAAC,iBAAiB;YAWX,oBAAoB;IAclC,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,cAAc;YAWR,aAAa;YAYb,aAAa;YAUb,kBAAkB;IAU1B,mBAAmB,CACvB,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;IAY/B,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAgC5E,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAoBtE,gBAAgB,CACpB,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC;IAmN5B,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyB5E,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAwC/E,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAyKzE,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA2GtE,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiHtE,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIvE,uBAAuB,CAC3B,OAAO,EAAE,sBAAsB,CAAC,SAAS,CAAC,GACzC,OAAO,CAAC,mBAAmB,CAAC;CAGhC;AAED,eAAe,qBAAqB,CAAA;AACpC,OAAO,EAAE,qBAAqB,EAAE,CAAA"}
|
|
@@ -11,7 +11,6 @@ function generateSessionId() {
|
|
|
11
11
|
return (0, crypto_1.randomUUID)();
|
|
12
12
|
}
|
|
13
13
|
catch {
|
|
14
|
-
// Fallback for environments where randomUUID isn't available
|
|
15
14
|
return `pp_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
16
15
|
}
|
|
17
16
|
}
|
|
@@ -72,10 +71,11 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
72
71
|
async getPayPalAccessToken() {
|
|
73
72
|
const paypal = this.resolvePayPalService();
|
|
74
73
|
if (!paypal) {
|
|
75
|
-
// Fallback: load credentials directly from paypal_connection table
|
|
76
74
|
const { Pool: _FbPool } = require("pg");
|
|
77
75
|
const _fbPool = new _FbPool({ connectionString: process.env.DATABASE_URL });
|
|
78
|
-
const _fbResult = await _fbPool
|
|
76
|
+
const _fbResult = await _fbPool
|
|
77
|
+
.query("SELECT metadata, environment, seller_client_id, seller_client_secret FROM paypal_connection WHERE status='connected' ORDER BY created_at DESC LIMIT 1")
|
|
78
|
+
.finally(() => _fbPool.end());
|
|
79
79
|
const _fbRow = _fbResult.rows[0];
|
|
80
80
|
if (!_fbRow)
|
|
81
81
|
throw new Error("No active PayPal connection found in DB");
|
|
@@ -87,7 +87,10 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
87
87
|
const _fbAuth = Buffer.from(`${_fbId}:${_fbSec}`).toString("base64");
|
|
88
88
|
const _fbResp = await fetch(`${_fbBase}/v1/oauth2/token`, {
|
|
89
89
|
method: "POST",
|
|
90
|
-
headers: {
|
|
90
|
+
headers: {
|
|
91
|
+
Authorization: `Basic ${_fbAuth}`,
|
|
92
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
93
|
+
},
|
|
91
94
|
body: "grant_type=client_credentials",
|
|
92
95
|
});
|
|
93
96
|
const _fbText = await _fbResp.text();
|
|
@@ -151,37 +154,28 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
151
154
|
}
|
|
152
155
|
mapCaptureStatus(status) {
|
|
153
156
|
const normalized = String(status || "").toUpperCase();
|
|
154
|
-
if (!normalized)
|
|
157
|
+
if (!normalized)
|
|
155
158
|
return null;
|
|
156
|
-
|
|
157
|
-
if (normalized === "COMPLETED") {
|
|
159
|
+
if (normalized === "COMPLETED")
|
|
158
160
|
return "captured";
|
|
159
|
-
|
|
160
|
-
if (normalized === "PENDING") {
|
|
161
|
+
if (normalized === "PENDING")
|
|
161
162
|
return "pending";
|
|
162
|
-
|
|
163
|
-
if (["DENIED", "DECLINED", "FAILED"].includes(normalized)) {
|
|
163
|
+
if (["DENIED", "DECLINED", "FAILED"].includes(normalized))
|
|
164
164
|
return "error";
|
|
165
|
-
|
|
166
|
-
if (["REFUNDED", "PARTIALLY_REFUNDED", "REVERSED"].includes(normalized)) {
|
|
165
|
+
if (["REFUNDED", "PARTIALLY_REFUNDED", "REVERSED"].includes(normalized))
|
|
167
166
|
return "canceled";
|
|
168
|
-
}
|
|
169
167
|
return null;
|
|
170
168
|
}
|
|
171
169
|
mapAuthorizationStatus(status) {
|
|
172
170
|
const normalized = String(status || "").toUpperCase();
|
|
173
|
-
if (!normalized)
|
|
171
|
+
if (!normalized)
|
|
174
172
|
return null;
|
|
175
|
-
|
|
176
|
-
if (["CREATED", "APPROVED", "PENDING"].includes(normalized)) {
|
|
173
|
+
if (["CREATED", "APPROVED", "PENDING"].includes(normalized))
|
|
177
174
|
return "authorized";
|
|
178
|
-
|
|
179
|
-
if (["VOIDED", "EXPIRED"].includes(normalized)) {
|
|
175
|
+
if (["VOIDED", "EXPIRED"].includes(normalized))
|
|
180
176
|
return "canceled";
|
|
181
|
-
|
|
182
|
-
if (["DENIED", "DECLINED", "FAILED"].includes(normalized)) {
|
|
177
|
+
if (["DENIED", "DECLINED", "FAILED"].includes(normalized))
|
|
183
178
|
return "error";
|
|
184
|
-
}
|
|
185
179
|
return null;
|
|
186
180
|
}
|
|
187
181
|
serializeError(error) {
|
|
@@ -193,76 +187,61 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
193
187
|
message: error.message,
|
|
194
188
|
stack: error.stack,
|
|
195
189
|
cause: cause instanceof Error
|
|
196
|
-
? {
|
|
197
|
-
name: cause.name,
|
|
198
|
-
message: cause.message,
|
|
199
|
-
stack: cause.stack,
|
|
200
|
-
}
|
|
190
|
+
? { name: cause.name, message: cause.message, stack: cause.stack }
|
|
201
191
|
: cause,
|
|
202
192
|
};
|
|
203
193
|
}
|
|
204
|
-
return {
|
|
205
|
-
message: String(error),
|
|
206
|
-
};
|
|
194
|
+
return { message: String(error) };
|
|
207
195
|
}
|
|
208
196
|
mapOrderStatus(status) {
|
|
209
197
|
const normalized = String(status || "").toUpperCase();
|
|
210
|
-
if (!normalized)
|
|
198
|
+
if (!normalized)
|
|
211
199
|
return "pending";
|
|
212
|
-
|
|
213
|
-
if (normalized === "COMPLETED") {
|
|
200
|
+
if (normalized === "COMPLETED")
|
|
214
201
|
return "captured";
|
|
215
|
-
|
|
216
|
-
if (normalized === "APPROVED") {
|
|
202
|
+
if (normalized === "APPROVED")
|
|
217
203
|
return "authorized";
|
|
218
|
-
|
|
219
|
-
if (["VOIDED", "CANCELLED"].includes(normalized)) {
|
|
204
|
+
if (["VOIDED", "CANCELLED"].includes(normalized))
|
|
220
205
|
return "canceled";
|
|
221
|
-
|
|
222
|
-
if (["CREATED", "SAVED", "PAYER_ACTION_REQUIRED"].includes(normalized)) {
|
|
206
|
+
if (["CREATED", "SAVED", "PAYER_ACTION_REQUIRED"].includes(normalized))
|
|
223
207
|
return "pending";
|
|
224
|
-
|
|
225
|
-
if (["FAILED", "EXPIRED"].includes(normalized)) {
|
|
208
|
+
if (["FAILED", "EXPIRED"].includes(normalized))
|
|
226
209
|
return "error";
|
|
227
|
-
}
|
|
228
210
|
return "pending";
|
|
229
211
|
}
|
|
230
212
|
async recordFailure(eventType, metadata) {
|
|
231
213
|
const paypal = this.resolvePayPalService();
|
|
232
|
-
if (!paypal)
|
|
214
|
+
if (!paypal)
|
|
233
215
|
return;
|
|
234
|
-
}
|
|
235
216
|
try {
|
|
236
217
|
await paypal.recordPaymentLog(eventType, metadata);
|
|
237
218
|
await paypal.recordAuditEvent(eventType, metadata);
|
|
238
219
|
await paypal.recordMetric(eventType);
|
|
239
220
|
}
|
|
240
221
|
catch {
|
|
241
|
-
// ignore
|
|
222
|
+
// ignore
|
|
242
223
|
}
|
|
243
224
|
}
|
|
244
225
|
async recordSuccess(metricName) {
|
|
245
226
|
const paypal = this.resolvePayPalService();
|
|
246
|
-
if (!paypal)
|
|
227
|
+
if (!paypal)
|
|
247
228
|
return;
|
|
248
|
-
}
|
|
249
229
|
try {
|
|
250
230
|
await paypal.recordMetric(metricName);
|
|
251
231
|
}
|
|
252
232
|
catch {
|
|
253
|
-
// ignore
|
|
233
|
+
// ignore
|
|
254
234
|
}
|
|
255
235
|
}
|
|
256
236
|
async recordPaymentEvent(eventType, metadata) {
|
|
257
237
|
const paypal = this.resolvePayPalService();
|
|
258
|
-
if (!paypal)
|
|
238
|
+
if (!paypal)
|
|
259
239
|
return;
|
|
260
|
-
}
|
|
261
240
|
try {
|
|
262
241
|
await paypal.recordPaymentLog(eventType, metadata);
|
|
263
242
|
}
|
|
264
243
|
catch {
|
|
265
|
-
// ignore
|
|
244
|
+
// ignore
|
|
266
245
|
}
|
|
267
246
|
}
|
|
268
247
|
async createAccountHolder(input) {
|
|
@@ -276,10 +255,6 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
276
255
|
},
|
|
277
256
|
};
|
|
278
257
|
}
|
|
279
|
-
/**
|
|
280
|
-
* Create a payment session when the customer selects PayPal.
|
|
281
|
-
* Must return an object containing an `id` and `data`.
|
|
282
|
-
*/
|
|
283
258
|
async initiatePayment(input) {
|
|
284
259
|
const providerId = input.data?.provider_id;
|
|
285
260
|
try {
|
|
@@ -300,15 +275,6 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
300
275
|
};
|
|
301
276
|
}
|
|
302
277
|
catch (error) {
|
|
303
|
-
console.error("[PayPal] provider initiate failed", {
|
|
304
|
-
provider_id: providerId,
|
|
305
|
-
payment_collection_id: input.data
|
|
306
|
-
?.payment_collection_id,
|
|
307
|
-
cart_id: input.data?.cart_id,
|
|
308
|
-
amount: input.amount,
|
|
309
|
-
currency_code: input.currency_code,
|
|
310
|
-
error: this.serializeError(error),
|
|
311
|
-
});
|
|
312
278
|
await this.recordFailure("initiate_failed", {
|
|
313
279
|
error: this.serializeError(error),
|
|
314
280
|
currency_code: input.currency_code,
|
|
@@ -339,83 +305,128 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
339
305
|
async authorizePayment(input) {
|
|
340
306
|
const { data, amount, currencyCode } = await this.normalizePaymentData(input);
|
|
341
307
|
const existingPayPal = (data.paypal || {});
|
|
308
|
+
const { additionalSettings } = await this.resolveSettings();
|
|
309
|
+
const paymentActionRaw = typeof additionalSettings.paymentAction === "string"
|
|
310
|
+
? additionalSettings.paymentAction
|
|
311
|
+
: "capture";
|
|
312
|
+
const returnStatus = paymentActionRaw === "authorize" ? "authorized" : "captured";
|
|
313
|
+
const timestampKey = paymentActionRaw === "authorize" ? "authorized_at" : "captured_at";
|
|
314
|
+
// ── CASE 1: Already processed (has capture_id, authorization_id, or timestamp) ──
|
|
342
315
|
if (existingPayPal.capture_id ||
|
|
343
316
|
existingPayPal.authorization_id ||
|
|
344
317
|
data.authorized_at ||
|
|
345
318
|
data.captured_at) {
|
|
346
|
-
|
|
347
|
-
const paymentAction = typeof additionalSettings.paymentAction === "string"
|
|
348
|
-
? additionalSettings.paymentAction
|
|
349
|
-
: "capture";
|
|
350
|
-
const returnStatus = paymentAction === "authorize" ? "authorized" : "captured";
|
|
319
|
+
console.info("[PayPal] authorizePayment: session already processed, returning", returnStatus);
|
|
351
320
|
return {
|
|
352
321
|
status: returnStatus,
|
|
353
322
|
data: {
|
|
354
323
|
...(input.data || {}),
|
|
355
|
-
|
|
356
|
-
? { authorized_at: new Date().toISOString() }
|
|
357
|
-
: { captured_at: new Date().toISOString() }),
|
|
324
|
+
[timestampKey]: data[timestampKey] || new Date().toISOString(),
|
|
358
325
|
},
|
|
359
326
|
};
|
|
360
327
|
}
|
|
328
|
+
// ── CASE 2: Has order_id — fetch current status from PayPal ──────────────
|
|
329
|
+
// This is the KEY fix: capture-order already processed the payment but
|
|
330
|
+
// Medusa's cart/complete workflow calls authorizePayment again with the
|
|
331
|
+
// original session data (before capture-order wrote authorization_id).
|
|
332
|
+
// We fetch the live PayPal order to get the real status.
|
|
333
|
+
const orderId = String(existingPayPal.order_id || data.order_id || "");
|
|
334
|
+
if (orderId) {
|
|
335
|
+
try {
|
|
336
|
+
console.info("[PayPal] authorizePayment: fetching live order status for", orderId);
|
|
337
|
+
const order = await this.getOrderDetails(orderId);
|
|
338
|
+
const capture = order?.purchase_units?.[0]?.payments?.captures?.[0];
|
|
339
|
+
const authorization = order?.purchase_units?.[0]?.payments?.authorizations?.[0];
|
|
340
|
+
// Order was already captured or authorized by capture-order route
|
|
341
|
+
if (capture?.id || authorization?.id) {
|
|
342
|
+
console.info("[PayPal] authorizePayment: order already processed by PayPal, returning", returnStatus);
|
|
343
|
+
return {
|
|
344
|
+
status: returnStatus,
|
|
345
|
+
data: {
|
|
346
|
+
...(input.data || {}),
|
|
347
|
+
paypal: {
|
|
348
|
+
...existingPayPal,
|
|
349
|
+
order_id: orderId,
|
|
350
|
+
order,
|
|
351
|
+
authorization_id: authorization?.id || existingPayPal.authorization_id,
|
|
352
|
+
capture_id: capture?.id || existingPayPal.capture_id,
|
|
353
|
+
},
|
|
354
|
+
[timestampKey]: new Date().toISOString(),
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
// Order exists and is APPROVED — customer completed PayPal flow
|
|
359
|
+
// but capture-order hasn't run yet (edge case). Mark as authorized.
|
|
360
|
+
if (["APPROVED", "CREATED", "SAVED"].includes(String(order?.status || "").toUpperCase())) {
|
|
361
|
+
console.info("[PayPal] authorizePayment: order approved, marking authorized");
|
|
362
|
+
return {
|
|
363
|
+
status: "authorized",
|
|
364
|
+
data: {
|
|
365
|
+
...(input.data || {}),
|
|
366
|
+
paypal: {
|
|
367
|
+
...existingPayPal,
|
|
368
|
+
order_id: orderId,
|
|
369
|
+
order,
|
|
370
|
+
},
|
|
371
|
+
authorized_at: new Date().toISOString(),
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
// Non-fatal — log and fall through to creating a new order below
|
|
378
|
+
console.warn("[PayPal] authorizePayment: order lookup failed:", e?.message);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// ── CASE 3: No order_id — create a new PayPal order ──────────────────────
|
|
382
|
+
// This handles the rare case where Medusa calls authorizePayment before
|
|
383
|
+
// the frontend has created a PayPal order (e.g. admin-created orders).
|
|
361
384
|
const requestId = this.getIdempotencyKey(input, "authorize");
|
|
362
385
|
let debugId = null;
|
|
363
|
-
const { additionalSettings } = await this.resolveSettings();
|
|
364
|
-
const paymentActionRaw = typeof additionalSettings.paymentAction === "string"
|
|
365
|
-
? additionalSettings.paymentAction
|
|
366
|
-
: "capture";
|
|
367
386
|
const orderIntent = paymentActionRaw === "authorize" ? "AUTHORIZE" : "CAPTURE";
|
|
368
387
|
try {
|
|
369
388
|
const { accessToken, base } = await this.getPayPalAccessToken();
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
reference_id: data.cart_id || data.payment_collection_id || undefined,
|
|
381
|
-
custom_id: data.session_id || data.cart_id || data.payment_collection_id || undefined,
|
|
382
|
-
amount: {
|
|
383
|
-
currency_code: currencyCode || "EUR",
|
|
384
|
-
value,
|
|
385
|
-
},
|
|
389
|
+
const value = (0, amounts_1.formatAmountForPayPal)(amount, currencyCode || "EUR");
|
|
390
|
+
const orderPayload = {
|
|
391
|
+
intent: orderIntent,
|
|
392
|
+
purchase_units: [
|
|
393
|
+
{
|
|
394
|
+
reference_id: data.cart_id || data.payment_collection_id || undefined,
|
|
395
|
+
custom_id: data.session_id || data.cart_id || data.payment_collection_id || undefined,
|
|
396
|
+
amount: {
|
|
397
|
+
currency_code: currencyCode || "EUR",
|
|
398
|
+
value,
|
|
386
399
|
},
|
|
387
|
-
],
|
|
388
|
-
custom_id: data.session_id || data.cart_id || data.payment_collection_id || undefined,
|
|
389
|
-
};
|
|
390
|
-
const ppResp = await fetch(`${base}/v2/checkout/orders`, {
|
|
391
|
-
method: "POST",
|
|
392
|
-
headers: {
|
|
393
|
-
Authorization: `Bearer ${accessToken}`,
|
|
394
|
-
"Content-Type": "application/json",
|
|
395
|
-
"PayPal-Request-Id": requestId,
|
|
396
400
|
},
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
401
|
+
],
|
|
402
|
+
custom_id: data.session_id || data.cart_id || data.payment_collection_id || undefined,
|
|
403
|
+
};
|
|
404
|
+
const ppResp = await fetch(`${base}/v2/checkout/orders`, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers: {
|
|
407
|
+
Authorization: `Bearer ${accessToken}`,
|
|
408
|
+
"Content-Type": "application/json",
|
|
409
|
+
"PayPal-Request-Id": requestId,
|
|
410
|
+
},
|
|
411
|
+
body: JSON.stringify(orderPayload),
|
|
412
|
+
});
|
|
413
|
+
const ppText = await ppResp.text();
|
|
414
|
+
debugId = ppResp.headers.get("paypal-debug-id");
|
|
415
|
+
if (!ppResp.ok) {
|
|
416
|
+
throw new Error(`PayPal create order error (${ppResp.status}): ${ppText}${debugId ? ` debug_id=${debugId}` : ""}`);
|
|
409
417
|
}
|
|
410
|
-
|
|
418
|
+
const order = JSON.parse(ppText);
|
|
419
|
+
const newOrderId = String(order.id || "");
|
|
420
|
+
if (!order || !newOrderId) {
|
|
411
421
|
throw new Error("Unable to resolve PayPal order details for authorization.");
|
|
412
422
|
}
|
|
413
423
|
const existingAuthorization = order?.purchase_units?.[0]?.payments?.authorizations?.[0] || null;
|
|
424
|
+
let authorization = null;
|
|
414
425
|
if (existingAuthorization) {
|
|
415
426
|
authorization = order;
|
|
416
427
|
}
|
|
417
428
|
else {
|
|
418
|
-
const authorizeResp = await fetch(`${base}/v2/checkout/orders/${
|
|
429
|
+
const authorizeResp = await fetch(`${base}/v2/checkout/orders/${newOrderId}/authorize`, {
|
|
419
430
|
method: "POST",
|
|
420
431
|
headers: {
|
|
421
432
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -434,7 +445,7 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
434
445
|
existingAuthorization?.id;
|
|
435
446
|
await this.recordSuccess("authorize_success");
|
|
436
447
|
await this.recordPaymentEvent("authorize", {
|
|
437
|
-
order_id:
|
|
448
|
+
order_id: newOrderId,
|
|
438
449
|
authorization_id: authorizationId,
|
|
439
450
|
amount,
|
|
440
451
|
currency_code: currencyCode,
|
|
@@ -446,7 +457,7 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
446
457
|
...(input.data || {}),
|
|
447
458
|
paypal: {
|
|
448
459
|
...(input.data || {}).paypal,
|
|
449
|
-
order_id:
|
|
460
|
+
order_id: newOrderId,
|
|
450
461
|
order: order || authorization,
|
|
451
462
|
authorization_id: authorizationId,
|
|
452
463
|
authorizations: authorization?.purchase_units?.[0]?.payments?.authorizations || [],
|
|
@@ -623,9 +634,7 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
623
634
|
}
|
|
624
635
|
const capture = JSON.parse(ppText);
|
|
625
636
|
const captureId = capture?.id || capture?.purchase_units?.[0]?.payments?.captures?.[0]?.id;
|
|
626
|
-
const existingCaptures = Array.isArray(paypalData.captures)
|
|
627
|
-
? paypalData.captures
|
|
628
|
-
: [];
|
|
637
|
+
const existingCaptures = Array.isArray(paypalData.captures) ? paypalData.captures : [];
|
|
629
638
|
const captureEntry = {
|
|
630
639
|
id: captureId,
|
|
631
640
|
status: capture?.status,
|
|
@@ -788,7 +797,6 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
788
797
|
await this.recordPaymentEvent("void", {
|
|
789
798
|
order_id: orderId,
|
|
790
799
|
authorization_id: authorizationId,
|
|
791
|
-
request_id: requestId,
|
|
792
800
|
});
|
|
793
801
|
}
|
|
794
802
|
else if (captureId) {
|
|
@@ -820,12 +828,6 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
820
828
|
paypalData.refund_status = refund?.status;
|
|
821
829
|
paypalData.refunds = [...existingRefunds, refundEntry];
|
|
822
830
|
await this.recordSuccess("cancel_refund_success");
|
|
823
|
-
await this.recordPaymentEvent("cancel_refund", {
|
|
824
|
-
order_id: orderId,
|
|
825
|
-
capture_id: captureId,
|
|
826
|
-
refund_id: refund?.id,
|
|
827
|
-
request_id: requestId,
|
|
828
|
-
});
|
|
829
831
|
}
|
|
830
832
|
return {
|
|
831
833
|
data: {
|
|
@@ -856,10 +858,6 @@ class PayPalPaymentProvider extends utils_1.AbstractPaymentProvider {
|
|
|
856
858
|
async deletePayment(_input) {
|
|
857
859
|
return { data: {} };
|
|
858
860
|
}
|
|
859
|
-
/**
|
|
860
|
-
* Required by AbstractPaymentProvider in Medusa v2.
|
|
861
|
-
* This is used by /hooks/payment/{identifier}_{providerId}
|
|
862
|
-
*/
|
|
863
861
|
async getWebhookActionAndData(payload) {
|
|
864
862
|
return (0, webhook_utils_1.getPayPalWebhookActionAndData)(payload);
|
|
865
863
|
}
|