Paypal 구독 결제 서비스 구현
Written by Sangmin on June 25, 2024

PayPal 구독 및 취소 기능 구현하기
페이팔 구독 서비스와 MongoDB를 연동해 구독 관리 기능을 구현한 내용을 공유하려 한다.
페이팔을 선택한 이유
결제 시스템을 구현하는데 먼저 생각한 건 당연히 stripe였다. 그러나 스트라이프는 한국계좌를 지원하지 않아서 다른 선택지를 찾았다. 나는 해외결제를 메인으로 생각했기에 국내 결제 플랫폼은 딱히 메리트가없었고 찾던 중 그나마 페이팔이 사용목적에 부합하는 것 같아서 선택했다. 구독 플랜도 만들 수 있고 구현 방식이 생각보다 간단해보였다. 또 페이팔을 개인적으로 가끔 사용하는데 사용경험이 딱히 나쁘지않았다.
데이터 베이스
구독 서비스를 구현하는 과정에서 가장 중요한 부분은 사용자 정보와 구독 정보를 안전하게 관리하는 것이다. 이를 위해 MongoDB를 선택했다. MongoDB는 유연한 스키마와 뛰어난 확장성을 가지고 있어, 사용자 정보와 구독 정보를 효과적으로 관리할 수 있다.
코드를 살펴보면, 페이팔 웹훅을 통해 구독 활성화, 취소 등의 이벤트를 처리하는 부분이 있다. 이벤트가 발생하면 MongoDB에 저장된 사용자 정보를 업데이트하는 식으로 구독 상태를 관리한다.
이번 포스팅에서는 PayPal 구독 기능을 Next.js 프로젝트에 통합한 과정을 공유하려 한다. 구독 기능 구현을 위해 어떤 작업을 했는지, 그리고 과정에서 배운 점들을 정리해보겠다.
페이팔 기본 세팅
페이팔 기본 세팅은 다른 api들 세팅과 크게 다르지 않다. 우선 developer.paypal 에서 가입을 한다. 개발자 대쉬보드로 들어가면 app을 추가할 수 있는 영역이 있고 거기서 추가하고 credentials를 받으면 된다. 조금 헷갈릴 수 있는건 sandbox라는 게 있다는건데 sandbox.paypal 아이디랑 그냥 Paypal 아이디는 다르다. Api 구축 테스트를 할때는 sandbox.paypal에 들어가서 주어진 테스트 계정으로 로그인하면 된다. 테스트 계정 중비즈니스 계정은 돈을 받는 계정이고 personal 계정은 돈을 내는 계정이라고 생각하면 된다. 물론 실제로 돈이 나가지 않는다. 테스트를 위한 계쩡이라고 볼 수 있다.
sandbox.paypal에서 비즈니스 계정으로 로그인하고 들어가서 /billing/overview 위치로 이동하면 구독 상품을 만들 수 있다. 가격, 결제 주기 등을 입력하고 plan_id 를 받으면 된다. 기본 세팅이 끝났으면 next.js 로 돌아오자.
PayPal 구독 버튼 구현하기
먼저, 프론트엔드에 PayPal 구독 버튼을 추가했다. @paypal/react-paypal-js
라이브러리를 사용하여 다음과 같이 구현했다:
import { PayPalButtons } from "@paypal/react-paypal-js";
const PayPalButton = ({ userId }) => {
return (
<PayPalButtons
// 스타일 설정
createSubscription={(data, actions) => {
// 구독 플랜 ID와 사용자 ID 전달
return actions.subscription.create({
plan_id: "P-******",
custom_id: userId,
});
}}
onApprove={async (data, actions) => {
// 구독 승인 처리
}}
/>
);
};
사용자가 PayPal 버튼을 클릭하면 구독 프로세스가 시작된다. createSubscription
함수에서 구독 플랜 ID와 사용자 ID를 전달하고 있다. 사용자가 구독을 승인하면 onApprove
핸들러가 호출되는데, 여기서 백엔드 API를 호출하여 구독 정보를 저장하게 된다.
백엔드 API 구현하기
백엔드에서는 PayPal 웹훅을 처리하는 API 엔드포인트를 구현했다. 웹훅을 통해 구독 활성화, 취소, 결제 완료 등의 이벤트를 받을 수 있다.
export async function POST(req) {
try {
const body = await req.json();
const { event_type, resource, billing_info } = body;
switch (event_type) {
case 'BILLING.SUBSCRIPTION.ACTIVATED':
console.log('Subscription activated');
break;
case 'BILLING.SUBSCRIPTION.CANCELLED':
console.log('Subscription cancelled');
await handleCancel();
break;
case 'PAYMENT.SALE.COMPLETED':
console.log('Payment completed');
await handleSubActivated(resource.custom, resource.billing_agreement_id, billing_info.next_billing_time);
break;
// 다른 이벤트 타입 처리
몽고 디비 연동하기
먼저 PayPal 버튼 컴포넌트에서 custom_id
를 설정했다. 이 custom_id
는 사용자 ID를 의미하며, 나중에 구독 상태를 MongoDB에 저장할 때 사용된다.
import { PayPalButtons } from "@paypal/react-paypal-js";
import BASE_URL from "@/app/config";
const PayPalButton = ({ userId }) => {
return (
<PayPalButtons
createSubscription={(data, actions) => {
return actions.subscription.create({
plan_id: "P-******",
custom_id: userId,
});
}}
onApprove={(data, actions) => {
console.log("구독 승인됨", data);
window.location.href = `${BASE_URL}/redirect`;
}}
onError={(err) => {
console.error("구독 오류", err);
}}
/>
);
};
이 컴포넌트는 페이팔 구독 버튼을 렌더링합니다. 사용자가 구독을 승인하
이 컴포넌트는 페이팔 구독 버튼을 렌더링한다. 사용자가 구독을 승인하면 /redirect
페이지로 리디렉션된다.
그리고 PayPal 웹훅에서 구독 활성화 이벤트를 처리하는 handleSubActivated
함수를 만들었다. 여기서 사용자 ID로 MongoDB에서 사용자 문서를 찾고, 구독 상태와 구독 ID, 다음 청구 날짜를 업데이트한다.
async function handleSubActivated(userId, subId, nextBill) {
try {
const userObjectId = new mongoose.Types.ObjectId(userId);
const user = await User.findOne({ \_id: userObjectId });
if (user) {
user.subscribed = true;
user.subscriptionId = subId;
user.nextBilling = nextBill;
await user.save();
console.log(`subscription activated`);
} else {
console.log('User not found for subscription activation');
}
} catch (error) {
console.log('Error handling subscription activation:', error);
throw error;
}
}
마무리
이번 프로젝트를 통해 PayPal 구독 서비스와 MongoDB를 성공적으로 연동하여 구독 관리 시스템을 구축했다. 주요 구현 사항은 다음과 같다:
- PayPal 구독 버튼을 프론트엔드에 통합
- 백엔드에서 PayPal 웹훅 처리
- MongoDB를 사용한 구독 정보 관리
이번 프로젝트를 통해 페이팔 구독 서비스와 MongoDB를 연동하는 방법을 배웠다. 사용자 정보와 구독 정보를 안전하게 관리할 수 있게 되었고, 구독 활성화 및 취소 등의 이벤트를 효과적으로 처리할 수 있게 되었다. 다음에는 구독 취소나 일시 정지 기능도 추가해 볼 계획이다.