You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
3.6 KiB

import paypal from '@paypal/checkout-server-sdk';
import assert from "assert";
import { paypal_private } from "../lib/config.js";
import { Product, Payment } from "../lib/models.js";
import { product_id } from "../client/config.js";
import logging from "../lib/logging.js";
import { developer_admin } from "../lib/api.js";
const log = logging.create("queue/paypal.js");
const create_paypal = () => {
if(developer_admin) {
log.warn("Running in DANGER_ADMIN mode so using the Sandbox for Paypal.");
const environment = new paypal.core.SandboxEnvironment(paypal_private.client_id, paypal_private.secret);
const client = new paypal.core.PayPalHttpClient(environment);
return [environment, client];
} else {
const environment = new paypal.core.LiveEnvironment(paypal_private.client_id, paypal_private.secret);
const client = new paypal.core.PayPalHttpClient(environment);
return [environment, client];
}
}
const [environment, client] = create_paypal();
const order_is_valid = (course, response, payment) => {
let order = response.result.purchase_units[0];
assert(course.id, "Product is invalid in order_is_valid check.");
assert(payment.id, "Payment is invalid in order_is_valid check.");
if(!order) {
log.error(`Invalid response payment ${payment.id}, no order in the purchase_units.`);
return [false, "invalid_purchase_units"];
} else if(parseInt(order.amount.value, 10) !== course.price) {
// BUG: this will fail if you use a float for the price
log.error(`Wrong price ${order.amount.value}`);
return [false, "invalid_order_amount"];
} else if(order.payments.captures.length < 1) {
log.error("Not enough captures, only 0.");
return [false, "invalid_capture_count"];
} else if(order.payments.captures[0].status !== "COMPLETED") {
log.error(`Capture not completed: ${order.payments.captures.status}`);
return [false, "capture_not_completed"];
} else if(parseInt(order.payments.captures[0].amount.value, 10) !== course.price) {
console.error(`Capture amount ${order.payments.captures[0].amount.value} doesn't match course price ${course.price}`);
return [false, "capture_price_mismatch"];
} else {
return [true, "validated"];
}
}
export const validate_order = async (job) => {
try {
assert(job.data.payment_id, `Invalid order ID: ${JSON.stringify(job)}`);
const course = await Product.first({id: product_id});
assert(course, `No course for ID ${ product_id } from client/config.js:product_id`);
const payment = await Payment.first({id: job.data.payment_id});
assert(payment, `No payment for ID ${ job.payment_id }`);
let request = new paypal.orders.OrdersGetRequest(payment.sys_primary_id);
// Call API with your client and get a response for your call
let response = await client.execute(request);
log.debug(`Validating Paypal ID ${response.result.id}`);
const [valid, status_reason] = order_is_valid(course, response, payment);
if(valid) {
log.info(`Order for payment ${payment.id} is validated.`);
await Payment.update({id: payment.id}, {status: "complete", status_reason});
} else {
log.error(`Invalid Order for payment ${payment.id}.`);
await Payment.update({id: payment.id}, {status: "failed", status_reason});
}
} catch(error) {
log.error(error, `Processing payment paypal verification for ${job.data.payment_id}`);
try {
await Payment.update({id: job.data.payment_id}, {status: "failed", status_reason: "check_logs"});
} catch (e) {
log.error(error, `Failed to update the database with failure on payment id ${job.data.payment_id}`);
}
}
}