Skip to content

Commit 5f2d62d

Browse files
author
Varadhan Kalidasan
committed
google deferral
1 parent 2bf2394 commit 5f2d62d

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ iap.cancelSubscription("google", payment, function (error, response) {
5757
});
5858
```
5959

60+
### Subscription deferral ( Google Play only )
61+
62+
Google exposes [an API for deferral](https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/defer) of recurring suscriptions. This might be used to extend a user's subscription purchase until a specified future expiration time ( useful to grant your users some free days or months ).
63+
64+
```javascript
65+
var deferralInfo = {
66+
expectedExpiryTimeMillis: 1546616722237,
67+
desiredExpiryTimeMillis: 1547716722237,
68+
};
69+
iap.deferSubscription("google", payment, deferralInfo, function (error, response) {
70+
/* your code */
71+
});
72+
```
73+
6074
## Supported platforms
6175

6276
### Amazon

index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,39 @@ exports.cancelSubscription = function (platform, payment, cb) {
6767
cb(null, result);
6868
});
6969
};
70+
71+
72+
exports.deferSubscription = function (platform, payment, deferralInfo, cb) {
73+
function syncError(error) {
74+
process.nextTick(function () {
75+
cb(error);
76+
});
77+
}
78+
79+
if (!payment) {
80+
return syncError(new Error('No payment given'));
81+
}
82+
83+
if (!deferralInfo) {
84+
return syncError(new Error('No deferralInfo given'));
85+
}
86+
87+
const engine = platforms[platform];
88+
89+
if (!engine) {
90+
return syncError(new Error(`Platform ${platform} not recognized`));
91+
}
92+
93+
if (!engine.deferSubscription) {
94+
return syncError(new Error(`Platform ${platform
95+
} does not have deferSubscription method`));
96+
}
97+
98+
engine.deferSubscription(payment, deferralInfo, function (error, result) {
99+
if (error) {
100+
return cb(error);
101+
}
102+
103+
cb(null, result);
104+
});
105+
};

lib/google/index.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ function validatePaymentAndParseKeyObject(payment) {
4141
return keyObject;
4242
}
4343

44+
function validateDeferralInfo(deferralInfo) {
45+
assert.equal(typeof deferralInfo, 'object', 'deferralInfo must be an object');
46+
assert.equal(typeof deferralInfo.expectedExpiryTimeMillis, 'number', 'expectedExpiryTimeMillis must be a number');
47+
assert.equal(typeof deferralInfo.desiredExpiryTimeMillis, 'number', 'desiredExpiryTimeMillis must be a number');
48+
49+
assert(deferralInfo.desiredExpiryTimeMillis > deferralInfo.expectedExpiryTimeMillis, 'desiredExpiryTimeMillis must be greater than expectedExpiryTimeMillis');
50+
51+
return deferralInfo;
52+
}
53+
54+
4455
exports.verifyPayment = function (payment, cb) {
4556
let keyObject;
4657

@@ -133,3 +144,52 @@ exports.cancelSubscription = function (payment, cb) {
133144
});
134145
});
135146
};
147+
148+
149+
exports.deferSubscription = function (payment, deferralInfo, cb) {
150+
let keyObject;
151+
const options;
152+
153+
try {
154+
keyObject = validatePaymentAndParseKeyObject(payment);
155+
options.json = {
156+
deferralInfo: validateDeferralInfo(deferralInfo),
157+
};
158+
} catch (error) {
159+
return process.nextTick(function () {
160+
cb(error);
161+
});
162+
}
163+
164+
jwt.getToken(keyObject.client_email, keyObject.private_key, apiUrls.publisherScope, function (error, token) {
165+
if (error) {
166+
return cb(error);
167+
}
168+
169+
const requestUrl = apiUrls.purchasesSubscriptionsDefer(
170+
payment.packageName,
171+
payment.productId,
172+
payment.receipt,
173+
token
174+
);
175+
176+
https.post(requestUrl, options, function (error, res, resultString) {
177+
if (error) {
178+
return cb(error);
179+
}
180+
181+
if (res.statusCode !== 200) {
182+
return cb(new Error(`Received ${res.statusCode} status code with body: ${resultString}`));
183+
}
184+
185+
var resultObject;
186+
try {
187+
resultObject = JSON.parse(resultString);
188+
} catch (e) {
189+
return cb(e);
190+
}
191+
192+
return cb(null, resultObject);
193+
});
194+
});
195+
};

lib/google/urls.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,17 @@ exports.purchasesSubscriptionsCancel = function (packageName, productId, receipt
5151
encodeURIComponent(accessToken) // API access token
5252
);
5353
};
54+
55+
56+
// Android Subscriptions URLs & generators
57+
exports.purchasesSubscriptionsDefer = function (packageName, productId, receipt, accessToken) {
58+
const urlFormat = 'https://www.googleapis.com/androidpublisher/v3/applications/%s/purchases/subscriptions/%s/tokens/%s:defer?access_token=%s';
59+
60+
return util.format(
61+
urlFormat,
62+
encodeURIComponent(packageName), // application package name
63+
encodeURIComponent(productId), // productId
64+
encodeURIComponent(receipt), // purchase token
65+
encodeURIComponent(accessToken) // API access token
66+
);
67+
};

0 commit comments

Comments
 (0)