Allow gems and subs to be gifted through in-app-purchases (#10892)

* Allow gems to be gifted through IAPs

* implement non recurring IAP subscriptions

* fix localization issue in error

* fix non renewing subscription handling

* Fix lint error

* fix tests

* move findbyId mock to helper file

* undo package-lock changes

* Fix lint error
This commit is contained in:
Phillip Thelen
2018-12-23 19:20:14 +01:00
committed by Matteo Pagliazzi
parent 88f28188a1
commit cfbfec34aa
12 changed files with 557 additions and 40 deletions

View File

@@ -13,15 +13,22 @@ let api = {};
api.constants = {
PAYMENT_METHOD_GOOGLE: 'Google',
PAYMENT_METHOD_GIFT: 'Google (Gift)',
RESPONSE_INVALID_RECEIPT: 'INVALID_RECEIPT',
RESPONSE_ALREADY_USED: 'RECEIPT_ALREADY_USED',
RESPONSE_INVALID_ITEM: 'INVALID_ITEM_PURCHASED',
RESPONSE_STILL_VALID: 'SUBSCRIPTION_STILL_VALID',
};
api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, signature, headers) {
const userCanGetGems = await user.canGetGems();
if (!userCanGetGems) throw new NotAuthorized(shared.i18n.t('groupPolicyCannotGetGems', user.preferences.language));
api.verifyGemPurchase = async function verifyGemPurchase (options) {
let {gift, user, receipt, signature, headers} = options;
if (gift) {
gift.member = await User.findById(gift.uuid).exec();
}
const receiver = gift ? gift.member : user;
const receiverCanGetGems = await receiver.canGetGems();
if (!receiverCanGetGems) throw new NotAuthorized(shared.i18n.t('groupPolicyCannotGetGems', user.preferences.language));
await iap.setup();
@@ -46,6 +53,7 @@ api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, signatu
await IapPurchaseReceipt.create({
_id: token,
consumed: true,
// This should always be the buying user even for a gift.
userId: user._id,
});
@@ -70,7 +78,7 @@ api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, signatu
if (!amount) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM);
await payments.buyGems({
user,
user: receiver,
paymentMethod: this.constants.PAYMENT_METHOD_GOOGLE,
amount,
headers,
@@ -132,6 +140,74 @@ api.subscribe = async function subscribe (sku, user, receipt, signature, headers
});
};
api.noRenewSubscribe = async function noRenewSubscribe (options) {
let {sku, gift, user, receipt, signature, headers} = options;
if (!sku) throw new BadRequest(shared.i18n.t('missingSubscriptionCode'));
let subCode;
switch (sku) {
case 'com.habitrpg.android.habitica.norenew_subscription.1month':
subCode = 'basic_earned';
break;
case 'com.habitrpg.android.habitica.norenew_subscription.3month':
subCode = 'basic_3mo';
break;
case 'com.habitrpg.android.habitica.norenew_subscription.6month':
subCode = 'basic_6mo';
break;
case 'com.habitrpg.android.habitica.norenew_subscription.12month':
subCode = 'basic_12mo';
break;
}
let sub = subCode ? shared.content.subscriptionBlocks[subCode] : false;
if (!sub) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM);
await iap.setup();
let testObj = {
data: receipt,
signature,
};
let receiptObj = typeof receipt === 'string' ? JSON.parse(receipt) : receipt; // passed as a string
let token = receiptObj.token || receiptObj.purchaseToken;
let existingReceipt = await IapPurchaseReceipt.findOne({ // eslint-disable-line no-await-in-loop
_id: token,
}).exec();
if (existingReceipt) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
await IapPurchaseReceipt.create({ // eslint-disable-line no-await-in-loop
_id: token,
consumed: true,
// This should always be the buying user even for a gift.
userId: user._id,
});
let googleRes = await iap.validate(iap.GOOGLE, testObj);
let isValidated = iap.isValidated(googleRes);
if (!isValidated) throw new NotAuthorized(this.constants.RESPONSE_INVALID_RECEIPT);
let data = {
user,
paymentMethod: this.constants.PAYMENT_METHOD_GOOGLE,
headers,
sub,
autoRenews: false,
};
if (gift) {
gift.member = await User.findById(gift.uuid).exec();
gift.subscription = sub;
data.gift = gift;
data.paymentMethod = this.constants.PAYMENT_METHOD_GIFT;
}
await payments.createSubscription(data);
return googleRes;
};
api.cancelSubscribe = async function cancelSubscribe (user, headers) {
let plan = user.purchased.plan;