무작정 프로젝트 | Kakao API - Kakao Pay편 [Android Java]

프로젝트 주요 기능
- 카카오 페이 기능을 구현합니다.
- Volley를 이용한 Http 통신
- Gson을 이용한 json 데이터 파싱
프로젝트 완성 파일
* '무작정 프로젝트 Kakao API - 로그인' 파트에 Developers 설정을 한 후 진행해야 합니다.
무작정 프로젝트 | Kakao API - Login편 [Android Java]
프로젝트 주요 기능 카카오 로그인 기능을 구현합니다. Kakao SDK 설정 로그인 사용자 정보 가져오기 프로젝트 완성 파일.zip 제작 1. Kakao Developers 설정 Kakao Developers Kakao Developers 카카오 API를..
jaeho0613.tistory.com
제작 1. 프로젝트 설정 - 라이브러리
프로젝트에 필요한 라이브러리를 설정합니다.

Empty Activity로 프로젝트를 생성합니다.

// Http 통신 라이브러리
implementation 'com.android.volley:volley:1.1.1'
// Json 데이터 파싱 라이브러리
implementation 'com.google.code.gson:gson:2.8.6'
build.gradle(Module 수준)에 사용할 라이브러리를 추가하고 Sync Now 눌러 저장합니다.
제작 2. 프로젝트 설정 - XML

Pay결제창이 열릴 Activity를 새로 생성합니다.

이름은 'PayActivity'로 해주겠습니다.

activity_main.xml입니다. 간단한 상품명과 상품 가격을 입력받고 버튼을 눌러서 결제를 시도합니다.

activity_pay.xml입니다. 화면을 꽉 채운 WebView만 존재합니다.
제작 3. Pay 구현 - 결제 준비
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com

결제를 준비하는 단계입니다. 요구하는 API를 담아 'POST'해주면 JSON 객체로 다시 돌려줍니다.
package com.jaeho; | |
import androidx.appcompat.app.AppCompatActivity; | |
import android.content.Intent; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.EditText; | |
public class MainActivity extends AppCompatActivity { | |
EditText editTextName; | |
EditText editTextPrice; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
editTextName = findViewById(R.id.editName); | |
editTextPrice = findViewById(R.id.editPrice); | |
// 버튼 클릭 이벤트 | |
Button button = findViewById(R.id.buttonPay); | |
button.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
// EditText에 입력한 상품 정보를 가져온다. | |
String name = editTextName.getText().toString(); | |
String price = editTextPrice.getText().toString(); | |
// 결제가 이루어지는 PayActivity를 생성한다. | |
// - 생성자를 이용하여 상품 정보를 입력한다. | |
PayActivity payActivity = new PayActivity(name, price); | |
// Intent로 새로운 Activity를 실행한다. | |
Intent intent = new Intent(getApplicationContext(), payActivity.getClass()); | |
startActivity(intent); | |
} | |
}); | |
} | |
} |
MainActivity.java입니다.
EditText의 값을 가져오기 위해 2개의 EditText변수를 선언하고 버튼 클릭 시 상품 이름, 가격을 입력받은 PayActivitiy를 생성하여 intent로 새로운 Activity를 띄워줍니다.
package com.jaeho; | |
import androidx.appcompat.app.AppCompatActivity; | |
import android.content.Intent; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.webkit.WebChromeClient; | |
import android.webkit.WebSettings; | |
import android.webkit.WebView; | |
import android.webkit.WebViewClient; | |
import com.android.volley.AuthFailureError; | |
import com.android.volley.Request; | |
import com.android.volley.RequestQueue; | |
import com.android.volley.Response; | |
import com.android.volley.VolleyError; | |
import com.android.volley.toolbox.StringRequest; | |
import com.android.volley.toolbox.Volley; | |
import com.google.gson.Gson; | |
import com.google.gson.JsonElement; | |
import com.google.gson.JsonParser; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class PayActivity extends AppCompatActivity { | |
// Request 작업을 할 Queue | |
static RequestQueue requestQueue; | |
// 결제 정보를 받을 변수 | |
static String productName; // 상품 이름 | |
static String productPrice; // 상품 가격 | |
// 웹 뷰 | |
WebView webView; | |
// json 파싱 | |
Gson gson; | |
// 커스텀 웹 뷰 클라이언트 | |
MyWebViewClient myWebViewClient; | |
// 결제 고유 번호 | |
String tidPin; | |
// 결제 요청 토큰 | |
String pgToken; | |
// 기본 생성자 | |
// - Activity는 기본 생성자가 없으면 Manifest에서 사용하지 못함. | |
// - 만약 생성자를 오버라이딩 했다면 기본 생성자를 작성해 둘것! | |
public PayActivity() { | |
} | |
// 상품 이름과 가격을 초기화할 생성자 | |
public PayActivity(String productName, String productPrice) { | |
PayActivity.productName = productName; | |
PayActivity.productPrice = productPrice; | |
} | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_pay); | |
// 초기화 | |
requestQueue = Volley.newRequestQueue(getApplicationContext()); | |
myWebViewClient = new MyWebViewClient(); | |
webView = findViewById(R.id.webView); | |
gson = new Gson(); | |
// 웹 뷰 설정 | |
webView.getSettings().setJavaScriptEnabled(true); | |
webView.setWebViewClient(myWebViewClient); | |
// 실행 시 바로 결제 Http 통신 실행 | |
requestQueue.add(myWebViewClient.readyRequest); | |
} | |
public class MyWebViewClient extends WebViewClient { | |
// 에러 - 통신을 받을 Response 변수 | |
Response.ErrorListener errorListener = new Response.ErrorListener() { | |
@Override | |
public void onErrorResponse(VolleyError error) { | |
Log.e("Debug", "Error : " + error); | |
} | |
}; | |
// 결제 준비 단계 - 통신을 받을 Response 변수 | |
Response.Listener<String> readyResponse = new Response.Listener<String>() { | |
@Override | |
public void onResponse(String response) { | |
Log.e("Debug", response); | |
// 결제가 성공 했다면 돌려받는 JSON객체를 파싱함. | |
JsonParser parser = new JsonParser(); | |
JsonElement element = parser.parse(response); | |
// get("받을 Key")로 Json 데이터를 받음 | |
// - 결제 요청에 필요한 next_redirect_mobile_url, tid를 파싱 | |
String url = element.getAsJsonObject().get("next_redirect_mobile_url").getAsString(); | |
String tid = element.getAsJsonObject().get("tid").getAsString(); | |
Log.e("Debug", "url : " + url); | |
Log.e("Debug", "tid : " + tid); | |
webView.loadUrl(url); | |
tidPin = tid; | |
} | |
}; | |
// 결제 준비 단계 - 통신을 넘겨줄 Request 변수 | |
StringRequest readyRequest = new StringRequest(Request.Method.POST, "https://kapi.kakao.com/v1/payment/ready", readyResponse, errorListener) { | |
@Override | |
protected Map<String, String> getParams() throws AuthFailureError { | |
Log.e("Debug", "name : " + productName); | |
Log.e("Debug", "price : " + productPrice); | |
Map<String, String> params = new HashMap<>(); | |
params.put("cid", "TC0ONETIME"); // 가맹점 코드 | |
params.put("partner_order_id", "1001"); // 가맹점 주문 번호 | |
params.put("partner_user_id", "gorany"); // 가맹점 회원 아이디 | |
params.put("item_name", productName); // 상품 이름 | |
params.put("quantity", "1"); // 상품 수량 | |
params.put("total_amount", productPrice); // 상품 총액 | |
params.put("tax_free_amount", "0"); // 상품 비과세 | |
params.put("approval_url", "https://www.naver.com/success"); // 결제 성공시 돌려 받을 url 주소 | |
params.put("cancel_url", "https://www.naver.com/cancel"); // 결제 취소시 돌려 받을 url 주소 | |
params.put("fail_url", "https://www.naver.com/fali"); // 결제 실패시 돌려 받을 url 주소 | |
return params; | |
} | |
@Override | |
public Map<String, String> getHeaders() throws AuthFailureError { | |
Map<String, String> headers = new HashMap<>(); | |
headers.put("Authorization", "KakaoAK " + "dfda30650082a405be23fce878038ad4"); | |
return headers; | |
} | |
}; | |
} | |
} |
PayActivity.java입니다.
실질적으로 통신, 처리하는 부분은 PayActivity의 내부 클래스인 MyWebViewClient 클래스입니다.
WebViewClient를 상속받게 되면 WebView 아이템의 여러 속성을 제어할 수 있습니다.
[Android] 웹뷰를 풍부하게 활용할 수 있도록 해주는 WebViewClient
[Android] 웹뷰를 풍부하게 활용할 수 있도록 해주는 WebViewClient
안드로이드 웹뷰에는 WebViewClient와 WebChromeClient가 있습니다. 처음에는 이 두 클라이언트 클래스가 헷갈리는데요. 하나하나 차근차근 정리하면서 각각의 역할을 구분한다면 그냥 웹뷰만 사용했을
readystory.tistory.com

결제 준비 Request단계에선 디벨로퍼 문서에 나와있는 값을 동일하게 입력해 주면 됩니다.
위 코드를 작성 후 실행해 보면

Logcat에 Response 된 값이 출력되는데 필요한 값인 Moblie Url과 tid값을 파싱 후
WebView에는 loadUrl로 결제창을 요청하고 tid값은 변수에 저장합니다.
넘겨받은 Response 데이터에 대한 설명은 문서에 자세히 나와있습니다.

제작 4. Pay 구현 - 결제 승인
package com.jaeho;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import java.util.HashMap;
import java.util.Map;
public class PayActivity extends AppCompatActivity {
// ''''' 생략 '''''' //
public class MyWebViewClient extends WebViewClient {
// ''''' 생략 '''''' //
// 결제 요청 단계 - 통신을 받을 Response 변수
Response.Listener<String> approvalResponse = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("Debug", response);
}
};
// 결제 요청 단계 - 통신을 넘겨줄 Request 변수
StringRequest approvalRequest = new StringRequest(Request.Method.POST, "https://kapi.kakao.com/v1/payment/approve", approvalResponse, errorListener) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("cid", "TC0ONETIME");
params.put("tid", tidPin);
params.put("partner_order_id", "1001");
params.put("partner_user_id", "gorany");
params.put("pg_token", pgToken);
params.put("total_amount", productPrice);
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "KakaoAK " + "dfda30650082a405be23fce878038ad4");
return headers;
}
};
// URL 변경시 발생 이벤트
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.e("Debug", "url" + url);
if (url != null && url.contains("pg_token=")) {
String pg_Token = url.substring(url.indexOf("pg_token=") + 9);
pgToken = pg_Token;
requestQueue.add(approvalRequest);
} else if (url != null && url.startsWith("intent://")) {
try {
Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
Intent existPackage = getPackageManager().getLaunchIntentForPackage(intent.getPackage());
if (existPackage != null) {
startActivity(intent);
}
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
view.loadUrl(url);
return false;
}
}
}
PayActivity.java입니다.

결제 준비 단계와 동일합니다. 카카오 문서에서 요구하는 파라미터 값을 넘겨줍니다.
실행을 해주면 에디터에 입력했던 상품명과 가격으로 카카오페이 결제가 진행됩니다.

webViewClient를 상속받으면 재정의 해서 사용할 수 있는 shouldOverrideUrlLoading 메서드입니다.
Url이 변경될 때마다 실행됩니다.

위 과정이 필요한 이유는 내부 웹 뷰를 이용하여 url을 변경하게 될 시
SCHEME(스키마) 오류가 있어서 "intent://" 부분을 제거한 실제 url로 변경해 주기 위함입니다.


그리고 pg_token값 또한 결제가 완료되면 url 값으로 넘어오게 되는데 원하는 token값을 얻기 위해서 subString을 사용하여 값을 파싱 했습니다.

( * www.naver.com/success? 는 우리가 결제 준비 과정에서 넣었던 url로 진행됩니다.)

( * 애뮬레이터에선 작동하지 않는 거 같습니다. 모바일 기기로 구동해 주셔야 합니다)
끝으로..
Kakao API의 사용법을 적어봤습니다.
Rest API라고 부르는 이 방법은 웹 페이지에 요청(Request)하면 응답(Response)된 데이터를 가지고
사용할 수 있습니다.
어떤 요청을 해야 되고 값을 넘겨주어야 되는지는 카카오 디벨로퍼 문서를 읽으면서 사용해 보시면
도움이 될 거 같네요!