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

string5

site_cd

Merchant ID


string14

tno

transaction number


stringvar

kcp_cert_info

Serialized KCP Server Certificate


stringvar

kcp_sign_data

KCP Signature Data
site_cd + "^" + tno + "^" + mod_type


string4

mod_type

Authorization Cancel - STSC / Partial Refund - STPC


number12

mod_mny

Refund Amount


number12

rem_mny

Remaining amount


string100

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

string4

res_cd

Result code
If the request is approved, ‘0000’ will return.


string100

res_msg

Result message


string14

tno

transaction number


number14

canc_time

cancel time


number12

mod_mny

Refund Amount


number12

rem_mny

Remaining Amountn


number14

mod_pacn_seq_no

Refund Sequence
Unique generated for each partial cancellation request.


number12

canc_mod_mny

Cancel amount(Partial cancellation)


number12

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'))