Skip to content

Instantly share code, notes, and snippets.

@ccurtin
Last active December 7, 2023 08:51
Show Gist options
  • Save ccurtin/27b0812351f6bface5a8ab46afeeed7d to your computer and use it in GitHub Desktop.
Save ccurtin/27b0812351f6bface5a8ab46afeeed7d to your computer and use it in GitHub Desktop.
Braintree API / node How to upgrade a MONTHLY subscription to YEARLY subscription. Rough example. Braintree does NOT automatically prorate user's monthly balance onto the yearly subscription so MUST refund the user's latest Transaction before cancelling current subscription and creating a new subscription.
// 1) Get the customer... required the get the payment method token
await braintreeGateway.customer.find(
billing_customer_id,
async (err, customer) => {
await braintreeGateway.subscription.find(
subscriptionId,
async (err, subscription) => {
if (err) {
res.status(400).json({
error_message: err && err.code
? `ERROR (braintree code ${err.code}) : ${err.message}`
: 'Unable to find subscription.'
})
}
const lastTransactionId = subscription.transactions[0].id
const lastTransactionAmount = Number(subscription.transactions[0].amount)
const billingPeriodStartDate = subscription.billingPeriodStartDate
const billingPeriodEndDate = subscription.billingPeriodEndDate
const paidForDays = daysPassedSince(billingPeriodStartDate)
const refundAmount = lastTransactionAmount - (lastTransactionAmount / (daysPassedSince(billingPeriodStartDate, billingPeriodEndDate)) * paidForDays).toFixed(2)
// 2) Prorate the rest of the month's subscription fee back to the user
await braintreeGateway.transaction.refund(
lastTransactionId, String(refundAmount),
async (err, refund) => {
if (err) {
res.status(400).json({
error_message: err && err.code
? `ERROR (braintree code ${err.code}) : ${err.message}`
: 'Unable to upgrade to new plan. Refund failure.'
})
} else {
// 3) Cancel the current subscription. Must be canceled since billing periods are different
await braintreeGateway.subscription.cancel(
subscriptionId,
async (err, result) => {
if (err) {
res.status(400).json({
error_message: err && err.code
? `ERROR (braintree code ${err.code}) : ${err.message}`
: 'Unable to upgrade to new plan. Cancellation failure.'
})
}
// 4) Create the new/yearly subscription
await braintreeGateway.subscription.create(
{
paymentMethodToken: customer.paymentMethods[0].token,
planId: new_plan_id
},
async (err, subscription) => {
if (err) {
res.status(400).json({
error_message: err && err.code
? `ERROR (braintree code ${err.code}) : ${err.message}`
: 'Unable to upgrade to new plan. Subscription create failure.'})
}
// set the new subscription_id in the database
await updateSubscriptionId(billing_customer_id, subscription.subscription.id )
// updates `user.upgrade_status` to 'upgraded' and `user.subscription_type` to 'yearly'
await setUserUpgradeStatus(req.user.id, 'upgraded', 'yearly')
res.json({subscription, refund, invoice: false})
}
)
}
)
}
}
)
}
)
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment