✅ 토스페이먼츠 결제위젯 SDK로 키인 결제를 연동할 수 있어요.
✅ 카드 번호만으로 간단히 결제하거나 법인카드와 같이 고객 명의로 발급되지 않은 카드로 결제하고 싶을 때 사용해요.
키인 결제는 추가 계약 후 사용할 수 있습니다. 추가 계약을 하고 싶다면 토스페이먼츠 고객센터(1544-7772, support@tosspayments.com)로 문의해주세요.
토스페이먼츠와 키인 결제 계약을 완료했다면, 상점 테스트 키로 결제위젯을 연동하고 상점관리자에서 커스터마이징을 해보세요.
상점 테스트 키는 상점관리자 > 매출·정산 관리 > 내 상점 이름 >'개발 정보'에서 확인하세요. 이동한 페이지의 왼쪽 메뉴에 상점아이디(MID)가 키인 결제 전용 MID가 맞는지 확인하세요.
상점관리자의 결제 UI 설정 메뉴로 들어가세요. 등록된 결제 UI에 이용서비스 > 키인(수기입력)결제가 있는지 확인하세요. 만약 없다면 이용 서비스 > 추가하기 +를 눌러서 기존 결제 UI에 키인 결제 서비스를 추가하세요.
결제 UI에 키인 결제는 아래와 같이 '카드번호 결제' 이름으로 등록됩니다. 기능 > 결제수단 목록에서 이름을 수정할 수 있어요. '카드번호 결제' 오른쪽에 보이는 '수정'을 선택하고 새로운 이름을 입력하세요.
아래 예제 코드는 주문서 페이지에 결제위젯, 이용약관 UI를 연동합니다. 결제하기 버튼을 눌러서 결제창을 띄워보세요. 실제로 결제되지 않으니 안심하세요.
<head>
<meta charset="utf-8" />
<!-- 결제위젯 SDK 추가 -->
<script src="https://js.tosspayments.com/v1/payment-widget"></script>
</head>
<body>
<!-- 결제위젯, 이용약관 영역 -->
<div id="payment-method"></div>
<div id="agreement"></div>
<!-- 결제하기 버튼 -->
<button id="payment-button">결제하기</button>
<script>
const clientKey = "test_ck_D5GePWvyJnrK0W0k6q8gLzN97Eoq"
const customerKey = "" // 내 상점의 고객을 식별하는 고유한 키
const button = document.getElementById("payment-button")
// ------ 결제위젯 초기화 ------
// 비회원 결제에는 customerKey 대신 ANONYMOUS를 사용하세요.
const paymentWidget = PaymentWidget(clientKey, customerKey) // 회원 결제
// const paymentWidget = PaymentWidget(clientKey, PaymentWidget.ANONYMOUS) // 비회원 결제
// ------ 결제위젯 렌더링 ------
// 결제수단 UI를 렌더링할 위치를 지정합니다. `#payment-method`와 같은 CSS 선택자와 결제 금액 객체를 추가하세요.
// https://docs.tosspayments.com/reference/widget-sdk#renderpaymentmethods선택자-결제-금액-옵션
paymentWidget.renderPaymentMethods(
"#payment-method",
{ value: 15000 },
{ variantKey: "KEYIN" } // 키인 결제가 추가된 결제 UI의 variantKey
)
// ------ 이용약관 렌더링 ------
// 이용약관 UI를 렌더링할 위치를 지정합니다. `#agreement`와 같은 CSS 선택자를 추가하세요.
// https://docs.tosspayments.com/reference/widget-sdk#renderagreement선택자
paymentWidget.renderAgreement('#agreement')
// ------ '결제하기' 버튼 누르면 결제창 띄우기 ------
// 더 많은 결제 정보 파라미터는 결제위젯 SDK에서 확인하세요.
// https://docs.tosspayments.com/reference/widget-sdk#requestpayment결제-정보
button.addEventListener("click", function () {
paymentWidget.requestPayment({
orderId: "", // 주문 ID(직접 만들어주세요)
orderName: "토스 티셔츠 외 2건", // 주문명
successUrl: "https://my-store.com/success", // 결제에 성공하면 이동하는 페이지(직접 만들어주세요)
failUrl: "https://my-store.com/fail", // 결제에 실패하면 이동하는 페이지(직접 만들어주세요)
customerEmail: "customer123@gmail.com",
customerName: "김토스"
})
})
</script>
</body>
결제 요청 결과를 확인할 차례에요. 결제 요청이 성공하면 requestPayment()의 파라미터로 설정했던 (successUrl
)을 통해 결제 성공 페이지로 이동합니다. 성공 리다이렉트 URL을 나타내는 success 뒤에 네 가지 쿼리 파라미터가 들어있습니다. 첫 번째 파라미터는 paymentType
입니다. 키인 결제이기 때문에 KEYIN
으로 돌아옵니다.
https://my-store.com/success?paymentType=KEYIN&paymentKey={PAYMENT_KEY}&orderId={ORDER_ID}&amount={AMOUNT}
paymentType
: 결제 유형입니다. 키인 결제는 항상KEYIN
입니다.paymentKey
: 결제의 키 값입니다. 결제를 식별하는 역할로, 중복되지 않는 고유한 값입니다. 결제 데이터 관리를 위해 반드시 저장해야 합니다. 결제 상태가 변해도 값이 유지됩니다. 결제 승인에 사용됩니다.orderId
:주문 ID입니다. 주문한 결제를 식별하는 역할로, 결제를 요청할 때 가맹점에서 만들어서requestPayment()
에 담아 보낸 값입니다. 결제 데이터 관리를 위해 반드시 저장해야 합니다. 중복되지 않는 고유한 값을 발급해야 합니다. 결제 상태가 변해도 값이 유지됩니다.amount
: 실제로 결제된 금액입니다.
결제 요청이 실패하면 설정했던 결제 실패 페이지(failUrl
)로 이동합니다. 에러 목록을 확인하세요.
https://my-store.com/fail?code={ERROR_CODE}&message={ERROR_MESSAGE}&orderId={ORDER_ID}
renderPaymentMethods()
메서드에 담아 보낸 결제 금액과 successUrl
로 돌아온 amount
값이 같은지 반드시 확인해보세요. 클라이언트에서 결제 금액을 조작해 승인하는 행위를 방지할 수 있습니다.
만약 값이 다르다면 결제를 취소하고 구매자에게 알려주세요. 적립금이나 쿠폰이 적용된 금액이 맞는지도 확인해보세요.
결제를 요청하면 차례로 인증과 승인 과정이 진행됩니다.
- 인증: 결제를 승인하기 전 결제 정보가 올바른지 검증하는 과정입니다.
- 승인: 인증에 성공한 결제를 최종 승인하는 과정입니다. 승인 요청에 성공하면 결제 요청 과정이 끝납니다.
결제 요청 과정에 대한 더 자세한 설명은 결제 흐름 이해하기에서 확인해보세요.
인증과 승인 사이에 리다이렉트 과정이 있습니다. 두 단계로 나누어져 있는 결제 요청 과정에서 첫 번째 단계인 인증 결과를 받아 다음 단계로 넘어가기 위해 리다이렉트 URL 처리가 필요합니다. 토스페이먼츠에서 인증 결과를 URL의 쿼리 파라미터에 담아 리다이렉트하면, 상점에서는 리다이렉트 URL을 받아 결제 승인을 처리할 수 있게 됩니다.
인증에 성공했다면 성공 리다이렉트 URL(successUrl
)에 쿼리 파라미터로 담긴 결제 정보를 이용해 승인 API를 호출합니다. 인증에 실패했다면 이동한 실패 리다이렉트 URL(failUrl
)에 쿼리 파라미터로 담긴 에러 정보를 보며 디버깅합니다.
더 자세한 내용은 리다이렉트 처리하기에서 확인해보세요.
결제 승인 API를 호출해서 마지막 단계를 완료합니다. 먼저 API 인증을 위해 아래와 같이 인증 헤더 값을 만듭니다.
시크릿 키 뒤에 :
을 추가하고 base64로 인코딩합니다. :
을 빠트리지 않도록 주의하세요.
base64('test_sk_zXLkKEypNArWmo50nX3lmeaxYG5R:')
─────────────────┬───────────────── ┬
secretKey :
발급받은 시크릿 키 콜론
아래 명령어를 터미널에서 실행하면 인코딩된 값을 얻을 수 있습니다.
echo -n 'test_sk_zXLkKEypNArWmo50nX3lmeaxYG5R:' | base64
인코딩된 값을 Basic 인증 헤더에 넣고 요청 본문도 추가하세요. 앞 단계에서 리다이렉트 URL로 받은 paymentKey
, orderId
, amount
를 넣어주세요.
아래 예제 코드에는 내 테스트 결제의 paymentKey
값을 넣어 실행해주세요.
curl --request POST \
--url https://api.tosspayments.com/v1/payments/confirm \
--header 'Authorization: Basic dGVzdF9za196WExrS0V5cE5BcldtbzUwblgzbG1lYXhZRzVSOg==' \
--header 'Content-Type: application/json' \
--data '{"paymentKey":"{PAYMENT_KEY}","amount":15000,"orderId":"YwRNQb4clUl6GS3BMaaHa"}'
성공 페이지로 리다이렉트 된 후 10분 이내에 결제 승인 API를 호출하지 않았거나, 결제창을 iframe 안에서 호출했기 때문입니다. 브라우저의 보안 정책에 따라 성공 페이지로 이동이 차단될 수 있으니 iframe은 사용하지 말아주세요.
결제 승인에 성공하면 HTTP 200 OK
와 Payment 객체를 받습니다.
{
"mId": "tosspayments",
"version": "2022-11-16",
"paymentKey": "",
"status": "DONE",
"lastTransactionKey": "",
"orderId": "",
"orderName": "토스 티셔츠 외 2건",
"requestedAt": "2022-06-08T15:40:09+09:00",
"approvedAt": "2022-06-08T15:40:49+09:00",
"useEscrow": false,
"cultureExpense": false,
"card": {
"issuerCode": "61",
"acquirerCode": "31",
"number": "12345678****789*",
"installmentPlanMonths": 0,
"isInterestFree": false,
"interestPayer": null,
"approveNo": "00000000",
"useCardPoint": false,
"cardType": "신용",
"ownerType": "개인",
"acquireStatus": "READY",
"amount": 15000
},
"virtualAccount": null,
"transfer": null,
"mobilePhone": null,
"giftCertificate": null,
"cashReceipt": null,
"cashReceipts": null,
"discount": null,
"cancels": null,
"secret": null,
"type": "NORMAL",
"easyPay": null,
"country": "KR",
"failure": null,
"isPartialCancelable": true,
"receipt": {
"url": "https://dashboard.tosspayments.com/sales-slip?transactionId=KAgfjGxIqVVXDxOiSW1wUnRWBS1dszn3DKcuhpm7mQlKP0iOdgPCKmwEdYglIHX&ref=PX"
},
"checkout": {
"url": "https://api.tosspayments.com/v1/payments//checkout"
},
"currency": "KRW",
"totalAmount": 15000,
"balanceAmount": 15000,
"suppliedAmount": 13636,
"vat": 1364,
"taxFreeAmount": 0,
"method": "카드"
}
✔️ 응답 객체에 card
필드가 있는지 확인하세요.
✔️ 응답 객체의 method
가 '카드'인지 확인하세요.