Android海外應用內支付之ONEstore(韓國支付SDK)集成

NO IMAGE

轉載請標明出處,謝謝Android 海外應用內支付之ONE store(韓國支付SDK)集成

前提:

之前寫過一篇Android Google應用內支付,剛好公司的主營業務是海外app開發。前段時間也需要支持了韓國那邊的應用內支付,給的文檔上面講了需要集成onestore支付。由於對韓國那邊的應用內支付沒有什麼概念,所以就百度了一番。奈何,文檔太少或者寫的不是很清晰(難道能說是因為自己不太理解)。所以,自己就看著官方文檔做了集成了一番,踩了不少坑。但是,總體來說,比較官方文檔講的比較清楚。好了,講了這麼多,下面看如何集成吧。

首先

  • 1、查看文檔
    要做ONE store集成開發,第一步當然是查看官方文檔了,我這裡集成的是最新的版本,也就是v5版本。也有之前的v3和v4等版本,但是,人要向前看不是。所以,就集成了最新的版本了。
Android海外應用內支付之ONEstore(韓國支付SDK)集成

Android海外應用內支付之ONEstore(韓國支付SDK)集成

  • 2、下載ONE store的jar包(在如下圖所示的地方下載對應的開發包和官方demo)
    題外話:感覺官方的demo寫的真的不咋地,還沒有文檔寫的清楚,並且讓人看得雲裡霧裡的。
Android海外應用內支付之ONEstore(韓國支付SDK)集成

注意:官方jar包下載地址在github上面,不清楚的可以點這裡查看

Android海外應用內支付之ONEstore(韓國支付SDK)集成

注意:上面紅框標註的就是我們需要的jar包

  • 3、支付接入文檔(兩種實現方式)
    1、調用AIDL接口實現
    2、使用SDK實現(建議使用第二種,筆者使用的就是第二種)

    Android海外應用內支付之ONEstore(韓國支付SDK)集成

    ### 其次
     上面講了那麼多,那麼看看官方文檔的接入到底是個什麼樣子的呢?下面是目錄截圖

    Android海外應用內支付之ONEstore(韓國支付SDK)集成

    +1、在看到了上面的截圖之後,我們這裡講一下支付的整個流程。

  • 第一步:安裝ONE store客戶端,如果不安裝ONE store的客戶端是不能支付的,並且需要自備梯子。

  • 第二步:初始化ONE store,如果初始化失敗或者連接不到ONE store也是不能調起ONE store支付的。

  • 第三步:查詢是否支持ONE store支付,如果支持再進行下一步。

  • 第四步:查詢購買記錄,並且進行消費(主要是針對管理型商品(inapp))。注意:如果不進行消費是不能進行購買請求的(和Google支付很相似),這裡筆者剛開始被坑了很久,一直不能支付,主要還是沒有認真看文檔,哈哈哈。

  • 第五步:填寫相關信息,進行購買請求。

  • 第六步:這裡呢,主要是對onestore購買請求的回調處理。比如說拿到支付ID和訂單號,上傳到自己的服務器進行驗證處理。如果驗證通過了,服務器那邊會和ONE store對接驗證,然後,ONE store會進行發貨處理,然後服務器給你一個反饋,然後,你再次調用__消費處理__,這樣,一個完整的支付流程就完了。

  • 第七步:釋放操作。

  • 2、說了那麼多,估計你們看文字都看累了,下面,看代碼。

    public class OneStorePlayManager {
    @SuppressLint("StaticFieldLeak")
    //支付客戶端
    private static PurchaseClient mPurchaseClient;
    //版本
    private static final int IAP_API_VERSION = 5;
    private static final String TAG = OneStorePlayManager.class.getSimpleName();
    //簽名驗證
    private static final String KEY_FACTORY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA512withRSA";
    //公鑰
    private static String mPublicKey;
    //是否初始化
    private static boolean mIsInit = false;
    private static final String PURCHASE_ID = "PURCHASE_ID";
    private static final String DEVELOPER_PAYLOAD = "DEVELOPER_PAYLOAD";
    //是否消費
    private static boolean mISConsume = false;
    private static void init(Context context, String publickey) {
    mPurchaseClient = new PurchaseClient(context, publickey);
    mPublicKey = publickey;
    }
    /**
    * 初始化
    *
    * @param context   上下文
    * @param mListener 回調
    */
    public static void initOneStore(final Activity context, final String publickey,
    final String productType,
    final onOneStoreSupportListener mListener) {
    if (!mIsInit) {
    init(context, publickey);
    mIsInit = true;
    }
    if (mPurchaseClient != null) {
    mPurchaseClient.connect(new PurchaseClient.ServiceConnectionListener() {
    @Override
    public void onConnected() {
    if (mListener != null) {
    mListener.onOneStoreConnected();
    }
    checkBillingSupportedAndLoadPurchases(context, LTAppID, LTAppKey, testID, productType, mListener, mUploadListener);
    }
    @Override
    public void onDisconnected() {
    if (mListener != null) {
    mListener.onOneStoreDisConnected();
    }
    }
    @Override
    public void onErrorNeedUpdateException() {//必須更新到最新的onestore客戶端後才能進行支付
    if (mListener != null) {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_CONNECTED_NEED_UPDATE);
    PurchaseClient.launchUpdateOrInstallFlow(context);
    }
    }
    });
    }
    }
    /**
    * 檢查是否支持
    */
    private static void checkBillingSupportedAndLoadPurchases(final Context context,
    final String productType,
    final onOneStoreSupportListener mListener) {
    if (mPurchaseClient == null) {
    if (mListener != null) {
    mListener.onOneStoreClientFailed("PurchaseClient is not initialized");
    }
    } else {
    mPurchaseClient.isBillingSupportedAsync(IAP_API_VERSION, new PurchaseClient.BillingSupportedListener() {
    @Override
    public void onSuccess() {
    mListener.onOneStoreSuccess(OneStoreResult.RESULT_BILLING_OK);
    // 然後通過對託管商品和每月採購歷史記錄的呼叫接收採購歷史記錄信息。
    //loadPurchases((Activity) context,  mListener);
    Log.e(TAG, "isBillingSupportedAsync : RESULT_BILLING_OK");
    mPurchaseClient.queryPurchasesAsync(IAP_API_VERSION, productType,
    new PurchaseClient.QueryPurchaseListener() {
    @Override
    public void onSuccess(List<PurchaseData> purchaseDataList, String productType) {
    Log.e(TAG, "queryPurchasesAsync onSuccess, " + purchaseDataList.toString());
    for (PurchaseData purchase : purchaseDataList) {
    consumeItem((Activity) context, purchase, mListener);
    }
    }
    @Override
    public void onError(IapResult iapResult) {
    mListener.onOneStoreError("onError====" + iapResult.toString());
    }
    @Override
    public void onErrorRemoteException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_REMOTE_ERROR);
    }
    @Override
    public void onErrorSecurityException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_SECURITY_ERROR);
    }
    @Override
    public void onErrorNeedUpdateException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_NEED_UPDATE);
    PurchaseClient.launchUpdateOrInstallFlow((Activity) context);
    }
    });
    }
    @Override
    public void onError(IapResult iapResult) {
    mListener.onOneStoreError(iapResult.toString());
    }
    @Override
    public void onErrorRemoteException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_BILLING_REMOTE_ERROR);
    }
    @Override
    public void onErrorSecurityException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_BILLING_SECURITY_ERROR);
    }
    @Override
    public void onErrorNeedUpdateException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_BILLING_NEED_UPDATE);
    PurchaseClient.launchUpdateOrInstallFlow((Activity) context);
    }
    });
    }
    }
    /**
    * 在管理商品 (inapp) 後或歷史記錄視圖完成後, 消耗託管商品的消費.
    *
    * @param purchaseData 產品數據
    */
    private static void consumeItem(final Activity context
    final PurchaseData purchaseData,
    final onOneStoreSupportListener mListener) {
    if (mPurchaseClient == null) {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_NEED_UPDATE);
    Log.e(TAG, "PurchaseClient is not initialized");
    return;
    }
    mPurchaseClient.consumeAsync(IAP_API_VERSION, purchaseData,
    new PurchaseClient.ConsumeListener() {
    @Override
    public void onSuccess(PurchaseData purchaseData) {
    Log.e(TAG, "consumeAsync===success");
    mListener.onOneStoreSuccess(OneStoreResult.RESULT_CONSUME_OK);
    if (!TextUtils.isEmpty(PreferencesUtils.getString(context, PURCHASE_ID)) &&
    !TextUtils.isEmpty(PreferencesUtils.getString(context, DEVELOPER_PAYLOAD))) {
    //上傳到服務器處理進行驗單,防止漏單
    uploadServer(xx,xx,xx);
    } else if (mISConsume) {
    uploadServer(xx,xx,xx);
    }
    mISConsume = false;
    }
    @Override
    public void onError(IapResult iapResult) {
    mListener.onOneStoreError(iapResult.toString());
    Log.e(TAG, "consumeAsync onError,  消費錯誤" + iapResult.toString());
    }
    @Override
    public void onErrorRemoteException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_CONSUME_REMOTE_ERROR);
    Log.e(TAG, "consumeAsync onError,  消費連接失敗");
    }
    @Override
    public void onErrorSecurityException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_CONSUME_SECURITY_ERROR);
    Log.e(TAG, "consumeAsync onError,  消費應用狀態異常下請求支付");
    }
    @Override
    public void onErrorNeedUpdateException() {
    mListener.onOneStoreFailed(OneStoreResult.RESULT_CONSUME_NEED_UPDATE);
    Log.e(TAG, "consumeAsync onError,  消費產品需要更新");
    }
    });
    }
    /**
    * oneStore回調
    *
    * @param requestCode     請求碼
    * @param resultCode      結果碼
    * @param selfRequestCode 自定義請求碼
    */
    public static void onActivityResult(Context context, int requestCode, int resultCode, Intent data, int selfRequestCode, final onOneStoreSupportListener mSupportListener) {
    if (requestCode == selfRequestCode)
    if (resultCode == Activity.RESULT_OK) {
    if (!mPurchaseClient.handlePurchaseData(data)) {
    Log.e(TAG, "onActivityResult handlePurchaseData false ");
    } else {
    String signature = data.getStringExtra("purchaseSignature");
    String purchaseData = data.getStringExtra("purchaseData");
    Gson gson = new Gson();
    PurchaseData mPurchaseData = gson.fromJson(purchaseData, PurchaseData.class);
    if (mPurchaseData != null) {//這邊是保存一下訂單,剛進入應用的時候進行相關處理
    PreferencesUtils.putString(context, PURCHASE_ID, mPurchaseData.getPurchaseId());
    PreferencesUtils.putString(context, DEVELOPER_PAYLOAD, mPurchaseData.getDeveloperPayload());
    consumeItem((Activity) context,  mPurchaseData, mSupportListener);
    Log.e(TAG, "onActivityResult handlePurchaseData true " + mPurchaseData.toString() + "==="
    + signature);
    }
    }
    } else {
    Log.e(TAG, "onActivityResult user canceled");
    }
    }
    /**
    * 獲得商品
    *
    * @param productId       商品ID
    * @param selfRequestCode 請求碼
    * @param productName     產品名稱
    * @param type 產品類型
    * @param onOneStoreSupportListener  是否支持的接口
    * @OnCreateOrderFailedListener 創建訂單的接口
    */
    public static void getProduct(final Activity context,
    int selfRequestCode, String productName,
    final String productId, String type,
    final onOneStoreSupportListener mListener, final OnCreateOrderFailedListener mCreateListener) {
    if (!mISConsume) {
    if (!mIsInit) {
    init(context, mPublicKey);
    } else {
    getLTOrderID(context,  selfRequestCode, productName, productId, type,
    mListener,
    mCreateListener);
    }
    }
    }
    /**
    * 購買
    * @ devPayLoad 訂單號(自己的服務器創建的)
    */
    private static void launchPurchase(final Activity context,
    int selfRequestCode, String productName,
    final String productId, String type, final String devPayLoad,
    final onOneStoreSupportListener mListener) {
    if (mPurchaseClient != null) {
    mPurchaseClient.launchPurchaseFlowAsync(IAP_API_VERSION,
    context, selfRequestCode, productId, productName,
    type, devPayLoad, "",
    false, new PurchaseClient.PurchaseFlowListener() {
    @Override
    public void onSuccess(PurchaseData purchaseData) {
    Log.e(TAG, "launchPurchaseFlowAsy======= " + purchaseData.getDeveloperPayload() + "====" + devPayLoad);
    }
    @Override
    public void onError(IapResult result) {
    Log.e(TAG, "launchPurchaseFlowAsync onError, " + result.toString());
    mListener.onOneStoreError(result.toString());
    }
    @Override
    public void onErrorRemoteException() {
    Log.e(TAG, "launchPurchaseFlowAsync onError=====onErrorRemoteException");
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_FLOW_REMOTE_ERROR);
    }
    @Override
    public void onErrorSecurityException() {
    Log.e(TAG, "launchPurchaseFlowAsync onError=====onErrorSecurityException");
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_FLOW_SECURITY_ERROR);
    }
    @Override
    public void onErrorNeedUpdateException() {
    Log.e(TAG, "launchPurchaseFlowAsync onError=====onErrorNeedUpdateException");
    mListener.onOneStoreFailed(OneStoreResult.RESULT_PURCHASES_FLOW_NEED_UPDATE);
    PurchaseClient.launchUpdateOrInstallFlow(context);
    }
    });
    }
    }
    /**
    * 創建訂單
    *
    */
    private static void getLTOrderID(final Activity activity, 
    final int selfRequestCode, final String productName,
    final String productId, final String type,
    final onOneStoreSupportListener mListener,
    final OnCreateOrderFailedListener mOrderListener) {
    //從服務器獲取訂單號然後進行購買處理
    launchPurchase(activity, selfRequestCode, productName, productId,
    type, result, mListener);
    mISConsume = true;
    }
    private static void uploadServer(final Context context, 
    String purchase_id, String devPayLoad) {
    //服務器驗單成功後進行相關的操作,比如說清除沒必要的緩存,等等這些操作
    mISConsume = false;
    if (!TextUtils.isEmpty(PreferencesUtils.getString(context, PURCHASE_ID))) {
    PreferencesUtils.remove(context, PURCHASE_ID);
    }
    if (!TextUtils.isEmpty(PreferencesUtils.getString(context, DEVELOPER_PAYLOAD))) {
    PreferencesUtils.remove(context, DEVELOPER_PAYLOAD);
    }
    }
    /**
    * 釋放
    */
    public static void release() {
    mISConsume = false;
    mIsInit = false;
    if (mPurchaseClient != null) {
    mPurchaseClient.terminate();
    mPurchaseClient = null;
    }
    }
    private static boolean verifyPurchase(String signedData, String signature) {
    if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(signature)) {
    return false;
    }
    PublicKey key = generatePublicKey(mPublicKey);
    return verify(key, signedData, signature);
    }
    private static PublicKey generatePublicKey(String encodedPublicKey) {
    try {
    byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
    return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
    } catch (NoSuchAlgorithmException e) {
    throw new SecurityException("RSA not available", e);
    } catch (InvalidKeySpecException e) {
    Log.e(TAG, "Invalid key specification.");
    throw new IllegalArgumentException(e);
    }
    }
    private static boolean verify(PublicKey publicKey, String signedData, String signature) {
    try {
    Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
    sig.initVerify(publicKey);
    sig.update(signedData.getBytes());
    if (!sig.verify(Base64.decode(signature, Base64.DEFAULT))) {
    Log.e(TAG, "Signature verification failed.");
    return false;
    }
    return true;
    } catch (NoSuchAlgorithmException e) {
    Log.e(TAG, "NoSuchAlgorithmException.");
    } catch (InvalidKeyException e) {
    Log.e(TAG, "Invalid key specification.");
    } catch (SignatureException e) {
    Log.e(TAG, "SignatureTest exception.");
    } catch (IllegalArgumentException e) {
    Log.e(TAG, "Base64 decoding failed.");
    }
    return false;
    }
    }
    
  • 是否支持支付的接口

    /**
    * 是否支持支付的接口
    */
    public interface onOneStoreSupportListener {
    /**
    * oneStore連接失敗
    *
    * @param failedMsg 失敗信息
    */
    void onOneStoreClientFailed(String failedMsg);
    /**
    * oneStore支付失敗
    *
    * @param result 失敗信息
    */
    void onOneStoreFailed(OneStoreResult result);
    /**
    * oneStore支付出錯
    *
    * @param result 錯誤信息
    */
    void onOneStoreError(String result);
    /**
    * oneStore支付成功
    *
    * @param result 成功信息
    */
    void onOneStoreSuccess(OneStoreResult result);
    /**
    * oneStore連接成功
    */
    void onOneStoreConnected();
    /**
    * oneStore未連接成功
    */
    void onOneStoreDisConnected();
    }
    
  • 創建訂單接口

    public interface OnCreateOrderFailedListener {
    void onCreateOrderFailed(String failed);
    void onCreateOrderError(String errorMsg);
    }
    
Android海外應用內支付之ONEstore(韓國支付SDK)集成

__如上圖所示:__只有點擊close的時候才能支付成功,直接退出app是支付取消處理。

最後

補充一點,接入onestore支付的一般是面相韓國那邊的app,並且大多數是遊戲app,那麼,就牽扯到了橫屏。那麼,onestore有沒有橫屏處理呢?當然了,在Manifest中配置就可以了。

Android海外應用內支付之ONEstore(韓國支付SDK)集成

__注意:__橫屏的話用popup就可以了,不用選full,如果豎屏呢,寫成full或者不寫都是可以的。

好了,以上就是這次onestore的應用內開發,如果有什麼不懂的地方,或者寫的不好的地方,歡迎指正。當然,也可以加群(493180098)問我

感謝

OneStore_Android_接入
可以看看,但是講的不是特別詳細,還是要感謝上面那篇文章的作者。

感悟

通過這次接入ONE store的工作完成後,充分認識了自己。以前覺得百度上找不到文檔,就感覺特別慌,有點無所適從。但是,通過這次ONE store的開發,覺得,也就那樣。生活本來就是這樣的,都是摸著石頭過河。魯迅先生還說過:世上本沒有路,走的人多了便有了路。收拾好心情,繼續努力吧,少年,哈哈哈哈。

相關文章

QQ平臺配置

微信平臺配置

nexus3.x私服配置(windows版)

AndroidGoogle應用內支付