2026/02/13

오늘의 이야기

인앱 결제를 하기 위해서 오늘도 구글링을 하시는 분들께...  기본적은 헤맴을 줄여보기 위해서 정리를 해 둡니다.


 


 


인앱 결제를 하려면 일단, 할 일은 앱을 하나 만들어서 구글 플레이에 등록을 하는 것이다. 등록하는 가이드는 구글링을 통해 많이 나와 있으니 생략한다.


 


여기서 등록한다고 해서 꼭 출시상태를 만들 필요는 없다. 알파테스트(비공개 테스트) 단계까지만 등록해도 된다. 


그리고 해야할 것은 인앱 상품을 만들어서 등록하는 것이다. 


 




이처럼 등록을 하고 나면 금방이 승인이 나지 않는 다.  등록하고 다음날 확인해 보는 게 마음 편한 방법이다.


승인이 확인되면,  manifest 부터 설정을 해 봐야 한다. 


    <uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.INTERNET"/>

권한은 꼭 등록이 되어야 한다. internet 사용 권한도 등록해 두어야 한다. 


그다음은 build.gradle의 내용 중에서 다음은 선언 있어야 한다. 


dependencies {


implementation 'com.android.billingclient:billing:4.1.0'
implementation 'com.google.code.gson:gson:2.8.8'

}

이런저런 다른 것들도 필요하면 넣어야 한다. 저 예시는 빌링을 위한 필수 항목이다.  2021.8.2. 부터는 모든 앱이 Ver 3.0을 사용해야 한다고 권고하고 있다.  빌링은 아래 예시 처럼 전체 소스를 확인해야 쉽게 이해가 쉽게 될 것 같다.




반응형






아래 예시 처럼 하나 만들어 두고 사용하는 것도 좋은 방법이 될 것 같다. 이 예제에서 수정할 부분은 


billcode는 구글스토어에서 등록해 놓은 인앱 상품의 상품 ID가 되며,  아래 예시에는 firebase의 realtime database와 연동을 통해서 구매한 사용자의 목록을 관리하기 위한 billingUserBean 항목이 있으나, 이건 개발자의 선택에 따라 필요 없을 수 도 있다.  그런 부분을 제거해 버리면 그냥 그대로 사용할 수 있다.


import android.app.Activity;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.billcoreatech.dream314.R;
import com.billcoreatech.dream314.util.StringUtil;
import com.google.gson.JsonObject;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class BillingManager<mConsumResListnere> implements PurchasesUpdatedListener {
String TAG = "BillingManager" ;
BillingClient mBillingClient ;
Activity mActivity ;
public List<SkuDetails> mSkuDetails ;
public enum connectStatusTypes { waiting, connected, fail, disconnected }
public connectStatusTypes connectStatus = connectStatusTypes.waiting ;
private ConsumeResponseListener mConsumResListnere ;
String billCode = "210420_onetime_pay" ; // 인앱결제 상품ID는 그때 그때 달라요

public BillingManager (Activity _activity) {
mActivity = _activity ;

mBillingClient = BillingClient.newBuilder(mActivity)
.setListener(this)
.enablePendingPurchases()
.build() ;

mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
Log.i(TAG, "respCode=" + billingResult.getResponseCode() ) ;
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
connectStatus = connectStatusTypes.connected ;
Log.i(TAG, "connected...") ;

Log.i(TAG, "resp=" + mBillingClient.queryPurchases(billCode).getBillingResult()
+ "=" + mBillingClient.queryPurchases(billCode).getResponseCode());

getSkuDetailList() ;

} else {
connectStatus = connectStatusTypes.fail ;
Log.i(TAG, "connected... fail ") ;
}
}

@Override
public void onBillingServiceDisconnected() {
connectStatus = connectStatusTypes.disconnected ;
Log.i(TAG, "disconnected ") ;
}
});

mConsumResListnere = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(@NonNull BillingResult billingResult, @NonNull String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.i(TAG, "사용끝 + " + purchaseToken) ;
return ;
} else {
Log.i(TAG, "소모에 실패 " + billingResult.getResponseCode() + " 대상 상품 " + purchaseToken) ;
return ;
}
}
};
}

public int purchase(SkuDetails skuDetails) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
return mBillingClient.launchBillingFlow(mActivity, flowParams).getResponseCode();
}


public void getSkuDetailList() {
List<String> skuIdList = new ArrayList<>() ;
skuIdList.add(billCode);

SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuIdList).setType(BillingClient.SkuType.INAPP); // INAPP 가 인앱결제라는 구분임.
mBillingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> skuDetailsList) {
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
Log.i(TAG, "detail respCode=" + billingResult.getResponseCode()) ;
return ;
}
if (skuDetailsList == null) {
Toast.makeText(mActivity, mActivity.getString(R.string.msgNotInfo), Toast.LENGTH_LONG).show();
return ;
}
Log.i(TAG, "listCount=" + skuDetailsList.size());
for(SkuDetails skuDetails : skuDetailsList) {
Log.i(TAG, "\n" + skuDetails.getSku()
+ "\n" + skuDetails.getTitle()
+ "\n" + skuDetails.getPrice()
+ "\n" + skuDetails.getDescription()
+ "\n" + skuDetails.getFreeTrialPeriod()
+ "\n" + skuDetails.getIconUrl()
+ "\n" + skuDetails.getIntroductoryPrice()
+ "\n" + skuDetails.getIntroductoryPriceAmountMicros()
+ "\n" + skuDetails.getOriginalPrice()
+ "\n" + skuDetails.getPriceCurrencyCode()) ;
}
mSkuDetails = skuDetailsList ;

}
});
}

/**
* @param billingResult
* @param purchases
*/
@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {

userinfoDB = FirebaseDatabase.getInstance().getReference("UserInfoDB");
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
Log.i(TAG, "구매 성공>>>" + billingResult.getDebugMessage());
JSONObject object = null ;
String pID = "" ;
String pDate = "" ;
for(Purchase purchase : purchases) {
Log.i(TAG, "성공값=" + purchase.getPurchaseToken()) ;
Log.i(TAG, "성공값=" + purchase.getOriginalJson());
try {
object = new JSONObject(purchase.getOriginalJson());
pID = object.getString("purchaseToken");
pDate = StringUtil.getDate(object.getLong("purchaseTime")); // 날자를 구하기 위해서

} catch (JSONException e) {
e.printStackTrace();
}
Log.i(TAG, "token=" + pID + "" + pDate) ;
ConsumeParams params = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build() ;
mBillingClient.consumeAsync(params, mConsumResListnere);
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Log.i(TAG, "결제 취소");
} else {
Log.i(TAG, "오류 코드=" + billingResult.getResponseCode()) ;
}
}
}

 


이제 위에서 작성한 billingManager 을 호출하는 MainActivity의 예시를 보자


onCreate 등에서 미리 billingManger 에 대한 선언을 해 두고,  화면에서 버튼을 클릭했거나, 결제 메뉴를 선택했을 떄 아래 부분만 추가해 주면 결제 흐름은 끝이다. 


BillingManager billingManager = new BillingManager(getActivity());

...

if(billingManager.connectStatus == BillingManager.connectStatusTypes.connected) {
Log.i(TAG, "connected ..") ;
SkuDetails skuDetails = (SkuDetails) billingManager.mSkuDetails.get(0);
int iResp = billingManager.purchase(skuDetails) ;
Log.i(TAG, "iResp=" + iResp) ;
}

물론 이 예시는 1회성 인앱 결제의 흐름을 따라가는 예시 이므로 정기 결제 흐름에 대한 부분은 추가로 더 확인을 해야 할 필요가 있다.  (정기결제 흐름을 만들다가 포기(?)한 이유는 정기결제 후에 결제 소모 구현을 해야 하는데, 잘 되지 않아서... ㅠㅠ;;)


 


이 처럼해 정리해 두면 차후에 라도 다른 거 인앱 결제를 구현할 때 복사해서 쓸 기본 소스는 준비하는 것이 될 것 같다.


 


p.s 2021.11.14 : 테스트는 어떻게 할 것 인가 ?


이번에 수정을 하면서 느낀건... 자주 해 봐야 한다는 것이다. 이것도 오랜만에 한번 해 볼려 했더니... 기억 가물거린다. 


아무튼... 추가해서 적어 두어야 하는 것은 테스트 하려면... 


 


1. 먼저 playstore 에 앱을 게시하자... 단, 내부테스트 까지만, 게시하면 가능 하다.  물론 테스트 해야 하는 이메일 계정은 등록을 해 두어야 한다.


 


2. 다음은 인앱 상품을 만들어 두어야 한다.  그것이 등록하면 바로 테스트를 할 수 있는 게 아니라서,  등록하고 나서 하루쯤 지나서 테스트 한다고 생각하는 것이 마음편하다. 그래서 인앱 상품을 개발할 꺼라면, 미리 테스트 앱을 게시하고, 인앱상품도 만들어 등록해 두어야 한다. 


 


3. 테스트는 어떻게 할 것인가 ? AVD 에서 테스트를 해도 되기는 하지만, 이번에 하다 보니, AVD에 계정을 2개 등록했다고 하면, 테스트 계정으로 2개다 등록을 해야 하는지 확인이 필요해 보인다. 그래서 깔끔하게 AVD 을 밀어버리고 계정은 한개만 등록된 상태에서 테스트를 진행 했다.  


 


이런 정도의 주의 사항을 상기 시키기 위해 추가 글을 적는다.  이만...


 


p.s 2022.02.27 구매 소모에 대한 추가 글.


구매 소모를 설정할 때 Token 을 전달해 주어야 하는데, 아래 처럼 전달해 주어야 1회성 구매에 대한 소모가 정상적으로 처리 되는 것을 확인할 수 있었다.


        ConsumeParams params = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build() ;
mBillingClient.consumeAsync(params, mConsumResListnere);

 


 


끝.





오늘의 이야기

안드로이드 폰에서 Restful 호출을 위해서 StringRequest 을 사용해 보기도 했지만, Retrofit 을 알고 나서는 간편하게 잘 쓰게 되었다.


 


data.go.kr 에서 제공하는 공공데이터를 이용해서 앱을 만들어 보고자 했던 건데... 문제는 covid19 관련된 앱은 공공기관에서 제출하는 것만 구글스토어에 등재를 할 수 있다는 것이다. ㅋ~ 뭐 그래도 일단 만들어 보면서 느꼈던 것들을 정리해 두고자 한다. 그래야 나중에 또 하게 되면 쉽게 삽집하지 않고 접근을 할 수 있을테니...


 


먼저 데이터 포털에서 사용인증을 신청해 보자




 


일단 내가 보고싶은 건 코로나 관련 병원정보 이력과 코로나 환자 발생현황 이다. 그중에 하나 신청한 내역은 위와 같다.


일단 신청은 되었고, 사용 승인도 금새 되었다.  안드로이드 앱을 만들어 가는 과정중에서 필수적으로 이해가 되어야 하는 부분만 기록해 둔다.


 


manifest 파일에서 중요한 건 internet 사용을 위한 권한 설정 부분과


http 통신을 하기 위해서 usesCleartextTraffic 을 설정하는 것이 우선 되어야 한다.


    <uses-permission android:name="android.permission.INTERNET" />

...

<application
android:allowBackup="false"
android:icon="@mipmap/ic_main_logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_main_logo_round"
android:supportsRtl="true"
android:theme="@style/Theme.Corona19Exam"
android:usesCleartextTraffic="true">

usesCleartextTraffic 설정은 cleartext HTTP와 같은 cleartext 네트워크 트래픽을 사용할지 여부를 나타내는 flag로 이 플래그가 flase 로 되어 있으면, 플랫폼 구성 요소 (예 : HTTP 및 FTP 스택, DownloadManager, MediaPlayer)는 일반 텍스트 트래픽 사용에 대한 앱의 요청을 거부하게 되어 true 설정해야 된다고 설명이 되어 있다.


 


그 다음은 build.gradle (module) 부분의 설정중에 다른 것들도 필요에 따라 들어가야 하지만, 아래 부분은 필수.


그래야 retrofit 을 사용할 수 있다. ( 버전인 2.7.2는 2021년 5월중순 현재 기준임)


dependencies {

implementation 'com.squareup.retrofit2:retrofit:2.7.2'
implementation 'com.squareup.retrofit2:converter-gson:2.7.2'
implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'
implementation 'com.google.code.gson:gson:2.8.6'

}

 


이제 호출하는 본문의 예제를 보자


MainActivity 에서 아래 처럼 CallAPi 함수를 만들어 쓰는 방법이 있을 테고... 


암튼 Retrofit 이용하여 호출할 떄 APIInterface 을 만들어서 호출할 떄 전달해야 되는 파라미터를 전달하고


 


아래 예시 처럼 onResponse 에서 수신된 데이터를 구조체로 받아서 데이터로 활용할 수 있다.


    public void CallApi(Context context) throws IOException {

UtilStrValue utilStrValue = new UtilStrValue();
Date date = Calendar.getInstance().getTime();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd") ;
String eDate = simpleDateFormat.format(date) ;
Log.d(TAG, "Today=" + eDate) ;
String bDate = eDate.substring(0, 6).toString() + "01";

mDatabase = FirebaseDatabase.getInstance().getReference();

Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UtilStrValue.BaseURL)
.addConverterFactory(SimpleXmlConverterFactory.create()) // 공공데이터 포털의 응답 방식 xml 이라서 이렇게 하는 것이고...json 이면 다른 방법으로 GsonConverterFactory 을 활용
.build() ;

APIInterface service = retrofit.create(APIInterface.class) ;
service.getData(utilStrValue.serviceKey, "1", "10", bDate, eDate).enqueue(new Callback<ResData>() {
@Override
public void onResponse(Call<ResData> call, Response<ResData> response) {
if ("00".equals(response.body().header.resultCode)) {
Log.d(TAG, "resultCode=" + response.body().header.resultCode + "\nresultMsg=" + response.body().header.resultMsg) ;
Log.d(TAG, "item Size=" + response.body().body.bodyItems.item.size()) ;
for(int i=0 ; i < response.body().body.bodyItems.item.size() ; i++) {
mDatabase.child("covid19").child(response.body().body.bodyItems.item.get(i).getSeq()).setValue(response.body().body.bodyItems.item.get(i)) ;
}
}
}

@Override
public void onFailure(Call<ResData> call, Throwable t) {
Log.e(TAG, "ERROR=" + t.toString()) ;
}
}) ;
}

위 예제 소스의 콜백에서 선언된 ResData 는 수신되는 데이터의 구조체 선언인데, 구조체는 공공데이터 포털에서 제공한 응답데이터 구조를 그대로 구성하면 수신된 데이터를 쉽게 활용할 수 있다.


 


ResData예시는 아래와 같다.   작성할 때 이노테이션을 잘 정리해야 한다.  이노테이션의 변수 이름은 공공데이터 포털에서 알려준 전문 구조체의 변수과 일치 되어야 하고, 이노테이션 아래 선언된 변수는 내 프로그램에서 사용할 변수명이다.


(예시의 Hederlist 나 BodyData 는 또 다른 구조를 가지고 있는 클래스 임을 참고하시길)


import com.exam.corona19exam.util.HeaderList;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name="response")
public class ResData {

@Element(name = "header")
public HeaderList header = new HeaderList();
@Element(name = "body", required = false)
public BodyData body = new BodyData() ;

}

 


호출할 때 사용한 APIInterface의 구조체는  Call 을 할 떄 데이터 구조체를 미리 정해서 호출을 하기 때문에 사용은 수월하게 할 수 있으며, 아래 예시 처럼 Query String 을 전달 파라미터로 설정하여 호출 할 수 있다.


import com.exam.corona19exam.hospitem.ResDataHosp;
import com.exam.corona19exam.covid19res.ResData;
import com.exam.corona19exam.placesapi.ResPlaces;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;

public interface APIInterface {

@Headers("Content-Type:application/json")
@GET("/openapi/service/rest/Covid19/getCovid19InfStateJson")
Call<ResData> getData (@Query(value = "serviceKey", encoded = true) String serviceKey // encoded = true 하면 % 기호가 그대로 전달됨
, @Query("pageNo") String pageNo
, @Query("numOfRows") String numOfRows
, @Query("startCreateDt") String startCreateDt
, @Query("endCreateDt") String endCreateDt) ;

}

 


이런 정도까지만 기록해 두면 훗날에서 쉽게 기억을 되살려 코드 작업을 할 수 있을 것 같다.





오늘의 이야기


#스하리1000명프로젝트,
Às vezes é difícil conversar com trabalhadores estrangeiros, certo?
Fiz um aplicativo simples que ajuda! Você escreve na sua língua e os outros veem na deles.
Ele é traduzido automaticamente com base nas configurações.
Muito útil para bate-papos fáceis. Dê uma olhada quando tiver uma chance!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

이앱은 


이앱의 목적은 POS 기기 없이 POS 처럼 사용할 수 있도록 구현 하는 데 목적이 있습니다.  이 앱은 이제 간편결제 기능 (Ver 1.1.4 ~) 도 채우게 됩니다. 
 
또한 간편결제 회원을 위한 수수료 적용율도 3%(부가세 별도)를 지원합니다. (Ver 1.1.5 ~ )  수수료 적용을 받기 위해서는 이 앱에서 제시하는 페이앱 회원 가입 링크를 통해서 가입하고 확인 된 경우에 한하여 적용 됩니다.
 
이제 결제 요청도 공유해 (Ver 1.1.6 ~) 보세요. 방문 하지 않은 고객에도 결제를 요청할 수 있습니다.
 


처음화면



 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


 


 


 


앱을 사용하기 전에  회원 가입 정보가 필요 합니다. 
 


회원 가입




 
 
처음 이 앱을 실행하는 경우에는  회원 정보 화면에서
 
1. 이메일주소, 비밀번호를 입력하고 사용자 가입을 클릭 합니다. 
 (카카오톡, 네이버 등의 간편 로그인을 진행한 경우에는 따로 이메일 입력 하거나 할 필요가 없습니다.)
2. 이미 등록 했던 경우, 또는 재 진입하는 경우에는 사용자 서명을 클릭 합니다. 
※ 입력된 이메일 주소와 비밀번호는 회원 확인을 위해서만 사용 되며, 외부로 유출등은 하지 않습니다.  메일로 홍보를 하거나 하지도 않습니다. 
 
3. PayApp UserID 는 페이앱 서비스를 이용해서 간편결제를 이용하고자 하는 경우에만 입력합니다.
 
4. 회원 확인을 하기 전에 반드시 페이앱 사용자 가입을 클릭 해서 회원 가입을 먼저한 이후에 해당 아이디를 입력하고 확인을 해야 됩니다. 
 
5. 이후 수수료 적용과 관련된 부분은 관리자에게 메일로 알려 주시면 적용해 드립니다. (help@billcorea.com)
※ 연락이 없으시면 관리자가 확인후 일괄 적용할 수 있습니다.
 
 
 
 
 
 


상품 등록 


상품정보



 
 
 
상품정보의 등록은 메인 화면에서 바코드 아이콘으로 바코드를 스캔 하면 등록 되지 않은 상품의 경우 상품을 등록 하는 화면으로 자동 이동 합니다. 
 
상품의 기본 정보를 입력하고 저장 버튼을 클릭 하면 메인 화면으로 돌아갑니다. 
 
이미 등록된 상품의 경우는 삭제 버튼으로 삭제 할 수 있습니다.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


상품 판매


메인화면



 
상품의 판매는 손님1 ~ 손님5까지 동시에 다섯명의 상품 판매 리스트을 스캔하고 처리할 수 있도록 지원 합니다.
 
바코드를 스캔해서 상품을 추가할 수 있으며, 동일 상품의 경우 +, - 버튼으로 갯수를 조절할 수 있습니다. 
 
현금으로 판매하는 경우에는 반은돈에 금액을 입력하면 차액이 자동 계산 되어 아래 표시 됩니다. 
 
 
간편 결제가 필요한 경우에는 카드 이미지 버튼을 클릭 하여 다음 순서와 같이 처리할 수 있습니다.
 
 
 
 
 
 
 
 
 
 
 
 


바코드 스캔


바코드 스캔



 
상품입력시  바코드 스캔은 바코드 이미지를 클릭 했을 때 나오는 화면으로 이동하여 화면 가운데 파란줄에 바코드를 또는 QR코드를 인식시켜 주는 것으로 처리는 완료 됩니다. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


상품목록 조회


상품목록 조회



 
등록된 상품 목록의 조회는 상품 조회 입력칸에 아무것도 입력하지 않은 상태에서 돋보기 버튼을 클릭 하면  등록된 상품 리스트가 조회 됩니다.
 
조회된 리스트에서 수정이 필요한 경우에는 수정 버튼 이미지를 클릭 하면 해당 상품의 상세 화면으로 넘어가도록 되어 있어서 수정을 진행할 수 있습니다.
  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


간편 결제


간편결제 시작



 
메인 화면에서 간편 결제 이미지 클릭 하면 수신 전화번호를 입력 받습니다. 
 
해당 전화번호는 나중에 취소거래가 필요한 경우가 있을 때 본인 확인을 위해서 만 사용 하게 됩니다.
 
전화번호를 입력하고 확인을 클릭 하면 화면에 QR 코드가 나오게 됩니다. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


간편결제 연동



 
이 화면 상태에서 고객의 휴대폰으로 카메라 앱을 열어서 QR코드를 보여 죽게 되면 결제를 요청하는 화면으로 이동 하게 됩니다. 
 
이후 고객의 휴대폰에서 결제가 완료 되면 왼쪽 화면에 표시된 목록에 카드 이미지가 파란색으로 변하게 되고, 이 때는 결제가 완료된 상태이니 다음 단계로 넘어가게 됩니다. 
 
혹시 취소를 하고자 하는 경우네는 파란색 카드 이미지를 클릭 하면 거래를 취소할 수 있습니다. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
** 이 앱에서는 카메라 권한이 사용 됩니다. 
바코드 스캔을 위해서 카메라 권한을 허용해 주어야 합니다.
 


*** 결제 공유


결제 요청을 공유할 수 있습니다. (Ver 1.1.6 ~)  이제 그림과 같이 결제 요청을 공유 하여 결제를 요청 할 수 있습니다.


간편결제 예시



 





오늘의 이야기

play.google.com/store/apps/details?id=com.billcoreatech.daycnt415



 


휴게시간 - Google Play 앱


휴일기간의 남은 시간을 위젯으로 표시해 드립니다.


play.google.com




휴일이 지나가면 아쉽다고 해야 할까요???


그래서 내폰 화면에 지나가는 시간의 경과정도를 볼 수 있도록 앱을 설치해 확인할 수 있습니다. 이앱은 광고가 들어가 있지만, 무시하고 사용하셔도 됩니다.


 


간단한 기능 설명을 드리자면...


 




앱을 설치하고 실행하게 되면 일단, 15년치 의 달력정보를 생성하게 됩니다. 그때 한국의 휴무일 정보를 같이 저장합니다.  설정이 다 된 이후에도 추가/삭제/수정을 할 수 있으므로 임시공휴일이 생기더라도 문제 없이 관리를 할 수 있습니다.


 




설정 화면에서는  휴게시간의 측정을 위한 시작시간 과 종료시간


갱신주기(분), 위젯의 투명도 등을 설정하게 됩니다. 


 


시작시간 : 휴일의 시작시간을 설정 합니다. 


종료시간 : 휴일의 종료시간을 설정 합니다. 


예시 그림과 같이 18:00 ~ 24:00 으로 설정하게 되면


 


휴일은 금요일 18:00  ~ 일요일 24:00 까지 휴일기간으로 지정되고


월요일이 되면 월요일 0:00 ~ 금요일 18:00 까지가 평일 기간으로 지정 되어 


됩니다. 


 


갱신주기(분)는 위젯에 표시되는 부분에 대한 갱신 주기를 설정하는 데, 


구글에서는 위젯의 갱신주기를 30분으로 권장하고 있습니다. 너무 자주 위젯이 갱신을 하다 보면 휴대폰의 배터리에 영향을 준다고 되어 있으니 사용시 주의가 필요해 보입니다. 


 


투명도는 위젯의 투명도를 지정할 수 있습니다.  위젯의 투명도는 위젯이 갱신될 떄 다시 설정 되므로 환경설정에서 지정한 내용은 위젯의 갱신될 떄 변경 된다고 보시면 맞습니다. 


 


 


 


 


 


기타의 특이사항은 광고를 포함하고 있기 때문에 광고가 화면 마다 나타납니다. 광고를 제거하고자 하는 경우는 환경설정에 나타나는 버튼으로 1회성 광고제거 기능을 구매 하시면 구매후 29일 동안은 광고가 나타나지 않습니다. 


광고가 나온다고 해서 기능이 다르거나 하지 않기 때문에 그냥 사용해도 문제가 있지는 않습니다.  


 


간혹 광고가 화면에 나타났다가 사라지는 정도...


 


잘 사용해 보시고 좋은 평가도 부탁 드립니다.


 


 


 


 





오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, um aplicativo obrigatório para clubes de badminton!
👉 Match Play – Grave pontuações e encontre oponentes 🎉
Perfeito para qualquer lugar, sozinho, com amigos ou em um clube! 🤝
Se você gosta de badminton, definitivamente experimente

Acesse o aplicativo 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




오늘의 이야기

몇해전에는 카카오 지도를 연동하는 데,  애로 사항을 많이 느꼈다.


카카오의 기술지원은 어디에 있는 것인지 찾을 수 도 없고...


이번에 다시금 도전~


 


apis.map.kakao.com/android/


이 페이지는 예나 지금이나 별 다른 감흥도 없고 변한 느낌도 없다.


 


API사용을 위해서는 카카오 개발자 페이지에 나도 개발자라고 등록을 해야 하고, 앱을 신규 등록할 수 있는 정도는 해야 한다.


 


샘플앱도 제공이 되기는 하지만, `21년 3월 마지막주 기준으로 컴파일 하는 동안 이런 저런 메시지를 만들어 내기 때문에 그닥 필요성을 느낄 수 없다.


 


그래서 난 그냥 가이드를 따라가 보기로 했다.


 




개발환경 : windows10, AndroidStudio 4.1.3 기준으로 


 


처음한 일  새로운 앱을 만들고 위 가이드가 말해주는 것고 같이 폴더 위치에 제공해준 라이브러리 파일을 복사 했다.


 




 얼추 위 가이드가 말해주는 것과 비슷한 구조가 되게 jar 파일과 so 파일들이 위치될 수 있도록 경로에 복사를 해 주었다.


 


먼저 jar 파일은  프로젝트 폴더 / app / libs 에 위치 될 수 있도록 담아 주어야 한다. 


 


그리고 so 파일들은 프로젝트 폴더 / app / src/ main / jniLibs 에 위치 될 수 있도록 담아 주어야 한다.


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


그 다음은 manifast 파일에 권한 설정


 




이건 그냥 가이드가 알려주는 것 그대로 ...


APP KEY 는 내 개발자 계정에 들어서 확인 후 저장해 주어야 한다.


아직 등록한 앱이 없다면




애플리케이션 추가하기를 클릭해서 새로운 앱을 만들고 아래 그림 처럼 나와 있는 앱키를 사용한다. 


이번에는 안드로이드 앱을 만들꺼기 때문에  네이트브 앱 키를 복사해다가 넣었다. (어디에 위에 가이드의 APP KEY 설정을 따라해서)




 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


(그림에 나와있는 키값은 일부이므로 전체 복사해서 넣어야 함)


 


그 다음 부터는 가이드의 내용과 같이 코드 작성하고 실행해 보면 된다.


다만, 가장 중요할 것 같은 것은 AVD (애뮬레이터) 에서는 실행되지 않으니 꼭 실제 폰에서 개발자테스트를 해야 한다.


뭔지는 아직 잘 모르지만, 애뮬레이터에서는 동작하니 않으니 괜시리 삽질 하지 않으시길 바라며...





오늘의 이야기

2021.03.29 - [분류 전체보기] - 작업일정관리 사용자 설명서 (한국어판)



 


작업일정관리 사용자 설명서 (한국어판)


이 앱은 작은 정비업소등에서 사용할 수 있도록 개발된 앱입니다. 1. 이앱은 5가지 정도의 공정을 가지고 있는 정비업소에서 각 공정별로 입고되는 수리대상 품목(이하 수리차량이라 명칭)을 입


billcorea.tistory.com




This app is an app developed to be used in small maintenance shops.


 




1 This app supports the management of items subject to repair (hereinafter referred to as repair vehicles) that are worn by each process at a maintenance business that has about 5 processes, and manages them according to classifications such as painting, lower part, sheet metal, and inspection. It is an app to do.
2. Since the data is based on the Firebase Realtime Database, you can share real-time work status.
3. The user can use it for a certain period of time free of charge to determine whether or not to apply the business, and if he/she wants to continue using it, he/she can maintain the use through a monthly payment. (The administrator manages the backup with regular payment income if the data needs to be backed up.)
4. In this app, user management is based on the Google account email, but the user's email account is used only for user verification, and it is not allowed to be provided to the outside. (When registering a user, you can use an alias for the user name.)
5. The e-mail account used for initial registration is used as the account of the data manager, so please enter it carefully. If you delete the app later, if you re-register the user e-mail account, the existing data may be lost, so remember the administrator e-mail account well. Must be. (If necessary, upon request, we can inform you after confirmation.)
As shown in the screen above, if you enter the administrator's e-mail account and the user's company name during initial execution, it will be displayed on the screen.
The basic menu composition of this app is as follows.


 




1. Receipt management: New vehicle registration screen
2. Work schedule management: Search the list of work targets by department and register the progress of work steps
3.Completed work: Search for completed targets from the work target list registered in warehousing management
4. Job status by department: Inquiry of job status by department and list of completed jobs by department
5. User registration: Approval processing for users logged in with Google account and change of the user's department
6. Administrator login: Administrators can only use the account designated by default, and manage department codes and names.
7. Logout: Log out of this app and close the app
After login is completed using your Google account,
As shown in the following figure
If you select Administrator Login on the menu screen,


 


 




1. A Google login button appears, and login starts with the Google account you are using.
2. From the next time, you can select a registered account.
3. Only when logged in as an administrator
   * Yellow background appears in email and user alias
   * Save button is activated (If you are not an administrator, the save button is disabled)
4. You can add the department code by clicking the Add button, and the order of registration here
   Since the work list is organized to match with, the department code
   Must be registered by granting (Code value is not important, but if possible
    It is given so that the order can be sorted using numbers)
5. When modifying the department code, click the column with the corresponding code to modify the department.
   The screen appears.
After the user registration screen is switched through Google login,


 




1. User alias can be entered and saved, and departments can be selected, and the department name entered from the department name appears on the administrator screen.


2. You can choose to save or delete. (If you delete it, you will be logged out, so you need to log in again to save the user information)


3. Up to 2 department codes can be selected, and the second department must be blank if not selected.


4. When a user is saved, it is displayed on a gray background as shown in the figure below. In this case, the administrator must click the user on the screen and approve it to process the work.
5. In the case of an approved user, the approval can be canceled by selecting as shown in the left figure. Upon cancellation of approval, it should be checked whether the background is gray.


 


The order of business processing is
1. Registration of new vehicle information in stock management management




2. Work schedule management manages the work schedule for each department using the department code information of the logged-in user for each department.
(If you click a work target in the list, it proceeds to the next step, and the completed work disappears from the list)




3. In the completed work, depending on the number of repair parts selected at the time of initial registration on the warehousing management screen, when the work is completed, it disappears from warehousing management and information of the vehicle is displayed only in the completed work.





4. The work status of each department can be checked in real time when each department starts work.




If you are curious or need improvement, please let us know by e-mail and we will improve it.


6k2emg@gmail.com.


 


Thank You !!!





오늘의 이야기


#스하리1000명프로젝트,
韓国で迷子になりましたか?韓国語が話せなくても、このアプリを使えば簡単に移動できます。
あなたの言語で話すだけで、翻訳、検索が行われ、結果があなたの言語で表示されます。
旅行者に最適!英語、日本語、中国語、ベトナム語などを含む 10 以上の言語をサポートします。
今すぐ試してみましょう!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




2026/02/12

오늘의 이야기



이 앱은  작은 정비업소등에서 사용할 수 있도록 개발된 앱입니다.


 


1. 이앱은 5가지 정도의 공정을 가지고 있는 정비업소에서 각 공정별로 입고되는 수리대상 품목(이하 수리차량이라 명칭)을 입고, 도장, 하부, 판금, 점검 등의 구분에 따라 관리할 수 있도록 지원하는 앱입니다.


 


2. 데이터는 Firebase Realtime Database 을 기반으로 하고 있기 때문에 실시간 작업현황을 공유 할 수 있습니다. 


 


3. 사용자는 일정기간 동안 무상으로 사용해 보면서 업무 적용여부를 판단할 수 있으며, 계속해서 사용하고자 하는 경우에는 매월 정기결제를 통해 사용을 유지할 수 있습니다. (관리자는 해당 데이터가 많아지거나 해서 백업이 필요한 경우 정기결제 수입으로 백업등을 관리합니다.)


 


4. 이앱에서는 구글계정 이메일을 기반으로 사용자 관리를 하고 있으나, 사용자의 이메일 계정은 사용자 확인을 위해서만 사용 되며,  외부로의 제공은 허용하지 않습니다. (사용자 등록시 사용자 이름은 별칭을 사용하여도 무방합니다.)


 


5. 초기 등록시 사용되는 이메일계정은 데이터 관리자의 계정으로 사용 되므로 신중하게 입력해 주시고, 추후 앱을 삭제한 경우 사용자 이메일 계정을 다시 등록할 경우 기존 데이터가 없어질 수 있으므로 관리자 이메일 계정은 잘 기억하고 있어야 합니다.  (필요시 요청에 의해 확인후 알려 드릴 수는 있습니다.)


 


 


위 화면과 같이 초기 실행시 관리자 이메일 계정과 사용자의 회사명칭을 입력하면 화면에 표시 됩니다.




이앱의 기본 메뉴 구성은 다음과 같습니다.


 


1.입고관리 : 신규 차량 등록 화면


2.작업일정관리 : 부서별 작업대상 목록을 조회 하고, 작업 단계의 진행을 등록


3.완료된작업 : 입고관리에서 등록된 작업대상목록 중에서 처리가 완료된 대상 조회


4.부서별 작업현황 : 부서별 작업현황 조회 및 완료된 부서별작업 리스트 조회


5.사용자등록 : 구글계정으로 로그인한 사용자에 대한 승인 처리 및 사용자의 소속부서 변경


6.관리자로그인 : 관리자는 기본지정된 계정만 사용이 가능하며, 부서코드 및 명칭관리


7.로그아웃 : 이 앱에서 로그아웃 하고 앱을 종료함


 


 


 


 


 


 


 





구글 계정을 이용하여 로그인이 완료된 이후에는


다음 그림의 내용과 같이


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


메뉴화면에서 관리자로그인을 선택하면


 


1.구글로그인 버튼이 나오며, 사용하는 구글계정으로 로그인시작


2.다음부터는 등록된 계정을 선택할 수 있음


3.관리자로 로그인된 경우만


   * 이메일 및 사용자별칭에 노란색 배경이 나옴


   * 저장버튼 활성화됨 (관리자 아닌경우 저장 버튼은 비활성화)


4. 추가 버튼으로 부서코드를 추가할 수 있으며, 여기 등록된 순서


   와 일치하도록 작업목록이 구성되므로, 작업순서에 맞게 부서코드


   를 부여하여 등록 하여야 함. (코드값은 중요하지 않으나 가급적


    숫자를 이용하여 순서 정렬이 될 수 있도록 부여함)


5. 부서코드 수정시에는 해당 코드가 있는 열을 클릭하면 부서 수정


   화면이 나옴.


 


 


 


 


 


 


 


 




구글 로그인을 통해서 사용자등록 화면 전환된 이후에는


 


1.사용자별칭을 입력 저장할 수 있고,  소속부서를 선택할 수 있으며, 여기서 나오는 부서명칭은 관리자 화면에서 입력한 부서 명칭이 나옴.


2.저장 또는 삭제를 선택할 수 있음. (삭제를 하게 되면, 로그아웃 상태로 나가기 때문에 다시 로그인을 해서 사용자 정보를 저장해야함)


3.부서코드를 2개까지 선택할 수 있으며, 두번째 부서는 선택하지 않을 경우 공백이 되어야 함.


4.사용자가 저장되면 아래 그림 처럼 회색 바탕으로 표시 되고 이 때는 관리자 이 화면에서 해당 사용자를 클릭 하여 승인을 해야 업무를 처리할 수 있음.


5. 승인되었던 사용자의 경우도 왼쪽 그림과 같이 선택하면 승인을 취소할 수 있음. 승인 취소시 배경이 회색이 되는 지 확인되어야 함.


 


 


 


 


 


 


 


 




업무처리순서는


1.입고관리 관리에서 새로운 차량 정보 등록


 


 


 


 


 


 


 




 


 


 


 


 


2.작업일정관리는 각 부서별로 로그인한 사용자의 부서코드 정보를 이용하여 부서별 작업일정을 관리함


(리스트에 작업대상을 클릭하면 다음단계로 진행되며 완료된 작업은 리스트에서 없어짐)


 


 


 


 


 


 


 


 


 


 





3.완료된 작업에서는 입고관리 화면에서 처음 등록시 선택한 수리부분의 개수에 따라 작업이 완료되면 입고관리에서는 사라지고 해당 차량의 정보가 완료된 작업에서만 나타남


 


 


 


 


 


 


 


 


 


 


 


 


 


4.부서별 작업현황은 각 부서에서 작업을 시작하게 되면 각부서의 작업중인 현황을 실시간으로 확인할 수 있음


 


 


 


 


 


 


 


 


 


 


 


 


 


1. 입고관리




 


2. 작업일정관리




3.완료된 작업




4.  부서별 작업현황




 


 


그외 궁금하거나 기능 개선이 필요한 경우는  이메일로 알려 주시면 개선을 진행해 드립니다. 


메일수신처 6k2emg@gmail.com 입니다.


 


감사합니다.


 





오늘의 이야기

다음 회차를 위한 로또 번호 분석 및 추천 결과입니다. --- **최종 추천 조합:** 추천 [01,06,07,16,30,38] 추천 [05,10,12,27,34,40] 추천 [03,06,11,16,21,29] 추천 [02,04,17,20,31,39]...