이성호
이성호13mo ago

테스트 결제에서 카드사 인증 화면이 뜨게 하는 방법이 뭔가요? (PG 최초 계약 작업)

안녕하세요. 테스트 결제 붙여서 심사를 넣고자 준비하고 있습니다. 첨부한 사진처럼 결제 경로 파일을 제출하라는 안내를 받았는데요. 비씨, 농협의 경우 결제 직전 카드사 인증화면을 캡쳐해야 한다고 합니다. 그런데 제가 개발하고 확인하니까 해당 창이 뜨지 않고 결제 완료로 넘어갑니다. 테스트 결제 내역에서도 결제 완료라고 뜨네요. 해당 창이 뜨지 않는데 혹시 제가 어떤 걸 놓치고 있는 걸까요?
No description
24 Replies
토스페이먼츠 BOT
⏳ 잠시만 기다려주세요! 곧 답변드리겠습니다
오류 문의일 경우 아래 정보를 미리 전달해주시면, 빠른 답변에 도움이 됩니다.
- 주문번호(orderId) : - 문의 내용 :
(img를 함께 첨부해주시면 도움이됩니다)
* 계약관련 내용은 1544-7772로 문의주세요. * 주말/공휴일에는 답변이 늦을 수 있어요.
박의원
박의원13mo ago
지금 live용 상점 키를 설정하신 것이지요? 결제창을 띄울때 설정한 주문번호를 회신주시겠어요?
이성호
이성호OP13mo ago
아니요 아직 라이브 키가 없습니다. 테스트 키만 있고, 테스트 키로 진행한 결과입니다. 주문번호는 clt2bt0tt0007vuimibusk2n9 입니다. 테스트 키로 연동을 붙여두고 심사를 넣어뒀는데 저 화면을 캡쳐하는 절차를 진행하라는 안내를 받았습니다
유부장
유부장13mo ago
죄송한데 업체 어디신가요?
이성호
이성호OP13mo ago
'도*** 주식회사' 입니다
유부장
유부장13mo ago
_skipAuth 에 대해서는 어떻게 인지하셨나요?
이성호
이성호OP13mo ago
아마 블로그 글을 참고한 거 같아서 정확히 기억은 안납니다... 혹시 _skipAuth 이슈일까요? _skipAuth를 지우고 잘 되는 거 확인했습니다!
유부장
유부장13mo ago
네, 지금 결제요청에 문제가 되는 부분이 많은데요. 1) _skipAuth 파라미터 제외 해주세요. 2) customerKey 로 시크릿키 보내고 계시는데요. 시크릿키 보내시면 안됩니다. 지워주세요. 3) customerId 로도 시크릿키 보내고 계시는데요. 시크릿키 보내시면 안됩니다. 지워주세요. 시크릿키는 결제승인에서 base64 인코드 된 API header로만 보내주셔야 하고 파라미터로 보내는 값이 아닙니다. 그리고 가능하시면, 어떤 블로그 였는지 알려주실 수 있다면 큰 도움이 될 듯 합니다.
이성호
이성호OP13mo ago
https://velog.io/@gonggi_bab/%EC%A3%BC%EB%AC%B8-%EA%B8%B0%EB%8A%A5-%EA%B0%9C%EB%B0%9C%EA%B3%BC-%ED%86%A0%EC%8A%A4-%ED%8E%98%EC%9D%B4%EB%A8%BC%EC%B8%A0-%EB%8F%84%EC%9E%85 이 블로그를 중점적으로 처리한 거 같습니다! _skipAuth는 제가 어디서 정보를 얻었는지 안찾아지네요 ㅠ customerKey, customerId 이부분은 어디서 보내고 있는지 확인이 안됩니다.
주문 기능 개발과 토스 페이먼츠 도입
쇼핑몰의 가장 중요한 기능이 무엇일까? 당연하다. 물건을 고르고 살 수 있어야 한다. 나는 웹에서의 주문과 결제에 대해서 아무것도 아는 것이 없었다. 실제 고객들의 돈이 오고 가는 중요한 기능이라 오류가 발생하면 어떡하나 마냥 두렵기도 했다. 그래도 일단 해봐야
유부장
유부장13mo ago
결제요청 파라미터에 있을겁니다 결제요청 파람 body 한번 보시겠어요?
김차장
김차장13mo ago
공식문서를 보고 연동하시는게 좋습니다. https://docs.tosspayments.com/reference
코어 API | 토스페이먼츠 개발자센터
토스페이먼츠 API 엔드포인트(Endpoint)와 객체 정보, 파라미터, 요청 및 응답 예제를 살펴보세요.
이성호
이성호OP13mo ago
requestPayment에서는 { orderId: prepareResult.data.paymentId, orderName, customerName, customerEmail: email, customerMobilePhone: mobile, successUrl: ${callbackBaseUrl}/success, failUrl: ${callbackBaseUrl}/fail, }; 이렇게만 보내고 있습니다 네 공식 문서 참고해서 전반적인 코드 다시 확인하겠습니다!
이성호
이성호OP13mo ago
_skipAuth: "FORCE_SUCCESS", 인지하게 된 곳 찾았습니다! https://docs.tosspayments.com/guides/payment-widget/integration?frontend=react Checkout.jsx 쪽에 파라미터로 넘기고 있길래 똑같이 넣었습니다
연동하기 | 토스페이먼츠 개발자센터
토스페이먼츠의 간편한 결제 연동 과정을 한눈에 볼 수 있습니다. 각 단계별 설명과 함께 달라지는 UI와 코드를 확인해보세요.
유부장
유부장13mo ago
오.... 확인 감사합니다
토스페이먼츠 BOT
❤️ 기술문의 경험이 어떠셨나요?!
간단히 코멘트 남겨주세요! 제품 발전에 큰 힘이 됩니다.
이성호
이성호OP13mo ago
혹시 답변 중에 이런 내용이 있었는데 넘겨도 되는 부분일까요? 2) customerKey 로 시크릿키 보내고 계시는데요. 시크릿키 보내시면 안됩니다. 지워주세요. 3) customerId 로도 시크릿키 보내고 계시는데요. 시크릿키 보내시면 안됩니다. 지워주세요. 시크릿키는 결제승인에서 base64 인코드 된 API header로만 보내주셔야 하고 파라미터로 보내는 값이 아닙니다. 전체 코드 다시 확인해봤는데 customerKey와 customerId 관련된 코드는 없으며, 시크릿키는 base64로 인코딩하여 오직 헤더로만 보내고 있습니다. (/confirm API) 또 결제 관련 코드는 requestPayment만 있는데 토스 제공 위젯을 통해 orderId, orderName, customerName, customerEmail, customerMobilePhone, successUrl, failUrl 이것만 보내주고 있습니다. https://apigw-sandbox.tosspayments.com/payment-gateway-window/open/api/v1/route 여기 API가 위젯을 통해 호출이 되는 것으로 파악되는데 시크릿키들은 제가 보낸 파라미터 값이 아닌 위젯에서 기본 포함하여 보내는 코드로 판단됩니다. 보안 이슈가 있지 않을까요?
김차장
김차장13mo ago
안보낸것으로 확인되신 주문번호 한개만 샘플로 전달부탁드립니다
이성호
이성호OP13mo ago
clt2kexz50005yatibxq21wx1 입니다 nextjs에서 결제 위젯으로 requestPayment 부르고 있어요
김차장
김차장13mo ago
Feb 26, 2024 @ 11:35:10.740
Header for User-agent = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Referer = http://localhost:3000/, Origin = http://localhost:3000 / path = /v1/payments/sdk, IP = 175.198.118.167, Query = null, Body = {"orderId":"clt2bt0tt0007vuimibusk2n9","orderName":"미니 슬링백","customerName":"테스","customerEmail":"test@test.com","customerMobilePhone":"0101111111","successUrl":"http://localhost:3000/orders/ORD-240226-9H7J/success?paymentType=NORMAL","failUrl":"http://localhost:3000/orders/ORD-240226-9H7J/fail","_skipAuth":"FORCE_SUCCESS","amount":10000,"currency":"KRW","country":"KR","customerKey":"test_sk_********************r7nO5Wml","customerId":"test_sk_********************r7nO5Wml","methodType":"CARD","isLegacy":true}
Feb 26, 2024 @ 11:35:10.740
Header for User-agent = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Referer = http://localhost:3000/, Origin = http://localhost:3000 / path = /v1/payments/sdk, IP = 175.198.118.167, Query = null, Body = {"orderId":"clt2bt0tt0007vuimibusk2n9","orderName":"미니 슬링백","customerName":"테스","customerEmail":"test@test.com","customerMobilePhone":"0101111111","successUrl":"http://localhost:3000/orders/ORD-240226-9H7J/success?paymentType=NORMAL","failUrl":"http://localhost:3000/orders/ORD-240226-9H7J/fail","_skipAuth":"FORCE_SUCCESS","amount":10000,"currency":"KRW","country":"KR","customerKey":"test_sk_********************r7nO5Wml","customerId":"test_sk_********************r7nO5Wml","methodType":"CARD","isLegacy":true}
Feb 26, 2024 @ 15:35:57.659
Header for User-agent = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Referer = Unknown, Origin = Unknown / path = /v1/payments/sdk, IP = 127.0.0.6, Query = null, Body = {"amount":3000,"orderId":"clt2kexz50005yatibxq21wx1","currency":"KRW","language":null,"useInternationalCardOnly":false,"useEscrow":null,"escrowProducts":null,"country":"KR","isLegacy":true,"cultureExpense":false,"orderName":"도톰부클 베이커 숄더백","customerName":"이성호","customerId":"test_sk_********************r7nO5Wml","customerEmail":"seongho051@naver.com","customerMobilePhone":"01062903047","customerBirthday":null,"cardCompany":"BC","useAppCardOnly":false,"easyPay":null,"provider":null,"discountCode":null,"successUrl":"https://www.doanity.com/orders/ORD-240226-K4CL/success?paymentType=NORMAL","failUrl":"https://www.doanity.com/orders/ORD-240226-K4CL/fail","appScheme":null,"useCardPoint":null,"cardInstallmentPlan":null,"maxCardInstallmentPlan":null,"customerKey":"test_sk_********************r7nO5Wml","validHours":null,"dueDate":null,"taxFreeAmount":0.0,"taxExemptionAmount":0,"cashReceipt":null,"freeInstallmentPlans":null,"windowTarget":null,"methodType":"CARD","affiliateCardInfo":null,"dividedSettlementInfo":null,"useHyundaiVoucher":false,"bank":null,"flowMode":"DIRECT","virtualAccountCallbackUrl":null,"refundReceiveAccount":null,"referer":"https://www.doanity.com","showCustomerMobilePhone":true,"pinpayEncryptedPayload":null,"hideMobileAppHeader":false,"products":null,"shipping":null,"paymentMethodOptions":null,"mId":null,"forceNewWindow":false,"forceOldWindow":false}
Feb 26, 2024 @ 15:35:57.659
Header for User-agent = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Referer = Unknown, Origin = Unknown / path = /v1/payments/sdk, IP = 127.0.0.6, Query = null, Body = {"amount":3000,"orderId":"clt2kexz50005yatibxq21wx1","currency":"KRW","language":null,"useInternationalCardOnly":false,"useEscrow":null,"escrowProducts":null,"country":"KR","isLegacy":true,"cultureExpense":false,"orderName":"도톰부클 베이커 숄더백","customerName":"이성호","customerId":"test_sk_********************r7nO5Wml","customerEmail":"seongho051@naver.com","customerMobilePhone":"01062903047","customerBirthday":null,"cardCompany":"BC","useAppCardOnly":false,"easyPay":null,"provider":null,"discountCode":null,"successUrl":"https://www.doanity.com/orders/ORD-240226-K4CL/success?paymentType=NORMAL","failUrl":"https://www.doanity.com/orders/ORD-240226-K4CL/fail","appScheme":null,"useCardPoint":null,"cardInstallmentPlan":null,"maxCardInstallmentPlan":null,"customerKey":"test_sk_********************r7nO5Wml","validHours":null,"dueDate":null,"taxFreeAmount":0.0,"taxExemptionAmount":0,"cashReceipt":null,"freeInstallmentPlans":null,"windowTarget":null,"methodType":"CARD","affiliateCardInfo":null,"dividedSettlementInfo":null,"useHyundaiVoucher":false,"bank":null,"flowMode":"DIRECT","virtualAccountCallbackUrl":null,"refundReceiveAccount":null,"referer":"https://www.doanity.com","showCustomerMobilePhone":true,"pinpayEncryptedPayload":null,"hideMobileAppHeader":false,"products":null,"shipping":null,"paymentMethodOptions":null,"mId":null,"forceNewWindow":false,"forceOldWindow":false}
customerKey, customerId 는 저희가 임의로 세팅하는 값이 아닙니다 결제창을 열기전 값세팅을 어떻게 하시는지 확인부탁드려요
이성호
이성호OP13mo ago
const paymentWidgetRef = useRef<PaymentWidgetInstance | null>(null);
const paymentMethodsWidgetRef = useRef<PaymentWidget | null>(null);

const initToss = useCallback(async (amount: number) => {
const key = process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY;
const secret = process.env.NEXT_PUBLIC_TOSS_SECRET_KEY;
if (!key || !secret) {
throw new Error("토스 결제창 설정이 잘못되었습니다.");
}
const paymentWidget = await loadPaymentWidget(key, secret);

const paymentMethodsWidget = paymentWidget.renderPaymentMethods(
"#payment-widget",
amount
);
paymentWidget.renderAgreement("#agreement");

paymentWidgetRef.current = paymentWidget;
paymentMethodsWidgetRef.current = paymentMethodsWidget;
}, []);

useEffect(() => {
if (!isFree) {
initToss(order.finalAmount);
}
}, [initToss, isFree, order.finalAmount]);
const paymentWidgetRef = useRef<PaymentWidgetInstance | null>(null);
const paymentMethodsWidgetRef = useRef<PaymentWidget | null>(null);

const initToss = useCallback(async (amount: number) => {
const key = process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY;
const secret = process.env.NEXT_PUBLIC_TOSS_SECRET_KEY;
if (!key || !secret) {
throw new Error("토스 결제창 설정이 잘못되었습니다.");
}
const paymentWidget = await loadPaymentWidget(key, secret);

const paymentMethodsWidget = paymentWidget.renderPaymentMethods(
"#payment-widget",
amount
);
paymentWidget.renderAgreement("#agreement");

paymentWidgetRef.current = paymentWidget;
paymentMethodsWidgetRef.current = paymentMethodsWidget;
}, []);

useEffect(() => {
if (!isFree) {
initToss(order.finalAmount);
}
}, [initToss, isFree, order.finalAmount]);
@Tosspayments/payment-widget-sdk에서 제공하는 loadPaymentWidget를 통해 초기 값을 세팅하고
const callbackBaseUrl = `${window.location.origin}/orders/${order.code}`;
const orderName = displayItemsWithLimit(
order.items.map((item) => item.product.name),
1
);

const requestPaymentDto = {
orderId: prepareResult.data.paymentId,
orderName,
customerName,
customerEmail: email,
customerMobilePhone: mobile,
successUrl: `${callbackBaseUrl}/success`,
failUrl: `${callbackBaseUrl}/fail`,
};

await paymentWidget
.requestPayment(requestPaymentDto)
const callbackBaseUrl = `${window.location.origin}/orders/${order.code}`;
const orderName = displayItemsWithLimit(
order.items.map((item) => item.product.name),
1
);

const requestPaymentDto = {
orderId: prepareResult.data.paymentId,
orderName,
customerName,
customerEmail: email,
customerMobilePhone: mobile,
successUrl: `${callbackBaseUrl}/success`,
failUrl: `${callbackBaseUrl}/fail`,
};

await paymentWidget
.requestPayment(requestPaymentDto)
paymentWidget의 requestPayment을 호출합니다. 외에 /confirm에 헤더로 시크릿키를 base64 인코딩하여 보내는 코드만 있습니다
유부장
유부장13mo ago
requestPayment 전에 body param 값들 로그로 한번 찍어 보시겠어요?
이성호
이성호OP13mo ago
requestPayment 전에 body param이 어떤 거죠?? requestPaymentDto 이거 말씀이시라면 찍어봤는데 코드와 동일합니다 사이에 어떤 코드없이 정의 직후 requestPayment에 넣어주고 있어요
이실장
이실장13mo ago
여기 넣어주고 계시네요 const secret = process.env.NEXT_PUBLIC_TOSS_SECRET_KEY; if (!key || !secret) { throw new Error("토스 결제창 설정이 잘못되었습니다."); } const paymentWidget = await loadPaymentWidget(key, secret); loadPaymentWidget 2번째 인자가 customerKey입니다.
이성호
이성호OP13mo ago
아 loadPaymentWidget 2번째 인자가 시크릿키가 아니라 customer의 고유한 값을 넣어줘야 하는 거군요?? 확인 감사합니다! 수정해서 사용하도록 하겠습니다

Did you find this page helpful?