Skip to content

Commit

Permalink
Merge pull request #241 from Pinelab-studio/feat/store-payment-status
Browse files Browse the repository at this point in the history
Storing failed and successful payments for Stripe Plugin
  • Loading branch information
martijnvdbrug authored Aug 16, 2023
2 parents 110a883 + 9101619 commit 5e6d335
Show file tree
Hide file tree
Showing 25 changed files with 1,580 additions and 573 deletions.
1 change: 1 addition & 0 deletions packages/vendure-plugin-stripe-subscription/codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ generates:
plugins:
- typescript
- typescript-operations
- typed-document-node
config:
avoidOptionals: false
scalars:
Expand Down
2 changes: 1 addition & 1 deletion packages/vendure-plugin-stripe-subscription/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pinelab/vendure-plugin-stripe-subscription",
"version": "1.1.1",
"version": "1.2.0",
"description": "Vendure plugin for selling subscriptions via Stripe",
"author": "Martijn van de Brug <[email protected]>",
"homepage": "https://pinelab-plugins.com/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ const _scalar = gql`
scalar DateTime
`;

const _interface = gql`
interface Node {
id: ID!
createdAt: DateTime
}
interface PaginatedList {
items: [Node!]!
totalItems: Int!
}
`;

const sharedTypes = gql`
enum SubscriptionInterval {
week
Expand All @@ -18,7 +29,10 @@ const sharedTypes = gql`
time_of_purchase
fixed_startdate
}
type StripeSubscriptionSchedule {
"""
For codegen to work this must implement Node
"""
type StripeSubscriptionSchedule implements Node {
id: ID!
createdAt: DateTime
updatedAt: DateTime
Expand All @@ -35,6 +49,21 @@ const sharedTypes = gql`
useProration: Boolean
autoRenew: Boolean
}
"""
For codegen to work this must implement Node
"""
type StripeSubscriptionPayment implements Node {
id: ID!
createdAt: DateTime
updatedAt: DateTime
collectionMethod: String
charge: Int
currency: String
orderCode: String
channelId: ID
eventType: String
subscriptionId: String
}
input UpsertStripeSubscriptionScheduleInput {
id: ID
name: String!
Expand Down Expand Up @@ -113,9 +142,39 @@ export const adminSchemaExtensions = gql`
STRIPE_SUBSCRIPTION_NOTIFICATION
}
"""
For codegen to work this must be non-empty
"""
input StripeSubscriptionPaymentListOptions {
skip: Int
}
"""
For codegen to work this must be non-empty
"""
input StripeSubscriptionScheduleListOptions {
skip: Int
}
type StripeSubscriptionPaymentList implements PaginatedList {
items: [StripeSubscriptionPayment!]!
totalItems: Int!
}
type StripeSubscriptionScheduleList implements PaginatedList {
items: [StripeSubscriptionSchedule!]!
totalItems: Int!
}
extend type Query {
stripeSubscriptionSchedules: [StripeSubscriptionSchedule!]!
stripeSubscriptionSchedules(
options: StripeSubscriptionScheduleListOptions
): StripeSubscriptionScheduleList!
stripeSubscriptionPayments(
options: StripeSubscriptionPaymentListOptions
): StripeSubscriptionPaymentList!
}
extend type Mutation {
upsertStripeSubscriptionSchedule(
input: UpsertStripeSubscriptionScheduleInput!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Injectable } from '@nestjs/common';
import {
ID,
ListQueryBuilder,
RequestContext,
TransactionalConnection,
UserInputError,
} from '@vendure/core';
import {
StripeSubscriptionSchedule,
StripeSubscriptionScheduleList,
StripeSubscriptionScheduleListOptions,
SubscriptionStartMoment,
UpsertStripeSubscriptionScheduleInput,
} from '../ui/generated/graphql';
Expand All @@ -15,18 +18,24 @@ import { Schedule } from './schedule.entity';

@Injectable()
export class ScheduleService {
constructor(private connection: TransactionalConnection) {}
constructor(
private listQueryBuilder: ListQueryBuilder,
private connection: TransactionalConnection
) {}

async getSchedules(
ctx: RequestContext
): Promise<StripeSubscriptionSchedule[]> {
const schedules = await this.connection
.getRepository(ctx, Schedule)
.find({ where: { channelId: String(ctx.channelId) } });

return schedules.map((schedule) => {
return cloneSchedule(ctx, schedule);
});
ctx: RequestContext,
options: StripeSubscriptionScheduleListOptions
): Promise<StripeSubscriptionScheduleList> {
return this.listQueryBuilder
.build(Schedule, options, { ctx })
.getManyAndCount()
.then(([items, totalItems]) => ({
items: items.map((schedule) => {
return cloneSchedule(ctx, schedule);
}),
totalItems,
}));
}

async upsert(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DeepPartial } from '@vendure/common/lib/shared-types';
import { VendureEntity, ID } from '@vendure/core';
import { Column, Entity } from 'typeorm';

@Entity()
export class StripeSubscriptionPayment extends VendureEntity {
constructor(input?: DeepPartial<StripeSubscriptionPayment>) {
super(input);
}

@Column({ nullable: true })
invoiceId!: string;

@Column({ nullable: true })
collectionMethod!: string;

@Column({ nullable: true })
eventType!: string;

@Column({ nullable: true })
charge!: number;

@Column({ nullable: true })
currency!: string;

@Column({ nullable: true })
orderCode!: string;

@Column({ nullable: true })
channelId!: string;

@Column({ nullable: true })
subscriptionId!: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,24 @@ import { Request } from 'express';
import { loggerCtx, PLUGIN_INIT_OPTIONS } from '../constants';
import { StripeSubscriptionPluginOptions } from '../stripe-subscription.plugin';
import {
StripeSubscriptionPaymentList,
StripeSubscriptionPaymentListOptions,
StripeSubscriptionPricing,
StripeSubscriptionPricingInput,
StripeSubscriptionSchedule,
StripeSubscriptionScheduleList,
StripeSubscriptionScheduleListOptions,
UpsertStripeSubscriptionScheduleInput,
} from '../ui/generated/graphql';
import { ScheduleService } from './schedule.service';
import { StripeSubscriptionService } from './stripe-subscription.service';
import { IncomingStripeWebhook } from './stripe.types';
import {
OrderLineWithSubscriptionFields,
VariantWithSubscriptionFields,
} from './subscription-custom-fields';
import { StripeInvoice } from './types/stripe-invoice';
import { StripePaymentIntent } from './types/stripe-payment-intent';
import { IncomingStripeWebhook } from './types/stripe.types';

export type RequestWithRawBody = Request & { rawBody: any };

Expand Down Expand Up @@ -141,14 +147,27 @@ export class AdminPriceIncludesTaxResolver {

@Resolver()
export class AdminResolver {
constructor(private scheduleService: ScheduleService) {}
constructor(
private stripeSubscriptionService: StripeSubscriptionService,
private scheduleService: ScheduleService
) {}

@Allow(Permission.ReadSettings)
@Query()
async stripeSubscriptionSchedules(
@Ctx() ctx: RequestContext
): Promise<StripeSubscriptionSchedule[]> {
return this.scheduleService.getSchedules(ctx);
@Ctx() ctx: RequestContext,
@Args('options') options: StripeSubscriptionScheduleListOptions
): Promise<StripeSubscriptionScheduleList> {
return this.scheduleService.getSchedules(ctx, options);
}

@Allow(Permission.ReadSettings)
@Query()
async stripeSubscriptionPayments(
@Ctx() ctx: RequestContext,
@Args('options') options: StripeSubscriptionPaymentListOptions
): Promise<StripeSubscriptionPaymentList> {
return this.stripeSubscriptionService.getPaymentEvents(ctx, options);
}

@Allow(Permission.UpdateSettings)
Expand Down Expand Up @@ -188,11 +207,11 @@ export class StripeSubscriptionController {
Logger.info(`Incoming webhook ${body.type}`, loggerCtx);
// Validate if metadata present
const orderCode =
body.data.object.metadata?.orderCode ||
body.data.object.lines?.data[0]?.metadata.orderCode;
body.data.object.metadata?.orderCode ??
(body.data.object as StripeInvoice).lines?.data[0]?.metadata.orderCode;
const channelToken =
body.data.object.metadata?.channelToken ||
body.data.object.lines?.data[0]?.metadata.channelToken;
body.data.object.metadata?.channelToken ??
(body.data.object as StripeInvoice).lines?.data[0]?.metadata.channelToken;
if (
body.type !== 'payment_intent.succeeded' &&
body.type !== 'invoice.payment_failed' &&
Expand Down Expand Up @@ -234,21 +253,45 @@ export class StripeSubscriptionController {
if (body.type === 'payment_intent.succeeded') {
await this.stripeSubscriptionService.handlePaymentIntentSucceeded(
ctx,
body,
body.data.object as StripePaymentIntent,
order
);
} else if (body.type === 'invoice.payment_succeeded') {
const invoiceObject = body.data.object as StripeInvoice;
await this.stripeSubscriptionService.handleInvoicePaymentSucceeded(
ctx,
body,
invoiceObject,
order
);
await this.stripeSubscriptionService.savePaymentEvent(
ctx,
body.type,
invoiceObject
);
} else if (body.type === 'invoice.payment_failed') {
const invoiceObject = body.data.object as StripeInvoice;
await this.stripeSubscriptionService.handleInvoicePaymentFailed(
ctx,
invoiceObject,
order
);
await this.stripeSubscriptionService.savePaymentEvent(
ctx,
body.type,
invoiceObject
);
} else if (body.type === 'invoice.payment_action_required') {
const invoiceObject = body.data.object as StripeInvoice;
await this.stripeSubscriptionService.handleInvoicePaymentFailed(
ctx,
body,
invoiceObject,
order
);
await this.stripeSubscriptionService.savePaymentEvent(
ctx,
body.type,
invoiceObject
);
}
Logger.info(`Successfully handled webhook ${body.type}`, loggerCtx);
} catch (error) {
Expand Down
Loading

0 comments on commit 5e6d335

Please sign in to comment.