Android开发Android开发经验谈Android技术知识

Android 海外应用内支付之ONE store(韩国支付SD

2019-05-30  本文已影响49人  Silence潇湘夜雨

前提:

之前写过一篇Android Google应用内支付,刚好公司的主营业务是海外app开发。前段时间也需要支持了韩国那边的应用内支付,给的文档上面讲了需要集成onestore支付。由于对韩国那边的应用内支付没有什么概念,所以就百度了一番。奈何,文档太少或者写的不是很清晰(难道能说是因为自己不太理解)。所以,自己就看着官方文档做了集成了一番,踩了不少坑。但是,总体来说,比较官方文档讲的比较清楚。好了,讲了这么多,下面看如何集成吧。

首先

onestore官方文档.png onestore官方文档集成.png image.png
注意:官方jar包下载地址在github上面,不清楚的可以点这里查看
jar下载地址.png
注意:上面红框标注的就是我们需要的jar包

其次

上面讲了那么多,那么看看官方文档的接入到底是个什么样子的呢?下面是目录截图


image.png

+1、在看到了上面的截图之后,我们这里讲一下支付的整个流程。

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;
}

}

Screenshot_20190520-104328.jpg
如上图所示:只有点击close的时候才能支付成功,直接退出app是支付取消处理。

最后

补充一点,接入onestore支付的一般是面相韩国那边的app,并且大多数是游戏app,那么,就牵扯到了横屏。那么,onestore有没有横屏处理呢?当然了,在Manifest中配置就可以了。

image.png
注意:横屏的话用popup就可以了,不用选full,如果竖屏呢,写成full或者不写都是可以的。

好了,以上就是这次onestore的应用内开发,如果有什么不懂的地方,或者写的不好的地方,欢迎指正。当然,也可以加群(493180098)问我

感谢

OneStore_Android_接入
可以看看,但是讲的不是特别详细,还是要感谢上面那篇文章的作者。

感悟

通过这次接入ONE store的工作完成后,充分认识了自己。以前觉得百度上找不到文档,就感觉特别慌,有点无所适从。但是,通过这次ONE store的开发,觉得,也就那样。生活本来就是这样的,都是摸着石头过河。鲁迅先生还说过:世上本没有路,走的人多了便有了路。收拾好心情,继续努力吧,少年,哈哈哈哈。

上一篇下一篇

猜你喜欢

热点阅读