Skip to content

Commit 92011f5

Browse files
committed
fix getting subscription with given purchase id to also work if it isn't the current purchase id
- this is needed to have a grace period longer than the subscription period, and also retroactively deal with not having this
1 parent 068f828 commit 92011f5

File tree

1 file changed

+42
-10
lines changed

1 file changed

+42
-10
lines changed

src/packages/server/purchases/maintain-subscriptions.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,16 @@ async function cancelOnePendingSubscription({ account_id, purchase_id }) {
181181
// They might manually pay anyways, but we don't want to count on that.
182182
const pendingBalance = await getPendingBalance(account_id);
183183
const { pay_as_you_go_min_payment } = await getServerSettings();
184-
if (Math.abs(pendingBalance) <= pay_as_you_go_min_payment + 1) { // pandingBalance is actually <=0.
184+
if (Math.abs(pendingBalance) <= pay_as_you_go_min_payment + 1) {
185+
// pandingBalance is actually <=0.
185186
return;
186187
}
187188
const client = await getTransactionClient();
188189
try {
189-
const x = await client.query(
190-
"SELECT id FROM subscriptions WHERE latest_purchase_id=$1",
191-
[purchase_id],
190+
const subscription_id = await getSubscriptionWithPurchaseId(
191+
purchase_id,
192+
client,
192193
);
193-
const subscription_id = x.rows[0]?.id;
194-
if (!subscription_id) {
195-
throw Error(
196-
`there is no subscription with latest purchase id ${purchase_id}`,
197-
);
198-
}
199194
await cancelSubscription({
200195
account_id,
201196
subscription_id,
@@ -214,6 +209,43 @@ async function cancelOnePendingSubscription({ account_id, purchase_id }) {
214209
}
215210
}
216211

212+
// Get the id of the subscription that was (or should be) paid for using the
213+
// given purchase.
214+
export async function getSubscriptionWithPurchaseId(
215+
purchase_id: number,
216+
client?,
217+
): Promise<number> {
218+
const pool = client ?? getPool();
219+
// very easy case:
220+
const x = await pool.query(
221+
"SELECT id FROM subscriptions WHERE latest_purchase_id=$1",
222+
[purchase_id],
223+
);
224+
let subscription_id = x.rows[0]?.id;
225+
if (subscription_id) {
226+
logger.debug("getSubscriptionWithPurchaseId -- latest purchase works");
227+
return subscription_id;
228+
}
229+
// Unfortunately, we don't store that subscription id in the purchase itself,
230+
// which makes it extra work to local the subscription. But we can get it this
231+
// way by using the metadata jsonb. Basically the purchase description has the license_id
232+
// and the subscription metadata also has the same license id, and there is only one
233+
// subscription for a license. I do select the newest subscription in case there is more
234+
// than one for the same license (should be impossible).
235+
logger.debug(
236+
"getSubscriptionWithPurchaseId -- use license_id metadata and a join",
237+
);
238+
const y = await pool.query(
239+
"SELECT subscriptions.id AS id FROM purchases,subscriptions WHERE (purchases.description#>>'{license_id}')::uuid=(subscriptions.metadata#>>'{license_id}')::uuid AND purchases.id=$1 ORDER BY subscriptions.created DESC",
240+
[purchase_id],
241+
);
242+
subscription_id = y.rows[0]?.id;
243+
if (subscription_id) {
244+
return subscription_id;
245+
}
246+
throw Error(`there is no subscription with purchase id ${purchase_id}`);
247+
}
248+
217249
// This export is only to make some private functions in this file available for unit testing.
218250
// Don't otherwise use or instead change those to explicit exports if needed for some reason.
219251
export const test = {

0 commit comments

Comments
 (0)