Apple pay tests (#10248)

* Added more tests for verifyGemPurchase

* Added more tests for subscribe

* Added user is subscribed check

* Reverted gulp task

* Added existence check
This commit is contained in:
Keith Holliday 2018-04-13 12:41:41 -05:00 committed by GitHub
parent fa044ffb44
commit 5da87640e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 158 additions and 40 deletions

View file

@ -57,6 +57,18 @@ describe('Apple Payments', () => {
});
});
it('should throw an error if getPurchaseData is invalid', async () => {
iapGetPurchaseDataStub.restore();
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_NO_ITEM_PURCHASED,
});
});
it('errors if the user cannot purchase gems', async () => {
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
@ -69,27 +81,76 @@ describe('Apple Payments', () => {
user.canGetGems.restore();
});
it('purchases gems', async () => {
it('errors if amount does not exist', async () => {
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
await applePayments.verifyGemPurchase(user, receipt, headers);
iapGetPurchaseDataStub.restore();
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
.returns([{productId: 'badProduct',
transactionId: token,
}]);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_INVALID_ITEM,
});
expect(paymentBuyGemsStub).to.be.calledOnce;
expect(paymentBuyGemsStub).to.be.calledWith({
user,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
amount: 5.25,
headers,
});
expect(user.canGetGems).to.be.calledOnce;
user.canGetGems.restore();
});
const gemsCanPurchase = [
{
productId: 'com.habitrpg.ios.Habitica.4gems',
amount: 1,
},
{
productId: 'com.habitrpg.ios.Habitica.20gems',
amount: 5.25,
},
{
productId: 'com.habitrpg.ios.Habitica.21gems',
amount: 5.25,
},
{
productId: 'com.habitrpg.ios.Habitica.42gems',
amount: 10.5,
},
{
productId: 'com.habitrpg.ios.Habitica.84gems',
amount: 21,
},
];
gemsCanPurchase.forEach(gemTest => {
it(`purchases ${gemTest.productId} gems`, async () => {
iapGetPurchaseDataStub.restore();
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
.returns([{productId: gemTest.productId,
transactionId: token,
}]);
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
await applePayments.verifyGemPurchase(user, receipt, headers);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
expect(paymentBuyGemsStub).to.be.calledOnce;
expect(paymentBuyGemsStub).to.be.calledWith({
user,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
amount: gemTest.amount,
headers,
});
expect(user.canGetGems).to.be.calledOnce;
user.canGetGems.restore();
});
});
});
describe('subscribe', () => {
@ -133,7 +194,16 @@ describe('Apple Payments', () => {
iapModule.validate.restore();
iapModule.isValidated.restore();
iapModule.getPurchaseData.restore();
payments.createSubscription.restore();
if (payments.createSubscription.restore) payments.createSubscription.restore();
});
it('should throw an error if sku is empty', async () => {
await expect(applePayments.subscribe('', user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
name: 'BadRequest',
message: i18n.t('missingSubscriptionCode'),
});
});
it('should throw an error if receipt is invalid', async () => {
@ -149,26 +219,69 @@ describe('Apple Payments', () => {
});
});
it('creates a user subscription', async () => {
const subOptions = [
{
sku: 'subscription1month',
subKey: 'basic_earned',
},
{
sku: 'com.habitrpg.ios.habitica.subscription.3month',
subKey: 'basic_3mo',
},
{
sku: 'com.habitrpg.ios.habitica.subscription.6month',
subKey: 'basic_6mo',
},
{
sku: 'com.habitrpg.ios.habitica.subscription.12month',
subKey: 'basic_12mo',
},
];
subOptions.forEach(option => {
it(`creates a user subscription for ${option.sku}`, async () => {
iapModule.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({day: 1}).toDate(),
productId: option.sku,
transactionId: token,
}]);
sub = common.content.subscriptionBlocks[option.subKey];
await applePayments.subscribe(option.sku, user, receipt, headers, nextPaymentProcessing);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledWith({
user,
customerId: token,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
sub,
headers,
additionalData: receipt,
nextPaymentProcessing,
});
});
});
it('errors when a user is already subscribed', async () => {
payments.createSubscription.restore();
user = new User();
await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledWith({
user,
customerId: token,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
sub,
headers,
additionalData: receipt,
nextPaymentProcessing,
});
await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_ALREADY_USED,
});
});
});

View file

@ -28,7 +28,7 @@ api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, headers
let appleRes = await iap.validate(iap.APPLE, receipt);
let isValidated = iap.isValidated(appleRes);
if (!isValidated) throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT);
let purchaseDataList = iap.getPurchaseData(appleRes);
const purchaseDataList = iap.getPurchaseData(appleRes);
if (purchaseDataList.length === 0) throw new NotAuthorized(api.constants.RESPONSE_NO_ITEM_PURCHASED);
let correctReceipt = false;
@ -81,8 +81,13 @@ api.verifyGemPurchase = async function verifyGemPurchase (user, receipt, headers
return appleRes;
};
api.subscribe = async function subscribe (sku, user, receipt, headers, nextPaymentProcessing = undefined) {
api.subscribe = async function subscribe (sku, user, receipt, headers, nextPaymentProcessing) {
if (user && user.isSubscribed()) {
throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
}
if (!sku) throw new BadRequest(shared.i18n.t('missingSubscriptionCode'));
let subCode;
switch (sku) {
case 'subscription1month':
@ -98,13 +103,12 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme
subCode = 'basic_12mo';
break;
}
let sub = subCode ? shared.content.subscriptionBlocks[subCode] : false;
const sub = subCode ? shared.content.subscriptionBlocks[subCode] : false;
if (!sub) throw new NotAuthorized(this.constants.RESPONSE_INVALID_ITEM);
await iap.setup();
let appleRes = await iap.validate(iap.APPLE, receipt);
let isValidated = iap.isValidated(appleRes);
const isValidated = iap.isValidated(appleRes);
if (!isValidated) throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT);
let purchaseDataList = iap.getPurchaseData(appleRes);
@ -121,6 +125,7 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme
break;
}
}
if (transactionId) {
let existingUser = await User.findOne({
'purchased.plan.customerId': transactionId,