Appearance
연동 프로세스 이해하기
앱인토스는 개발의 편의성을 위해 SDK/API를 제공해요.
SDK는 React Native와 WebView, 두 가지 방식으로 제공하고 있어요.
서버 mTLS 인증서 발급받기
앱인토스 API를 사용하려면 먼저 mTLS (mutual TLS, 양방향 인증) 인증서를 설정해야 해요.
이 인증서는 파트너사 서버와 앱인토스 서버 사이의 통신을 암호화하고, 서로 신원을 확인하는 데 사용돼요.
인증서 발급 방법
1. 앱 선택하기
앱인토스 콘솔에 접속한 뒤, 인증서를 발급받을 앱을 선택해요.
왼쪽 메뉴에서 mTLS 인증서 탭을 클릭해 주세요.
+ 발급받기 버튼을 눌러 주세요.
2. 인증서 발급하기
mTLS 인증서가 발급되었어요. 다운로드된 인증서와 키 파일을 확인해 주세요.
🚨 잠시만요
인증서와 키 파일은 유출되지 않도록 안전한 위치에 저장해 주세요. 또한 인증서가 만료되기 전에 꼭 재발급을 진행해 주세요.
발급된 인증서는 다음과 같이 보여요.
일반적으로 인증서는 하나만 사용하지만, 무중단 교체를 위해 두 개 이상 등록하는 경우도 있어요.
그래서 콘솔에서는 인증서를 여러 개 등록할 수 있도록 구성했어요.
인증서로 요청 보내기 예제
앱인토스 서버에 요청을 보내려면, 발급받은 인증서와 키 파일을 클라이언트에 설정해야 해요.
아래는 다양한 언어에서 mTLS 인증서를 사용해 요청을 보내는 예제예요.
자신의 개발 환경에 맞는 코드를 참고해 적용해 보세요.
Kotlin 예제
kotlin
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.security.KeyFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.io.FileReader
import java.io.ByteArrayInputStream
import java.util.Base64
import javax.net.ssl.*
class TLSClient {
fun createSSLContext(certPath: String, keyPath: String): SSLContext {
val cert = loadCertificate(certPath)
val key = loadPrivateKey(keyPath)
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
keyStore.load(null, null)
keyStore.setCertificateEntry("client-cert", cert)
keyStore.setKeyEntry("client-key", key, "".toCharArray(), arrayOf(cert))
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, "".toCharArray())
return SSLContext.getInstance("TLS").apply {
init(kmf.keyManagers, null, null)
}
}
private fun loadCertificate(path: String): X509Certificate {
val content = FileReader(path).readText()
.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "")
.replace("\\s".toRegex(), "")
val bytes = Base64.getDecoder().decode(content)
return CertificateFactory.getInstance("X.509")
.generateCertificate(ByteArrayInputStream(bytes)) as X509Certificate
}
private fun loadPrivateKey(path: String): java.security.PrivateKey {
val content = FileReader(path).readText()
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("\\s".toRegex(), "")
val bytes = Base64.getDecoder().decode(content)
val spec = PKCS8EncodedKeySpec(bytes)
return KeyFactory.getInstance("RSA").generatePrivate(spec)
}
fun makeRequest(url: String, context: SSLContext): String {
val connection = (URL(url).openConnection() as HttpsURLConnection).apply {
sslSocketFactory = context.socketFactory
requestMethod = "GET"
connectTimeout = 5000
readTimeout = 5000
}
return connection.inputStream.bufferedReader().use { it.readText() }.also {
connection.disconnect()
}
}
}
fun main() {
val client = TLSClient()
val context = client.createSSLContext("/path/to/client-cert.pem", "/path/to/client-key.pem")
val response = client.makeRequest("https://apps-in-toss-api.toss.im/endpoint", context)
println(response)
}
Python 예제
python
import requests
class TLSClient:
def __init__(self, cert_path, key_path):
self.cert_path = cert_path
self.key_path = key_path
def make_request(self, url):
response = requests.get(
url,
cert=(self.cert_path, self.key_path),
headers={'Content-Type': 'application/json'}
)
return response.text
if __name__ == '__main__':
client = TLSClient(
cert_path='/path/to/client-cert.pem',
key_path='/path/to/client-key.pem'
)
result = client.make_request('https://apps-in-toss-api.toss.im/endpoint')
print(result)
JavaScript(Node.js) 예제
js
const https = require('https');
const fs = require('fs');
const options = {
cert: fs.readFileSync('/path/to/client-cert.pem'),
key: fs.readFileSync('/path/to/client-key.pem'),
rejectUnauthorized: true,
};
const req = https.request(
'https://apps-in-toss-api.toss.im/endpoint',
{ method: 'GET', ...options },
(res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
console.log('Response:', data);
});
}
);
req.on('error', (e) => console.error(e));
req.end();
C# 예제
csharp
using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(
new X509Certificate2("/path/to/client-cert.pem")
);
using var client = new HttpClient(handler);
var response = await client.GetAsync("https://apps-in-toss-api.toss.im/endpoint");
string body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
}
C++ 예제(libcurl 사용)
cpp
#include <curl/curl.h>
#include <iostream>
#include <string>
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
userp->append((char*)contents, size * nmemb);
return size * nmemb;
}
int main() {
CURL* curl = curl_easy_init();
if (curl) {
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "https://apps-in-toss-api.toss.im/endpoint");
curl_easy_setopt(curl, CURLOPT_SSLCERT, "/path/to/client-cert.pem");
curl_easy_setopt(curl, CURLOPT_SSLKEY, "/path/to/client-key.pem");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
if (res == CURLE_OK) {
std::cout << "Response: " << response << std::endl;
} else {
std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
}
curl_easy_cleanup(curl);
}
return 0;
}
자주 묻는 질문
Q. 'ERR_NETWORK' 에러가 발생해요.
mTLS 인증서가 적용되지 않은 상태에서 API를 호출하면 이 오류가 발생합니다.
간편 로그인, 간편 결제, 광고 등의 기능을 사용하려면 mTLS 인증서 적용이 필요해요.
위 가이드를 참고하셔서 파트너사 서버에 mTLS를 설정한 후 API를 호출해 주세요.
Q. 인증서가 노출되면 어떻게 되나요?
인증서와 키는 서버 인증에 직접 사용돼요.
노출될 경우, 다른 파트너사가 인증서를 도용해 API를 사용할 수 있고, 의도하지 않은 사용자에게 토스포인트가 지급될 수도 있어요.
문제가 발생하면 즉시 해당 인증서를 삭제하고 새로 발급받아야 해요.
Q. 인증서가 만료되면 어떻게 하나요?
mTLS 인증서는 390일 동안 유효해요.
만료되면 인증이 실패하고 서버 간 통신이 중단돼요.
콘솔에서 미리 새 인증서를 발급받고 교체해 주세요.
추후에는 인증서 만료 1개월 전, 1주일 전, 1일 전에 이메일로 안내할 예정이에요.
Q. 인증서는 몇 개까지 발급할 수 있나요?
제한 없이 여러 개 발급할 수 있어요.
대부분은 하나의 인증서만 사용하지만, 무중단 교체를 위해 두 개 이상을 등록해 사용하는 경우도 많아요.
그래서 콘솔에서는 여러 개의 인증서를 동시에 관리할 수 있도록 설계되어 있어요.