영수증 미리보기

구분 | IP | 비고 |
---|---|---|
테스트 | 211.193.35.11 | STG |
운영 - 결제 | 211.193.35.20 | |
운영 - 재통보 | 211.193.35.216 211.193.35.217 |
재통보 서비스 사용시 |
파라미터 | 항목명 | 길이(char) | 필수여부 | 설명 |
---|---|---|---|---|
Mid | Mid | 10 | 필수 | Cat ID 또는 PG Mid |
Tid | 거래번호 | 30 | 필수 | 취소할 거래의 Tid |
EdiDate | 전문전송일시 | 14 | 필수 | YYYYMMDDHHmmss(형식) |
TraceNo | 전문추적번호 | 4 | 필수 | 0001 ~ 9999 (가맹점에서 정한 임의의 값으로 세팅 하지만 동일거래건의 부분취소시 중복되지 않도록 설정) |
AppNo | 카드사 승인번호 | 12 | 필수 | |
AppDt | 원거래 승인일자 | 8 | 필수 | YYYYMMDD |
CancelAmt | 취소금액 | 12 | 필수 | 숫자만 가능, 문장부호 제외 |
CancelSvcAmt | 취소봉사료 | 12 | 선택 | 숫자만 가능, 문장부호 제외 |
CancelVatAmt | 취소부가세 | 12 | 선택 | 숫자만 가능, 문장부호 제외 |
PartialCancelCode | 부분취소 여부 | 1 | 필수 | 0: 전체, 1: 부분 |
CurrencyCode | 통화코드 | 3 | 필수 | 410 : 원화 |
CancelPwd | 취소 패스워드 | 16 | 선택 | 해외카드 취소패스워드 |
파라미터 | 항목명 | 길이(char) |
---|---|---|
Tid | 거래번호 | 30 |
Mid | 상점아이디 | 10 |
ResultCode | 결과코드 | 10 |
ResultMsg | 취소메세지 | 100 |
Moid | 주문번호 | 100 |
PTid | 부분취소 Tid(원거래 Tid와 상이) | 30 |
CancelAmt | 취소금액 | 12 |
AppNo | 카드사 승인번호 | 12 |
AppCardCode | 발급사 코드 | 2 |
AppCardName | 발급사 명 | 18 |
AcquCardCode | 매입사 코드 | 2 |
AcquCardName | 매입사 명 | 18 |
CardMerchantNo | 카드사 가맹점 번호 | 15 |
AcquInfoCode | 매입코드 (DDC 유무(1:유, 2:무 , 3:포인트) 4:EDC , 5:전자상품 가맹점 , 6: DESC , C : CDC 조회) | 1 |
TraceNo | 전문 추적 번호 | 4 |
TransactionNo | 거래 고유 번호 | 12 |
TrDt | 거래 일시 (YYYYMMDDhhmmssN (N 0:일요일 ~ 6:토요일)) | 15 |
ResultType | 응답 업무구분 코드 (0 :default,1: 전자상품권) | 1 |
String url = "https://tapproval.smartropay.co.kr/payment/approval/offCancel.do"; // 테스트
// String url = "https://approval.smartropay.co.kr/payment/approval/offCancel.do"; // 운영
JSONObject body = new JSONObject();
JSONObject paramData = new JSONObject()
String Tid = ""; // 취소 요청할 Tid 입력
String Mid = ""; // 발급받은 테스트 Mid 설정(Real 전환 시 운영 Mid 설정)
String CancelAmt = "1004"; // 취소할 거래금액
String Edidate = "202301231120734"; // 전문전송일시
String PartialCancelCode = "0"; // 0: 전체취소, 1: 부분취소
String AppNo = "1234567890"; // 카드사 승인번호
String AppDt = "20231231"; // 원거래 승인일자
String TraceNo = "0006"; // 전문추적번호
String CurrencyCode = "410"; // 통화코드
String CancelPwd = "9999"; // 취소비밀번호
String MerchantKey = ""; // 발급받은 테스트 상점키 설정(Real 전환 시 운영 상점키 설정)
// 취소 요청 파라미터 셋팅
paramData.put("Tid", Tid);
paramData.put("Mid", Mid);
paramData.put("CancelAmt", CancelAmt);
paramData.put("Edidate", Edidate);
paramData.put("PartialCancelCode", PartialCancelCode);
paramData.put("AppNo", AppNo);
paramData.put("AppDt", AppDt);
paramData.put("TraceNo", TraceNo);
paramData.put("CurrencyCode", CurrencyCode);
paramData.put("CancelPwd", CancelPwd);
// 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);
}
}
(참고) 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'에서 확인할 수 있습니다.