Transaction Modification
Request Cancellation
Transaction cancel section describes how a merchant requests transaction to be cancelled.
Request Authorization of cancellation data with API URL.
API Request
<!-- API Call -->
1https://spl.kcp.co.kr/gw/mod/v1/cancel
test : https://stg-spl.kcp.co.kr/gw/mod/v1/cancel
Server Certificate
Extraction of data values from NHN KCP issued certificate,
For Merchant authentication, the certificate information issued by KCP must be delivered in text format.
The text value within the certificate must be serialized and delivered as the value of kcp_cert_info.
the parameter kcp_cert_info is needed for requesting Authorization cancel(mod) trade registration inquery
kcp_sign_data
Below codes for processing authorization is included in the auto-cancel logic and,
kcp_sign_data is required for transaction query and cancel request.
To create kcp_sign_data private key is required. For test server private key refer to download menu.
kcp_sign_data example
public class MakeSplParam
{
static{ Security.addProvider( new BouncyCastleProvider() ); }
private static final String PRIVATE_KEY = "../splPrikeyPKCS8.pem";
private static final String PRIVATE_KEY_PASSWD = "changeit";
private static final String ORG_SIGN_DATA = "T0000^20210719000000^PACA";
private static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";
public static void main(String[] args)
{
PrivateKey priKey = loadSplMctPrivateKeyPKCS8( PRIVATE_KEY, PRIVATE_KEY_PASSWD );
String signData = makeSignatureData( priKey, ORG_SIGN_DATA );
System.out.println( "\n[signdata(kcp_sign_data)] : " );
System.out.println( signData );
System.out.println( "-----------------------------\n" );
}
public static PrivateKey loadSplMctPrivateKeyPKCS8( String filePath, String privateKeyPassword )
{
PrivateKey priKey = null;
try
{
Path path = Paths.get( filePath );
String strPriKeyData = Files.readAllLines( path )
.stream()
.filter( line -> !line.startsWith( "-----BEGIN" ) && !line.startsWith( "-----END" ) )
.collect( Collectors.joining() );
// Base64 decoding
byte[] btArrPriKey = Base64.getDecoder().decode( strPriKeyData );
ASN1Sequence derSeq = ASN1Sequence.getInstance( btArrPriKey );
PKCS8EncryptedPrivateKeyInfo encPkcs8PriKeyInfo = new PKCS8EncryptedPrivateKeyInfo( org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance( derSeq ) );
JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();
InputDecryptorProvider decProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build( privateKeyPassword.toCharArray() );
PrivateKeyInfo priKeyInfo = encPkcs8PriKeyInfo.decryptPrivateKeyInfo( decProvider );
priKey = pemKeyConverter.getPrivateKey( priKeyInfo );
}
catch (IOException e)
{
e.printStackTrace();
}
catch (OperatorCreationException e)
{
e.printStackTrace();
}
catch (PKCSException e)
{
e.printStackTrace();
}
return priKey;
}
public static String makeSignatureData(PrivateKey priKey, String targetData)
{
String signData = null;
byte[] btArrTargetData = targetData.getBytes( StandardCharsets.UTF_8 );
try {
Signature sign = Signature.getInstance( SIGNATURE_ALGORITHM );
sign.initSign( priKey );
sign.update( btArrTargetData );
byte[] btArrSignData = sign.sign();
signData = Base64.getEncoder().encodeToString( btArrSignData );
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return signData;
}
}
<?php
$cancel_target_data = "T0000^22284971100001^STSC";
echo "cancel_target_data : ".$cancel_target_data."<br><br>";
$inquery_target_data = "T0000^22284971100001^PACA";
echo "inquery_target_data : ".$inquery_target_data."<br><br>";
$key_data = file_get_contents('../splPrikeyPKCS8.pem');
$pri_key = openssl_pkey_get_private($key_data,'changeit');
openssl_sign($cancel_target_data, $signature, $pri_key, 'sha256WithRSAEncryption');
echo "cancel_signature :".base64_encode($signature)."<br><br>";
openssl_sign($inquery_target_data, $signature, $pri_key, 'sha256WithRSAEncryption');
echo "inquery_signature :".base64_encode($signature)."<br><br>";
?>
namespace kcp_sign_data_sample
{
class Program
{
static void Main(string[] args)
{
// PKCS#8 PEM READ
StreamReader sr = new StreamReader("../splPrikeyPKCS8.pem");
String privateKeyText = sr.ReadToEnd();
string privateKeyPass = "changeit";
StringReader stringReader = new StringReader(privateKeyText);
PemReader pemReader = new PemReader(stringReader, new PasswordFinder(privateKeyPass));
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
var textToSign = "T0000^22671971380028^PACA";
byte[] tmpSource = Encoding.ASCII.GetBytes(textToSign);
ISigner sign = SignerUtilities.GetSigner(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id);
sign.Init(true, keyParams);
sign.BlockUpdate(tmpSource, 0, tmpSource.Length);
var kcp_sign_data = sign.GenerateSignature();
// Console
Console.WriteLine("kcp_sign_data:" + Convert.ToBase64String(kcp_sign_data));
Console.ReadKey();
}
private class PasswordFinder : IPasswordFinder
{
private string password;
public PasswordFinder(string pwd)
{
password = pwd;
}
public char[] GetPassword()
{
return password.ToCharArray();
}
}
}
}
<%
KeyPath = "../splPrikeyPKCS8.pem"
KeyPw = "changeit"
textToSign = "T0000^22671971380028^PACA"
set kcpSign = server.createobject("kcp_sign_data_lib.GenSign")
kcp_sign_data = kcpSign.Sign(KeyPath, KeyPw, textToSign)
response.write "original: " + textToSign + "<br/>"
response.write "kcp_sign_data : " + kcp_sign_data + "<br/>"
%>
// Inquery
const data = "T0000^22296971511092^PACA";
console.log('\n>>> Message:\n\n' + data);
// crypto Set
const crypto = require('crypto');
const fs = require('fs');
const ALGORITHM = "RSA-SHA256";
const SIGNATURE_FORMAT = "base64";
const signature = getSignatureToVerify(data);
// READ
function getPrivateKeySomehow()
{
const pKey = fs.readFileSync('../splPrikeyPKCS8.pem', 'utf8' ); // "splPrikeyPKCS8.pem"
const pKeyObj = crypto.createPrivateKey({
key : pKey,
passphrase : "changeit", // "changeit"
format : "pem",
type : "pkcs8"
})
const pKeyStr = pKeyObj.export({
format: 'pem',
type: 'pkcs8'
}).toString();
return pKeyStr;
}
function getSignatureToVerify(data)
{
const privateKey = getPrivateKeySomehow();
const sign = crypto.createSign(ALGORITHM);
sign.update(data);
const signature = sign.sign(privateKey, SIGNATURE_FORMAT);
console.log(">>> Signature:\n\n" + signature);
return signature;
}
import OpenSSL
from OpenSSL import crypto
import base64
data = 'T0000^22671971380028^PACA'
# READ
# "splPrikeyPKCS8.pem"
key_file = open('../splPrikeyPKCS8.pem', 'r')
key = key_file.read()
key_file.close()
# "changeit"
password = 'changeit'.encode('utf-8')
pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, key, password)
sign = OpenSSL.crypto.sign(pkey, data, 'sha256')
kcp_sign_data = base64.b64encode(sign)
print("kcp_sign_data : ", kcp_sign_data)
Transaction Cancellation Request Parameter
Request Parameter
site_cd
Merchant ID
tno
transaction number
kcp_cert_info
Serialized KCP Server Certificate
kcp_sign_data
KCP Signature Data
site_cd + "^" + tno + "^" + mod_type
mod_type
Authorization Cancel - STSC / Partial Refund - STPC
mod_mny
Refund Amount
rem_mny
Remaining amount
mod_desc
Reason for conducting refund
Cancel Request Example
<!--KCP Authorization Cancel/Full Refund Request Data -->
1<!-- Refund Request Data -->
2{
3 "site_cd":"T0000",
4 "kcp_cert_info":"-----BEGIN CERTIFICATE-----MIID3DCCAsSgAwIBAgIJAMzLXkRXpY3KMA0GCSqGSIb3DQEBCwU...bpE1aPTjDDQsuUduNaCu1jYuBALO+LelrFA...VNeequGLUZSx1il+tJU=-----END CERTIFICATE-----",
5 "kcp_sign_data":"QdwMF6y3GU1JTVkSv7Yn20CCCTeFrKkjvrdZOjShiFibFo...cA0nyX+4HEUZ4Fy3U+htmkZqAfJljeujC1KAL5Flnzqbp5Tst5p5SvZ...0qH7NSq0c6BpedDZb04w==",
6 "mod_type":"STSC",
7 "tno":"2099123112345"
8}
<!-- KCP Partial Refund Request Data -->
1<!-- Refund Request Data -->
2{
3 "site_cd":"T0000",
4 "kcp_cert_info":"-----BEGIN CERTIFICATE-----MIID3DCCAsSgAwIBAgIJAMzLXkRXpY3KMA0GCSqGSIb3DQEBCwU...bpE1aPTjDDQsuUduNaCu1jYuBALO+LelrFA...VNeequGLUZSx1il+tJU=-----END CERTIFICATE-----",
5 "kcp_sign_data":"QdwMF6y3GU1JTVkSv7Yn20CCCTeFrKkjvrdZOjShiFibFo...cA0nyX+4HEUZ4Fy3U+htmkZqAfJljeujC1KAL5Flnzqbp5Tst5p5SvZ...0qH7NSq0c6BpedDZb04w==",
6 "mod_type":"STPC",
7 "tno":"20210721000000",
8 "mod_mny":"5000",
9 "rem_mny":"10000",
10 "mod_desc":"Enter Reason"
11}
Response is returned in the same form of Json
Transaction Cancellation Result
Result Parameter
res_cd
Result code
If the request is approved, ‘0000’ will return.
res_msg
Result message
tno
transaction number
canc_time
cancel time
mod_mny
Refund Amount
rem_mny
Remaining Amountn
mod_pacn_seq_no
Refund Sequence
Unique generated for each partial cancellation request.
canc_mod_mny
Cancel amount(Partial cancellation)
coupon_mod_mny
Coupon Cancel amount(Partial cancellation)
Cancle Sample Example
target_URL = "https://stg-spl.kcp.co.kr/gw/mod/v1/cancel"; //dev
json_req = new JSONObject();
json_req.put("site_cd", site_cd);
json_req.put("kcp_cert_info", kcp_cert_info);
json_req.put("kcp_sign_data", kcp_sign_data);
json_req.put("tno", tno);
json_req.put("mod_type", "STSC");
json_req.put("mod_desc", "cancel");
temp_req_data = json_req.toString();
req_data = temp_req_data.replace(",",",\r\n");
inputLine = null;
outResult = new StringBuffer();
// API REQ
URL url = new URL(target_URL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept-Charset", "UTF-8");
OutputStream os = conn.getOutputStream();
os.write(req_data.getBytes("UTF-8"));
os.flush();
// API RES
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
while ((inputLine = in.readLine()) != null)
{
outResult.append(inputLine);
}
conn.disconnect();
temp_result = outResult.toString();
res_data = temp_result.replace(",",",\r\n");
// RES JSON DATA Parsing
parser = new JSONParser();
json_res = (JSONObject)parser.parse(temp_result);
res_cd = f_get_parm((String)json_res.get("res_cd"));
res_msg = f_get_parm((String)json_res.get("res_msg"));
$target_URL = "https://stg-spl.kcp.co.kr/gw/mod/v1/cancel"; //dev
$data = [
'site_cd' => $site_cd,
'kcp_cert_info' => $kcp_cert_info,
'kcp_sign_data' => $kcp_sign_data,
'tno' => $tno,
'mod_type' => 'STSC',
'mod_desc' => 'cancel',
];
$req_data = json_encode($data);
$header_data = array( "Content-Type: application/json", "charset=utf-8" );
// API REQ
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_URL);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header_data);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $req_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// API RES
$res_data = curl_exec($ch);
// RES JSON DATA Parsing
$json_res = json_decode($res_data, true);
$res_cd = $json_res["res_cd"];
$res_msg = $json_res["res_msg"];
curl_close($ch);
target_URL = "https://stg-spl.kcp.co.kr/gw/mod/v1/cancel"; //dev
req_data = "{\"site_cd\" : \"" + site_cd + "\"," +
"\"kcp_cert_info\":\"" + KCP_CERT_INFO + "\"," +
"\"kcp_sign_data\":\"" + kcp_sign_data + "\"," +
"\"tno\":\"" + tno + "\"," +
"\"mod_type\":\"STSC\"," +
"\"mod_desc\":\"cancel\"}";
// API REQ
req = (HttpWebRequest)WebRequest.Create(target_URL);
req.Method = "POST";
req.ContentType = "application/json";
byte_req = Encoding.UTF8.GetBytes(req_data);
req.ContentLength = byte_req.Length;
st = req.GetRequestStream();
st.Write(byte_req, 0, byte_req.Length);
st.Close();
// API RES
res = (HttpWebResponse)req.GetResponse();
st_read = new StreamReader(res.GetResponseStream(), Encoding.GetEncoding("utf-8"));
res_data = st_read.ReadToEnd();
st_read.Close();
res.Close();
// RES JSON DATA Parsing
json_data = JObject.Parse(res_data);
res_cd = json_data["res_cd"].ToString();
res_msg = json_data["res_msg"].ToString();
' request API
target_URL = "https://stg-spl.kcp.co.kr/gw/mod/v1/cancel" ' dev
req_data = "{""site_cd"":""" & site_cd & """,""kcp_cert_info"":""" & kcp_cert_info & """,""kcp_sign_data"":""" & kcp_sign_data & """,""tno"":""" & tno & """,""mod_type"":""STSC"",""mod_desc"":""cancel""}"
' API REQ
set cancel_req = Server.CreateObject("MSXML2.ServerXMLHTTP")
cancel_req.open "POST", target_URL, false
cancel_req.setRequestHeader "Content-Type", "application/json;charset=UTF-8"
cancel_req.send req_data
' API RES
if cancel_req.status = 200 then
res_data = cancel_req.ResponseText
else
res_data = "http error code : " & cancel_req.status
end if
set cancel_req = nothing
'' RES JSON DATA Parsing
set json_cancel_data = JSON.parse(res_data)
res_cd = json_cancel_data.res_cd
res_msg = json_cancel_data.res_msg
req_data = {
site_cd : site_cd,
tno : tno,
kcp_cert_info : KCP_CERT_INFO,
kcp_sign_data : kcp_sign_data,
mod_type : mod_type,
mod_desc : ''
};
// API URL
fetch("https://stg-spl.kcp.co.kr/gw/mod/v1/cancel", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(req_data),
})
// API RES
.then(response => {
return response.json();
})
// RES JSON DATA Parsing
.then(data => {
res.render('kcp_api_pay', {
req_data : JSON.stringify(req_data),
res_data : JSON.stringify(data),
});
})
# API
target_URL = 'https://stg-spl.kcp.co.kr/gw/mod/v1/cancel' # dev
tno = res_data['tno']
mod_type = 'STSC' #
req_data = {
'site_cd' : site_cd,
'tno' : tno,
'kcp_cert_info' : KCP_CERT_INFO,
'kcp_sign_data' : kcp_sign_data,
'mod_type' : mod_type,
'mod_desc' : 'cancel'
}
res = requests.post(target_URL, headers=headers, data=json.dumps(req_data, ensure_ascii=False, indent="\t").encode('utf8'))