韩国ONE store平台支付集成(android)

前端时间接入韩国Onestore平台的时候踩了一些坑,国内的文档又很少,记录一下,废话不多说。我们开始吧。

官方接入文档

官方文档有中英韩三种语言,可移步官方接入文档

开始集成

1】目前的新版本是V5,直接放V5的jar包。从gizub下载OneStore在应用程序中购买SDK
2】清单文件添加

//(必需)添加API版本:它需要显式声明IAPV 5
<meta-data
     android:name="iap:api_version"
     android:value="5" />
//(可选)指定支付屏幕UI:API版本5支持弹出格式中的支付屏幕UI。如果在下面的iap:view_选项中输入弹出值,则支付窗口将弹出。如果没有设置值,默认情况下它将被视为全屏(Android:value=“Full”)。
<meta-data
    android:name="iap:view_option"
    android:value="popup | full" >

支付逻辑集成

一、集成步骤

1】初始化API并绑定One Store服务
2】绑定成功,检查是否支持onestore支付,如果支持,继续
3】检查未完成的订单(掉单的情况处理),如果有执行验单,消单逻辑(如果是游戏,也可放到进入游戏后进行)
4】验单:玩家请求支付,支付成功后将有效数据发给服务器进行验证(本地也可进行验证,详见代码)
5】消单:服务器验证成功,告知客户端,客户端进行消单,服务器发送奖励

二、集成代码

初始化,绑定

	//需要引入的头文件
	import com.onestore.iap.api.IapEnum;
	import com.onestore.iap.api.IapResult;
	import com.onestore.iap.api.ProductDetail;
	import com.onestore.iap.api.PurchaseClient;
	import com.onestore.iap.api.PurchaseData;

    // onestore 渠道参数配置
    private static final int IAP_API_VERSION = 5;
    private static final int COIN_PER_GAME = 5;
    private static final int PURCHASE_REQUEST_CODE = 1000;
    private static final int LOGIN_REQUEST_CODE = 2000;
    private static final String PRODUCT_TYPE = IapEnum.ProductType.IN_APP.getType(); // "inapp"
    
//此处在你的初始化方法中执行
{
        // PurchaseClient 初始化——将公钥作为参数传递,以验证context和Signature。
        mPurchaseClient = new PurchaseClient(this, mPublicKey);

        // 请求绑定ONE store服务,以启动应用内支付。
        mPurchaseClient.connect(mServiceConnectionListener);
}

/*
     * PurchaseClient的 connect API 回调监听器
     * 返回绑定成功或失败以及是否要更新ONE store服务的结果。
     */
    PurchaseClient.ServiceConnectionListener mServiceConnectionListener = new PurchaseClient.ServiceConnectionListener() {
        @Override
        public void onConnected() {
            Log.d(TAG, "Service connected");
            checkBillingSupportedAndLoadPurchases();
        }
     
        @Override
        public void onDisconnected() {
            Log.d(TAG, "Service disconnected");
        }
     
        @Override
        public void onErrorNeedUpdateException() {
            Log.e(TAG, "connect onError, 需要更新ONE store客户端 ");
            updateOrInstallOneStoreService();
        }
    };

检查是否支持

private void checkBillingSupportedAndLoadPurchases() {
        Log.d(TAG, "checkBillingSupportedAndLoadPurchases()");

        if (mPurchaseClient == null) {
            Log.d(TAG, "PurchaseClient is not initialized");
            return;
        }

        // 方法名字中以postfix加上“Async”,UI线程中为保证安全执行,创建线程处理
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 先执行 查询是否支持应用内支付
                mPurchaseClient.isBillingSupportedAsync(IAP_API_VERSION, mBillingSupportedListener);
            }
        });

    }
    /*
     * PurchaseClient的isBillingSupportedAsync (查询是否支持)回调监听器
     */
    PurchaseClient.BillingSupportedListener mBillingSupportedListener = new PurchaseClient.BillingSupportedListener() {
     
        @Override
        public void onSuccess() {
            Log.d(TAG, "isBillingSupportedAsync onSuccess");
            isBillingSupported = true;
            //检查掉单的情况
            // queryPurchase(IapEnum.ProductType.IN_APP);
        }
     
        @Override
        public void onError(IapResult result) {
            Log.e(TAG, "isBillingSupportedAsync onError, " + result.toString());
            if (IapResult.RESULT_NEED_LOGIN == result) {
                loadLoginFlow();
            }
        }
     
        @Override
        public void onErrorRemoteException() {
            Log.e(TAG, "isBillingSupportedAsync onError, 无法连接ONE store服务");
        }
     
        @Override
        public void onErrorSecurityException() {
            Log.e(TAG, "isBillingSupportedAsync onError, 应用状态异常下请求支付");
        }
     
        @Override
        public void onErrorNeedUpdateException() {
            Log.e(TAG, "isBillingSupportedAsync onError, 需要更新ONE store客户端");
            updateOrInstallOneStoreService();
        }
    };

发起支付

String devPayload = AppSecurity.generatePayload();
//参数大多数在开头部分说明了,单独说明第9个参数false:是否支持onestore 促销活动
mPurchaseClient.launchPurchaseFlowAsync(IAP_API_VERSION, this, PURCHASE_REQUEST_CODE, goods_id, goods_name,
                                            PRODUCT_TYPE, devPayload_, role_id, false, mPurchaseFlowListener);
                                            
	/*
    * PurchaseClient的 launchPurchaseFlowAsync API (购买)回调监听器
     */
    PurchaseClient.PurchaseFlowListener mPurchaseFlowListener = new PurchaseClient.PurchaseFlowListener() {
        @Override
        public void onSuccess(PurchaseData purchaseData) {
            String logStr = "pay success, purchaseData: " + purchaseData.toString();
            reportErrorToServer(Code_PayResult, logStr);
            // 购买成功后客户端检查开发者payload。
            // if (!isValidPayload(purchaseData.getDeveloperPayload())) {
            //     Log.d(TAG, "launchPurchaseFlowAsync onSuccess, Payload is not valid.");
            //     return;
            // }
     
            // 购买成功后客户端检查签名。
            // boolean validPurchase = AppSecurity.verifyPurchase(purchaseData.getPurchaseData(), purchaseData.getSignature(), mPublicKey);
            // if (validPurchase) {
                // 管理型商品(inapp)购买成功后消耗。
                verifyPurchaseToServerAndConsumeItem(purchaseData);
            // } else {
            //     Log.d(TAG, "launchPurchaseFlowAsync onSuccess, Signature is not valid.");
            //     return;
            // }
        }
     
        @Override
        public void onError(IapResult result) {
            if(result == IapResult.RESULT_NEED_LOGIN ) {
            	//需要登陆onestore
                loadLoginFlow();
            }
            else if(result == IapResult.RESULT_ITEM_UNAVAILABLE){
                LogD("purchase failed , item unavailable");
            }
            else if(result == IapResult.RESULT_ITEM_ALREADY_OWNED){
                LogD("purchase failed , item ALREADY_OWNED");
                //掉单了
                queryPurchase(IapEnum.ProductType.IN_APP);
            }
            else{
            }
        }
     
        @Override
        public void onErrorRemoteException() {
            Log.e(TAG, "queryPurchasesAsync onError, 无法连接ONE store服务");
        }

        @Override
        public void onErrorSecurityException() {
            Log.e(TAG, "queryPurchasesAsync onError, 应用状态异常下请求支付");
        }

        @Override
        public void onErrorNeedUpdateException() {
            Log.e(TAG, "queryPurchasesAsync onError, 需要更新ONE store客户端 ");
            updateOrInstallOneStoreService();
        }
    };

服务器验单(略) + 客户端消单

private void verifyPurchaseToServerAndConsumeItem(final PurchaseData purchaseData) {
        if (mPurchaseClient == null) {
            Log.d(TAG, "PurchaseClient is not initialized");
            return;
        }
        ......
        //服务器验单成功,执行消耗
        mPurchaseClient.consumeAsync(IAP_API_VERSION, purchaseData, mConsumeListener);
}

	/*
     * PurchaseClient的 consumeAsync API (商品消耗)回调监听器
     */
    PurchaseClient.ConsumeListener mConsumeListener = new PurchaseClient.ConsumeListener() {
        @Override
        public void onSuccess(PurchaseData purchaseData) {
            Log.d(TAG, "consumeAsync onSuccess, " + purchaseData.toString());
            // 商品消耗成功后,按各开发者编写的购买成功方案进行。
        }
     
        @Override
        public void onErrorRemoteException() {
            Log.e(TAG, "consumeAsync onError, 无法连接ONE store服务");
        }
     
        @Override
        public void onErrorSecurityException() {
            Log.e(TAG, "consumeAsync onError, 应用状态异常下请求支付");
        }
     
        @Override
        public void onErrorNeedUpdateException() {
            Log.e(TAG, "consumeAsync onError, 需要更新ONE store客户端 ");
            updateOrInstallOneStoreService();
        }
     
        @Override
        public void onError(IapResult result) {
            Log.e(TAG, "consumeAsync onError, " + result.toString());
        }
    }; 

其他代码里用到的方法

	//未登陆onestore调用
	private void loadLoginFlow() {
        if (mPurchaseClient == null) {
            Log.d(TAG, "PurchaseClient is not initialized");
            return;
        }

        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mPurchaseClient.launchLoginFlowAsync(IAP_API_VERSION, mActivity, LOGIN_REQUEST_CODE, mLoginFlowListener) == false) {
                    // listener is null
                }
            }
        });
    }
    
	//查询订单
	private void queryPurchase(final IapEnum.ProductType productType) {
        Log.d(TAG, "queryPurchase() :: productType - " + productType.getType());

        if (mPurchaseClient == null) {
            Log.d(TAG, "PurchaseClient is not initialized");
            return;
        }

        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mPurchaseClient.queryPurchasesAsync(IAP_API_VERSION, PRODUCT_TYPE, 		mQueryPurchaseListener);
            }
        });
    }
    
	/*
     * PurchaseClient的queryPurchasesAsync API (查询购买记录)回调监听器
     */
    PurchaseClient.QueryPurchaseListener mQueryPurchaseListener = new PurchaseClient.QueryPurchaseListener() {
        @Override
        public void onSuccess(List<PurchaseData> purchaseDataList, String productType) {
            Log.d(TAG, "queryPurchasesAsync onSuccess, " + purchaseDataList.toString());
            if (IapEnum.ProductType.IN_APP.getType().equalsIgnoreCase(productType)) {
                onLoadPurchaseInApp(purchaseDataList);
            } else if (IapEnum.ProductType.AUTO.getType().equalsIgnoreCase(productType)) {
                // 如为查询购买记录后获取的包月自动支付商品( auto),先验证签名,成功后根据开发者应用处理需求编写方案。
            }
        }
        @Override
        public void onErrorRemoteException() {
            Log.e(TAG, "queryPurchasesAsync onError, 无法连接ONE store服务");
        }
     
        @Override
        public void onErrorSecurityException() {
            Log.e(TAG, "queryPurchasesAsync onError, 应用状态异常下请求支付");
        }
     
        @Override
        public void onErrorNeedUpdateException() {
            Log.e(TAG, "queryPurchasesAsync onError, 需要更新ONE store客户端 ");
            updateOrInstallOneStoreService();
        }
     
        @Override
        public void onError(IapResult result) {
            Log.e(TAG, "queryPurchasesAsync onError, " + result.toString());
        }
    };
    
	//掉单消耗
	private void onLoadPurchaseInApp(List<PurchaseData> purchaseDataList) {
        for (PurchaseData purchase : purchaseDataList) {  
        	......
            verifyPurchaseToServerAndConsumeItem(purchase);
        }
    }
    
    //商店更新
    private void updateOrInstallOneStoreService() {
        PurchaseClient.launchUpdateOrInstallFlow(mActivity);
    }

onActivityResult

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);
        LogD("onActivityResult: " + requestCode + " resultCode: " + resultCode);

        switch (requestCode) {
            case LOGIN_REQUEST_CODE:
                if (resultCode == Activity.RESULT_OK) {
                    if (mPurchaseClient.handleLoginData(data) == false) {
                        Log.e(TAG, "onActivityResult handleLoginData false ");
                        // listener is null
                    }
                } else {
                    Log.e(TAG, "onActivityResult user canceled");
                }
                break;
            case PURCHASE_REQUEST_CODE:
                /*
                 * 调用 launchPurchaseFlowAsync API 时收到的intent数据通过handlePurchaseData解析返回值。
                 * 解析后返回结果通过调用 launchPurchaseFlowAsync 时的 PurchaseFlowListener 返回。
                 */
                if (resultCode == Activity.RESULT_OK) {
                    if (mPurchaseClient.handlePurchaseData(data) == false) {
                        Log.e(TAG, "onActivityResult handlePurchaseData false ");
                        // listener is null
                        reportErrorToServer(Code_CommonLog, "listener is null");
                    }
                } else {
                    Log.e(TAG, "onActivityResult user canceled");
                    reportErrorToServer(Code_CommonLog, "onActivityResult user canceled");
                    // user canceled , do nothing..
                }
                break;
            default:
        }
    }
AppSecurity,这个是案例里有的一个类,可以拿来用。

官方案例

三、集成问题

1、无法拉起支付
1.1】手机必须安装最新版本的OneStore客户端,否则无法支付,自备梯子去拿,分享目前最新版连接
提取码:w0nu
1.2】手机需要翻墙,并且能够进入onestore客户端(可在初始化回调里面查看错误码知晓)
1.3】手机必须支持onestore支付(可在查询是否支持的回调里面查看错误码知晓)
1.4】查看支付回调错误码,常见的错误类型详见代码示例
1.5】查询购买记录,并且进行消费(主要是针对管理型商品(inapp))。注意:如果不进行消费是不能进行购买请求的1

2、 掉单的问题
如果代码逻辑正常,这种情况一般发生在玩家支付过程中网络中断、强退应用等情况。出现这种情况会导致2.1】玩家支付成功,但是没有收到相应的奖励。–未完成验单
2.2】玩家支付成功,没收到奖励,并且还不能继续购买这个商品。–未消单
俗称掉单。掉单了,如果无法处理,我们就只能补单。
为了减少补单的情况发生(总会有掉单),我们需要进行一些规避的方法。在应用启动后或者在发生无法继续购买商品时,重新查询当前是否存在未消耗的商品,如果存在,继续执行验单和消单逻辑,

3、极端操作的情况
3.1】新版本中无法传递透传字段(可以试下payload传递是否可行)
我们项目内商品的分类比较特殊,如果玩家连续多次点击多个不同的商品,会导致错单的情况(订单号跟商品无法匹配),这就需要特殊处理,各位大佬一般用不着,本文不再赘述。


  1. 此处内容参考https://blog.csdn.net/Silencemuyi/article/details/90708000,写的也很详细,想必也是被坑的厉害。 ↩︎

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值