연동 가이드
소프트먼트 결제 API를 연동하여 온라인 결제를 구현하는 방법을 안내합니다.
사전 준비
소프트먼트 결제 API를 연동하기 위해 아래 사항을 확인하세요.
- 가맹점은 소프트먼트에서 발급받은
Pay Key가 필요합니다. - HTTPS 통신 (TLS v1.0 ~ v1.2), 128bit 이상 Cipher Suite를 지원해야 합니다.
- 모든 API 요청과 응답의 데이터 형식은
JSON입니다.
개발/운영 환경
소프트먼트는 개발(테스트)과 운영 환경을 분리하여 제공합니다. 연동 개발 시에는 개발 환경을, 실서비스에는 운영 환경을 사용하세요.
| 환경 | Base URL | 용도 |
|---|---|---|
| 개발 | https://devpapi.softment.co.kr | 연동 개발 및 테스트 |
| 운영 | https://papi.softment.co.kr | 실서비스 운영 |
인증 방식
모든 API 요청 시 HTTP Header의 Authorization에 발급받은 Pay Key를 전달합니다.
Authorization: {your-pay-key} Content-Type: application/json
연동 방식 선택
소프트먼트는 두 가지 결제 연동 방식을 제공합니다.
- 소프트먼트 결제창을 통해 고객이 결제수단(신용카드/간편결제)을 선택
- 별도 파라미터 없이 기본 거래등록 API로 연동
- 모든 결제수단을 한 화면에서 제공
- 통합결제창을 거치지 않고 특정 간편결제사 인증창을 바로 호출
- 거래등록 시
directPay=Y와cpCode파라미터 필수 - 지원: 카카오페이(
KKO), 네이버페이(NPAY), 토스페이(TOSS), 삼성페이(SAM), 페이코(PCO) - 네이버페이/토스페이는
cpType으로 결제타입(C:카드,M:포인트) 선택 가능
Step 1. 거래등록
결제에 필요한 거래 정보를 등록하고, 결제창 URL을 받아옵니다. 거래등록 API를 호출하면 고객을 결제창으로 안내할 수 있는 authPageUrl이 반환됩니다.
요청 파라미터
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
trackId | String | 필수 | 가맹점 주문번호 (고유값) |
returnUrl | String | 필수 | 인증 완료 후 리다이렉트 URL |
amount | String | 필수 | 결제 금액 |
goodsName | String | 필수 | 상품명 |
device | String | 필수 | 디바이스 타입 (pc / mobile) |
payerName | String | 선택 | 구매자명 |
payerTel | String | 선택 | 구매자 연락처 |
appScheme | String(256) | 선택 | 가맹점 앱스키마. 앱 WebView에서 결제 시 대외기관 앱 호출 후 복귀에 사용 |
directPay | String(1) | 선택 | 간편결제 직접호출 여부 (Y/N). 통합결제창을 거치지 않고 간편결제 인증창 직접 호출 시 Y |
cpCode | String(4) | 조건부 | 간편결제사 코드. directPay=Y일 때 필수. NPAY(네이버페이), KKO(카카오페이), TOSS(토스페이), SAM(삼성페이), PCO(페이코) |
cpType | String(1) | 선택 | 간편결제 결제타입. 네이버페이/토스페이만 가능. C:신용카드, M:머니/포인트 |
rcptType | String(1) | 선택 | 네이버페이 포인트 결제시 현금영수증 신청타입. 0:미발행, 1:소득공제용(휴대폰번호), 2:지출증빙용(사업자번호) |
rcptNo | String(11) | 선택 | 네이버페이 포인트 결제시 현금영수증 번호. 휴대폰번호(10~11자) 또는 사업자번호(10자) |
요청 예제
{
"amount": "1004",
"returnUrl": "https://www.shop.co.kr/returnUrl",
"device": "pc",
"trackId": "TEST-202508080001",
"goodsName": "테스트상품",
"payerName": "홍길동",
"payerTel": "010-1234-5678",
"cardMethodInfo": {
"cardInfoList": [
{"cardCd": "029"},
{"cardCd": "016"},
{"cardCd": "006"}
]
},
"shopValueInfo": {
"value1": "값1",
"value2": "값2"
}
}
네이버페이 카드결제 직접호출 예제입니다.
{
"amount": "1004",
"returnUrl": "https://www.shop.co.kr/returnUrl",
"device": "pc",
"trackId": "TEST-202509150006",
"goodsName": "테스트상품",
"payerName": "홍길동",
"payerTel": "010-1234-5678",
"directPay": "Y",
"cpCode": "NPAY",
"cpType": "C",
"shopValueInfo": {
"value1": "값1",
"value2": "값2"
}
}
cURL 요청 예시
# 거래등록 요청 curl -X POST https://papi.softment.co.kr/api/webpay/create \ -H "Authorization: your-pay-key" \ -H "Content-Type: application/json" \ -d '{ "trackId": "ORDER-20260702001", "returnUrl": "https://yoursite.com/return", "amount": "50000", "goodsName": "프리미엄 구독", "device": "pc", "payerName": "홍길동" }'
const axios = require('axios'); const { data } = await axios.post( 'https://papi.softment.co.kr/api/webpay/create', { trackId: 'ORDER-20260702001', returnUrl: 'https://yoursite.com/return', amount: '50000', goodsName: '프리미엄 구독', device: 'pc', payerName: '홍길동' }, { headers: { Authorization: 'your-pay-key' } } );
import requests res = requests.post( 'https://papi.softment.co.kr/api/webpay/create', json={ 'trackId': 'ORDER-20260702001', 'returnUrl': 'https://yoursite.com/return', 'amount': '50000', 'goodsName': '프리미엄 구독', 'device': 'pc', 'payerName': '홍길동' }, headers={'Authorization': 'your-pay-key'} )
응답 예시
{
"resultCode": "S000",
"resultMsg": "성공",
"data": {
"trxId": "T250702001234",
"trackId": "ORDER-20260702001",
"authPageUrl": "https://papi.softment.co.kr/auth/pay?token=abc123..."
}
}
S019 에러가 발생합니다.Step 2. 결제창 호출
거래등록 응답에서 받은 authPageUrl로 고객을 리다이렉트합니다. 고객은 결제창에서 카드를 선택하고 결제 정보를 입력합니다.
// 결제창으로 리다이렉트 window.location.href = data.authPageUrl;
결제 흐름
- 고객이 결제창에서 카드사를 선택하고 인증을 완료합니다.
- 인증이 완료되면 거래등록 시 지정한
returnUrl로 리다이렉트됩니다. returnUrl에는result파라미터가 포함되어 전달됩니다.
returnUrl 콜백 데이터
returnUrl로 전달되는 result 파라미터에는 아래 데이터가 JSON 형태로 포함됩니다.
| 필드 | 타입 | 설명 |
|---|---|---|
trxId | String | 거래 고유번호 |
amount | String | 인증된 결제 금액 |
authorizationId | String | 인증 데이터 (승인요청 시 필요) |
// returnUrl 콜백 데이터 예시 { "trxId": "T250702001234", "amount": "50000", "authorizationId": "AUTH_DATA_abc123..." }
Step 3. 승인요청
returnUrl에서 받은 인증 데이터로 최종 승인 요청을 수행합니다. 승인이 완료되면 실제 결제가 이루어집니다.
요청 파라미터
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
trxId | String | 필수 | 거래 고유번호 |
amount | String | 필수 | 결제 금액 |
authorizationId | String | 필수 | 인증 데이터 |
curl -X POST https://papi.softment.co.kr/api/approval \ -H "Authorization: your-pay-key" \ -H "Content-Type: application/json" \ -d '{ "trxId": "T250702001234", "amount": "50000", "authorizationId": "AUTH_DATA_abc123..." }'
const { data } = await axios.post( 'https://papi.softment.co.kr/api/approval', { trxId: req.body.trxId, amount: req.body.amount, authorizationId: req.body.authorizationId }, { headers: { Authorization: 'your-pay-key' } } );
res = requests.post( 'https://papi.softment.co.kr/api/approval', json={ 'trxId': trx_id, 'amount': '50000', 'authorizationId': authorization_id }, headers={'Authorization': 'your-pay-key'} )
S037 에러가 발생합니다.응답 예시
{
"resultCode": "S000",
"resultMsg": "성공",
"data": {
"trxId": "T250702001234",
"trackId": "ORDER-20260702001",
"amount": "50000",
"status": "APPROVED",
"approvedAt": "2026-07-02T14:30:00+09:00",
"cardName": "신한카드",
"cardNumber": "9410-****-****-1234"
}
}
거래 취소
승인 완료된 거래를 취소(환불)합니다. 전체 취소 또는 부분 취소를 지원합니다.
요청 파라미터
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
trxId | String | 필수 | 거래 고유번호 |
cancelReason | String | 필수 | 취소 사유 |
amount | String | 선택 | 부분 취소 금액 (미입력 시 전액 취소) |
curl -X POST https://papi.softment.co.kr/api/webpay/cancel \ -H "Authorization: your-pay-key" \ -H "Content-Type: application/json" \ -d '{ "trxId": "T250702001234", "cancelReason": "고객 요청에 의한 환불", "amount": "20000" }'
amount 값을 원래 결제 금액보다 적게 입력합니다. 미입력 시 전액 취소됩니다.거래 조회
거래의 현재 상태를 조회합니다.
curl -X POST https://papi.softment.co.kr/api/webpay/status \ -H "Authorization: your-pay-key" \ -H "Content-Type: application/json" \ -d '{ "trxId": "T250702001234" }'
거래 상태값
WAITING— 결제 대기 중APPROVED— 승인 완료PARTIAL_CANCELLED— 부분 취소CANCELLED— 전액 취소
WebView 연동
브라우저가 아닌 앱 WebView에서 결제창을 호출할 경우 앱스킴 처리가 필요합니다.
- 거래등록 API의
appScheme파라미터에 가맹점 앱 스킴을 전달 - Android와 iOS 각각 설정 필요
Android 설정
AndroidManifest.xml의 <queries>에 패키지명을 등록합니다.
<queries> <package android:name="com.shcard.smartpay" /> <!-- 신한페이판 --> <package android:name="kr.co.samsungcard.mpocket" /> <!-- 삼성앱카드 --> <package android:name="com.kbcard.cxh.appcard" /> <!-- KB Pay --> <package android:name="nh.smart.nhallonepay" /> <!-- NH올원페이 --> <package android:name="com.lcacApp" /> <!-- 롯데카드 --> <package android:name="kvp.jjy.MispAndroid320" /> <!-- ISP/페이북 --> <package android:name="com.hanaskcard.paycla" /> <!-- 하나카드 --> <package android:name="com.hyundaicard.appcard" /> <!-- 현대카드 --> <package android:name="com.nhn.android.search" /> <!-- 네이버페이 --> <package android:name="com.kakao.talk" /> <!-- 카카오페이 --> <package android:name="viva.republica.toss" /> <!-- 토스페이 --> <package android:name="com.samsung.android.spay" /> <!-- 삼성페이 --> <!-- ... 기타 카드사/대외기관 앱 --> </queries>
WebViewClient의 shouldOverrideUrlLoading을 오버라이드합니다.
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (!URLUtil.isNetworkUrl(url) && !URLUtil.isJavaScriptUrl(url)) { final Uri uri; try { uri = Uri.parse(url); } catch (Exception e) { return false; } if ("intent".equals(uri.getScheme())) { return startSchemeIntent(url); } else { try { startActivity(new Intent(Intent.ACTION_VIEW, uri)); return true; } catch (Exception e) { return false; } } } return false; }
iOS 설정
Info.plist의 LSApplicationQueriesSchemes에 앱스킴을 등록합니다.
<key>LSApplicationQueriesSchemes</key> <array> <string>ispmobile</string> <string>kb-acp</string> <string>shinhan-sr-ansimclick</string> <string>nhallonepayansimclick</string> <string>lottesmartpay</string> <string>mpocket.online.ansimclick</string> <string>cloudpay</string> <string>hdcardappcardansimclick</string> <string>naversearchthirdlogin</string> <string>kakaotalk</string> <string>supertoss</string> <string>samsungpay</string> <!-- ... 기타 앱스킴 --> </array>
WKWebView의 decidePolicyForNavigationAction을 구현합니다.
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url else { decisionHandler(.allow) return } if url.scheme != "http" && url.scheme != "https" { UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) return } decisionHandler(.allow) }
코드 샘플
전체 결제 연동 흐름을 언어별로 확인할 수 있습니다.
# 1. 거래등록 curl -X POST https://papi.softment.co.kr/api/webpay/create \ -H "Authorization: your-pay-key" \ -H "Content-Type: application/json" \ -d '{ "trackId": "ORDER-20260702001", "returnUrl": "https://yoursite.com/return", "amount": "50000", "goodsName": "프리미엄 구독", "device": "pc", "payerName": "홍길동" }' # 2. 승인요청 (returnUrl에서 받은 데이터로) curl -X POST https://papi.softment.co.kr/api/approval \ -H "Authorization: your-pay-key" \ -H "Content-Type: application/json" \ -d '{ "trxId": "T250702001234", "amount": "50000", "authorizationId": "인증데이터..." }'
const axios = require('axios'); const PAY_KEY = 'your-pay-key'; const BASE_URL = 'https://papi.softment.co.kr'; // 1. 거래등록 const { data: createRes } = await axios.post(`${BASE_URL}/api/webpay/create`, { trackId: 'ORDER-20260702001', returnUrl: 'https://yoursite.com/return', amount: '50000', goodsName: '프리미엄 구독', device: 'pc', payerName: '홍길동' }, { headers: { Authorization: PAY_KEY, 'Content-Type': 'application/json' } }); // 결제창으로 리다이렉트 // res.redirect(createRes.data.authPageUrl); // 2. returnUrl 콜백에서 승인요청 const { data: approvalRes } = await axios.post(`${BASE_URL}/api/approval`, { trxId: req.body.trxId, amount: req.body.amount, authorizationId: req.body.authorizationId }, { headers: { Authorization: PAY_KEY, 'Content-Type': 'application/json' } });
import requests PAY_KEY = 'your-pay-key' BASE_URL = 'https://papi.softment.co.kr' headers = {'Authorization': PAY_KEY, 'Content-Type': 'application/json'} # 1. 거래등록 create_res = requests.post(f'{BASE_URL}/api/webpay/create', json={ 'trackId': 'ORDER-20260702001', 'returnUrl': 'https://yoursite.com/return', 'amount': '50000', 'goodsName': '프리미엄 구독', 'device': 'pc', 'payerName': '홍길동' }, headers=headers) auth_page_url = create_res.json()['data']['authPageUrl'] # 2. 승인요청 approval_res = requests.post(f'{BASE_URL}/api/approval', json={ 'trxId': trx_id, 'amount': '50000', 'authorizationId': authorization_id }, headers=headers)
import java.net.http.*; import java.net.URI; HttpClient client = HttpClient.newHttpClient(); String payKey = "your-pay-key"; // 1. 거래등록 String createBody = """ {"trackId":"ORDER-20260702001","returnUrl":"https://yoursite.com/return", "amount":"50000","goodsName":"프리미엄 구독","device":"pc","payerName":"홍길동"} """; HttpRequest createReq = HttpRequest.newBuilder() .uri(URI.create("https://papi.softment.co.kr/api/webpay/create")) .header("Authorization", payKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(createBody)) .build(); HttpResponse<String> createRes = client.send( createReq, HttpResponse.BodyHandlers.ofString() ); // authPageUrl로 리다이렉트 후, returnUrl에서 승인요청 수행
$payKey = 'your-pay-key'; $baseUrl = 'https://papi.softment.co.kr'; // 1. 거래등록 $ch = curl_init("$baseUrl/api/webpay/create"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: $payKey", "Content-Type: application/json" ], CURLOPT_POSTFIELDS => json_encode([ 'trackId' => 'ORDER-20260702001', 'returnUrl' => 'https://yoursite.com/return', 'amount' => '50000', 'goodsName' => '프리미엄 구독', 'device' => 'pc', 'payerName' => '홍길동' ]), CURLOPT_RETURNTRANSFER => true ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); $authPageUrl = $result['data']['authPageUrl']; // 2. returnUrl 콜백에서 승인요청 $ch = curl_init("$baseUrl/api/approval"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: $payKey", "Content-Type: application/json" ], CURLOPT_POSTFIELDS => json_encode([ 'trxId' => $_POST['trxId'], 'amount' => $_POST['amount'], 'authorizationId' => $_POST['authorizationId'] ]), CURLOPT_RETURNTRANSFER => true ]); $approval = json_decode(curl_exec($ch), true); curl_close($ch);
자주 묻는 질문
/api/approval)까지 호출해야 결제가 확정됩니다.directPay=Y와 cpCode를 전달하여 특정 간편결제사 인증창을 바로 호출하는 방식입니다.directPay를 Y로 설정한 경우 cpCode(간편결제사 코드)는 필수입니다. 누락 시 S040, 사용할 수 없는 코드인 경우 S041로 응답됩니다.