diff --git a/website/client/src/components/admin/admin-panel/user-support/subscriptionAndPerks.vue b/website/client/src/components/admin/admin-panel/user-support/subscriptionAndPerks.vue index a6f1f0aa49..e8697f432e 100644 --- a/website/client/src/components/admin/admin-panel/user-support/subscriptionAndPerks.vue +++ b/website/client/src/components/admin/admin-panel/user-support/subscriptionAndPerks.vue @@ -450,7 +450,7 @@ Yes No - {{ formatDate(value) }} @@ -467,6 +467,13 @@ :href="playOrdersUrl"> Play Console + + PayPal Dashboard + @@ -605,6 +612,14 @@ const humandReadablePaymentDetails = { label: 'Next Payment Date', help: 'The date when the next payment is due. If the subscription is canceled or expired, this may be null.', }, + lastPaymentDate: { + label: 'Last Payment Date', + help: 'The date when the lastpayment was made for the subscription.', + }, + failedPayments: { + label: 'Failed Payments', + help: 'Number of times the payment failed for this subscription.', + }, } export default { diff --git a/website/server/controllers/api-v4/admin.js b/website/server/controllers/api-v4/admin.js index 800d33a135..9070483d1f 100644 --- a/website/server/controllers/api-v4/admin.js +++ b/website/server/controllers/api-v4/admin.js @@ -11,6 +11,7 @@ import { } from '../../libs/errors'; import apple from '../../libs/payments/apple'; import google from '../../libs/payments/google'; +import paypal from '../../libs/payments/paypal'; const api = {}; @@ -218,7 +219,7 @@ api.validateSubscriptionPaymentDetails = { } else if (user.purchased.plan.paymentMethod === 'Google') { paymentDetails = await google.getSubscriptionPaymentDetails(userId, user.purchased.plan); } else if (user.purchased.plan.paymentMethod === 'Paypal') { - throw new NotFound(res.t('paypalSubscriptionNotValidated')); + paymentDetails = await paypal.getSubscriptionPaymentDetails({ user }); } else if (user.purchased.plan.paymentMethod === 'Stripe') { throw new NotFound(res.t('stripeSubscriptionNotValidated')); } else if (user.purchased.plan.paymentMethod === 'Amazon Payments') { diff --git a/website/server/libs/payments/paypal.js b/website/server/libs/payments/paypal.js index 70099e6541..370604b617 100644 --- a/website/server/libs/payments/paypal.js +++ b/website/server/libs/payments/paypal.js @@ -223,6 +223,51 @@ api.subscribeSuccess = async function subscribeSuccess (options = {}) { }); }; +api.getSubscriptionPaymentDetails = async function getSubscriptionPaymentDetails (options = {}) { + const { user, groupId } = options; + let customerId; + if (groupId) { + const groupFields = basicGroupFields.concat(' purchased'); + const group = await Group.getGroup({ + user, groupId, populateLeader: false, groupFields, + }); + + if (!group) { + throw new NotFound(i18n.t('groupNotFound')); + } + + if (group.leader !== user._id) { + throw new NotAuthorized(i18n.t('onlyGroupLeaderCanManageSubscription')); + } + customerId = group.purchased.plan.customerId; + } else { + customerId = user.purchased.plan.customerId; + } + if (!customerId) throw new NotAuthorized(i18n.t('missingSubscription')); + + const customer = await this.paypalBillingAgreementGet(customerId); + if (!customer) throw new NotFound(i18n.t('subscriptionNotFound')); + + console.log('PayPal subscription details:', customer); + return { + customerId: customer.id, + originalPurchaseDate: customer.start_date, + expirationDate: customer.agreement_details.ended_at + ? customer.agreement_details.ended_at + : null, + nextPaymentDate: customer.agreement_details.next_billing_date + ? customer.agreement_details.next_billing_date + : null, + lastPaymentDate: customer.agreement_details.last_payment_date + ? customer.agreement_details.last_payment_date + : null, + productId: customer.description, + transactionId: customer.id, + isCanceled: customer.agreement_details.state === 'Inactive', + failedPayments: customer.agreement_details.failed_payment_count, + }; +}; + /** * Cancel a PayPal Subscription *