feat(core): impl billing settings (#4652)

This commit is contained in:
liuyi
2023-10-20 09:42:33 +08:00
committed by forehalo
parent 1d62133f4f
commit 858a1da35f
16 changed files with 480 additions and 27 deletions

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "user_invoices" ADD COLUMN "link" TEXT;

View File

@@ -224,6 +224,8 @@ model UserInvoice {
// billing reason
reason String @db.VarChar
lastPaymentError String? @map("last_payment_error") @db.Text
// stripe hosted invoice link
link String? @db.Text
user User @relation(fields: [userId], references: [id], onDelete: Cascade)

View File

@@ -117,6 +117,9 @@ class UserInvoiceType implements Partial<UserInvoice> {
@Field(() => String, { nullable: true })
lastPaymentError?: string | null;
@Field(() => String, { nullable: true })
link?: string | null;
@Field(() => Date)
createdAt!: Date;
@@ -183,6 +186,13 @@ export class SubscriptionResolver {
return session.url;
}
@Mutation(() => String, {
description: 'Create a stripe customer portal to manage payment methods',
})
async createCustomerPortal(@CurrentUser() user: User) {
return this.service.createCustomerPortal(user.id);
}
@Mutation(() => UserSubscriptionType)
async cancelSubscription(@CurrentUser() user: User) {
return this.service.cancelSubscription(user.id);

View File

@@ -296,6 +296,29 @@ export class SubscriptionService {
}
}
async createCustomerPortal(id: string) {
const user = await this.db.userStripeCustomer.findUnique({
where: {
userId: id,
},
});
if (!user) {
throw new Error('Unknown user');
}
try {
const portal = await this.stripe.billingPortal.sessions.create({
customer: user.stripeCustomerId,
});
return portal.url;
} catch (e) {
this.logger.error('Failed to create customer portal.', e);
throw new Error('Failed to create customer portal');
}
}
@OnEvent('customer.subscription.created')
@OnEvent('customer.subscription.updated')
async onSubscriptionChanges(subscription: Stripe.Subscription) {
@@ -519,6 +542,7 @@ export class SubscriptionService {
currency: stripeInvoice.currency,
amount: stripeInvoice.total,
status: stripeInvoice.status ?? InvoiceStatus.Void,
link: stripeInvoice.hosted_invoice_url,
};
// handle payment error

View File

@@ -112,6 +112,7 @@ type UserInvoice {
status: InvoiceStatus!
reason: String!
lastPaymentError: String
link: String
createdAt: DateTime!
updatedAt: DateTime!
}
@@ -278,6 +279,9 @@ type Mutation {
"""Create a subscription checkout link of stripe"""
checkout(recurring: SubscriptionRecurring!): String!
"""Create a stripe customer portal to manage payment methods"""
createCustomerPortal: String!
cancelSubscription: UserSubscription!
resumeSubscription: UserSubscription!
updateSubscriptionRecurring(recurring: SubscriptionRecurring!): UserSubscription!