영수증 미리보기

구분 | IP | 비고 |
---|---|---|
테스트 | 211.193.35.11 | STG |
운영 - 결제 | 211.193.35.20 | |
운영 - 재통보 | 211.193.35.216 211.193.35.217 |
재통보 서비스 사용시 |
취소 상세 파라미터
파라미터 | 항목명 | 길이(char) | 설명 |
---|---|---|---|
Mid | Mid | 10 | 상점 ID |
Tid | 거래번호 | 30 | 취소할 거래의 Tid |
CancelPwd | 취소 패스워드 | 20 | 관리자 페이지에서 설정한 취소 패스워드 |
CancelAmt | 취소금액 | 12 | 숫자만 가능, 문장부호 제외 |
CancelMsg | 취소사유 | 100 | |
CancelSeq | 취소차수 | 3 | 기본값: 1(부분취소 시마다 차수가 1씩 늘어남. 첫번째 부분취소=1, 두번째 부분취소=2, ...) |
CancelTaxAmt | 취소 과세 | 12 | 부가세 직접 가맹점일 경우 취소 시에도 과세 계산 필요(숫자만 가능, 문장부호 제외) |
CancelTaxFreeAmt | 취소 비과세 | 12 | 부가세 직접 가맹점일 경우 취소 시에도 비과세 계산 필요(숫자만 가능, 문장부호 제외) |
CancelVatAmt | 취소 부가세 | 12 | 부가세 직접 가맹점일 경우 취소 시에도 부가세 계산 필요(숫자만 가능, 문장부호 제외) |
PartialCancelCode | 부분취소 여부 | 1 | 0: 전체, 1: 부분 |
HashData | 검증데이터 | 가변 | SHA256 암호화(Tid + MerchantKey + CancelAmt + PartialCancelCode) |
DivideInfo | 분할정산 정보 | 가변 | 분할정산 가맹점 전용(설정 방법-일반연동 참고) |
복합결제 전문 취소 | |||
CpxTid | 복합결제 취소Tid | 30 | 복합결제 취소Tid (복합결제 취소시 필수) |
GreenDeposit | 컵보증금 | 12 | 컵보증금 거래 취소시 필수 |
파라미터 | 항목명 | 길이(char) |
---|---|---|
PayMethod | 지불수단 | 10 |
Tid | 거래번호 | 30 |
Mid | 상점아이디 | 10 |
ResultCode | 지불수단 별 결과코드 | 10 |
ResultMsg | 취소메세지 | 100 |
CancelDate | 취소일자 | 8 |
CancelTime | 취소시간 | 6 |
CancelNum | 지불수단 별 취소번호(가상계좌 채번취소시 취소된 가상계좌번호가 응답됩니다.) | |
CancelSeq | 취소차수 | 3 |
Moid | 주문번호 | 100 |
PTid | 부분취소 Tid (전체취소 : 원거래 Tid, 부분취소 : 부분취소Tid) | 30 |
CancelAmt | 취소금액 | 12 |
CancelTaxAmt | 취소 과세 | 12 |
CancelTaxFreeAmt | 취소 비과세 | 12 |
CancelVatAmt | 취소 부가세 | 12 |
RemainAmt | 부분 취소 후 남은 총 금액 | 12 |
RemainVatAmt | 부분 취소 후 남은 부가세 금액 | 12 |
RemainTaxAmt | 부분 취소 후 남은 과세금액 | 12 |
RemainTaxFreeAmt | 부분 취소 후 남은 비과세금액 | 12 |
복합결제 취소 | ||
CpxTid | 복합결제 Tid | 30 |
CpxAuthCode | 복합결제 승인번호 | 30 |
CpxAmt | 복합결제 취소금액 | 12 |
PayAmt | 주결제수단금액 | 12 |
InstDiscTid | 즉시할인 취소Tid | 30 |
InstDiscAuthCode | 즉시할인 승인번호 | 30 |
InstDiscAmt | 즉시할인 금액 | 12 |
String url = "https://tapproval.smartropay.co.kr/payment/approval/cancel.do"; // 테스트
// String url = "https://approval.smartropay.co.kr/payment/approval/cancel.do"; // 운영
JSONObject body = new JSONObject();
JSONObject paramData = new JSONObject()
String Tid = ""; // 취소 요청할 Tid 입력
String Mid = ""; // 발급받은 테스트 Mid 설정(Real 전환 시 운영 Mid 설정)
String CancelAmt = "1004"; // 취소할 거래금액
String CancelSeq = "1"; // 취소차수(기본값: 1, 부분취소 시마다 차수가 1씩 늘어남. 첫번째 부분취소=1, 두번째 부분취소=2, ...)
String PartialCancelCode = "0"; // 0: 전체취소, 1: 부분취소
String MerchantKey = ""; // 발급받은 테스트 상점키 설정(Real 전환 시 운영 상점키 설정)
// 검증값 SHA256 암호화(Tid + MerchantKey + CancelAmt + PartialCancelCode)
String HashData = encodeSHA256Base64(Tid + MerchantKey + CancelAmt + PartialCancelCode);
// 취소 요청 파라미터 셋팅
paramData.put("SERVICE_MODE", "CL1");
paramData.put("Tid", Tid);
paramData.put("Mid", Mid);
paramData.put("CancelAmt", CancelAmt);
paramData.put("CancelPwd", "");
paramData.put("CancelMsg", "");
paramData.put("CancelSeq", CancelSeq);
paramData.put("PartialCancelCode", PartialCancelCode);
// 과세, 비과세, 부가세 셋팅(부가세 직접 계산 가맹점의 경우 각 값을 계산하여 설정해야 합니다.)
paramData.put("CancelTaxAmt", "");
paramData.put("CancelTaxFreeAmt", "");
paramData.put("CancelVatAmt", "");
// 분할정산 사용 가맹점의 경우, DivideInfo 파라미터를 가맹점에 맞게 설정해 주세요. (일반연동 참고)
paramData.put("DivideInfo", "");
// HASH 설정 (필수)
paramData.put("HashData", HashData);
// json 데이터 AES256 암호화
try {
body.put("EncData", AES256Cipher.AES_Encode(paramData.toString(), merchantKey.substring(0,32)));
body.put("Mid", Mid);
} catch(Exception e){
e.printStackTrace();
}
HashMap<String, Object> result = callApi(body, url);
public HashMap<String, Object> callApi(JSONObject json, String callUrl) {
StringBuilder responseBody = null;
HashMap<String, Object> result = new HashMap<>();
// http urlCall 승인 요청 및 TrAuthKey 유효성 검증
int connectTimeout = 1000;
int readTimeout = 10000; // 가맹점에 맞게 TimeOut 조절
URL url = null;
HttpsURLConnection connection = null;
try {
SSLContext sslCtx = SSLContext.getInstance("TLSv1.2");
sslCtx.init(null, null, new SecureRandom());
url = new URL(callUrl);
System.out.println(" url " + url.toString());
connection = (HttpsURLConnection)url.openConnection();
connection.setSSLSocketFactory(sslCtx.getSocketFactory());
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Accept", "application/json");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
OutputStreamWriter osw = new OutputStreamWriter(new BufferedOutputStream(connection.getOutputStream()) , "utf-8" );
char[] bytes = json.toString().toCharArray();
osw.write(bytes,0,bytes.length);
osw.flush();
osw.close();
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String line = null;
responseBody = new StringBuilder();
while ((line = br.readLine()) != null) {
System.out.println(" response " + line);
responseBody.append(line);
}
br.close();
// 결제결과
result = new ObjectMapper().readValue(responseBody.toString(), HashMap.class);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Illegal key size
예외가 발생했을 경우 여기를 클릭해 주세요.
import org.apache.commons.codec.binary.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
public class AES256Cipher {
public static byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
public static String AES_Encode(String str, String key) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] textBytes = str.getBytes("UTF-8");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec newKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
return Base64.encodeBase64String(cipher.doFinal(textBytes));
}
public static String AES_Decode(String str, String key, byte[] ivBytes) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] textBytes = Base64.decodeBase64(str.getBytes());
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec newKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(textBytes), "UTF-8");
}
public static String AES_Decode(String str, String key) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
return AES_Decode(str, key, ivBytes);
}
}
과세 및 비과세 필드를 직접 계산하는 가맹점일 경우 아래와 같은 방식으로 계산하세요.
// 총금액(Amt) = 과세금액(TaxAmt) + 부가세(VatAmt) + 비과세(TaxFreeAmt)
// 비과세(TaxFreeAmt) 금액이 0원일 경우
tmp = Amt / 1.1;
VatAmt = (long)Math.floor(Amt - tmp); // 부가세 절사
TaxAmt = Amt - VatAmt
/** 예시 : 총금액(Amt:5000) = 과세금액(TaxAmt:4546) + 부가세(VatAmt:454) **/
// 비과세(TaxFreeAmt) 금액이 0원 이상일 경우
tmp = (Amt- TaxFreeAmt) / 1.1;
VatAmt = (long)Math.floor(Amt - tmp - TaxFreeAmt); // 부가세 절사
TaxAmt = Amt - VatAmt - TaxFreeAmt
/** 예시 : 총금액(Amt:5000) = 과세금액(TaxAmt:4364) + 부가세(VatAmt:436)+ 비과세(TaxFreeAmt:200) **/
(참고) AES256 암호화 중 예외 발생 시 조치사항
'java.security.InvalidKeyException: Illegal key size' 라는 예외가 발생했을 경우 아래와 같이 해결해 주세요.
1. 현재 사용중인 Java 버전에 맞춰 Unlimited Strength 정책 파일을 다운받습니다.
2. 다운받은 파일의 local_policy.jar, US_export_policy.jar 파일을 <JAVA_HOME>/jre/lib/security/ 폴더로 옮겨 기존 정책을 덮어씌웁니다.
그러면 JCE로 사용 가능한 모든 암호화의 키 길이 제한이 해제됩니다.
※ 8u151 Release Notes에서는 해당 버전부터 별도의 다운로드 없이 Unlimited Strength 정책을 설정할 수 있게 추가 번들이 같이 제공됩니다.
해당 버전에서 정책 파일은 <JAVA_HOME>/jre/lib/security/policy 경로에 limited와 unlimited 폴더로 구성되며,
unlimited 설정은 <JAVA_HOME>/jre/lib/security/java.security 파일을 열어
crypto.policy=unlimited
부분의 주석 처리를 지워주면 제한 해제된 정책을 사용할 수 있습니다.
※ 변경된 기본 정책
2018년 1월 업데이트된 Java8u161 Release Notes에 따르면, Java8u161 버전부터는 JCE 기본 정책이 unlimited이며,
길이를 제한하고 싶다면 crypto.policy=unlimited
부분을 주석처리하면 됩니다.
Unlimited를 기본으로 사용하는 Java 버전은 'JDK-8170157 : Enable unlimited cryptographic policy by default in Oracle JDK builds'에서 확인할 수 있습니다.