mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-29 13:52:10 +03:00
Updated subscriptions for Members Admin API
refs https://github.com/TryGhost/Team/issues/616 We need a way to assign Products to Members via a Subscription, and we've followed the same pattern as the editSubscription method for the Members API controller, which acts upon Subscriptions as a nested resource. Subscriptions now are linked to products, and we've included those links by default in the Member Admin API as we already include subscriptions by default, and Products are now a core part of the Members feature-set.
This commit is contained in:
parent
7bce05ab86
commit
33f26fbf32
@ -40,7 +40,7 @@ module.exports = {
|
||||
permissions: true,
|
||||
validation: {},
|
||||
async query(frame) {
|
||||
frame.options.withRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer'];
|
||||
frame.options.withRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct'];
|
||||
const page = await membersService.api.members.list(frame.options);
|
||||
|
||||
return page;
|
||||
@ -65,7 +65,7 @@ module.exports = {
|
||||
},
|
||||
permissions: true,
|
||||
async query(frame) {
|
||||
const defaultWithRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer'];
|
||||
const defaultWithRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct'];
|
||||
|
||||
if (!frame.options.withRelated) {
|
||||
frame.options.withRelated = defaultWithRelated;
|
||||
@ -109,7 +109,7 @@ module.exports = {
|
||||
permissions: true,
|
||||
async query(frame) {
|
||||
let member;
|
||||
frame.options.withRelated = ['stripeSubscriptions', 'stripeSubscriptions.customer'];
|
||||
frame.options.withRelated = ['stripeSubscriptions', 'products', 'labels', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct'];
|
||||
try {
|
||||
if (!membersService.config.isStripeConnected()
|
||||
&& (frame.data.members[0].stripe_customer_id || frame.data.members[0].comped)) {
|
||||
@ -185,7 +185,7 @@ module.exports = {
|
||||
permissions: true,
|
||||
async query(frame) {
|
||||
try {
|
||||
frame.options.withRelated = ['stripeSubscriptions', 'labels'];
|
||||
frame.options.withRelated = ['stripeSubscriptions', 'products', 'labels', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct'];
|
||||
const member = await membersService.api.members.update(frame.data.members[0], frame.options);
|
||||
|
||||
const hasCompedSubscription = !!member.related('stripeSubscriptions').find(sub => sub.get('plan_nickname') === 'Complimentary' && sub.get('status') === 'active');
|
||||
@ -255,7 +255,51 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
let model = await membersService.api.members.get({id: frame.options.id}, {
|
||||
withRelated: ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer']
|
||||
withRelated: ['labels', 'products', 'stripeSubscriptions', 'stripeSubscriptions.customer', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct']
|
||||
});
|
||||
if (!model) {
|
||||
throw new errors.NotFoundError({
|
||||
message: i18n.t('errors.api.members.memberNotFound')
|
||||
});
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
},
|
||||
|
||||
createSubscription: {
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
options: [
|
||||
'id'
|
||||
],
|
||||
data: [
|
||||
'stripe_price_id'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
id: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: {
|
||||
stripe_price_id: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
},
|
||||
permissions: {
|
||||
method: 'edit'
|
||||
},
|
||||
async query(frame) {
|
||||
await membersService.api.members.createSubscription({
|
||||
id: frame.options.id,
|
||||
subscription: {
|
||||
stripe_price_id: frame.data.stripe_price_id
|
||||
}
|
||||
});
|
||||
let model = await membersService.api.members.get({id: frame.options.id}, {
|
||||
withRelated: ['labels', 'products', 'stripeSubscriptions', 'stripeSubscriptions.customer', 'stripeSubscriptions.stripePrice', 'stripeSubscriptions.stripePrice.stripeProduct']
|
||||
});
|
||||
if (!model) {
|
||||
throw new errors.NotFoundError({
|
||||
|
@ -10,6 +10,7 @@ module.exports = {
|
||||
edit: createSerializer('edit', singleMember),
|
||||
add: createSerializer('add', singleMember),
|
||||
editSubscription: createSerializer('editSubscription', singleMember),
|
||||
createSubscription: createSerializer('createSubscription', singleMember),
|
||||
bulkDestroy: createSerializer('bulkDestroy', passthrough),
|
||||
|
||||
exportCSV: createSerializer('exportCSV', exportCSV),
|
||||
@ -197,11 +198,16 @@ function createSerializer(debugString, serialize) {
|
||||
* @prop {null|string} customer.name
|
||||
* @prop {string} customer.email
|
||||
*
|
||||
* @prop {Object} plan
|
||||
* @prop {string} plan.id
|
||||
* @prop {string} plan.nickname
|
||||
* @prop {number} plan.amount
|
||||
* @prop {string} plan.currency
|
||||
* @prop {Object} price
|
||||
* @prop {string} price.id
|
||||
* @prop {string} price.nickname
|
||||
* @prop {number} price.amount
|
||||
* @prop {string} price.interval
|
||||
* @prop {string} price.currency
|
||||
*
|
||||
* @prop {Object} price.product
|
||||
* @prop {string} price.product.id
|
||||
* @prop {string} price.product.product_id
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -7,10 +7,14 @@ const StripeCustomerSubscription = ghostBookshelf.Model.extend({
|
||||
return this.belongsTo('MemberStripeCustomer', 'customer_id', 'customer_id');
|
||||
},
|
||||
|
||||
stripePrice() {
|
||||
return this.hasOne('StripePrice', 'stripe_price_id', 'stripe_price_id');
|
||||
},
|
||||
|
||||
serialize(options) {
|
||||
const defaultSerializedObject = ghostBookshelf.Model.prototype.serialize.call(this, options);
|
||||
|
||||
return {
|
||||
const serialized = {
|
||||
id: defaultSerializedObject.subscription_id,
|
||||
customer: {
|
||||
id: defaultSerializedObject.customer_id,
|
||||
@ -32,6 +36,26 @@ const StripeCustomerSubscription = ghostBookshelf.Model.extend({
|
||||
cancellation_reason: defaultSerializedObject.cancellation_reason,
|
||||
current_period_end: defaultSerializedObject.current_period_end
|
||||
};
|
||||
|
||||
if (defaultSerializedObject.stripePrice) {
|
||||
serialized.price = {
|
||||
id: defaultSerializedObject.stripePrice.stripe_price_id,
|
||||
nickname: defaultSerializedObject.stripePrice.nickname,
|
||||
amount: defaultSerializedObject.stripePrice.amount,
|
||||
interval: defaultSerializedObject.stripePrice.interval,
|
||||
currency: String.prototype.toUpperCase.call(defaultSerializedObject.stripePrice.currency)
|
||||
};
|
||||
|
||||
if (defaultSerializedObject.stripePrice.stripeProduct) {
|
||||
serialized.price.product = {
|
||||
id: defaultSerializedObject.stripePrice.stripeProduct.stripe_product_id,
|
||||
name: defaultSerializedObject.stripePrice.stripeProduct.name,
|
||||
product_id: defaultSerializedObject.stripePrice.stripeProduct.product_id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
}, {
|
||||
|
@ -121,6 +121,7 @@ module.exports = function apiRoutes() {
|
||||
router.put('/members/:id', mw.authAdminApi, http(apiCanary.members.edit));
|
||||
router.del('/members/:id', mw.authAdminApi, http(apiCanary.members.destroy));
|
||||
|
||||
router.post('/members/:id/subscriptions/', mw.authAdminApi, http(apiCanary.members.createSubscription));
|
||||
router.put('/members/:id/subscriptions/:subscription_id', mw.authAdminApi, http(apiCanary.members.editSubscription));
|
||||
|
||||
router.get('/members/:id/signin_urls', mw.authAdminApi, http(apiCanary.memberSigninUrls.read));
|
||||
|
@ -234,7 +234,7 @@ describe('Members API', function () {
|
||||
should.exist(jsonResponse2);
|
||||
should.exist(jsonResponse2.members);
|
||||
jsonResponse2.members.should.have.length(1);
|
||||
localUtils.API.checkResponse(jsonResponse2.members[0], 'member', 'subscriptions');
|
||||
localUtils.API.checkResponse(jsonResponse2.members[0], 'member', ['subscriptions', 'products']);
|
||||
jsonResponse2.members[0].name.should.equal(memberChanged.name);
|
||||
jsonResponse2.members[0].email.should.equal(memberChanged.email);
|
||||
jsonResponse2.members[0].email.should.not.equal(memberToChange.email);
|
||||
|
Loading…
Reference in New Issue
Block a user