import { Injectable } from '@nestjs/common';
import Stripe from 'stripe';
import { SubscriptionDto } from './dto/subscription.dto';
@Injectable()
export class StripeService {
private stripe: Stripe;
constructor() {
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2024-10-28.acacia',
})
}
subscription(dto: SubscriptionDto) {
const totalPrice = dto.amount
const redirectUrl = dto.redirectUrl
const userId = dto.userId
return this.stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ["card"],
line_items: [
{
price_data: {
currency: 'usd',
unit_amount: Math.round(totalPrice * 100), // cents
product_data: {
name: 'Cart Payment',
metadata: { // userId
userId,
},
},
recurring: {
interval: 'month',
interval_count: 1
}
},
quantity: 1,
},
],
metadata: {
userId,
},
success_url: redirectUrl,
cancel_url: redirectUrl,
client_reference_id: userId,
});
}
constructEvent(payload: any, signature: any) {
return this.stripe.webhooks.constructEvent(payload, signature, process.env.STRIPE_WEBHOOK_SECRET);
}
}
import { Controller, Post, Headers, HttpCode, HttpStatus, RawBody, Body, NestMiddleware } from '@nestjs/common';
import { StripeService } from 'src/stripe/stripe.service';
import * as bodyParser from 'body-parser';
import { Request, Response } from 'express';
import { UserService } from 'src/user/user.service';
@Controller('webhook')
export class WebhookController implements NestMiddleware {
constructor(
private stripeService: StripeService,
private userService: UserService
) {}
use(req: Request, res: Response, next: () => any) {
bodyParser.raw({type: '*/*'})(req, res, next);
}
@Post()
@HttpCode(HttpStatus.OK)
async handleWebhook(@RawBody() payload: Buffer, @Headers('stripe-signature') signature: string) {
const event = this.stripeService.constructEvent(payload, signature);
switch (event.type) {
case 'customer.subscription.created':
// Обработка успешной сессии оплаты
console.log('YES customer.subscription.created ', event.data.object.metadata?.userId); // undefined
break;
case 'customer.subscription.deleted':
// Обработка отмененной подписки
console.log('YES customer.subscription.deleted ', event.data.object.metadata?.userId);
break;
default:
// console.warn(`Unhandled event type ${event.type}`);
break;
}
}}
'use client';
import { axiosWithAuth } from '@/api/interceptors';
import { absoluteUrl } from '@/lib/absolute-url';
import { useUserStore } from '@/store/use-user-store';
const Payment = () => {
const { user } = useUserStore();
const handleClick = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const res = await axiosWithAuth.post(`/stripe/subscription`, {
userId: user?.id,
amount: 1, // dollars
redirectUrl: absoluteUrl('/test'),
});
console.log('res ', res.data?.url);
window.location.href = res.data?.url;
};
return (
<form onClick={handleClick}>
<h1>dddddddd</h1>
<button type='submit'>PAY</button>
</form>
);
};
export default Payment;