0. Paymentwall 계정 생성 및 프로젝트 세팅
계정을 생성해야 sandbox 환경에서 테스트를 할 수 있다.
프로젝트 생성
- 프로젝트를 생성하면 Project Key와 Secret Key를 발급받음
- API 모드: 초기 개발 단계에서는 테스트 모드로 프로젝트를 설정해준다. 이를 통해 실제 결제가 이루어지지 않고 개발 중에 테스트할 수 있다.

- 해외에서 구독결제를 하기 위해 checkout API의 subscribtion 사용

Accept Payments Worldwide - Paymentwall
Paymentwall is a global payment provider that allows you to process credit card payments and local payment methods in 200 countries and territories.
www.paymentwall.com
1. 웹에서 파라미터 값들과 api/payment/subscribe 요청

const response = await axios.post('https://paymentwall-test-lyflp.run.goorm.site/api/payment/subscribe', {
user_id: user.id,
amount: amount,
email: user.email,
currencyCode:'USD',
ag_name:productName, //product name
ag_external_id : ag_external_id, //product id
ag_period_length : 1, //ag_period_type를 얼만큼 지속할건지
ag_period_type:periodType, // day/week/month/year
ag_recurring:1,
ps:'all' //결제 페이지에 표시할 결제 방법 allthegate(한국 신용카드), cc(신용카드), test
});
2. api/payment/subscribe
- 구독 결제 요청 시 (/api/payment/subscribe): 사용자가 결제를 시작할 때 초기 결제 정보를 저장. 이때는 사용자가 결제 과정을 시작하기 위한 모든 정보를 기록해두고, 결제가 이루어질 때의 초기 데이터를 남김
# 구독 결제 요청 엔드포인트
@app.post("/api/payment/subscribe")
async def subscribe(request: Request):
# 사용자 ID, 구독 금액 및 기타 필요한 정보는 요청 바디에서 전달받습니다.
data = await request.json()
user_id = data.get("user_id", "test_user")
amount = data.get("amount", 10.0) # 예시로 $10.0
email = data.get("email", "test@example.com")
currency_code = data.get("currencyCode", "USD")
ag_name = data.get("ag_name", "") # 상품명
ag_external_id = data.get("ag_external_id", "") # 상품 ID
ag_period_length = data.get("ag_period_length", 1) # 구독 기간 길이
ag_period_type = data.get("ag_period_type", "month") # 구독 기간 타입
ag_recurring = data.get("ag_recurring", 1) # 반복 여부 (1이면 반복)
ps = data.get("ps", "all") # 결제 방법
current_timestamp = int(time.time())
# 매개변수 구성
parameters = {
"key": "3ab56705f9e6fab8596701fc71af194e",
"uid": user_id,
"widget": "p1_1",
"email": email,
"history": str(current_timestamp),
"amount": str(amount),
"currencyCode": currency_code,
"sign_version": "2",
"ag_name": ag_name,
"ag_external_id": ag_external_id,
"ag_type": "subscription",
"ag_period_length": ag_period_length,
"ag_period_type": ag_period_type,
"ag_recurring": ag_recurring,
"ps": ps
}
# 서명 생성
sign = generate_signature(parameters, SECRET_KEY)
print(f"Generated Signature: {sign}")
parameters["sign"] = sign
# Paymentwall 결제 요청 URL 구성
payment_url = "https://api.paymentwall.com/api/subscription?" + "&".join([f"{k}={urllib.parse.quote(str(v))}" for k, v in parameters.items()])
print(payment_url)
return {"payment_url": payment_url}
- 결제 요청 처리: 사용자로부터 결제 정보를 받고 Paymentwall의 API와 통신하여 결제 URL을 생성
- 서명 생성: Paymentwall과의 보안 통신을 위해 서명(signature)을 생성. Paymentwall은 보안 강화를 위해 요청 시 서명을 요구하며, 이 서명은 모든 매개변수를 정렬하고 비밀 키를 추가하여 MD5 해시로 생성.
def generate_signature(params, secret_key):
# 매개변수 정렬
sorted_params = sorted(params.items())
# base_string 생성 (매개변수를 "="와 "없이 연결, 마지막에 SECRET_KEY 추가)
base_string = "".join([f"{k}={v}" for k, v in sorted_params]) + secret_key
# MD5 해시 생성
sign = hashlib.md5(base_string.encode()).hexdigest()
return sign
- db 생성 추가해야함
3. 올바른 파라미터값들이 전달되고 서명이 올바르게 생성되면 paymentwall의 결제 위젯 생성
- 아래 결제 방식들은 필요한것만 보이게 선택 가능


4. 결제 완료되면 pingback 호출
- 결제 완료 확인 시 (핑백, /api/payment/pingback): 결제가 실제로 성공했거나 취소된 경우에는 핑백(Webhook)을 통해서 Paymentwall 서버에서 결제 상태를 알려주게 됨. 이때 결제 성공 또는 실패와 관련된 세부 정보를 데이터베이스에 저장하는 것이 필요.

Paymentwall 설정에서 url과 사이닝 버전 올바르게 설정해야 사용 가능
# Webhook (Pingback) 엔드포인트
@app.get("/api/payment/pingback")
async def payment_pingback(request: Request):
# GET 요청의 경우 쿼리 파라미터에서 데이터 가져오기
query_params = dict(request.query_params)
received_signature = query_params.get("sig")
# 서명 검증
if not received_signature:
raise HTTPException(status_code=400, detail="Missing signature")
if not verify_signature(query_params, received_signature):
raise HTTPException(status_code=400, detail="Invalid signature")
# 핑백 데이터 처리 (예: 결제 성공, 결제 취소, 기타 이벤트 등)
if query_params["type"] == "0": # 결제 완료
# 결제 성공 처리 로직
return {"status": "success"}
elif query_params["type"] == "1": # 결제 취소
# 결제 취소 처리 로직
return {"status": "cancel"}
else:
# 기타 결제 상태 처리
return {"status": "unknown"}
5. paymentwallUrl 이 생성되면 웹에서 결제 상태 체크
const checkPaymentStatus = async () => {
try {
const response = await axios.get('https://paymentwall-test-lyflp.run.goorm.site/api/payment/status', {
params: { user_id: user.id }
});
setPaymentStatus(response.data.status);
} catch (error) {
console.error('Error checking payment status:', error);
}
};
useEffect(() => {
let timer: NodeJS.Timeout;
if (paymentUrl) {
timer = setInterval(checkPaymentStatus, 10000);
}
return () => clearInterval(timer);
}, [paymentUrl]);

# 테스트용. 실제로는 db에서 결제상태 가져와야함
dummy_db = {
"1": "success",
"2": "cancel",
"3": "pending",
"5": "pending"
}
# 구독 상태 조회 엔드포인트
@app.get("/api/payment/status")
async def get_payment_status(user_id: str):
if user_id in dummy_db:
status = dummy_db["1"]
return {"status": status}
else:
raise HTTPException(status_code=404, detail="User not found")
웹에서 동작할 결제 상태 이후 로직 필요
6. 완료되면 paymentwall의 transaction 페이지에서 기록 볼 수 있음
근데 테스트 transaction 페이지 오류가 너무 많,,,

api 문서
API Reference
Change subscription’s package Tokidoki offers the ability to change subscription’s package. We provide possibility to upgrade/downgrade from stored package to stored package, non-stored (flexible) package to non-stored package, stored package to flexib
docs.paymentwall.com
TODO
- 테스트 모드 아닌 실 결제 사용하려면 심사 필요

[페이먼트월] FAQ 2 - 웹사이트 심사 가이드라인
페이먼트월 FAQ - 웹사이트 심사 가이드라인 페이먼트월 웹사이트 및 비즈니스 모델 심사를 앞두고 있습...
blog.naver.com
- 요청값의 ps 사용해서 결제할 방법 선택
'💙Frontend' 카테고리의 다른 글
React Next.js에서 다국어 지원 구현하기 (1) | 2024.08.29 |
---|
0. Paymentwall 계정 생성 및 프로젝트 세팅
계정을 생성해야 sandbox 환경에서 테스트를 할 수 있다.
프로젝트 생성
- 프로젝트를 생성하면 Project Key와 Secret Key를 발급받음
- API 모드: 초기 개발 단계에서는 테스트 모드로 프로젝트를 설정해준다. 이를 통해 실제 결제가 이루어지지 않고 개발 중에 테스트할 수 있다.

- 해외에서 구독결제를 하기 위해 checkout API의 subscribtion 사용

Accept Payments Worldwide - Paymentwall
Paymentwall is a global payment provider that allows you to process credit card payments and local payment methods in 200 countries and territories.
www.paymentwall.com
1. 웹에서 파라미터 값들과 api/payment/subscribe 요청

const response = await axios.post('https://paymentwall-test-lyflp.run.goorm.site/api/payment/subscribe', {
user_id: user.id,
amount: amount,
email: user.email,
currencyCode:'USD',
ag_name:productName, //product name
ag_external_id : ag_external_id, //product id
ag_period_length : 1, //ag_period_type를 얼만큼 지속할건지
ag_period_type:periodType, // day/week/month/year
ag_recurring:1,
ps:'all' //결제 페이지에 표시할 결제 방법 allthegate(한국 신용카드), cc(신용카드), test
});
2. api/payment/subscribe
- 구독 결제 요청 시 (/api/payment/subscribe): 사용자가 결제를 시작할 때 초기 결제 정보를 저장. 이때는 사용자가 결제 과정을 시작하기 위한 모든 정보를 기록해두고, 결제가 이루어질 때의 초기 데이터를 남김
# 구독 결제 요청 엔드포인트
@app.post("/api/payment/subscribe")
async def subscribe(request: Request):
# 사용자 ID, 구독 금액 및 기타 필요한 정보는 요청 바디에서 전달받습니다.
data = await request.json()
user_id = data.get("user_id", "test_user")
amount = data.get("amount", 10.0) # 예시로 $10.0
email = data.get("email", "test@example.com")
currency_code = data.get("currencyCode", "USD")
ag_name = data.get("ag_name", "") # 상품명
ag_external_id = data.get("ag_external_id", "") # 상품 ID
ag_period_length = data.get("ag_period_length", 1) # 구독 기간 길이
ag_period_type = data.get("ag_period_type", "month") # 구독 기간 타입
ag_recurring = data.get("ag_recurring", 1) # 반복 여부 (1이면 반복)
ps = data.get("ps", "all") # 결제 방법
current_timestamp = int(time.time())
# 매개변수 구성
parameters = {
"key": "3ab56705f9e6fab8596701fc71af194e",
"uid": user_id,
"widget": "p1_1",
"email": email,
"history": str(current_timestamp),
"amount": str(amount),
"currencyCode": currency_code,
"sign_version": "2",
"ag_name": ag_name,
"ag_external_id": ag_external_id,
"ag_type": "subscription",
"ag_period_length": ag_period_length,
"ag_period_type": ag_period_type,
"ag_recurring": ag_recurring,
"ps": ps
}
# 서명 생성
sign = generate_signature(parameters, SECRET_KEY)
print(f"Generated Signature: {sign}")
parameters["sign"] = sign
# Paymentwall 결제 요청 URL 구성
payment_url = "https://api.paymentwall.com/api/subscription?" + "&".join([f"{k}={urllib.parse.quote(str(v))}" for k, v in parameters.items()])
print(payment_url)
return {"payment_url": payment_url}
- 결제 요청 처리: 사용자로부터 결제 정보를 받고 Paymentwall의 API와 통신하여 결제 URL을 생성
- 서명 생성: Paymentwall과의 보안 통신을 위해 서명(signature)을 생성. Paymentwall은 보안 강화를 위해 요청 시 서명을 요구하며, 이 서명은 모든 매개변수를 정렬하고 비밀 키를 추가하여 MD5 해시로 생성.
def generate_signature(params, secret_key):
# 매개변수 정렬
sorted_params = sorted(params.items())
# base_string 생성 (매개변수를 "="와 "없이 연결, 마지막에 SECRET_KEY 추가)
base_string = "".join([f"{k}={v}" for k, v in sorted_params]) + secret_key
# MD5 해시 생성
sign = hashlib.md5(base_string.encode()).hexdigest()
return sign
- db 생성 추가해야함
3. 올바른 파라미터값들이 전달되고 서명이 올바르게 생성되면 paymentwall의 결제 위젯 생성
- 아래 결제 방식들은 필요한것만 보이게 선택 가능


4. 결제 완료되면 pingback 호출
- 결제 완료 확인 시 (핑백, /api/payment/pingback): 결제가 실제로 성공했거나 취소된 경우에는 핑백(Webhook)을 통해서 Paymentwall 서버에서 결제 상태를 알려주게 됨. 이때 결제 성공 또는 실패와 관련된 세부 정보를 데이터베이스에 저장하는 것이 필요.

Paymentwall 설정에서 url과 사이닝 버전 올바르게 설정해야 사용 가능
# Webhook (Pingback) 엔드포인트
@app.get("/api/payment/pingback")
async def payment_pingback(request: Request):
# GET 요청의 경우 쿼리 파라미터에서 데이터 가져오기
query_params = dict(request.query_params)
received_signature = query_params.get("sig")
# 서명 검증
if not received_signature:
raise HTTPException(status_code=400, detail="Missing signature")
if not verify_signature(query_params, received_signature):
raise HTTPException(status_code=400, detail="Invalid signature")
# 핑백 데이터 처리 (예: 결제 성공, 결제 취소, 기타 이벤트 등)
if query_params["type"] == "0": # 결제 완료
# 결제 성공 처리 로직
return {"status": "success"}
elif query_params["type"] == "1": # 결제 취소
# 결제 취소 처리 로직
return {"status": "cancel"}
else:
# 기타 결제 상태 처리
return {"status": "unknown"}
5. paymentwallUrl 이 생성되면 웹에서 결제 상태 체크
const checkPaymentStatus = async () => {
try {
const response = await axios.get('https://paymentwall-test-lyflp.run.goorm.site/api/payment/status', {
params: { user_id: user.id }
});
setPaymentStatus(response.data.status);
} catch (error) {
console.error('Error checking payment status:', error);
}
};
useEffect(() => {
let timer: NodeJS.Timeout;
if (paymentUrl) {
timer = setInterval(checkPaymentStatus, 10000);
}
return () => clearInterval(timer);
}, [paymentUrl]);

# 테스트용. 실제로는 db에서 결제상태 가져와야함
dummy_db = {
"1": "success",
"2": "cancel",
"3": "pending",
"5": "pending"
}
# 구독 상태 조회 엔드포인트
@app.get("/api/payment/status")
async def get_payment_status(user_id: str):
if user_id in dummy_db:
status = dummy_db["1"]
return {"status": status}
else:
raise HTTPException(status_code=404, detail="User not found")
웹에서 동작할 결제 상태 이후 로직 필요
6. 완료되면 paymentwall의 transaction 페이지에서 기록 볼 수 있음
근데 테스트 transaction 페이지 오류가 너무 많,,,

api 문서
API Reference
Change subscription’s package Tokidoki offers the ability to change subscription’s package. We provide possibility to upgrade/downgrade from stored package to stored package, non-stored (flexible) package to non-stored package, stored package to flexib
docs.paymentwall.com
TODO
- 테스트 모드 아닌 실 결제 사용하려면 심사 필요

[페이먼트월] FAQ 2 - 웹사이트 심사 가이드라인
페이먼트월 FAQ - 웹사이트 심사 가이드라인 페이먼트월 웹사이트 및 비즈니스 모델 심사를 앞두고 있습...
blog.naver.com
- 요청값의 ps 사용해서 결제할 방법 선택
'💙Frontend' 카테고리의 다른 글
React Next.js에서 다국어 지원 구현하기 (1) | 2024.08.29 |
---|