fix(paypal): log no request bodies, headers or PayPal orders
The PayPal SDK client logged every request body to the console, and with it the buyer's name and address in the order request, as well as the response headers. createSessionOrThrow logged the whole request at silly, which production wrote, and PayPal's whole answer at info. The webhook logged its whole body, and the controller's webhook error handler the error object as JSON. The SDK now logs method, URL and status of each request, without bodies or headers. createSessionOrThrow logs the shop's order id, the PayPal order's id and its status, and an error by describeError with the order id. The webhook logs the event type and the id and status of its resource; its error handler describes the error. Refs libretech/mp#71 Refs libretech/mp#67
This commit is contained in:
2 files changed
+27
-21
No files matched your search
@@ -358,7 +358,7 @@ export default factories.createCoreController("api::order.order", ({ strapi }) =
|
|||||||
try {
|
try {
|
||||||
return await paypalApi.handleWebhook(ctx.request.body);
|
return await paypalApi.handleWebhook(ctx.request.body);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
strapi.log.error(`paypal-api: ${JSON.stringify({ error })}`);
|
strapi.log.error("app:e:order-controller: Handling the PayPal webhook failed", { error: describeError(error) });
|
||||||
return ctx.internalServerError();
|
return ctx.internalServerError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-20
@@ -16,10 +16,12 @@ class PayPalApi {
|
|||||||
},
|
},
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
environment,
|
environment,
|
||||||
|
// The SDK logs method, URL and status of each request to the console. Not the bodies: a request body holds the buyer's name
|
||||||
|
// and address. Not the headers: they hold the token.
|
||||||
logging: {
|
logging: {
|
||||||
logLevel: LogLevel.Info,
|
logLevel: LogLevel.Info,
|
||||||
logRequest: { logBody: true },
|
logRequest: { logBody: false, logHeaders: false },
|
||||||
logResponse: { logHeaders: true }
|
logResponse: { logBody: false, logHeaders: false }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.ordersController = new OrdersController(this.client);
|
this.ordersController = new OrdersController(this.client);
|
||||||
@@ -29,28 +31,25 @@ class PayPalApi {
|
|||||||
* Creates the PayPal order for the order and returns PayPal's id for it, which the capture accepts, and its links. Throws
|
* Creates the PayPal order for the order and returns PayPal's id for it, which the capture accepts, and its links. Throws
|
||||||
* CheckoutNotReadyError or ProductUnavailableError before PayPal is asked, for an order PayPal cannot take.
|
* CheckoutNotReadyError or ProductUnavailableError before PayPal is asked, for an order PayPal cannot take.
|
||||||
*/
|
*/
|
||||||
async createSessionOrThrow(returnUrl: string, order: PayPalOrderSource): Promise<{ id: string; links: unknown }> {
|
async createSessionOrThrow(returnUrl: string, order: PayPalOrderSource & { id?: number | string }): Promise<{ id: string; links: unknown }> {
|
||||||
// Outside the try block: an order that is not ready is the customer's to fix, not a PayPal failure.
|
// Outside the try block: an order that is not ready is the customer's to fix, not a PayPal failure.
|
||||||
const request = buildPayPalOrderRequest(returnUrl, order) as OrderRequest;
|
const request = buildPayPalOrderRequest(returnUrl, order) as OrderRequest;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const collect = { body: request, prefer: "return=minimal" };
|
const { body } = await this.ordersController.ordersCreate({ body: request, prefer: "return=minimal" });
|
||||||
|
const session = JSON.parse(body as string);
|
||||||
|
if (typeof session?.id !== "string" || session.id === "") throw new Error("PayPal answered without an order id");
|
||||||
|
|
||||||
strapi.log.silly("app:d:paypal-api: Creating PayPal order " + JSON.stringify({ collect }));
|
// The PayPal order's id and status only: the request and PayPal's order hold the buyer's name and address.
|
||||||
|
strapi.log.info("app:i:paypal-api: Created the PayPal order", {
|
||||||
const { body, ...httpResponse } = await this.ordersController.ordersCreate(collect);
|
order: order.id,
|
||||||
const sessionData = JSON.parse(body as string);
|
paypalOrderId: session.id,
|
||||||
|
status: typeof session.status === "string" ? session.status : undefined
|
||||||
strapi.log.info("app:i:paypal-api: " + JSON.stringify({ session: sessionData }));
|
});
|
||||||
|
return { id: session.id, links: session.links };
|
||||||
if (typeof sessionData?.id !== "string" || sessionData.id === "") throw new Error("PayPal answered without an order id");
|
|
||||||
return {
|
|
||||||
id: sessionData.id,
|
|
||||||
links: sessionData.links
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Never the error object: an SDK or axios error carries the request with its Authorization header.
|
// Never the error object: an SDK or axios error carries the request with its Authorization header.
|
||||||
strapi.log.error("app:e:paypal-api: Creating the PayPal order failed", { error: describeError(error) });
|
strapi.log.error("app:e:paypal-api: Creating the PayPal order failed", { order: order.id, error: describeError(error) });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,9 +70,16 @@ class PayPalApi {
|
|||||||
return JSON.parse(body as string);
|
return JSON.parse(body as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleWebhook(req: any) {
|
async handleWebhook(req: unknown) {
|
||||||
// Implement PayPal webhook validation and processing
|
// Implement PayPal webhook validation and processing. The event's resource holds the buyer's name and address, so only the
|
||||||
strapi.log.verbose("app:i:paypal-api: " + JSON.stringify({ req }));
|
// event type and the resource's id and status are logged.
|
||||||
|
const event = (typeof req === "object" && req !== null ? req : {}) as { event_type?: unknown; resource?: { id?: unknown; status?: unknown } };
|
||||||
|
const textOf = (value: unknown) => (typeof value === "string" ? value : undefined);
|
||||||
|
strapi.log.verbose("app:v:paypal-api: Webhook received", {
|
||||||
|
eventType: textOf(event.event_type),
|
||||||
|
resource: textOf(event.resource?.id),
|
||||||
|
status: textOf(event.resource?.status)
|
||||||
|
});
|
||||||
|
|
||||||
// Note: PayPal webhook handling requires specific implementation
|
// Note: PayPal webhook handling requires specific implementation
|
||||||
// This is a placeholder and should be replaced with actual PayPal webhook validation
|
// This is a placeholder and should be replaced with actual PayPal webhook validation
|
||||||
|
|||||||
Reference in new issue
Block a user