mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
feat: listen to stripe account updated webhook
This commit is contained in:
@@ -2,7 +2,7 @@ import { NextFunction, Request, Response, Router } from 'express';
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import { StripeCheckoutSessionCompletedEventPayload } from '@/interfaces/StripePayment';
|
import { StripeWebhookEventPayload } from '@/interfaces/StripePayment';
|
||||||
import { StripePaymentService } from '@/services/StripePayment/StripePaymentService';
|
import { StripePaymentService } from '@/services/StripePayment/StripePaymentService';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
@@ -59,12 +59,16 @@ export class StripeWebhooksController {
|
|||||||
events.stripeWebhooks.onCheckoutSessionCompleted,
|
events.stripeWebhooks.onCheckoutSessionCompleted,
|
||||||
{
|
{
|
||||||
event,
|
event,
|
||||||
} as StripeCheckoutSessionCompletedEventPayload
|
} as StripeWebhookEventPayload
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'payment_intent.payment_failed':
|
case 'account.updated':
|
||||||
// Handle failed payment intent
|
this.eventPublisher.emitAsync(
|
||||||
console.log('PaymentIntent failed.');
|
events.stripeWebhooks.onAccountUpdated,
|
||||||
|
{
|
||||||
|
event,
|
||||||
|
} as StripeWebhookEventPayload
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
// Add more cases as needed
|
// Add more cases as needed
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -14,3 +14,8 @@ export interface StripeInvoiceCheckoutSessionPOJO {
|
|||||||
publishableKey: string;
|
publishableKey: string;
|
||||||
redirectTo: string;
|
redirectTo: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface StripeWebhookEventPayload {
|
||||||
|
event: any;
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInject
|
|||||||
@Service()
|
@Service()
|
||||||
export class GetInvoicePaymentLinkMetadata {
|
export class GetInvoicePaymentLinkMetadata {
|
||||||
@Inject()
|
@Inject()
|
||||||
tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private transformer: TransformerInjectable;
|
private transformer: TransformerInjectable;
|
||||||
@@ -23,12 +23,9 @@ export class GetInvoicePaymentLinkMetadata {
|
|||||||
async getInvoicePaymentLinkMeta(linkId: string) {
|
async getInvoicePaymentLinkMeta(linkId: string) {
|
||||||
const paymentLink = await PaymentLink.query()
|
const paymentLink = await PaymentLink.query()
|
||||||
.findOne('linkId', linkId)
|
.findOne('linkId', linkId)
|
||||||
|
.where('resourceType', 'SaleInvoice')
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
//
|
|
||||||
if (paymentLink.resourceType !== 'SaleInvoice') {
|
|
||||||
throw new ServiceError('');
|
|
||||||
}
|
|
||||||
// Validate the expiry at date.
|
// Validate the expiry at date.
|
||||||
if (paymentLink.expiryAt) {
|
if (paymentLink.expiryAt) {
|
||||||
const currentDate = moment();
|
const currentDate = moment();
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class GetInvoicePaymentLinkEntryMetaTransformer extends ItemEntryTransformer {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
itemName(entry) {
|
public itemName(entry) {
|
||||||
return entry.item.name;
|
return entry.item.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import events from '@/subscribers/events';
|
import events from '@/subscribers/events';
|
||||||
import { CreatePaymentReceiveStripePayment } from '../CreatePaymentReceivedStripePayment';
|
import { CreatePaymentReceiveStripePayment } from '../CreatePaymentReceivedStripePayment';
|
||||||
import { StripeCheckoutSessionCompletedEventPayload } from '@/interfaces/StripePayment';
|
import {
|
||||||
|
StripeCheckoutSessionCompletedEventPayload,
|
||||||
|
StripeWebhookEventPayload,
|
||||||
|
} from '@/interfaces/StripePayment';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import { Tenant } from '@/system/models';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class StripeWebhooksSubscriber {
|
export class StripeWebhooksSubscriber {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private createPaymentReceiveStripePayment: CreatePaymentReceiveStripePayment;
|
private createPaymentReceiveStripePayment: CreatePaymentReceiveStripePayment;
|
||||||
|
|
||||||
@@ -16,6 +24,10 @@ export class StripeWebhooksSubscriber {
|
|||||||
events.stripeWebhooks.onCheckoutSessionCompleted,
|
events.stripeWebhooks.onCheckoutSessionCompleted,
|
||||||
this.handleCheckoutSessionCompleted.bind(this)
|
this.handleCheckoutSessionCompleted.bind(this)
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.stripeWebhooks.onAccountUpdated,
|
||||||
|
this.handleAccountUpdated.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,10 +47,38 @@ export class StripeWebhooksSubscriber {
|
|||||||
// Convert from Stripe amount (cents) to normal amount (dollars)
|
// Convert from Stripe amount (cents) to normal amount (dollars)
|
||||||
const amountInDollars = amount / 100;
|
const amountInDollars = amount / 100;
|
||||||
|
|
||||||
|
// Creates a new payment received transaction.
|
||||||
await this.createPaymentReceiveStripePayment.createPaymentReceived(
|
await this.createPaymentReceiveStripePayment.createPaymentReceived(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
amountInDollars
|
amountInDollars
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the account updated.
|
||||||
|
* @param {StripeWebhookEventPayload}
|
||||||
|
*/
|
||||||
|
async handleAccountUpdated({ event }: StripeWebhookEventPayload) {
|
||||||
|
const { metadata } = event.data.object;
|
||||||
|
const account = event.data.object;
|
||||||
|
const tenantId = parseInt(metadata.tenantId, 10);
|
||||||
|
|
||||||
|
if (!metadata?.paymentIntegrationId || !metadata.tenantId) return;
|
||||||
|
|
||||||
|
// Find the tenant or throw not found error.
|
||||||
|
await Tenant.query().findById(tenantId).throwIfNotFound();
|
||||||
|
|
||||||
|
// Check if the account capabilities are active
|
||||||
|
if (account.capabilities.card_payments === 'active') {
|
||||||
|
const { PaymentIntegration } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Marks the payment method integration as active.
|
||||||
|
await PaymentIntegration.query()
|
||||||
|
.findById(metadata?.paymentIntegrationId)
|
||||||
|
.patch({
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -728,6 +728,7 @@ export default {
|
|||||||
|
|
||||||
// Stripe Payment Webhooks
|
// Stripe Payment Webhooks
|
||||||
stripeWebhooks: {
|
stripeWebhooks: {
|
||||||
onCheckoutSessionCompleted: 'onStripeCheckoutSessionCompleted'
|
onCheckoutSessionCompleted: 'onStripeCheckoutSessionCompleted',
|
||||||
|
onAccountUpdated: 'onStripeAccountUpdated'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.root {
|
||||||
|
border: 1px solid #e1e1e1;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: 120px;
|
||||||
|
padding: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover .removeButton{
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewImage {
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title{
|
||||||
|
font-size: 13px;
|
||||||
|
color: #738091;
|
||||||
|
}
|
||||||
|
|
||||||
|
.removeButton{
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
border-radius: 32px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user