Hey there, dev community! π Are you building a cool app and want to handle payments like a pro? Look no further! Today, weβre diving into Stripe integration with NestJS (using TypeORM ποΈ), and Iβll walk you through the steps to get everything up and running smoothly π. Let's dive right in! π
Step 1: First Things First β Stripe Keys π
Before we do anything, youβll need to grab your Stripe Public and Secret Keys from the Stripe Dashboard.
ποΈ These keys are important because:
Public Key: This is used on the frontend to securely send card details to Stripe.
Secret Key: This stays in the backend and allows us to perform actions like creating customers, payment intents, etc. (π° we don't want this to leak!)
How to connect them in NestJS? Well, letβs use .env to store these keys securely!
# In .env
STRIPE_PUBLIC_KEY=pk_test_123
STRIPE_API_KEY=sk_test_456
Next, you can inject this into your NestJS project using @nestjs/config. Weβll use this in our StripeModule. Keep reading! π
Step 2: Setting Up Stripe in NestJS βοΈ
Alright, letβs install what we need in our NestJS project!
npm install @nestjs/config stripe @types/stripe
Now, create a Stripe module. Hereβs the code to get that rolling:
@Module({
imports: [TypeOrmModule.forFeature([UserDetails])],
controllers: [StripeController],
providers: [
StripeService,
{
provide: 'STRIPE_API_KEY',
useFactory: async (configService: ConfigService) =>
configService.get('STRIPE_API_KEY'),
inject: [ConfigService],
},
],
exports: [StripeService],
})
export class StripeModule {}
We import the UserDetails entity because weβll be linking payments to users later! π
Step 3: The Flow of Payments π³
Stripe payments follow a simple but powerful flow. Here's a bird's eye view of what we'll be doing:
- Be a Stripe customer π§βπΌ
- Create a payment intent π‘
- Attach payment methods π³
- Confirm the payment intent π
Letβs break these steps down further! ππ
Step 4: Creating the Stripe Service π οΈ
First, weβll initialize Stripe in our service by creating a stripe instance:
import Stripe from 'stripe';
@Injectable()
export class StripeService {
private readonly stripe: Stripe;
constructor(
@Inject('STRIPE_API_KEY') private readonly apiKey: string,
) {
this.stripe = new Stripe(this.apiKey, {
apiVersion: '2024-06-20',
});
}
}
Thatβs it! π Now our service is ready to handle all Stripe-related operations!
Step 5: Be a Stripe Customer π₯
Before you can make any payments, you need to create a customer in Stripe! Here's a neat way to do it:
const stripeCustomer = await this.stripe.customers.create({
email: 'john.doe@example.com',
name: 'John Doe',
phone: '555-555-5555',
address: {
line1: '123 NestJS St.',
city: 'Codingville',
country: 'US',
},
metadata: {
userId: '12345', // Store anything you like here!
},
});
You can save the stripeCustomer.id to your database (in the UserDetails table or elsewhere). Donβt worry, you can always edit the details later! βοΈ
Step 6: Creating Payment Intents π―
Now that you're a Stripe customer, letβs talk about creating a payment intent. The payment intent will tell Stripe how much to charge, which currency to use, and which payment methods are acceptable (weβll use 'card' in this case π³).
async createPaymentIntent(
amount: number,
currency: string,
): Promise<Stripe.PaymentIntent> {
try {
const paymentIntent = await this.stripe.paymentIntents.create({
amount,
currency,
payment_method_types: ['card'],
});
this.logger.log('Payment Intent created successfully');
return paymentIntent;
} catch (error) {
this.logger.error('Failed to create Payment Intent', error.stack);
throw new Error('Unable to create Payment Intent');
}
}
@Post('create-payment-intent')
async createPaymentIntent(
@Body('amount') amount: number,
@Body('currency') currency: string,
): Promise<{ paymentIntentId: string }> {
try {
const paymentIntent = await this.stripeService.createPaymentIntent(
amount,
currency,
);
this.logger.log('Payment Intent created successfully');
return { paymentIntentId: paymentIntent.id };
} catch (error) {
this.logger.error('Failed to create Payment Intent', error.stack);
throw new HttpException(
'Failed to create Payment Intent',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
Don't forget! After creating the intent, Stripe gives you a paymentIntent.id β store this in local storage or your database for future use! π¦
Step 7: Adding Payment Methods π³
Once you have a payment intent, you'll need a payment method. This links the customerβs payment details (like a card) to their account.
async addingPaymentMethod(
userId: string,
paymentMethodId: string,
): Promise<Stripe.PaymentMethod> {
try {
const userDetails = await this.userDetailsRepository.findOne({
where: { userID: userId },
});
const paymentMethod = await this.stripe.paymentMethods.attach(
paymentMethodId,
{
customer: userDetails.stripeCustomerId,
},
);
return paymentMethod;
} catch (error) {
this.logger.error('Failed to add payment method', error.stack);
throw error;
}
}
@Post('add-payment-method')
async addPaymentMethod(
@AuthenticatedUser() user: any,
@Body('paymentMethodId') paymentMethodId: string,
): Promise<Stripe.PaymentMethod> {
try {
const paymentMethod = await this.stripeService.addPaymentMethod(
user.sub,
paymentMethodId,
);
this.logger.log('Payment method added successfully');
return paymentMethod;
} catch (error) {
this.logger.error('Failed to add payment method', error.stack);
throw new HttpException(
'Failed to add payment method',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
π Note: Youβll need the Stripe customer ID (from Step 5) to attach a payment method!
Step 8: Retrieving Payment Methods π
Want to see all payment methods a customer has? You can easily fetch them using the following code:
async retrievePaymentMethods(
userId: string,
): Promise<Stripe.PaymentMethod[]> {
try {
const userDetails = await this.userDetailsRepository.findOne({
where: { userID: userId },
});
const paymentMethods = await this.stripe.paymentMethods.list({
customer: userDetails.stripeCustomerId,
type: 'card',
});
return paymentMethods.data;
} catch (error) {
this.logger.error('Failed to fetch payment methods', error.stack);
throw error;
}
}
@Get('get-payment-methods')
async retrievePaymentMethods(
@AuthenticatedUser() user: any,
): Promise<Stripe.PaymentMethod[]> {
try {
const paymentMethods = await this.stripeService.retrievePaymentMethods(
user.sub,
);
this.logger.log('Payment methods fetched successfully');
return paymentMethods;
} catch (error) {
this.logger.error('Failed to fetch payment methods', error.stack);
throw new HttpException(
'Failed to fetch payment methods',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
Stripe will return a payment method ID for every card added. π οΈ Use it to confirm payments!
Step 9: Confirming the Payment Intent β
The final piece is to confirm the payment intent. To do this, you need both the paymentIntentId and the paymentMethodId.
async paymentConfirmation(
paymentIntentId: string,
paymentMethodId: string,
): Promise<Stripe.PaymentIntent> {
try {
const confirmedPaymentIntent = await this.stripe.paymentIntents.confirm(
paymentIntentId,
{
payment_method: paymentMethodId,
},
);
return confirmedPaymentIntent;
} catch (error) {
this.logger.error('Failed to confirm Payment Intent', error.stack);
throw new Error('Unable to confirm Payment Intent');
}
}
@Post('confirm-payment-intent')
async confirmPaymentIntent(
@Body('paymentIntentId') paymentIntentId: string,
@Body('paymentMethodId') paymentMethodId: string,
@AuthenticatedUser() user: any,
): Promise<Stripe.PaymentIntent> {
try {
const paymentIntent = await this.stripeService.confirmPaymentIntent(
paymentIntentId,
paymentMethodId,
user.sub,
);
this.logger.log('Payment Intent confirmed successfully');
return paymentIntent;
} catch (error) {
this.logger.error('Failed to confirm Payment Intent', error.stack);
throw new HttpException(
'Failed to confirm Payment Intent',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
Once the payment is confirmed, you can remove the paymentIntentId from local storage and redirect the user to a success screen! π
π¬ Wrapping It Up
And thatβs a wrap! π Integrating Stripe with NestJS isnβt rocket science, but with this guide, youβll have a smooth ride from start to finish. π You'll be able to handle payments like a boss, and your users will love the seamless experience!
Have fun coding, and happy transactions! πΈ
Top comments (0)