@unchainedshop/plugins
Version:
Official plugin collection for the Unchained Engine with payment, delivery, and pricing adapters
104 lines (103 loc) • 5.27 kB
JavaScript
import {} from '@unchainedshop/api';
import { EnrollmentStatus } from '@unchainedshop/core-enrollments';
import { createLogger } from '@unchainedshop/logger';
import { fixPeriods } from "./fix-periods.js";
const logger = createLogger('unchained:apple-iap:handler');
const { APPLE_IAP_SHARED_SECRET } = process.env;
const AppleNotificationTypes = {
INITIAL_BUY: 'INITIAL_BUY',
DID_RECOVER: 'DID_RECOVER',
DID_CHANGE_RENEWAL_STATUS: 'DID_CHANGE_RENEWAL_STATUS',
DID_FAIL_TO_RENEW: 'DID_FAIL_TO_RENEW',
DID_CHANGE_RENEWAL_PREF: 'DID_CHANGE_RENEWAL_PREF',
};
export const appleIAPHandler = async (req, res) => {
if (req.method === 'POST') {
try {
const resolvedContext = req.unchainedContext;
const { modules, services } = resolvedContext;
const responseBody = req.body || {};
if (responseBody.password !== APPLE_IAP_SHARED_SECRET) {
throw new Error('shared secret not valid');
}
const transactions = responseBody?.unified_receipt?.latest_receipt_info;
const latestTransaction = transactions[0];
if (responseBody.notification_type === AppleNotificationTypes.INITIAL_BUY) {
const orderPayment = await modules.orders.payments.findOrderPaymentByTransactionId(latestTransaction.transaction_id);
if (!orderPayment)
throw new Error('Could not find any matching order payment');
const order = await services.orders.checkoutOrder(orderPayment.orderId, {
paymentContext: {
receiptData: responseBody?.unified_receipt?.latest_receipt,
},
});
if (!order)
throw new Error(`Order with id ${orderPayment.orderId} not found`);
const orderId = order._id;
const enrollment = await modules.enrollments.findEnrollment({
orderId,
});
if (!enrollment)
throw new Error('Could not find any matching enrollment');
await fixPeriods({
transactionId: latestTransaction.original_transaction_id,
transactions,
enrollmentId: enrollment._id,
orderId,
}, resolvedContext);
logger.info(`Apple IAP Webhook: Confirmed checkout for order ${order.orderNumber}`, {
orderId: order._id,
});
}
else {
const originalOrderPayment = await modules.orders.payments.findOrderPaymentByTransactionId(latestTransaction.original_transaction_id);
if (!originalOrderPayment)
throw new Error('Could not find any matching order payment');
const originalOrder = await modules.orders.findOrder({
orderId: originalOrderPayment.orderId,
});
const enrollment = originalOrder &&
(await modules.enrollments.findEnrollment({
orderId: originalOrder._id,
}));
if (!enrollment?.payment)
throw new Error('Could not find a valid enrollment payment method');
await services.orders.registerPaymentCredentials(enrollment.payment.paymentProviderId, {
transactionContext: {
receiptData: responseBody?.unified_receipt?.latest_receipt,
},
userId: enrollment.userId,
});
await fixPeriods({
transactionId: latestTransaction.original_transaction_id,
transactions,
enrollmentId: enrollment._id,
orderId: originalOrder._id,
}, resolvedContext);
logger.info(`Apple IAP Webhook: Processed notification for ${latestTransaction.original_transaction_id} and type ${responseBody.notification_type}`);
if (responseBody.notification_type === AppleNotificationTypes.DID_RECOVER) {
if (enrollment.status !== EnrollmentStatus.TERMINATED &&
responseBody.auto_renew_status === 'false') {
await services.enrollments.terminateEnrollment(enrollment);
}
}
if (responseBody.notification_type === AppleNotificationTypes.DID_CHANGE_RENEWAL_STATUS) {
if (enrollment.status !== EnrollmentStatus.TERMINATED &&
responseBody.auto_renew_status === 'false') {
await services.enrollments.terminateEnrollment(enrollment);
}
}
logger.info(`Apple IAP Webhook: Updated enrollment from Apple`);
}
res.status(200).end();
return;
}
catch (e) {
logger.warn(`Apple IAP Webhook: ${e.name} - ${e.message}`);
res.status(503).send({ name: e.name, code: e.code, message: e.message });
return;
}
}
res.status(404).end();
};
export default appleIAPHandler;