Android华为SDK接入之——联运游戏开发SDK

最近应公司项目需求接入华为联运游戏开发的SDK,特记录接入过程的那些坑!!
官网接入文档:
后台申请的过程自行到官网查看,此处只介绍接入流程。
注意事项:
1.后台配置的SHA256必须要和打包的签名文件一致,debug也要用这个签名文件去跑
配置
SHA256生成:keytool -list -v -keystore + 秘钥绝对路径
在这里插入图片描述

2.包名要以.huawei后缀 例:com.xxx.huawei

接入流程:
1.将“agconnect-services.json”文件拷贝到应用级根目录下:
在这里插入图片描述
2. 在build.gradle(project)添加HUAWEI agcp插件以及Maven代码库:

buildscript {
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
    }
    dependencies {
        ...
        classpath 'com.huawei.agconnect:agcp:1.4.2.301'
    }
}
allprojects {
    repositories {
        google()
        jcenter()
        maven {url 'https://developer.huawei.com/repo/'}
    }
}

3.在build.gradle(app)添加依赖:

dependencies {
	......

    implementation 'com.huawei.hms:base:5.0.5.300'
    implementation 'com.huawei.hms:hwid:5.0.5.301'
    implementation 'com.huawei.hms:iap:5.0.4.301'
    implementation 'com.huawei.hms:game:5.0.4.302'
    implementation 'com.huawei.hms:hianalytics:5.0.5.301'
}

具体版本请参考官网
注意:如果还需接入华为Ads的不需要再加入广告接入文档中的base和ppskit,否则会闪退

4.在build.gradle(app)文件头apply plugin: 'com.android.application’下一行添加如下配置:

apply plugin: 'com.huawei.agconnect'

5.多语言设置:
参考官网语言设置:

6.混淆:
Android Studio开发环境里的配置文件是“proguard-rules.pro”

-ignorewarnings 
-keepattributes *Annotation* 
-keepattributes Exceptions 
-keepattributes InnerClasses 
-keepattributes Signature 
-keepattributes SourceFile,LineNumberTable 
-keep class com.hianalytics.**{*;} 
-keep class com.huawei.updatesdk.**{*;} 
-keep class com.huawei.hms.**{*;} 
-keep interface com.huawei.hms.analytics.type.HAEventType{*;}
-keep interface com.huawei.hms.analytics.type.HAParamType{*;}
-keep class com.huawei.hms.analytics.HiAnalyticsInstance{*;}
-keep class com.huawei.hms.analytics.HiAnalytics{*;}

初始化:
在Application的onCreate方法中添加如下代码,用于注册Activity的回调监听

public class MyApplication extends Application{ 
    @Override
    public void onCreate(){
        super.onCreate();
        HuaweiMobileServicesUtil.setApplication(this);
    }
}

Manifest文件中配置:
在这里插入图片描述

在Activity onCreate中调用:

private void init() {
    JosAppsClient appsClient = JosApps.getJosAppsClient(this);
    appsClient.init();
    Log.i(TAG, "init success");
}

登录:
1.账号登录:
MainActivity.LOGIN_CODE 为 8000

 public void signIn(Activity activity) {
        Log.e(TAG, "signIn----:");

        HuaweiIdAuthParams authParams = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams();
        HuaweiIdAuthService service = HuaweiIdAuthManager.getService(activity, authParams);
        activity.startActivityForResult(service.getSignInIntent(), MainActivity.LOGIN_CODE);
    }

登录授权完成后处理登录结果:

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        //授权登录结果处理,从AuthHuaweiId中获取Authorization Code
        super.onActivityResult(requestCode, resultCode, data);
        if (LOGIN_CODE == requestCode) {
            Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
            if (authHuaweiIdTask.isSuccessful()) {
                //登录成功,获取用户的华为帐号信息和Authorization Code
                AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
                HuaWeiSDKController.getInstance().getCurrentPlayer(this);//登录成功,获取playerId
            } else {
                //登录失败
                Log.e(TAG, "sign in failed:" + ((ApiException) authHuaweiIdTask.getException()).getStatusCode());
            }
        }
    }

2.静默登录:

/**
     * sdk静默登录
     * @param activity
     */
    public void silenceIn(final Activity activity){
        Log.e(TAG, "login---");

        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                authParams = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).createParams();
                HuaweiIdAuthService service = HuaweiIdAuthManager.getService(activity, authParams);
                Task<AuthHuaweiId> task = service.silentSignIn();
                task.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
                    @Override
                    public void onSuccess(AuthHuaweiId authHuaweiId) {
                        //获取华为帐号信息
                        Log.e(TAG, "displayName:" + authHuaweiId.getDisplayName());
                        getCurrentPlayer(activity);//登录成功,获取playerId
                    }
                });
                task.addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(Exception e) {
                        //登录失败,您可以尝试使用getSignInIntent()方法显式登录
                        if (e instanceof ApiException) {
                            ApiException apiException = (ApiException)e;
                            Log.e(TAG, "sign failed status:" + apiException.getStatusCode());
                            /**
                             * 静默登录失败
                             * 调用显式登录
                             */
                            signIn(activity);
                        }
                    }
                });
            }
        });
    }

3.登录成功后,从Player对象中获取玩家信息:

/**
     * 登录成功后调
     * 获取当前登录的玩家对象
     * 从Player对象中获取玩家信息。
     * @param activity
     */
    public void getCurrentPlayer(Activity activity) {
        //PlayersClientImpl playersClient = (PlayersClientImpl) Games.getPlayersClient(activity);

        Task<Player> task = client.getCurrentPlayer();
        task.addOnSuccessListener(new OnSuccessListener<Player>() {
            @Override
            public void onSuccess(Player player) {
                playerId = player.getPlayerId();
                EnterGame();//进入游戏,调用防沉迷监听,事件上传
                WeakReference_Timer.getInstance().intentTimer(activity);//每15分钟调一次
                Log.e(TAG,"onSuccess--playerId = "+player.getPlayerId());
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                if (e instanceof ApiException) {
                    String result = "rtnCode:" + ((ApiException) e).getStatusCode();
                    Log.e(TAG,"onFailure--result = "+result);
                }
            }
        });
    }

浮标:
注意事项

发布地区包含中国大陆的联运游戏,要求必须集成游戏浮标,游戏的所有界面都需要展示游戏浮标。
对于EMUI 9.1.1以下版本的华为设备,您需要按照本文档要求集成游戏浮标,并且设备必须安装9.0以上版本华为应用市场客户端才能展示浮标。

对于EMUI 9.1.1及以上版本的华为设备,HMS Core(APK)会自动展示游戏浮标而忽略本接口的请求,您无需关注

BuoyClient 实例:

BuoyClient buoyClient = Games.getBuoyClient(this);

展示浮标:

 buoyClient.showFloatWindow();

隐藏浮标:

buoyClient.hideFloatWindow();

防沉迷:

/**
     * 检测是否已实名
     */
    public void preAddiction(){
        Log.e(TAG, "PreAddiction---");
        client.setGameTrialProcess(new GameTrialProcess() {
            @Override
            public void onTrialTimeout() {
                //试玩时间结束
                Log.e(TAG, "PreAddiction---onTrialTimeout");
            }
            @Override
            public void onCheckRealNameResult(boolean hasRealName) {
                Log.e(TAG, "PreAddiction---hasRealName = "+hasRealName);
                if (hasRealName) {
                    // 已实名,继续后续的游戏登录处理
                    return;
                }
                //未实名,建议您提示玩家后退出游戏或引导玩家重新登录并实名认证
            }
        });
    }

防沉迷监控:

/**
     * 防沉迷监听
     * 事件上传
     * 进入游戏
     */
    public void EnterGame(){
        Log.e(TAG, "EnterGame---");
        if (TextUtils.isEmpty(playerId)) {
            Log.e(TAG, "GetCurrentPlayer first.");
            return;
        }
        String uid = UUID.randomUUID().toString();
        Task<String> task = client.submitPlayerEvent(playerId, uid, "GAMEBEGIN");
        task.addOnSuccessListener(new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String jsonRequest) {
                try {
                    JSONObject data = new JSONObject(jsonRequest);
                    sessionId = data.getString("transactionId");
                } catch (JSONException e) {
                    Log.e(TAG,"parse jsonArray meet json exception");
                    return;
                }
                Log.e(TAG,"submitPlayerEvent traceId: " + jsonRequest);
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                if (e instanceof ApiException) {
                    String result = "rtnCode:" + ((ApiException) e).getStatusCode();
                    Log.e(TAG,"EnterGame--"+result);
                }
            }
        });
    }
    
	/**
     * 防沉迷监听
     * 事件上传
     * 退出游戏
     */
    public void ExitGame(){
        if (TextUtils.isEmpty(playerId)) {
            Log.e(TAG,"GetCurrentPlayer first.");
            return;
        }
        if (TextUtils.isEmpty(sessionId)) {
            Log.e(TAG,"SessionId is empty.");
            return;
        }

        Task<String> task = client.submitPlayerEvent(playerId, sessionId, "GAMEEND");
        task.addOnSuccessListener(new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String s) {
                Log.e(TAG,"submitPlayerEvent traceId: " + s);
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                if (e instanceof ApiException) {
                    String result = "rtnCode:" + ((ApiException) e).getStatusCode();
                    Log.e(TAG,"ExitGame--"+result);
                }
            }
        });
    }

内支付
MainActivity.PAYMENT_CODE 为 6000

/**
     * 判断是否支持应用内支付
     * @param activity
     */
    public void isSupportPay(final Activity activity){
        Log.e(TAG, "pay---");
        // 获取调用接口的Activity对象
        Task<IsEnvReadyResult> task = Iap.getIapClient(activity).isEnvReady();
        task.addOnSuccessListener(new OnSuccessListener<IsEnvReadyResult>() {
            @Override
            public void onSuccess(IsEnvReadyResult result) {
                // 获取接口请求的结果
                Log.e(TAG, "isSupportPay-支持应用内支付---");
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                if (e instanceof IapApiException) {
                    IapApiException apiException = (IapApiException) e;
                    Status status = apiException.getStatus();
                    if (status.getStatusCode() == OrderStatusCode.ORDER_HWID_NOT_LOGIN) {
                        // 未登录帐号
                        Log.e(TAG, "isSupportPay-登录帐号---");
                        if (status.hasResolution()) {
                            try {
                                // 启动IAP返回的登录页面
                                Log.e(TAG, "isSupportPay-启动IAP返回的登录页面---");
                                status.startResolutionForResult(activity, MainActivity.PAYMENT_CODE);
                            } catch (IntentSender.SendIntentException exp) {
                            }
                        }
                    } else if (status.getStatusCode() == OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED) {
                        // 用户当前登录的华为帐号所在的服务地不在华为IAP支持结算的国家或地区中
                        Log.e(TAG, "isSupportPay-不在华为IAP支持结算的国家或地区中---");
                    }
                }
            }
        });
    }
    
	/**
     * 发起购买
     * @param activity
     */
    public void buyProduct(Activity activity){
        // 构造一个PurchaseIntentReq对象
        PurchaseIntentReq req = new PurchaseIntentReq();
        // 通过createPurchaseIntent接口购买的商品必须是您在AppGallery Connect网站配置的商品。
        req.setProductId("CProduct1");
        // priceType: 0:消耗型商品; 1:非消耗型商品; 2:订阅型商品
        req.setPriceType(0);
        req.setDeveloperPayload("test");
        // 调用createPurchaseIntent接口创建托管商品订单
        Task<PurchaseIntentResult> task = Iap.getIapClient(activity).createPurchaseIntent(req);
        task.addOnSuccessListener(new OnSuccessListener<PurchaseIntentResult>() {
            @Override
            public void onSuccess(PurchaseIntentResult result) {
                // 获取创建订单的结果
                Status status = result.getStatus();
                if (status.hasResolution()) {
                    try {
                        // 启动IAP返回的收银台页面
                        Log.e(TAG, "buyProduct-启动IAP返回的收银台页面---");
                        status.startResolutionForResult(activity, MainActivity.PAYMENT_CODE);
                    } catch (IntentSender.SendIntentException exp) {
                    }
                }
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                if (e instanceof IapApiException) {
                    IapApiException apiException = (IapApiException) e;
                    Status status = apiException.getStatus();
                    int returnCode = apiException.getStatusCode();
                    Log.e(TAG, "buyProduct-onFailure---"+returnCode);
                } else {
                    // 其他外部错误
                    Log.e(TAG, "buyProduct-其他外部错误---");
                }
            }
        });
    }
    
	/**
     * 确认交易
     * @param activity
     * @param inAppPurchaseData 购买信息
     */
    public void confirmBuy(Activity activity,String inAppPurchaseData){
        // 构造ConsumeOwnedPurchaseReq对象
        ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
        String purchaseToken = "";
        try {
            // purchaseToken需从购买信息InAppPurchaseData中获取
            InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseData);
            purchaseToken = inAppPurchaseDataBean.getPurchaseToken();
        } catch (JSONException e) {
        }
        req.setPurchaseToken(purchaseToken);
        // 消耗型商品发货成功后,需调用consumeOwnedPurchase接口进行消耗
        Task<ConsumeOwnedPurchaseResult> task = Iap.getIapClient(activity).consumeOwnedPurchase(req);
        task.addOnSuccessListener(new OnSuccessListener<ConsumeOwnedPurchaseResult>() {
            @Override
            public void onSuccess(ConsumeOwnedPurchaseResult result) {
                // 获取接口请求结果
                Log.e(TAG, "confirmBuy-获取接口请求结果---");
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                if (e instanceof IapApiException) {
                    IapApiException apiException = (IapApiException) e;
                    Status status = apiException.getStatus();
                    int returnCode = apiException.getStatusCode();
                    Log.e(TAG, "confirmBuy-onFailure---"+returnCode);
                } else {
                    // 其他外部错误
                    Log.e(TAG, "confirmBuy-其他外部错误---");
                }
            }
        });
    }

以上,此处接入流程已完成!
SDK中还有其它功能接入,具体按项目需求自行接入,此处就不再做详细介绍了。
最后附上完整demo
华为ads sdk 完整demo
谢谢!!!

/** * 初始化SDK */ private static void initSDK(String appid, String appkey) { ProxySelector defaultProxySelector = ProxySelector.getDefault(); Proxy proxy = null; List<Proxy> proxyList = null; try { proxyList = defaultProxySelector.select(new URI( "http://www.google.it")); } catch (URISyntaxException e) { e.printStackTrace(); } if (proxyList != null && proxyList.size() > 0) { proxy = proxyList.get(0); Log.d(TAG, "Current Proxy Configuration: " + proxy.toString()); } AppInfo appInfo = new AppInfo(); appInfo.setAppId(appid);// 应用ID appInfo.setAppKey(appkey);// 应用Key appInfo.setCtx(ctx); /* * VersionCheckLevelNormal 版本检查失败可以继续进行游戏 VersionCheckLevelStrict * 版本检查失败则不能进入游戏 默认取值为VersionCheckLevelStrict */ appInfo.setVersionCheckStatus(AppInfo.VERSION_CHECK_LEVEL_STRICT); // 初始化SDK Commplatform.getInstance().Init(0, appInfo, new CallbackListener<Integer>() { @Override public void callback(final int paramInt, Integer paramT) { ctx.runOnUiThread(new Runnable() { @Override public void run() { Log.i(TAG, "Init paramInt = " + paramInt); // ok.setEnabled(true); LogUtil.send("初始化: " + paramInt); } }); } }); } /** * 用户登录 * */ public static void loginEx(Context context) { Bundle bundle = new Bundle(); bundle.putString("nounce", UUID.randomUUID().toString() .replace("-", "")); Commplatform.getInstance().LoginEx(context, bundle, new CallbackListener<Bundle>() { @Override public void callback(int resultCode, Bundle bundle) { if (resultCode == ErrorCode.COM_PLATFORM_SUCCESS) { // 完成参数验签 // 处理登录成功逻辑 // HomeActivity.show(ctx); // String uin= bundle.getString("uin"); LogUtil.send("登录成功 :"); // String uin= bundle.get("nounce").toString(); String uin = Commplatform.getInstance() .getLoginUin(); LogUtil.send("登录成功 uin :" + uin); UnityPlayer.UnitySendMessage("MainScript", "HuaweiLoginBack", uin); } else { // 处理登录失败逻辑 LogUtil.send("登录失败"); } } }); } /** * 充值 * */ public static int pay(String currency) { isPaying = true; Payment payment = new Payment(); ProductBean productBean = productMap.get(currency); makeSerial(); payment.setTradeNo(orderKen); payment.setProductId(productBean.getProductId()); payment.setSubject(productBean.getTitle()); payment.setDesc(productBean.getDescription()); payment.setAmount(productBean.getPrice_amount()); payment.setCurrency(productBean.getPrice_currency_code()); payment.setNote(""); payment.setNotifyURL(""); payment.setThirdAppId(id); payment.setThirdAppName("Spot Battle"); payment.setThirdAppPkgname("com.testcrecool.mi"); final String tradeNo = payment.getTradeNo(); final String productId = payment.getProductId(); // 将订单的详细信息插入数据库 PaymentTableAdapter.insert(ctx, payment); int res = Commplatform.getInstance().UniPayExt(payment, ctx, new CallbackListener<PayResult>() { @Override public void callback(final int code, final PayResult arg1) { ctx.runOnUiThread(new Runnable() { public void run() { // 回调结果,即支付过程结束 isPaying = false; LogUtil.send("回调结果,即支付过程结束 code: " + code); if (code == ErrorCode.COM_PLATFORM_SUCCESS) { // TODO Example 1 // 根据final 的 productID 或者 orderId // 去处理商品,比如查询道具,发放道具等 // TODO Example 2 // 可以根据订单号查询订单详细信息,在做订单的处理,比如查询道具,发放道具等 // 如下: Payment payment = PaymentTableAdapter .queryByOrderId(ctx, orderKen); // 购买有结果,即删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx, orderKen); Log.i(TAG, "COM_PLATFORM_SUCCESS"); LogUtil.send("成功"); UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", orderKen); } else if (code == ErrorCode.COM_PLATFORM_ERROR_PAY_FAILURE) { Log.i(TAG, "COM_PLATFORM_ERROR_PAY_FAILURE"); } else if (code == ErrorCode.COM_PLATFORM_ERROR_PAY_CANCEL) { // 购买失败 // 购买有结果,即删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx, tradeNo); Log.i(TAG, "COM_PLATFORM_ERROR_PAY_CANCEL"); UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", "error"); } else if (code == ErrorCode.COM_PLATFORM_ERROR_PAY_CANCEL) { LogUtil.send("取消购买"); // 取消购买 // 购买有结果,即删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx, tradeNo); UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", "error"); } else { LogUtil.send("Purchase failed. Error code:" + code); Log.i(TAG, "COM_PLATFORM_ERROR_UNKNOWN"); } } }); } }); if (res == 0) { LogUtil.send("000"); return 0; } else { // 返回错误,即支付过程结束 isPaying = false; LogUtil.send("返回错误,即支付过程结束"); return -1; } } /** * 获取应用内商品信息 **/ public static void querySkuDetail() { Commplatform.getInstance().getSkuDetails(ctx, new CallbackListener<List<SkuDetail>>() { @Override public void callback(int errorCode, List<SkuDetail> skuDetails) { if (errorCode == ErrorCode.COM_PLATFORM_SUCCESS && skuDetails != null) { for (SkuDetail detail : skuDetails) { ProductBean prBean = new ProductBean(); prBean.setProductId(detail.productId); prBean.setPrice(detail.price); prBean.setPrice_amount(detail.price_amount); prBean.setPrice_currency_code(detail.price_currency_code); prBean.setTitle(detail.title); prBean.setDescription(detail.description); productMap.put(detail.productId, prBean); } // showText.setText(buffer.toString()); LogUtil.send("获取应用内商品信息"); } else { // showText.setText("query error"); LogUtil.send("query error"); } } }); } /** * 生成订单号 * */ private static String makeSerial() { // 生成订单号 orderKen = UUID.randomUUID().toString().replaceAll("-", ""); return orderKen; } // 同步支付订单的漏单查询接口调用 private static void checkPay(final Payment paymentSerial) { QueryPayment queryPayment = new QueryPayment(); queryPayment.setTradeNo(paymentSerial.getTradeNo()); queryPayment.setThirdAppId(paymentSerial.getThirdAppId()); final String tradeNo= queryPayment.getTradeNo(); Commplatform.getInstance().queryPayment(queryPayment, ctx, new CallbackListener<PaymentState>() { @Override public void callback(int paramInt, PaymentState paramT) { if (paramInt == ErrorCode.COM_PLATFORM_SUCCESS) { // Step2:订单查询成功 从数据库删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx,tradeNo); // 订单支付成功,可以根据订单号查询订单详细信息,在做订单的处理,比如查询道具,发放道具等 // TODO… 游戏代码 LogUtil.send("漏单查询成功!!!"); UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", orderKen); } else if (paramInt == ErrorCode.COM_PLATFORM_ERROR_UNEXIST_ORDER) { // Step2:订单不存在 从数据库删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx, tradeNo); // 根据游戏自身的体验决定如何处理 UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", "error"); } else if (paramInt == ErrorCode.COM_PLATFORM_ERROR_PAY_FAILURE) { // Step2:订单支付失败 从数据库删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx, tradeNo); // 根据游戏自身的体验决定如何处理 UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", "error"); } else if (paramInt == ErrorCode.COM_PLATFORM_ERROR_SERVER_RETURN_ERROR) { // Step2:服务端返回错误 从数据库删除此订单号 PaymentTableAdapter.deleteByOrderId(ctx, tradeNo); // 根据游戏自身的体验决定如何处理 UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", "error"); } else if (paramInt == ErrorCode.COM_PLATFORM_ERROR_PAY_REQUEST_SUBMITTED) { // 订单已提交 // 根据游戏自身的体验决定如何处理 // 后续还需要继续查询 UnityPlayer.UnitySendMessage("MainScript", "hwPayCallback", "error"); } else { // 未知错误 // 根据游戏自身的体验决定如何处理 // 后续还需要继续查询 } } }); }
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值